diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b983c7..e9ace83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ # Changelog -## [1.0.3] - 2022 April 4 -* Updated igraph headers using igraph version [v0.9.7](https://github.com/igraph/igraph/releases/tag/0.9.7). Related discussion for bug found [here](https://github.com/kharchenkolab/leidenAlg/issues/9). -## [1.0.2] - 2022 March 3 -* Modified the Makevars file to correctly add an rpath (Runpath Search Path) for Mac OS users with `install_name_tool`. From the manual, the command used is `-change old new` whereby this "Changes the dependent shared library install name old to new in the specified Mach-O binary. More than one of these options can be specified. If the Mach-O binary does not contain the old install name in a specified -change option the option is ignored." +## [1.0.3] - 2022 April 08 +* Install rigraph directly in `/src`. Remove previous linking to `igraph` R package in Makevars. + +## [1.0.2] - 2022 March 03 +* Fixed Makevars to use `install_name_tool -change`. ## [1.0.1] - 2021 Dec 03 * Modified the Makevars to use SHLIB_EXT to account for both shared library extensions on Mac OS (either *.so or *dylib) @@ -12,13 +13,9 @@ ## [1.0.0] - 2021-11-19 * `leidenAlg` has gone through no major revisions in over a year. In order to avoid any confusion, this should be released with a major version. - ## [0.1.1] - 2021-03-02 - * Fixed issue with unweighted graph. In this case, we set all edge weights to 1. - ## [0.1.0] - 2020-11-11 - * Version published on CRAN: https://cran.r-project.org/web/packages/leidenAlg/index.html -* Tagged version on github released on 2 Jan 2021: https://github.com/kharchenkolab/leidenAlg/releases/tag/0.1.0 \ No newline at end of file +* Tagged version on github released on 2 Jan 2021: https://github.com/kharchenkolab/leidenAlg/releases/tag/0.1.0 diff --git a/DESCRIPTION b/DESCRIPTION index 95c6d53..28d4526 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -8,10 +8,11 @@ License: GPL-3 Copyright: See the file COPYRIGHTS for various leidenAlg copyright details Encoding: UTF-8 LazyData: true -Depends: R (>= 3.5.0), Matrix, igraph +Depends: R (>= 3.5.0), Matrix Imports: graphics, grDevices, + igraph, Matrix.utils, parallel, Rcpp (>= 1.0.5), @@ -21,7 +22,7 @@ Suggests: pbapply, testthat (>= 3.1.0) LinkingTo: Rcpp, RcppArmadillo, RcppEigen -SystemRequirements: GNU make +SystemRequirements: C++11, GNU make (optional), libxml2 (optional), glpk (>= 4.57, optional) RoxygenNote: 7.1.2 URL: https://github.com/kharchenkolab/leidenAlg BugReports: https://github.com/kharchenkolab/leidenAlg/issues diff --git a/README.md b/README.md index e8fda2b..62d6b08 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,12 @@ Implements the Leiden algorithm via an R interface -## Note: the Leiden clustering now in igraph -Since [October 2020](https://github.com/igraph/rigraph/pull/399), the R package [igraph](https://CRAN.R-project.org/package=igraph) contains the function `cluster_leiden()` implemented by Vincent Traag ([@vtraag](https://github.com/vtraag)). This function allows uses to find the community structure of a graph, as detailed in the original publication by [Traag et al, 2019](https://www.nature.com/articles/s41598-019-41695-z). The usage of this function is detailed in the igraph documentation [here](https://igraph.org/r/html/1.2.7/cluster_leiden.html). We recommend users use this function. +#### Note: cluster_leiden() now in igraph + +Since [October 2020](https://github.com/igraph/rigraph/pull/399), the R package [igraph](https://cran.r-project.org/package=igraph) contains the function `cluster_leiden()` implemented by Vincent Traag ([@vtraag](https://github.com/vtraag)). The usage of this function is detailed in the igraph documentation [here](https://igraph.org/r/html/1.2.7/cluster_leiden.html). We recommend users use this function. + +There is still no R package which entirely encompasses all of the functionality of the original Python/C++ implementation here from https://github.com/vtraag/leidenalg. We hope interested developers could use this package as a starting point for creating an R package which mirrors the full available functionality of the Python package. See [here](https://github.com/vtraag/leidenalg#usage) for details. ## Summary diff --git a/src/Makevars b/src/Makevars index b7d3cdc..3213dba 100644 --- a/src/Makevars +++ b/src/Makevars @@ -1,29 +1,29 @@ ## include OpenMP -PKG_CXXFLAGS= -I"../inst/include" -I"./leidenalg/igraph-R" -I"./leidenalg/include" $(SHLIB_OPENMP_CXXFLAGS) +PKG_CXXFLAGS= -I"../inst/include" -I"./rigraph/include" -I"./leidenalg/include" $(SHLIB_OPENMP_CXXFLAGS) -PKGB_PATH=`echo 'library(igraph); cat(system.file("libs", package="igraph"))' \ - | ${R_HOME}/bin/R --vanilla --slave` +PKG_CFLAGS = -pthread +PKG_CFLAGS += -I"./include" -I"../rigraph/include" +LDFLAGS += -lpthread -PKG_LIBS=-L/usr/lib/ -L"." -lpthread -lstdc++ -lleidenalg -lm `$(R_HOME)/bin/Rscript -e "Rcpp:::LdFlags()"` $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) $(SHLIB_OPENMP_CXXFLAGS) $(PKGB_PATH)/igraph$(SHLIB_EXT) + + +PKG_LIBS=-L/usr/lib/ -L"." -lpthread -lstdc++ -lleidenalg -lrigraph -lm `$(R_HOME)/bin/Rscript -e "Rcpp:::LdFlags()"` $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) $(SHLIB_OPENMP_CXXFLAGS) CXX_STD = CXX11 MkInclude = $(R_HOME)/etc${R_ARCH}/Makeconf #.PHONY: all sublibs #OBJECTS = $(.cpp=.o) -SUBDIRS = leidenalg -SUBLIBS = leidenalg.a - -IGRAPH_LIB = $(PKGB_PATH)/igraph$(SHLIB_EXT) +SUBDIRS = rigraph leidenalg +SUBLIBS = librigraph.a leidenalg.a -all: $(SHLIB) - if [ "$(OS)" != "Windows_NT" ] && [ `uname -s` = 'Darwin' ]; then install_name_tool -change 'igraph$(SHLIB_EXT)' $(PKGB_PATH)/igraph$(SHLIB_EXT) $(SHLIB); fi +all: $(SHLIB) -$(SHLIB): $(OBJECTS) sublibs +$(SHLIB): sublibs sublibs: sublibraries sublibraries: diff --git a/src/Makevars.win b/src/Makevars.win index aa9433b..056e1e4 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -1,34 +1,28 @@ ## include OpenMP -# detect R_arch -rarch := $(shell echo 'Sys.getenv("R_ARCH")' | ${R_HOME}/bin/R --vanilla --slave) +PKG_CXXFLAGS= -I"../inst/include" -I"./rigraph/include" -I"./leidenalg/include" $(SHLIB_OPENMP_CXXFLAGS) -PKGB_PATH := $(shell echo 'library(igraph); cat(system.file("libs", package="igraph"))' | ${R_HOME}/bin/R --vanilla --slave) +PKG_CFLAGS = -pthread +PKG_CFLAGS += -I"./include" -I"../rigraph/include" +LDFLAGS += -lpthread -## note: the space in '[1] "/i386" ' is important... -## else, '[1] "/x64" ' -ifeq ($(rarch),[1] "/i386" ) - IGRAPH_LIB := $(PKGB_PATH)/i386/igraph.dll -else - IGRAPH_LIB := $(PKGB_PATH)/x64/igraph.dll -endif -PKG_CXXFLAGS=-I"../inst/include" -I"./leidenalg/igraph-R" -I"./leidenalg/include" $(SHLIB_OPENMP_CXXFLAGS) - -PKG_LIBS=-L/usr/lib/ -L"." -lpthread -lstdc++ -lleidenalg -lm $(shell "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" -e "Rcpp:::LdFlags()") $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) $(SHLIB_OPENMP_CXXFLAGS) $(IGRAPH_LIB) +PKG_LIBS=-L/usr/lib/ -L"." -lpthread -lstdc++ -lleidenalg -lrigraph -lm `$(R_HOME)/bin/Rscript -e "Rcpp:::LdFlags()"` $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) $(SHLIB_OPENMP_CXXFLAGS) CXX_STD = CXX11 MkInclude = $(R_HOME)/etc${R_ARCH}/Makeconf #.PHONY: all sublibs #OBJECTS = $(.cpp=.o) -SUBDIRS = leidenalg -SUBLIBS = leidenalg.a +SUBDIRS = rigraph leidenalg +SUBLIBS = librigraph.a leidenalg.a + all: $(SHLIB) -$(SHLIB): $(OBJECTS) sublibs + +$(SHLIB): sublibs sublibs: sublibraries sublibraries: diff --git a/src/leiden.cpp b/src/leiden.cpp index 1440bf3..2164975 100644 --- a/src/leiden.cpp +++ b/src/leiden.cpp @@ -76,4 +76,4 @@ std::vector find_partition(SEXP graph, std::vector& edge_weights //return(igraph_ecount(&g)); } - + \ No newline at end of file diff --git a/src/leidenalg/CPMVertexPartition.cpp b/src/leidenalg/CPMVertexPartition.cpp index 57f08f7..3d8c961 100644 --- a/src/leidenalg/CPMVertexPartition.cpp +++ b/src/leidenalg/CPMVertexPartition.cpp @@ -1,4 +1,5 @@ #include "CPMVertexPartition.h" +#include "igraph.h" CPMVertexPartition::CPMVertexPartition(Graph* graph, vector membership, double resolution_parameter) : diff --git a/src/leidenalg/GraphHelper.cpp b/src/leidenalg/GraphHelper.cpp index 4a9e45f..85bc664 100644 --- a/src/leidenalg/GraphHelper.cpp +++ b/src/leidenalg/GraphHelper.cpp @@ -1,5 +1,8 @@ #include "GraphHelper.h" +#include "igraph.h" + + #ifdef DEBUG using std::cerr; using std::endl; diff --git a/src/leidenalg/LinearResolutionParameterVertexPartition.cpp b/src/leidenalg/LinearResolutionParameterVertexPartition.cpp index 12d5925..3737eca 100644 --- a/src/leidenalg/LinearResolutionParameterVertexPartition.cpp +++ b/src/leidenalg/LinearResolutionParameterVertexPartition.cpp @@ -1,4 +1,6 @@ #include "LinearResolutionParameterVertexPartition.h" +#include "igraph.h" + LinearResolutionParameterVertexPartition::LinearResolutionParameterVertexPartition(Graph* graph, vector membership, double resolution_parameter) : diff --git a/src/leidenalg/Makefile b/src/leidenalg/Makefile index 0b7e5d9..c023e2f 100644 --- a/src/leidenalg/Makefile +++ b/src/leidenalg/Makefile @@ -1,6 +1,6 @@ CXX ?= g++ PKG_CXXFLAGS = -O3 -std=c++11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) -PKG_CXXFLAGS += -I"./include" -I"./igraph-R" +PKG_CXXFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread diff --git a/src/leidenalg/Makefile.win b/src/leidenalg/Makefile.win index fbb4ab0..66eb712 100644 --- a/src/leidenalg/Makefile.win +++ b/src/leidenalg/Makefile.win @@ -1,6 +1,6 @@ CXX ?= g++ PKG_CXXFLAGS = -O3 -std=c++11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) -PKG_CXXFLAGS += -I"./include" -I"./igraph-R" +PKG_CXXFLAGS += -I"./include" -I"../rigraph/include" LDFLAGS += -lpthread diff --git a/src/leidenalg/ModularityVertexPartition.cpp b/src/leidenalg/ModularityVertexPartition.cpp index 26b32e8..a074b04 100644 --- a/src/leidenalg/ModularityVertexPartition.cpp +++ b/src/leidenalg/ModularityVertexPartition.cpp @@ -1,5 +1,7 @@ #include "ModularityVertexPartition.h" +#include "igraph.h" + #ifdef DEBUG #include using std::cerr; diff --git a/src/leidenalg/MutableVertexPartition.cpp b/src/leidenalg/MutableVertexPartition.cpp index 0cb69ce..013caba 100644 --- a/src/leidenalg/MutableVertexPartition.cpp +++ b/src/leidenalg/MutableVertexPartition.cpp @@ -1,5 +1,7 @@ #include "MutableVertexPartition.h" +#include "igraph.h" + #ifdef DEBUG using std::cerr; using std::endl; diff --git a/src/leidenalg/Optimiser.cpp b/src/leidenalg/Optimiser.cpp index d8dd0dc..3a672ac 100644 --- a/src/leidenalg/Optimiser.cpp +++ b/src/leidenalg/Optimiser.cpp @@ -1,4 +1,6 @@ #include "Optimiser.h" +#include "igraph.h" + /**************************************************************************** Create a new Optimiser object diff --git a/src/leidenalg/RBConfigurationVertexPartition.cpp b/src/leidenalg/RBConfigurationVertexPartition.cpp index e2d6f2d..b5c66ce 100644 --- a/src/leidenalg/RBConfigurationVertexPartition.cpp +++ b/src/leidenalg/RBConfigurationVertexPartition.cpp @@ -1,4 +1,6 @@ #include "RBConfigurationVertexPartition.h" +#include "igraph.h" + RBConfigurationVertexPartition::RBConfigurationVertexPartition(Graph* graph, vector const& membership, double resolution_parameter) : diff --git a/src/leidenalg/RBERVertexPartition.cpp b/src/leidenalg/RBERVertexPartition.cpp index 9350a08..6642337 100644 --- a/src/leidenalg/RBERVertexPartition.cpp +++ b/src/leidenalg/RBERVertexPartition.cpp @@ -1,5 +1,7 @@ #include "RBERVertexPartition.h" +#include "igraph.h" + RBERVertexPartition::RBERVertexPartition(Graph* graph, vector const& membership, double resolution_parameter) : LinearResolutionParameterVertexPartition(graph, diff --git a/src/leidenalg/ResolutionParameterVertexPartition.cpp b/src/leidenalg/ResolutionParameterVertexPartition.cpp index 56bb1e5..9e991bd 100644 --- a/src/leidenalg/ResolutionParameterVertexPartition.cpp +++ b/src/leidenalg/ResolutionParameterVertexPartition.cpp @@ -1,4 +1,6 @@ #include "ResolutionParameterVertexPartition.h" +#include "igraph.h" + ResolutionParameterVertexPartition::ResolutionParameterVertexPartition(Graph* graph, vector membership, double resolution_parameter) : diff --git a/src/leidenalg/SignificanceVertexPartition.cpp b/src/leidenalg/SignificanceVertexPartition.cpp index 1dbd9fb..1a1de37 100644 --- a/src/leidenalg/SignificanceVertexPartition.cpp +++ b/src/leidenalg/SignificanceVertexPartition.cpp @@ -1,5 +1,7 @@ #include "SignificanceVertexPartition.h" +#include "igraph.h" + #ifdef DEBUG #include using std::cerr; diff --git a/src/leidenalg/SurpriseVertexPartition.cpp b/src/leidenalg/SurpriseVertexPartition.cpp index 90a9ab8..6046f73 100644 --- a/src/leidenalg/SurpriseVertexPartition.cpp +++ b/src/leidenalg/SurpriseVertexPartition.cpp @@ -1,5 +1,7 @@ #include "SurpriseVertexPartition.h" +#include "igraph.h" + SurpriseVertexPartition::SurpriseVertexPartition(Graph* graph, vector const& membership) : MutableVertexPartition(graph, diff --git a/src/leidenalg/igraph-R/igraph_export.h b/src/leidenalg/igraph-R/igraph_export.h deleted file mode 100644 index e0a1b68..0000000 --- a/src/leidenalg/igraph-R/igraph_export.h +++ /dev/null @@ -1,42 +0,0 @@ - -#ifndef IGRAPH_EXPORT_H -#define IGRAPH_EXPORT_H - -#ifdef IGRAPH_STATIC -# define IGRAPH_EXPORT -# define IGRAPH_NO_EXPORT -#else -# ifndef IGRAPH_EXPORT -# ifdef igraph_EXPORTS - /* We are building this library */ -# define IGRAPH_EXPORT -# else - /* We are using this library */ -# define IGRAPH_EXPORT -# endif -# endif - -# ifndef IGRAPH_NO_EXPORT -# define IGRAPH_NO_EXPORT -# endif -#endif - -#ifndef IGRAPH_DEPRECATED -# define IGRAPH_DEPRECATED __attribute__ ((__deprecated__)) -#endif - -#ifndef IGRAPH_DEPRECATED_EXPORT -# define IGRAPH_DEPRECATED_EXPORT IGRAPH_EXPORT IGRAPH_DEPRECATED -#endif - -#ifndef IGRAPH_DEPRECATED_NO_EXPORT -# define IGRAPH_DEPRECATED_NO_EXPORT IGRAPH_NO_EXPORT IGRAPH_DEPRECATED -#endif - -#if 0 /* DEFINE_NO_DEPRECATED */ -# ifndef IGRAPH_NO_DEPRECATED -# define IGRAPH_NO_DEPRECATED -# endif -#endif - -#endif /* IGRAPH_EXPORT_H */ diff --git a/src/leidenalg/include/CPMVertexPartition.h b/src/leidenalg/include/CPMVertexPartition.h index 639d9ed..ca820b9 100644 --- a/src/leidenalg/include/CPMVertexPartition.h +++ b/src/leidenalg/include/CPMVertexPartition.h @@ -2,6 +2,7 @@ #define CPMVERTEXPARTITION_H #include +#include "igraph.h" class CPMVertexPartition : public LinearResolutionParameterVertexPartition { diff --git a/src/leidenalg/include/GraphHelper.h b/src/leidenalg/include/GraphHelper.h index fd44b62..4339106 100644 --- a/src/leidenalg/include/GraphHelper.h +++ b/src/leidenalg/include/GraphHelper.h @@ -1,7 +1,7 @@ #ifndef GRAPHHELPER_INCLUDED #define GRAPHHELPER_INCLUDED -#include +#include "igraph.h" #include #include #include @@ -13,6 +13,8 @@ using std::endl; //#endif + + class MutableVertexPartition; using std::vector; diff --git a/src/leidenalg/include/LinearResolutionParameterVertexPartition.h b/src/leidenalg/include/LinearResolutionParameterVertexPartition.h index 751cf5b..9050928 100644 --- a/src/leidenalg/include/LinearResolutionParameterVertexPartition.h +++ b/src/leidenalg/include/LinearResolutionParameterVertexPartition.h @@ -2,6 +2,7 @@ #define LINEARRESOLUTIONPARAMETERVERTEXPARTITION_H #include +#include "igraph.h" class LinearResolutionParameterVertexPartition : public ResolutionParameterVertexPartition { diff --git a/src/leidenalg/include/ModularityVertexPartition.h b/src/leidenalg/include/ModularityVertexPartition.h index 0157536..b9c06b7 100644 --- a/src/leidenalg/include/ModularityVertexPartition.h +++ b/src/leidenalg/include/ModularityVertexPartition.h @@ -2,6 +2,8 @@ #define MODULARITYVERTEXPARTITION_H #include +#include "igraph.h" + class ModularityVertexPartition : public MutableVertexPartition { diff --git a/src/leidenalg/include/MutableVertexPartition.h b/src/leidenalg/include/MutableVertexPartition.h index 6449662..2dbd097 100644 --- a/src/leidenalg/include/MutableVertexPartition.h +++ b/src/leidenalg/include/MutableVertexPartition.h @@ -7,6 +7,8 @@ #include #include #include +#include "igraph.h" + using std::string; using std::map; diff --git a/src/leidenalg/include/Optimiser.h b/src/leidenalg/include/Optimiser.h index b77fbe1..3f52635 100644 --- a/src/leidenalg/include/Optimiser.h +++ b/src/leidenalg/include/Optimiser.h @@ -5,6 +5,9 @@ #include #include +#include "igraph.h" + + #include using std::cerr; using std::endl; diff --git a/src/leidenalg/include/RBConfigurationVertexPartition.h b/src/leidenalg/include/RBConfigurationVertexPartition.h index 4220f54..860856d 100644 --- a/src/leidenalg/include/RBConfigurationVertexPartition.h +++ b/src/leidenalg/include/RBConfigurationVertexPartition.h @@ -3,6 +3,9 @@ #include "LinearResolutionParameterVertexPartition.h" +#include "igraph.h" + + class RBConfigurationVertexPartition : public LinearResolutionParameterVertexPartition { public: diff --git a/src/leidenalg/include/RBERVertexPartition.h b/src/leidenalg/include/RBERVertexPartition.h index e79c313..dff2ad5 100644 --- a/src/leidenalg/include/RBERVertexPartition.h +++ b/src/leidenalg/include/RBERVertexPartition.h @@ -3,6 +3,8 @@ #include +#include "igraph.h" + class RBERVertexPartition : public LinearResolutionParameterVertexPartition { diff --git a/src/leidenalg/include/ResolutionParameterVertexPartition.h b/src/leidenalg/include/ResolutionParameterVertexPartition.h index df886c7..544021c 100644 --- a/src/leidenalg/include/ResolutionParameterVertexPartition.h +++ b/src/leidenalg/include/ResolutionParameterVertexPartition.h @@ -3,6 +3,9 @@ #include +#include "igraph.h" + + class ResolutionParameterVertexPartition : public MutableVertexPartition { public: diff --git a/src/leidenalg/include/SignificanceVertexPartition.h b/src/leidenalg/include/SignificanceVertexPartition.h index dd93849..afc6bd0 100644 --- a/src/leidenalg/include/SignificanceVertexPartition.h +++ b/src/leidenalg/include/SignificanceVertexPartition.h @@ -3,6 +3,8 @@ #include +#include "igraph.h" + class SignificanceVertexPartition : public MutableVertexPartition { diff --git a/src/leidenalg/include/SurpriseVertexPartition.h b/src/leidenalg/include/SurpriseVertexPartition.h index 535caa4..9591034 100644 --- a/src/leidenalg/include/SurpriseVertexPartition.h +++ b/src/leidenalg/include/SurpriseVertexPartition.h @@ -2,6 +2,10 @@ #define SURPRISEVERTEXPARTITION_H #include "MutableVertexPartition.h" + +#include "igraph.h" + + #include using std::cerr; using std::endl; diff --git a/src/rigraph/Makefile b/src/rigraph/Makefile new file mode 100644 index 0000000..62664fd --- /dev/null +++ b/src/rigraph/Makefile @@ -0,0 +1,26 @@ +C ?= gcc +CFLAGS = -O3 -std=c11 -std=gnu11 +PKG_CFLAGS = -O3 -std=c11 -std=gnu11 -pthread -fPIC +PKG_CFLAGS += -I"./include" -I"../rigraph/include" +LDFLAGS += -lpthread + + +CXX ?= g++ +PKG_CXXFLAGS = -O3 -std=c++11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) +PKG_CXXFLAGS += -I"./include" -I"../rigraph/include" +LDFLAGS += -lpthread + + +LIB=../librigraph.a +lib: $(LIB) + +## ignoring: core/centrality/prpack.o + +$(LIB): core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o + ar rvs $@ $? + + +clean: + @-rm -f *.o $(LIB) + + diff --git a/src/rigraph/Makefile.win b/src/rigraph/Makefile.win new file mode 100644 index 0000000..a282b37 --- /dev/null +++ b/src/rigraph/Makefile.win @@ -0,0 +1,24 @@ +C ?= gcc +CFLAGS = -O3 -std=c11 -std=gnu11 +PKG_CFLAGS = -O3 -std=c11 -std=gnu11 -pthread -fPIC +PKG_CFLAGS += -I"./include" -I"../rigraph/include" +LDFLAGS += -lpthread + +CXX ?= g++ +PKG_CXXFLAGS = -O3 -std=c++11 -pthread -fPIC $(SHLIB_OPENMP_CXXFLAGS) +PKG_CXXFLAGS += -I"./include" -I"../rigraph/include" +LDFLAGS += -lpthread + +LIB=../librigraph.a +lib: $(LIB) + +## ignoring: core/centrality/prpack.o + +$(LIB): core/centrality/betweenness.o core/centrality/centrality_other.o core/centrality/centralization.o core/centrality/closeness.o core/centrality/coreness.o core/centrality/prpack/prpack_base_graph.o core/centrality/prpack/prpack_igraph_graph.o core/centrality/prpack/prpack_preprocessed_ge_graph.o core/centrality/prpack/prpack_preprocessed_gs_graph.o core/centrality/prpack/prpack_preprocessed_scc_graph.o core/centrality/prpack/prpack_preprocessed_schur_graph.o core/centrality/prpack/prpack_result.o core/centrality/prpack/prpack_solver.o core/centrality/prpack/prpack_utils.o core/cliques/cliquer/cliquer.o core/cliques/cliquer/cliquer_graph.o core/cliques/cliquer/reorder.o core/cliques/cliquer_wrapper.o core/cliques/cliques.o core/cliques/glet.o core/cliques/maximal_cliques.o core/community/community_misc.o core/community/edge_betweenness.o core/community/fast_modularity.o core/community/fluid.o core/community/infomap/infomap.o core/community/infomap/infomap_FlowGraph.o core/community/infomap/infomap_Greedy.o core/community/infomap/infomap_Node.o core/community/label_propagation.o core/community/leading_eigenvector.o core/community/leiden.o core/community/louvain.o core/community/modularity.o core/community/optimal_modularity.o core/community/spinglass/NetDataTypes.o core/community/spinglass/NetRoutines.o core/community/spinglass/clustertool.o core/community/spinglass/pottsmodel_2.o core/community/walktrap/walktrap.o core/community/walktrap/walktrap_communities.o core/community/walktrap/walktrap_graph.o core/community/walktrap/walktrap_heap.o core/connectivity/cohesive_blocks.o core/connectivity/components.o core/connectivity/separators.o core/constructors/adjacency.o core/constructors/atlas.o core/constructors/basic_constructors.o core/constructors/de_bruijn.o core/constructors/famous.o core/constructors/full.o core/constructors/kautz.o core/constructors/lcf.o core/constructors/linegraph.o core/constructors/prufer.o core/constructors/regular.o core/core/array.o core/core/buckets.o core/core/cutheap.o core/core/dqueue.o core/core/error.o core/core/estack.o core/core/fixed_vectorlist.o core/core/grid.o core/core/heap.o core/core/indheap.o core/core/interruption.o core/core/marked_queue.o core/core/matrix.o core/core/memory.o core/core/printing.o core/core/progress.o core/core/psumtree.o core/core/set.o core/core/sparsemat.o core/core/spmatrix.o core/core/stack.o core/core/statusbar.o core/core/strvector.o core/core/trie.o core/core/vector.o core/core/vector_ptr.o core/flow/flow.o core/flow/st-cuts.o core/games/barabasi.o core/games/callaway_traits.o core/games/citations.o core/games/correlated.o core/games/degree_sequence.o core/games/degree_sequence_vl/gengraph_box_list.o core/games/degree_sequence_vl/gengraph_degree_sequence.o core/games/degree_sequence_vl/gengraph_graph_molloy_hash.o core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.o core/games/degree_sequence_vl/gengraph_mr-connected.o core/games/degree_sequence_vl/gengraph_powerlaw.o core/games/degree_sequence_vl/gengraph_random.o core/games/dotproduct.o core/games/erdos_renyi.o core/games/establishment.o core/games/forestfire.o core/games/grg.o core/games/growing_random.o core/games/islands.o core/games/k_regular.o core/games/preference.o core/games/recent_degree.o core/games/sbm.o core/games/static_fitness.o core/games/tree.o core/games/watts_strogatz.o core/graph/adjlist.o core/graph/attributes.o core/graph/basic_query.o core/graph/cattributes.o core/graph/iterators.o core/graph/type_indexededgelist.o core/graph/visitors.o core/hrg/hrg.o core/hrg/hrg_types.o core/internal/glpk_support.o core/internal/hacks.o core/internal/lsap.o core/internal/qsort.o core/internal/qsort_r.o core/internal/zeroin.o core/io/dimacs.o core/io/dl-lexer.o core/io/dl-parser.o core/io/dl.o core/io/dot.o core/io/edgelist.o core/io/gml-lexer.o core/io/gml-parser.o core/io/gml-tree.o core/io/gml.o core/io/graphdb.o core/io/graphml.o core/io/leda.o core/io/lgl-lexer.o core/io/lgl-parser.o core/io/lgl.o core/io/ncol-lexer.o core/io/ncol-parser.o core/io/ncol.o core/io/pajek-lexer.o core/io/pajek-parser.o core/io/pajek.o core/isomorphism/bliss.o core/isomorphism/bliss/defs.o core/isomorphism/bliss/graph.o core/isomorphism/bliss/heap.o core/isomorphism/bliss/orbit.o core/isomorphism/bliss/partition.o core/isomorphism/bliss/uintseqhash.o core/isomorphism/bliss/utils.o core/isomorphism/isoclasses.o core/isomorphism/isomorphism_misc.o core/isomorphism/lad.o core/isomorphism/queries.o core/isomorphism/vf2.o core/layout/circular.o core/layout/davidson_harel.o core/layout/drl/DensityGrid.o core/layout/drl/DensityGrid_3d.o core/layout/drl/drl_graph.o core/layout/drl/drl_graph_3d.o core/layout/drl/drl_layout.o core/layout/drl/drl_layout_3d.o core/layout/drl/drl_parse.o core/layout/fruchterman_reingold.o core/layout/gem.o core/layout/graphopt.o core/layout/kamada_kawai.o core/layout/large_graph.o core/layout/layout_bipartite.o core/layout/layout_grid.o core/layout/layout_random.o core/layout/mds.o core/layout/merge_dla.o core/layout/merge_grid.o core/layout/reingold_tilford.o core/layout/sugiyama.o core/linalg/arpack.o core/linalg/blas.o core/linalg/eigen.o core/linalg/lapack.o core/math/bfgs.o core/math/complex.o core/math/utils.o core/misc/bipartite.o core/misc/chordality.o core/misc/cocitation.o core/misc/coloring.o core/misc/conversion.o core/misc/degree_sequence.o core/misc/embedding.o core/misc/feedback_arc_set.o core/misc/graphicality.o core/misc/matching.o core/misc/microscopic_update.o core/misc/mixing.o core/misc/motifs.o core/misc/other.o core/misc/scan.o core/misc/sir.o core/misc/spanning_trees.o core/operators/add_edge.o core/operators/complementer.o core/operators/compose.o core/operators/connect_neighborhood.o core/operators/contract.o core/operators/difference.o core/operators/disjoint_union.o core/operators/intersection.o core/operators/misc_internal.o core/operators/permute.o core/operators/rewire.o core/operators/rewire_edges.o core/operators/simplify.o core/operators/subgraph.o core/operators/union.o core/paths/all_shortest_paths.o core/paths/bellman_ford.o core/paths/dijkstra.o core/paths/distances.o core/paths/eulerian.o core/paths/histogram.o core/paths/johnson.o core/paths/random_walk.o core/paths/shortest_paths.o core/paths/simple_paths.o core/paths/unweighted.o core/properties/basic_properties.o core/properties/constraint.o core/properties/convergence_degree.o core/properties/dag.o core/properties/degrees.o core/properties/girth.o core/properties/loops.o core/properties/multiplicity.o core/properties/neighborhood.o core/properties/spectral.o core/properties/trees.o core/properties/triangles.o core/random/random.o core/scg/scg.o core/scg/scg_approximate_methods.o core/scg/scg_exact_scg.o core/scg/scg_kmeans.o core/scg/scg_optimal_method.o core/scg/scg_utils.o core/version.o vendor/cs/cs_add.o vendor/cs/cs_amd.o vendor/cs/cs_chol.o vendor/cs/cs_cholsol.o vendor/cs/cs_compress.o vendor/cs/cs_counts.o vendor/cs/cs_cumsum.o vendor/cs/cs_dfs.o vendor/cs/cs_dmperm.o vendor/cs/cs_droptol.o vendor/cs/cs_dropzeros.o vendor/cs/cs_dupl.o vendor/cs/cs_entry.o vendor/cs/cs_ereach.o vendor/cs/cs_etree.o vendor/cs/cs_fkeep.o vendor/cs/cs_gaxpy.o vendor/cs/cs_happly.o vendor/cs/cs_house.o vendor/cs/cs_ipvec.o vendor/cs/cs_leaf.o vendor/cs/cs_load.o vendor/cs/cs_lsolve.o vendor/cs/cs_ltsolve.o vendor/cs/cs_lu.o vendor/cs/cs_lusol.o vendor/cs/cs_malloc.o vendor/cs/cs_maxtrans.o vendor/cs/cs_multiply.o vendor/cs/cs_norm.o vendor/cs/cs_permute.o vendor/cs/cs_pinv.o vendor/cs/cs_post.o vendor/cs/cs_print.o vendor/cs/cs_pvec.o vendor/cs/cs_qr.o vendor/cs/cs_qrsol.o vendor/cs/cs_randperm.o vendor/cs/cs_reach.o vendor/cs/cs_scatter.o vendor/cs/cs_scc.o vendor/cs/cs_schol.o vendor/cs/cs_spsolve.o vendor/cs/cs_sqr.o vendor/cs/cs_symperm.o vendor/cs/cs_tdfs.o vendor/cs/cs_transpose.o vendor/cs/cs_updown.o vendor/cs/cs_usolve.o vendor/cs/cs_util.o vendor/cs/cs_utsolve.o vendor/mini-gmp/mini-gmp.o vendor/plfit/gss.o vendor/plfit/hzeta.o vendor/plfit/kolmogorov.o vendor/plfit/lbfgs.o vendor/plfit/mt.o vendor/plfit/options.o vendor/plfit/platform.o vendor/plfit/plfit.o vendor/plfit/plfit_error.o vendor/plfit/rbinom.o vendor/plfit/sampling.o vendor/arpack/dgetv0.o vendor/arpack/dlaqrb.o vendor/arpack/dmout.o vendor/arpack/dnaitr.o vendor/arpack/dnapps.o vendor/arpack/dnaup2.o vendor/arpack/dnaupd.o vendor/arpack/dnconv.o vendor/arpack/dneigh.o vendor/arpack/dneupd.o vendor/arpack/dngets.o vendor/arpack/dsaitr.o vendor/arpack/dsapps.o vendor/arpack/dsaup2.o vendor/arpack/dsaupd.o vendor/arpack/dsconv.o vendor/arpack/dseigt.o vendor/arpack/dsesrt.o vendor/arpack/dseupd.o vendor/arpack/dsgets.o vendor/arpack/dsortc.o vendor/arpack/dsortr.o vendor/arpack/dstatn.o vendor/arpack/dstats.o vendor/arpack/dstqrb.o vendor/arpack/dvout.o vendor/arpack/ivout.o vendor/arpack/second.o vendor/arpack/wrap.o vendor/simpleraytracer/Color.o vendor/simpleraytracer/Light.o vendor/simpleraytracer/Point.o vendor/simpleraytracer/RIgraphRay.o vendor/simpleraytracer/Ray.o vendor/simpleraytracer/RayTracer.o vendor/simpleraytracer/RayVector.o vendor/simpleraytracer/Shape.o vendor/simpleraytracer/Sphere.o vendor/simpleraytracer/Triangle.o vendor/simpleraytracer/unit_limiter.o vendor/uuid/R.o vendor/uuid/clear.o vendor/uuid/compare.o vendor/uuid/copy.o vendor/uuid/gen_uuid.o vendor/uuid/isnull.o vendor/uuid/pack.o vendor/uuid/parse.o vendor/uuid/unpack.o vendor/uuid/unparse.o rinterface.o rinterface_extra.o rrandom.o lazyeval.o + ar rvs $@ $? + + +clean: + @-rm -f *.o $(LIB) + + diff --git a/src/rigraph/core/centrality/betweenness.c b/src/rigraph/core/centrality/betweenness.c new file mode 100644 index 0000000..1dde4a5 --- /dev/null +++ b/src/rigraph/core/centrality/betweenness.c @@ -0,0 +1,1018 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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 . +*/ + +#include "igraph_centrality.h" + +#include "igraph_memory.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_stack.h" +#include "igraph_dqueue.h" + +#include "config.h" + +#include "../core/indheap.h" +#include "../core/interruption.h" +#include "../core/math.h" + +/***** Vertex betweenness *****/ + +/** + * \ingroup structural + * \function igraph_betweenness + * \brief Betweenness centrality of some vertices. + * + * The betweenness centrality of a vertex is the number of geodesics + * going through it. If there are more than one geodesic between two + * vertices, the value of these geodesics are weighted by one over the + * number of geodesics. + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * betweenness scores for the specified vertices. + * \param vids The vertices of which the betweenness centrality scores + * will be calculated. + * \param directed Logical, if true directed paths will be considered + * for directed graphs. It is ignored for undirected graphs. + * \param weights An optional vector containing edge weights for + * calculating weighted betweenness. No edge weight may be NaN. + * Supply a null pointer here for unweighted betweenness. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * \c IGRAPH_EINVVID, invalid vertex id passed in + * \p vids. + * + * Time complexity: O(|V||E|), + * |V| and + * |E| are the number of vertices and + * edges in the graph. + * Note that the time complexity is independent of the number of + * vertices for which the score is calculated. + * + * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness(). + * See \ref igraph_edge_betweenness() for calculating the betweenness score + * of the edges in a graph. See \ref igraph_betweenness_cutoff() to + * calculate the range-limited betweenness of the vertices in a graph. + */ +int igraph_betweenness(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_bool_t directed, + const igraph_vector_t* weights) { + return igraph_betweenness_cutoff(graph, res, vids, directed, weights, -1); +} + +static int igraph_i_betweenness_cutoff_weighted( + const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + igraph_bool_t directed, + igraph_real_t cutoff, + const igraph_vector_t *weights) { + + igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph); + igraph_integer_t no_of_edges = (igraph_integer_t) igraph_ecount(graph); + igraph_2wheap_t Q; + igraph_inclist_t inclist; + igraph_adjlist_t fathers; + long int source, j; + igraph_stack_t S; + igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; + igraph_vector_t dist, nrgeo, tmpscore; + igraph_vector_t v_tmpres, *tmpres = &v_tmpres; + igraph_vit_t vit; + int cmp_result; + const double eps = IGRAPH_SHORTEST_PATH_EPSILON; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length must agree with number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight <= 0) { + IGRAPH_ERROR("Weight vector must be positive.", IGRAPH_EINVAL); + } else if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } else if (minweight <= eps) { + IGRAPH_WARNING("Some weights are smaller than epsilon, calculations may suffer from numerical precision."); + } + } + + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + IGRAPH_CHECK(igraph_adjlist_init_empty(&fathers, no_of_nodes)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &fathers); + + IGRAPH_CHECK(igraph_stack_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_stack_destroy, &S); + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&tmpscore, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&nrgeo, no_of_nodes); + + if (igraph_vs_is_all(&vids)) { + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + igraph_vector_null(res); + tmpres = res; + } else { + IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_nodes); + } + + for (source = 0; source < no_of_nodes; source++) { + IGRAPH_PROGRESS("Betweenness centrality: ", 100.0 * source / no_of_nodes, 0); + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_2wheap_push_with_index(&Q, source, -1.0); + VECTOR(dist)[source] = 1.0; + VECTOR(nrgeo)[source] = 1; + + while (!igraph_2wheap_empty(&Q)) { + long int minnei = igraph_2wheap_max_index(&Q); + igraph_real_t mindist = -igraph_2wheap_delete_max(&Q); + igraph_vector_int_t *neis; + long int nlen; + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && mindist > cutoff + 1.0) { + /* Reset variables if node is too distant */ + VECTOR(tmpscore)[minnei] = 0; + VECTOR(dist)[minnei] = 0; + VECTOR(nrgeo)[minnei] = 0; + igraph_vector_int_clear(igraph_adjlist_get(&fathers, minnei)); + continue; + } + + igraph_stack_push(&S, minnei); + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_inclist_get(&inclist, minnei); + nlen = igraph_vector_int_size(neis); + for (j = 0; j < nlen; j++) { + long int edge = (long int) VECTOR(*neis)[j]; + long int to = IGRAPH_OTHER(graph, edge, minnei); + igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; + igraph_real_t curdist = VECTOR(dist)[to]; + + if (curdist == 0) { + /* this means curdist is infinity */ + cmp_result = -1; + } else { + cmp_result = igraph_cmp_epsilon(altdist, curdist, eps); + } + + if (curdist == 0) { + /* This is the first non-infinite distance */ + igraph_vector_int_t *v = igraph_adjlist_get(&fathers, to); + igraph_vector_int_resize(v, 1); + VECTOR(*v)[0] = minnei; + VECTOR(nrgeo)[to] = VECTOR(nrgeo)[minnei]; + + VECTOR(dist)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, to, -altdist)); + } else if (cmp_result < 0) { + /* This is a shorter path */ + igraph_vector_int_t *v = igraph_adjlist_get(&fathers, to); + igraph_vector_int_resize(v, 1); + VECTOR(*v)[0] = minnei; + VECTOR(nrgeo)[to] = VECTOR(nrgeo)[minnei]; + + VECTOR(dist)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_modify(&Q, to, -altdist)); + } else if (cmp_result == 0 && + (altdist <= cutoff + 1.0 || cutoff < 0)) { + /* Only add if the node is not more distant than the cutoff */ + igraph_vector_int_t *v = igraph_adjlist_get(&fathers, to); + igraph_vector_int_push_back(v, minnei); + VECTOR(nrgeo)[to] += VECTOR(nrgeo)[minnei]; + } + } + + } /* !igraph_2wheap_empty(&Q) */ + + while (!igraph_stack_empty(&S)) { + long int w = (long int) igraph_stack_pop(&S); + igraph_vector_int_t *fatv = igraph_adjlist_get(&fathers, w); + long int fatv_len = igraph_vector_int_size(fatv); + for (j = 0; j < fatv_len; j++) { + long int f = (long int) VECTOR(*fatv)[j]; + VECTOR(tmpscore)[f] += VECTOR(nrgeo)[f] / VECTOR(nrgeo)[w] * (1 + VECTOR(tmpscore)[w]); + } + if (w != source) { + VECTOR(*tmpres)[w] += VECTOR(tmpscore)[w]; + } + + /* Reset variables */ + VECTOR(tmpscore)[w] = 0; + VECTOR(dist)[w] = 0; + VECTOR(nrgeo)[w] = 0; + igraph_vector_int_clear(igraph_adjlist_get(&fathers, w)); + } + + } /* source < no_of_nodes */ + + if (!igraph_vs_is_all(&vids)) { + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_VIT_SIZE(vit))); + + for (j = 0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), j++) { + long int node = IGRAPH_VIT_GET(vit); + VECTOR(*res)[j] = VECTOR(*tmpres)[node]; + } + + no_of_nodes = (igraph_integer_t) j; + + igraph_vit_destroy(&vit); + igraph_vector_destroy(tmpres); + IGRAPH_FINALLY_CLEAN(2); + } + + if (!directed || !igraph_is_directed(graph)) { + for (j = 0; j < no_of_nodes; j++) { + VECTOR(*res)[j] /= 2.0; + } + } + + IGRAPH_PROGRESS("Betweenness centrality: ", 100.0, 0); + + igraph_vector_destroy(&nrgeo); + igraph_vector_destroy(&tmpscore); + igraph_vector_destroy(&dist); + igraph_stack_destroy(&S); + igraph_adjlist_destroy(&fathers); + igraph_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + IGRAPH_FINALLY_CLEAN(7); + + return 0; +} + + +/** + * \ingroup structural + * \function igraph_betweenness_cutoff + * \brief Range-limited betweenness centrality. + * + * This function computes a range-limited version of betweenness centrality + * by considering only those shortest paths whose length is no greater + * then the given cutoff value. + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * range-limited betweenness scores for the specified vertices. + * \param vids The vertices for which the range-limited betweenness centrality + * scores will be computed. + * \param directed Logical, if true directed paths will be considered + * for directed graphs. It is ignored for undirected graphs. + * \param weights An optional vector containing edge weights for + * calculating weighted betweenness. No edge weight may be NaN. + * Supply a null pointer here for unweighted betweenness. + * \param cutoff The maximal length of paths that will be considered. + * If negative, the exact betweenness will be calculated, and + * there will be no upper limit on path lengths. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * \c IGRAPH_EINVVID, invalid vertex id passed in + * \p vids. + * + * Time complexity: O(|V||E|), + * |V| and + * |E| are the number of vertices and + * edges in the graph. + * Note that the time complexity is independent of the number of + * vertices for which the score is calculated. + * + * \sa \ref igraph_betweenness() to calculate the exact betweenness and + * \ref igraph_edge_betweenness_cutoff() to calculate the range-limited + * edge betweenness. + */ +int igraph_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_bool_t directed, + const igraph_vector_t *weights, igraph_real_t cutoff) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + long int *distance; + /* Note: nrgeo holds the number of shortest paths, which may be very large in some cases, + * e.g. in a grid graph. If using an integer type, this results in overflow. + * With a 'long long int', overflow already affects the result for a grid as small as 36*36. + * Therefore, we use a 'double' instead. While a 'double' holds fewer digits than a 'long long int', + * i.e. its precision is lower, it is effectively immune to overflow. The impact on the precision + * of the final result is negligible. The max betweenness is correct to 14 decimal digits, + * i.e. the precision limit of 'double', even for a 101*101 grid graph. */ + double *nrgeo = 0; + double *tmpscore; + igraph_stack_t stack = IGRAPH_STACK_NULL; + long int source; + long int j, k, nneis; + igraph_vector_int_t *neis; + igraph_vector_t v_tmpres, *tmpres = &v_tmpres; + igraph_vit_t vit; + + igraph_adjlist_t adjlist_out, adjlist_in; + igraph_adjlist_t *adjlist_out_p, *adjlist_in_p; + + if (weights) { + return igraph_i_betweenness_cutoff_weighted(graph, res, vids, directed, + cutoff, weights); + } + + if (!igraph_vs_is_all(&vids)) { + /* subset */ + IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_nodes); + } else { + /* only */ + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + igraph_vector_null(res); + tmpres = res; + } + + directed = directed && igraph_is_directed(graph); + if (directed) { + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_out, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_out); + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_in, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_in); + adjlist_out_p = &adjlist_out; + adjlist_in_p = &adjlist_in; + } else { + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_out, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_out); + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_in, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_in); + adjlist_out_p = &adjlist_out; + adjlist_in_p = &adjlist_in; + } + for (j = 0; j < no_of_nodes; j++) { + igraph_vector_int_clear(igraph_adjlist_get(adjlist_in_p, j)); + } + + distance = IGRAPH_CALLOC(no_of_nodes, long int); + if (distance == 0) { + IGRAPH_ERROR("Insufficient memory for betweenness calculation.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, distance); + nrgeo = IGRAPH_CALLOC(no_of_nodes, double); + if (nrgeo == 0) { + IGRAPH_ERROR("Insufficient memory for betweenness calculation.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, nrgeo); + tmpscore = IGRAPH_CALLOC(no_of_nodes, double); + if (tmpscore == 0) { + IGRAPH_ERROR("Insufficient memory for betweenness calculation.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, tmpscore); + + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + igraph_stack_init(&stack, no_of_nodes); + IGRAPH_FINALLY(igraph_stack_destroy, &stack); + + /* here we go */ + + for (source = 0; source < no_of_nodes; source++) { + IGRAPH_PROGRESS("Betweenness centrality: ", 100.0 * source / no_of_nodes, 0); + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_dqueue_push(&q, source)); + nrgeo[source] = 1; + distance[source] = 1; + + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && distance[actnode] > cutoff + 1) { + /* Reset variables if node is too distant */ + distance[actnode] = 0; + nrgeo[actnode] = 0; + tmpscore[actnode] = 0; + igraph_vector_int_clear(igraph_adjlist_get(adjlist_in_p, actnode)); + continue; + } + + IGRAPH_CHECK(igraph_stack_push(&stack, actnode)); + neis = igraph_adjlist_get(adjlist_out_p, actnode); + nneis = igraph_vector_int_size(neis); + for (j = 0; j < nneis; j++) { + long int neighbor = (long int) VECTOR(*neis)[j]; + if (distance[neighbor] == 0) { + distance[neighbor] = distance[actnode] + 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + } + if (distance[neighbor] == distance[actnode] + 1 && + (distance[neighbor] <= cutoff + 1 || cutoff < 0)) { + /* Only add if the node is not more distant than the cutoff */ + igraph_vector_int_t *v = igraph_adjlist_get(adjlist_in_p, + neighbor); + igraph_vector_int_push_back(v, actnode); + nrgeo[neighbor] += nrgeo[actnode]; + } + } + } /* while !igraph_dqueue_empty */ + + /* Ok, we've the distance of each node and also the number of + shortest paths to them. Now we do an inverse search, starting + with the farthest nodes. */ + while (!igraph_stack_empty(&stack)) { + long int actnode = (long int) igraph_stack_pop(&stack); + neis = igraph_adjlist_get(adjlist_in_p, actnode); + nneis = igraph_vector_int_size(neis); + for (j = 0; j < nneis; j++) { + long int neighbor = (long int) VECTOR(*neis)[j]; + tmpscore[neighbor] += (tmpscore[actnode] + 1) * nrgeo[neighbor] / nrgeo[actnode]; + } + + if (actnode != source) { + VECTOR(*tmpres)[actnode] += tmpscore[actnode]; + } + + /* Reset variables */ + distance[actnode] = 0; + nrgeo[actnode] = 0; + tmpscore[actnode] = 0; + igraph_vector_int_clear(igraph_adjlist_get(adjlist_in_p, actnode)); + } + + } /* for source < no_of_nodes */ + + IGRAPH_PROGRESS("Betweenness centrality: ", 100.0, 0); + + /* clean */ + IGRAPH_FREE(distance); + IGRAPH_FREE(nrgeo); + IGRAPH_FREE(tmpscore); + + igraph_dqueue_destroy(&q); + igraph_stack_destroy(&stack); + IGRAPH_FINALLY_CLEAN(5); + + /* Keep only the requested vertices */ + if (!igraph_vs_is_all(&vids)) { + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_VIT_SIZE(vit))); + + for (k = 0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), k++) { + long int node = IGRAPH_VIT_GET(vit); + VECTOR(*res)[k] = VECTOR(*tmpres)[node]; + } + + igraph_vit_destroy(&vit); + igraph_vector_destroy(tmpres); + IGRAPH_FINALLY_CLEAN(2); + } + + /* divide by 2 for undirected graph */ + if (!directed) { + nneis = igraph_vector_size(res); + for (j = 0; j < nneis; j++) { + VECTOR(*res)[j] /= 2.0; + } + } + + igraph_adjlist_destroy(&adjlist_out); + igraph_adjlist_destroy(&adjlist_in); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + + +/** + * \ingroup structural + * \function igraph_betweenness_estimate + * \brief Estimated betweenness centrality of some vertices. + * + * \deprecated-by igraph_betweenness_cutoff 0.9 + * + * + * The betweenness centrality of a vertex is the number of geodesics + * going through it. If there are more than one geodesic between two + * vertices, the value of these geodesics are weighted by one over the + * number of geodesics. When estimating betweenness centrality, igraph + * takes into consideration only those paths that are shorter than or + * equal to a prescribed length. Note that the estimated centrality + * will always be less than the real one. + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * estimated betweenness scores for the specified vertices. + * \param vids The vertices of which the betweenness centrality scores + * will be estimated. + * \param directed Logical, if true directed paths will be considered + * for directed graphs. It is ignored for undirected graphs. + * \param cutoff The maximal length of paths that will be considered. + * If negative, the exact betweenness will be calculated, and + * there will be no upper limit on path lengths. + * \param weights An optional vector containing edge weights for + * calculating weighted betweenness. No edge weight may be NaN. + * Supply a null pointer here for unweighted betweenness. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * \c IGRAPH_EINVVID, invalid vertex id passed in + * \p vids. + * + * Time complexity: O(|V||E|), + * |V| and + * |E| are the number of vertices and + * edges in the graph. + * Note that the time complexity is independent of the number of + * vertices for which the score is calculated. + * + * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness(). + * See \ref igraph_edge_betweenness() for calculating the betweenness score + * of the edges in a graph. + */ + +int igraph_betweenness_estimate(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_bool_t directed, + igraph_real_t cutoff, const igraph_vector_t *weights) { + IGRAPH_WARNING("igraph_betweenness_estimate is deprecated, use igraph_betweenness_cutoff."); + return igraph_betweenness_cutoff(graph, res, vids, directed, weights, cutoff); +} + +/***** Edge betweenness *****/ + +static int igraph_i_edge_betweenness_cutoff_weighted( + const igraph_t *graph, + igraph_vector_t *result, + igraph_bool_t directed, + igraph_real_t cutoff, + const igraph_vector_t *weights) { + + igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph); + igraph_integer_t no_of_edges = (igraph_integer_t) igraph_ecount(graph); + igraph_2wheap_t Q; + igraph_inclist_t inclist; + igraph_inclist_t fathers; + igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; + igraph_vector_t distance, tmpscore; + igraph_vector_long_t nrgeo; + long int source, j; + int cmp_result; + const double eps = IGRAPH_SHORTEST_PATH_EPSILON; + igraph_stack_t S; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length must match number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight <= 0) { + IGRAPH_ERROR("Weight vector must be positive.", IGRAPH_EINVAL); + } else if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } else if (minweight <= eps) { + IGRAPH_WARNING("Some weights are smaller than epsilon, calculations may suffer from numerical precision."); + } + } + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + IGRAPH_CHECK(igraph_inclist_init_empty(&fathers, no_of_nodes)); + IGRAPH_FINALLY(igraph_inclist_destroy, &fathers); + + IGRAPH_VECTOR_INIT_FINALLY(&distance, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&tmpscore, no_of_nodes); + IGRAPH_CHECK(igraph_vector_long_init(&nrgeo, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &nrgeo); + + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + IGRAPH_CHECK(igraph_stack_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_stack_destroy, &S); + + IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges)); + igraph_vector_null(result); + + for (source = 0; source < no_of_nodes; source++) { + IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0 * source / no_of_nodes, 0); + IGRAPH_ALLOW_INTERRUPTION(); + + /* printf("source: %li\n", source); */ + + igraph_2wheap_push_with_index(&Q, source, -1.0); + VECTOR(distance)[source] = 1.0; + VECTOR(nrgeo)[source] = 1; + + while (!igraph_2wheap_empty(&Q)) { + long int minnei = igraph_2wheap_max_index(&Q); + igraph_real_t mindist = -igraph_2wheap_delete_max(&Q); + igraph_vector_int_t *neis; + long int nlen; + + /* printf("SP to %li is final, dist: %g, nrgeo: %li\n", minnei, */ + /* VECTOR(distance)[minnei]-1.0, VECTOR(nrgeo)[minnei]); */ + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && VECTOR(distance)[minnei] > cutoff + 1.0) { + /* Reset variables if node is too distant */ + VECTOR(tmpscore)[minnei] = 0; + VECTOR(distance)[minnei] = 0; + VECTOR(nrgeo)[minnei] = 0; + igraph_vector_int_clear(igraph_inclist_get(&fathers, minnei)); + continue; + } + + igraph_stack_push(&S, minnei); + + neis = igraph_inclist_get(&inclist, minnei); + nlen = igraph_vector_int_size(neis); + for (j = 0; j < nlen; j++) { + long int edge = (long int) VECTOR(*neis)[j]; + long int to = IGRAPH_OTHER(graph, edge, minnei); + igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; + igraph_real_t curdist = VECTOR(distance)[to]; + + if (curdist == 0) { + /* this means curdist is infinity */ + cmp_result = -1; + } else { + cmp_result = igraph_cmp_epsilon(altdist, curdist, eps); + } + + /* printf("to=%ld, altdist = %lg, curdist = %lg, cmp = %d\n", + to, altdist, curdist-1, cmp_result); */ + if (curdist == 0) { + /* This is the first finite distance to 'to' */ + igraph_vector_int_t *v = igraph_inclist_get(&fathers, to); + /* printf("Found first path to %li (from %li)\n", to, minnei); */ + igraph_vector_int_resize(v, 1); + VECTOR(*v)[0] = edge; + VECTOR(nrgeo)[to] = VECTOR(nrgeo)[minnei]; + VECTOR(distance)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, to, -altdist)); + } else if (cmp_result < 0) { + /* This is a shorter path */ + igraph_vector_int_t *v = igraph_inclist_get(&fathers, to); + /* printf("Found a shorter path to %li (from %li)\n", to, minnei); */ + igraph_vector_int_resize(v, 1); + VECTOR(*v)[0] = edge; + VECTOR(nrgeo)[to] = VECTOR(nrgeo)[minnei]; + VECTOR(distance)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_modify(&Q, to, -altdist)); + } else if (cmp_result == 0 && + (altdist <= cutoff + 1.0 || cutoff < 0)) { + /* Only add if the edge is not more distant than the cutoff */ + igraph_vector_int_t *v = igraph_inclist_get(&fathers, to); + /* printf("Found a second SP to %li (from %li)\n", to, minnei); */ + IGRAPH_CHECK(igraph_vector_int_push_back(v, edge)); + VECTOR(nrgeo)[to] += VECTOR(nrgeo)[minnei]; + } + } + + } /* igraph_2wheap_empty(&Q) */ + + while (!igraph_stack_empty(&S)) { + long int w = (long int) igraph_stack_pop(&S); + igraph_vector_int_t *fatv = igraph_inclist_get(&fathers, w); + long int fatv_len = igraph_vector_int_size(fatv); + /* printf("Popping %li.\n", w); */ + for (j = 0; j < fatv_len; j++) { + long int fedge = (long int) VECTOR(*fatv)[j]; + long int neighbor = IGRAPH_OTHER(graph, fedge, w); + VECTOR(tmpscore)[neighbor] += ((double)VECTOR(nrgeo)[neighbor]) / + VECTOR(nrgeo)[w] * (1.0 + VECTOR(tmpscore)[w]); + /* printf("Scoring %li (edge %li)\n", neighbor, fedge); */ + VECTOR(*result)[fedge] += + ((VECTOR(tmpscore)[w] + 1) * VECTOR(nrgeo)[neighbor]) / + VECTOR(nrgeo)[w]; + } + + /* Reset variables */ + VECTOR(tmpscore)[w] = 0; + VECTOR(distance)[w] = 0; + VECTOR(nrgeo)[w] = 0; + igraph_vector_int_clear(fatv); + } + + } /* source < no_of_nodes */ + + if (!directed || !igraph_is_directed(graph)) { + for (j = 0; j < no_of_edges; j++) { + VECTOR(*result)[j] /= 2.0; + } + } + + IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0, 0); + + igraph_stack_destroy(&S); + igraph_2wheap_destroy(&Q); + IGRAPH_FINALLY_CLEAN(2); + + igraph_inclist_destroy(&inclist); + igraph_inclist_destroy(&fathers); + igraph_vector_destroy(&distance); + igraph_vector_destroy(&tmpscore); + igraph_vector_long_destroy(&nrgeo); + IGRAPH_FINALLY_CLEAN(5); + + return 0; +} + +/** + * \ingroup structural + * \function igraph_edge_betweenness + * \brief Betweenness centrality of the edges. + * + * The betweenness centrality of an edge is the number of geodesics + * going through it. If there are more than one geodesics between two + * vertices, the value of these geodesics are weighted by one over the + * number of geodesics. + * + * \param graph The graph object. + * \param result The result of the computation, vector containing the + * betweenness scores for the edges. + * \param directed Logical, if true directed paths will be considered + * for directed graphs. It is ignored for undirected graphs. + * \param weights An optional weight vector for weighted edge + * betweenness. No edge weight may be NaN. Supply a null + * pointer here for the unweighted version. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * + * Time complexity: O(|V||E|), + * |V| and + * |E| are the number of vertices and + * edges in the graph. + * + * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness(). + * See \ref igraph_edge_betweenness() for calculating the betweenness score + * of the edges in a graph. See \ref igraph_edge_betweenness_cutoff() to + * compute the range-limited betweenness score of the edges in a graph. + */ +int igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, + igraph_bool_t directed, + const igraph_vector_t *weights) { + return igraph_edge_betweenness_cutoff(graph, result, directed, + weights, -1); +} + +/** + * \ingroup structural + * \function igraph_edge_betweenness_cutoff + * \brief Range-limited betweenness centrality of the edges. + * + * This function computes a range-limited version of edge betweenness centrality + * by considering only those shortest paths whose length is no greater + * then the given cutoff value. + * + * \param graph The graph object. + * \param result The result of the computation, vector containing the + * betweenness scores for the edges. + * \param directed Logical, if true directed paths will be considered + * for directed graphs. It is ignored for undirected graphs. + * \param weights An optional weight vector for weighted + * betweenness. No edge weight may be NaN. Supply a null + * pointer here for unweighted betweenness. + * \param cutoff The maximal length of paths that will be considered. + * If negative, the exact betweenness will be calculated (no + * upper limit on path lengths). + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * + * Time complexity: O(|V||E|), + * |V| and + * |E| are the number of vertices and + * edges in the graph. + * + * \sa \ref igraph_edge_betweenness() to compute the exact edge betweenness and + * \ref igraph_betweenness_cutoff() to compute the range-limited vertex betweenness. + */ +int igraph_edge_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *result, + igraph_bool_t directed, + const igraph_vector_t *weights, igraph_real_t cutoff) { + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + long int *distance; + double *nrgeo; + double *tmpscore; + igraph_stack_t stack = IGRAPH_STACK_NULL; + long int source; + long int j; + + igraph_inclist_t elist_out, elist_in; + igraph_inclist_t *elist_out_p, *elist_in_p; + igraph_vector_int_t *neip; + long int neino; + long int i; + + if (weights) { + return igraph_i_edge_betweenness_cutoff_weighted(graph, result, + directed, cutoff, weights); + } + + directed = directed && igraph_is_directed(graph); + if (directed) { + IGRAPH_CHECK(igraph_inclist_init(graph, &elist_out, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out); + IGRAPH_CHECK(igraph_inclist_init(graph, &elist_in, IGRAPH_IN, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &elist_in); + elist_out_p = &elist_out; + elist_in_p = &elist_in; + } else { + IGRAPH_CHECK(igraph_inclist_init(graph, &elist_out, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out); + elist_out_p = elist_in_p = &elist_out; + } + + distance = IGRAPH_CALLOC(no_of_nodes, long int); + if (distance == 0) { + IGRAPH_ERROR("Insufficient memory for edge betweenness calculation.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, distance); + nrgeo = IGRAPH_CALLOC(no_of_nodes, double); + if (nrgeo == 0) { + IGRAPH_ERROR("Insufficient memory for edge betweenness calculation.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, nrgeo); + tmpscore = IGRAPH_CALLOC(no_of_nodes, double); + if (tmpscore == 0) { + IGRAPH_ERROR("Insufficient memory for edge betweenness calculation.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, tmpscore); + + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + IGRAPH_CHECK(igraph_stack_init(&stack, no_of_nodes)); + IGRAPH_FINALLY(igraph_stack_destroy, &stack); + + IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges)); + + igraph_vector_null(result); + + /* here we go */ + + for (source = 0; source < no_of_nodes; source++) { + IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0 * source / no_of_nodes, 0); + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_dqueue_push(&q, source)); + + nrgeo[source] = 1; + distance[source] = 0; + + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + + if (cutoff >= 0 && distance[actnode] > cutoff ) { + /* Reset variables if node is too distant */ + distance[actnode] = 0; + tmpscore[actnode] = 0; + nrgeo[actnode] = 0; + continue; + } + + IGRAPH_CHECK(igraph_stack_push(&stack, actnode)); + + /* check the neighbors and add to them to the queue if unseen before */ + neip = igraph_inclist_get(elist_out_p, actnode); + neino = igraph_vector_int_size(neip); + for (i = 0; i < neino; i++) { + igraph_integer_t edge = (igraph_integer_t) VECTOR(*neip)[i]; + long int neighbor = (long int) IGRAPH_OTHER(graph, edge, actnode); + if (nrgeo[neighbor] != 0) { + /* we've already seen this node, another shortest path? */ + if (distance[neighbor] == distance[actnode] + 1) { + nrgeo[neighbor] += nrgeo[actnode]; + } + } else if (distance[actnode] + 1 <= cutoff || cutoff < 0) { + /* we haven't seen this node yet, but we only consider + * it if it is not more distant than the cutoff. */ + nrgeo[neighbor] += nrgeo[actnode]; + distance[neighbor] = distance[actnode] + 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + } + } + } /* while !igraph_dqueue_empty */ + + /* Ok, we've the distance of each node and also the number of + shortest paths to them. Now we do an inverse search, starting + with the farthest nodes. */ + while (!igraph_stack_empty(&stack)) { + long int actnode = (long int) igraph_stack_pop(&stack); + if (distance[actnode] < 1) { + distance[actnode] = 0; + tmpscore[actnode] = 0; + nrgeo[actnode] = 0; + continue; /* skip source node */ + } + /* set the temporary score of the friends */ + neip = igraph_inclist_get(elist_in_p, actnode); + neino = igraph_vector_int_size(neip); + for (i = 0; i < neino; i++) { + igraph_integer_t edgeno = (igraph_integer_t) VECTOR(*neip)[i]; + long int neighbor = (long int) IGRAPH_OTHER(graph, edgeno, actnode); + if (distance[neighbor] == distance[actnode] - 1 && + nrgeo[neighbor] != 0) { + tmpscore[neighbor] += + (tmpscore[actnode] + 1) * nrgeo[neighbor] / nrgeo[actnode]; + VECTOR(*result)[edgeno] += + (tmpscore[actnode] + 1) * nrgeo[neighbor] / nrgeo[actnode]; + } + } + /* Reset variables */ + distance[actnode] = 0; + tmpscore[actnode] = 0; + nrgeo[actnode] = 0; + } + /* Ok, we've the scores for this source */ + } /* for source <= no_of_nodes */ + IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0, 0); + + /* clean and return */ + IGRAPH_FREE(distance); + IGRAPH_FREE(nrgeo); + IGRAPH_FREE(tmpscore); + igraph_dqueue_destroy(&q); + igraph_stack_destroy(&stack); + IGRAPH_FINALLY_CLEAN(5); + + if (directed) { + igraph_inclist_destroy(&elist_out); + igraph_inclist_destroy(&elist_in); + IGRAPH_FINALLY_CLEAN(2); + } else { + igraph_inclist_destroy(&elist_out); + IGRAPH_FINALLY_CLEAN(1); + } + + /* divide by 2 for undirected graph */ + if (!directed || !igraph_is_directed(graph)) { + for (j = 0; j < igraph_vector_size(result); j++) { + VECTOR(*result)[j] /= 2.0; + } + } + + return 0; +} + +/** + * \ingroup structural + * \function igraph_edge_betweenness_estimate + * \brief Estimated betweenness centrality of the edges. + * + * \deprecated-by igraph_edge_betweenness_cutoff 0.9 + * + * + * The betweenness centrality of an edge is the number of geodesics + * going through it. If there are more than one geodesics between two + * vertices, the value of these geodesics are weighted by one over the + * number of geodesics. When estimating betweenness centrality, igraph + * takes into consideration only those paths that are shorter than or + * equal to a prescribed length. Note that the estimated centrality + * will always be less than the real one. + * + * \param graph The graph object. + * \param result The result of the computation, vector containing the + * betweenness scores for the edges. + * \param directed Logical, if true directed paths will be considered + * for directed graphs. It is ignored for undirected graphs. + * \param cutoff The maximal length of paths that will be considered. + * If negative, the exact betweenness will be calculated (no + * upper limit on path lengths). + * \param weights An optional weight vector for weighted betweenness. + * No edge weight may be NaN. Supply a null pointer here for + * unweighted betweenness. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * + * Time complexity: O(|V||E|), + * |V| and + * |E| are the number of vertices and + * edges in the graph. + * + * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness(). + * See \ref igraph_betweenness() for calculating the betweenness score + * of the vertices in a graph. + */ +int igraph_edge_betweenness_estimate(const igraph_t *graph, igraph_vector_t *result, + igraph_bool_t directed, igraph_real_t cutoff, + const igraph_vector_t *weights) { + IGRAPH_WARNING("igraph_edge_betweenness_estimate is deprecated, use igraph_edge_betweenness_cutoff."); + return igraph_edge_betweenness_cutoff(graph, result, directed, weights, cutoff); +} diff --git a/src/rigraph/core/centrality/centrality_other.c b/src/rigraph/core/centrality/centrality_other.c new file mode 100644 index 0000000..3c4c2ee --- /dev/null +++ b/src/rigraph/core/centrality/centrality_other.c @@ -0,0 +1,1584 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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 . +*/ + +#include "igraph_centrality.h" + +#include "igraph_memory.h" +#include "igraph_random.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_structural.h" +#include "igraph_topology.h" +#include "igraph_stack.h" +#include "igraph_dqueue.h" + +#include "../centrality/prpack_internal.h" +#include "../core/indheap.h" +#include "../core/interruption.h" +#include "../core/math.h" + +#include "config.h" + +#include +#include /* memset */ + +static int igraph_i_personalized_pagerank_arpack(const igraph_t *graph, + igraph_vector_t *vector, + igraph_real_t *value, const igraph_vs_t vids, + igraph_bool_t directed, igraph_real_t damping, + const igraph_vector_t *reset, + const igraph_vector_t *weights, + igraph_arpack_options_t *options); + +static igraph_bool_t igraph_i_vector_mostly_negative(const igraph_vector_t *vector) { + /* Many of the centrality measures correspond to the eigenvector of some + * matrix. When v is an eigenvector, c*v is also an eigenvector, therefore + * it may happen that all the scores in the eigenvector are negative, in which + * case we want to negate them since the centrality scores should be positive. + * However, since ARPACK is not always stable, sometimes it happens that + * *some* of the centrality scores are small negative numbers. This function + * helps distinguish between the two cases; it should return true if most of + * the values are relatively large negative numbers, in which case we should + * negate the eigenvector. + */ + long int n = igraph_vector_size(vector); + igraph_real_t mi, ma; + + if (n == 0) { + return 0; + } + + igraph_vector_minmax(vector, &mi, &ma); + + if (mi >= 0) { + return 0; + } + if (ma <= 0) { + return 1; + } + + /* is the most negative value larger in magnitude than the most positive? */ + return (-mi/ma > 1); +} + +static int igraph_i_eigenvector_centrality(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + igraph_adjlist_t *adjlist = extra; + igraph_vector_int_t *neis; + long int i, j, nlen; + + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(adjlist, i); + nlen = igraph_vector_int_size(neis); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + to[i] += from[nei]; + } + } + + + return 0; +} + +typedef struct igraph_i_eigenvector_centrality_t { + const igraph_t *graph; + const igraph_inclist_t *inclist; + const igraph_vector_t *weights; +} igraph_i_eigenvector_centrality_t; + +static int igraph_i_eigenvector_centrality2(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + igraph_i_eigenvector_centrality_t *data = extra; + const igraph_t *graph = data->graph; + const igraph_inclist_t *inclist = data->inclist; + const igraph_vector_t *weights = data->weights; + igraph_vector_int_t *edges; + long int i, j, nlen; + + for (i = 0; i < n; i++) { + edges = igraph_inclist_get(inclist, i); + nlen = igraph_vector_int_size(edges); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int edge = VECTOR(*edges)[j]; + long int nei = IGRAPH_OTHER(graph, edge, i); + igraph_real_t w = VECTOR(*weights)[edge]; + to[i] += w * from[nei]; + } + } + + return IGRAPH_SUCCESS; +} + +static int igraph_i_eigenvector_centrality_undirected(const igraph_t *graph, igraph_vector_t *vector, + igraph_real_t *value, igraph_bool_t scale, + const igraph_vector_t *weights, + igraph_arpack_options_t *options) { + + igraph_vector_t values; + igraph_matrix_t vectors; + igraph_vector_t degree; + long int i; + + options->n = igraph_vcount(graph); + options->start = 1; /* no random start vector */ + + if (igraph_ecount(graph) == 0) { + /* special case: empty graph */ + if (value) { + *value = 0; + } + if (vector) { + igraph_vector_resize(vector, igraph_vcount(graph)); + igraph_vector_fill(vector, 1); + } + return IGRAPH_SUCCESS; + } + + if (weights) { + igraph_real_t min, max; + + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid length of weights vector when calculating " + "eigenvector centrality", IGRAPH_EINVAL); + } + /* Safe to call minmax, ecount == 0 case was caught earlier */ + IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max)); + if (min == 0 && max == 0) { + /* special case: all weights are zeros */ + if (value) { + *value = 0; + } + if (vector) { + igraph_vector_resize(vector, igraph_vcount(graph)); + igraph_vector_fill(vector, 1); + } + return IGRAPH_SUCCESS; + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&values, 0); + IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1); + + IGRAPH_VECTOR_INIT_FINALLY(°ree, options->n); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_ALL, /*loops=*/ 0)); + RNG_BEGIN(); + for (i = 0; i < options->n; i++) { + if (VECTOR(degree)[i]) { + MATRIX(vectors, i, 0) = VECTOR(degree)[i] + RNG_UNIF(-1e-4, 1e-4); + } else { + MATRIX(vectors, i, 0) = 1.0; + } + } + RNG_END(); + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + + options->n = igraph_vcount(graph); + options->nev = 1; + options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rssolve */ + options->which[0] = 'L'; options->which[1] = 'A'; + options->start = 1; /* no random start vector */ + + if (!weights) { + + igraph_adjlist_t adjlist; + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_eigenvector_centrality, + &adjlist, options, 0, &values, &vectors)); + + igraph_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + + } else { + + igraph_inclist_t inclist; + igraph_i_eigenvector_centrality_t data; + + data.graph = graph; + data.inclist = &inclist; + data.weights = weights; + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + + IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_eigenvector_centrality2, + &data, options, 0, &values, &vectors)); + + igraph_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(1); + } + + if (value) { + *value = VECTOR(values)[0]; + } + + if (vector) { + igraph_real_t amax = 0; + long int which = 0; + long int i; + IGRAPH_CHECK(igraph_vector_resize(vector, options->n)); + + if (VECTOR(values)[0] <= 0) { + /* Pathological case: largest eigenvalue is zero, therefore all the + * scores can also be zeros, this will be a valid eigenvector. + * This usually happens with graphs that have lots of sinks and + * sources only. */ + igraph_vector_fill(vector, 0); + } else { + for (i = 0; i < options->n; i++) { + igraph_real_t tmp; + VECTOR(*vector)[i] = MATRIX(vectors, i, 0); + tmp = fabs(VECTOR(*vector)[i]); + if (tmp > amax) { + amax = tmp; + which = i; + } + } + if (scale && amax != 0) { + igraph_vector_scale(vector, 1 / VECTOR(*vector)[which]); + } else if (igraph_i_vector_mostly_negative(vector)) { + igraph_vector_scale(vector, -1.0); + } + + /* Correction for numeric inaccuracies (eliminating -0.0) */ + for (i = 0; i < options->n; i++) { + if (VECTOR(*vector)[i] < 0) { + VECTOR(*vector)[i] = 0; + } + } + } + } + + if (options->info) { + IGRAPH_WARNING("Non-zero return code from ARPACK routine!"); + } + + igraph_matrix_destroy(&vectors); + igraph_vector_destroy(&values); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/* int igraph_i_evcent_dir(igraph_real_t *to, const igraph_real_t *from, */ +/* long int n, void *extra) { */ +/* /\* TODO *\/ */ +/* return 0; */ +/* } */ + +/* int igraph_i_evcent_dir2(igraph_real_t *to, const igraph_real_t *from, */ +/* long int n, void *extra) { */ +/* /\* TODO *\/ */ +/* return 0; */ +/* } */ + +static int igraph_i_eigenvector_centrality_directed(const igraph_t *graph, igraph_vector_t *vector, + igraph_real_t *value, igraph_bool_t scale, + const igraph_vector_t *weights, + igraph_arpack_options_t *options) { + + igraph_matrix_t values; + igraph_matrix_t vectors; + igraph_vector_t indegree; + igraph_bool_t dag; + long int i; + + if (igraph_ecount(graph) == 0) { + /* special case: empty graph */ + if (value) { + *value = 0; + } + if (vector) { + igraph_vector_resize(vector, igraph_vcount(graph)); + igraph_vector_fill(vector, 1); + } + return IGRAPH_SUCCESS; + } + + /* Quick check: if the graph is a DAG, all the eigenvector centralities are + * zeros, and so is the eigenvalue */ + IGRAPH_CHECK(igraph_is_dag(graph, &dag)); + if (dag) { + /* special case: graph is a DAG */ + IGRAPH_WARNING("graph is directed and acyclic; eigenvector centralities " + "will be zeros"); + if (value) { + *value = 0; + } + if (vector) { + igraph_vector_resize(vector, igraph_vcount(graph)); + igraph_vector_fill(vector, 0); + } + return IGRAPH_SUCCESS; + } + + if (weights) { + igraph_real_t min, max; + + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid length of weights vector when calculating " + "eigenvector centrality", IGRAPH_EINVAL); + } + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("Weighted directed graph in eigenvector centrality"); + } + + /* Safe to call minmax, ecount == 0 case was caught earlier */ + IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max)); + + if (min < 0.0) { + IGRAPH_WARNING("Negative weights, eigenpair might be complex"); + } + if (min == 0.0 && max == 0.0) { + /* special case: all weights are zeros */ + if (value) { + *value = 0; + } + if (vector) { + igraph_vector_resize(vector, igraph_vcount(graph)); + igraph_vector_fill(vector, 1); + } + return IGRAPH_SUCCESS; + } + } + + options->n = igraph_vcount(graph); + options->start = 1; + options->nev = 1; + options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rnsolve */ + /* LM mode is not OK here because +1 and -1 can be eigenvalues at the + * same time, e.g.: a -> b -> a, c -> a */ + options->which[0] = 'L' ; options->which[1] = 'R'; + + IGRAPH_MATRIX_INIT_FINALLY(&values, 0, 0); + IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1); + + IGRAPH_VECTOR_INIT_FINALLY(&indegree, options->n); + IGRAPH_CHECK(igraph_strength(graph, &indegree, igraph_vss_all(), + IGRAPH_IN, /*loops=*/ 1, weights)); + RNG_BEGIN(); + for (i = 0; i < options->n; i++) { + if (VECTOR(indegree)[i]) { + MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(-1e-4, 1e-4); + } else { + MATRIX(vectors, i, 0) = 1.0; + } + } + RNG_END(); + igraph_vector_destroy(&indegree); + IGRAPH_FINALLY_CLEAN(1); + + if (!weights) { + igraph_adjlist_t adjlist; + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + IGRAPH_CHECK(igraph_arpack_rnsolve(igraph_i_eigenvector_centrality, + &adjlist, options, 0, &values, + &vectors)); + + igraph_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + } else { + igraph_inclist_t inclist; + igraph_i_eigenvector_centrality_t data; + + data.graph = graph; + data.inclist = &inclist; + data.weights = weights; + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_IN, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + + IGRAPH_CHECK(igraph_arpack_rnsolve(igraph_i_eigenvector_centrality2, + &data, options, 0, &values, &vectors)); + + igraph_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(1); + } + + if (value) { + *value = MATRIX(values, 0, 0); + } + + if (vector) { + igraph_real_t amax = 0; + long int which = 0; + long int i; + IGRAPH_CHECK(igraph_vector_resize(vector, options->n)); + + if (MATRIX(values, 0, 0) <= 0) { + /* Pathological case: largest eigenvalue is zero, therefore all the + * scores can also be zeros, this will be a valid eigenvector. + * This usually happens with graphs that have lots of sinks and + * sources only. */ + igraph_vector_fill(vector, 0); + MATRIX(values, 0, 0) = 0; + } else { + for (i = 0; i < options->n; i++) { + igraph_real_t tmp; + VECTOR(*vector)[i] = MATRIX(vectors, i, 0); + tmp = fabs(VECTOR(*vector)[i]); + if (tmp > amax) { + amax = tmp; + which = i; + } + } + if (scale && amax != 0) { + igraph_vector_scale(vector, 1 / VECTOR(*vector)[which]); + } else if (igraph_i_vector_mostly_negative(vector)) { + igraph_vector_scale(vector, -1.0); + } + } + + /* Correction for numeric inaccuracies (eliminating -0.0) */ + for (i = 0; i < options->n; i++) { + if (VECTOR(*vector)[i] < 0) { + VECTOR(*vector)[i] = 0; + } + } + } + + if (options->info) { + IGRAPH_WARNING("Non-zero return code from ARPACK routine!"); + } + + igraph_matrix_destroy(&vectors); + igraph_matrix_destroy(&values); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \function igraph_eigenvector_centrality + * Eigenvector centrality of the vertices + * + * Eigenvector centrality is a measure of the importance of a node in a + * network. It assigns relative scores to all nodes in the network based + * on the principle that connections from high-scoring nodes contribute + * more to the score of the node in question than equal connections from + * low-scoring nodes. Specifically, the eigenvector centrality of each + * vertex is proportional to the sum of eigenvector centralities of its + * neighbors. In practice, the centralities are determined by calculating the + * eigenvector corresponding to the largest positive eigenvalue of the + * adjacency matrix. In the undirected case, this function considers + * the diagonal entries of the adjacency matrix to be \em twice the number of + * self-loops on the corresponding vertex. + * + * + * In the weighted case, the eigenvector centrality of a vertex is proportional + * to the weighted sum of centralities of its neighbours, i.e. + * c_i = sum_j w_ij c_j, where w_ij is the weight + * of the edge connecting vertices \c i and \c j. The weights of parallel edges + * are added up. + * + * + * The centrality scores returned by igraph can be normalized + * (using the \p scale parameter) such that the largest eigenvector centrality + * score is 1 (with one exception, see below). + * + * + * In the directed case, the left eigenvector of the adjacency matrix is + * calculated. In other words, the centrality of a vertex is proportional + * to the sum of centralities of vertices pointing to it. + * + * + * Eigenvector centrality is meaningful only for connected graphs. + * Graphs that are not connected should be decomposed into connected + * components, and the eigenvector centrality calculated for each separately. + * This function does not verify that the graph is connected. If it is not, + * in the undirected case the scores of all but one component will be zeros. + * + * + * Also note that the adjacency matrix of a directed acyclic graph or the + * adjacency matrix of an empty graph does not possess positive eigenvalues, + * therefore the eigenvector centrality is not defined for these graphs. + * igraph will return an eigenvalue of zero in such cases. The eigenvector + * centralities will all be equal for an empty graph and will all be zeros + * for a directed acyclic graph. Such pathological cases can be detected + * by asking igraph to calculate the eigenvalue as well (using the \p value + * parameter, see below) and checking whether the eigenvalue is very close + * to zero. + * + * \param graph The input graph. It may be directed. + * \param vector Pointer to an initialized vector, it will be resized + * as needed. The result of the computation is stored here. It can + * be a null pointer, then it is ignored. + * \param value If not a null pointer, then the eigenvalue + * corresponding to the found eigenvector is stored here. + * \param directed Boolean scalar, whether to consider edge directions + * in a directed graph. It is ignored for undirected graphs. + * \param scale If not zero then the result will be scaled such that + * the absolute value of the maximum centrality is one. + * \param weights A null pointer (= no edge weights), or a vector + * giving the weights of the edges. The algorithm might produce + * complex numbers when some weights are negative. In this case only + * the real part is reported. + * \param options Options to ARPACK. See \ref igraph_arpack_options_t + * for details. Note that the function overwrites the + * n (number of vertices) parameter and + * it always starts the calculation from a non-random vector + * calculated based on the degree of the vertices. + * \return Error code. + * + * Time complexity: depends on the input graph, usually it is O(|V|+|E|). + * + * \sa \ref igraph_pagerank and \ref igraph_personalized_pagerank for + * modifications of eigenvector centrality. + * + * \example examples/simple/eigenvector_centrality.c + */ + +int igraph_eigenvector_centrality(const igraph_t *graph, + igraph_vector_t *vector, + igraph_real_t *value, + igraph_bool_t directed, igraph_bool_t scale, + const igraph_vector_t *weights, + igraph_arpack_options_t *options) { + + if (directed && igraph_is_directed(graph)) { + return igraph_i_eigenvector_centrality_directed(graph, vector, value, + scale, weights, options); + } else { + return igraph_i_eigenvector_centrality_undirected(graph, vector, value, + scale, weights, options); + } +} + +/* struct for the unweighted variant of the HITS algorithm */ +typedef struct igraph_i_kleinberg_data_t { + igraph_adjlist_t *in; + igraph_adjlist_t *out; + igraph_vector_t *tmp; +} igraph_i_kleinberg_data_t; + +/* struct for the weighted variant of the HITS algorithm */ +typedef struct igraph_i_kleinberg_data2_t { + const igraph_t *graph; + igraph_inclist_t *in; + igraph_inclist_t *out; + igraph_vector_t *tmp; + const igraph_vector_t *weights; +} igraph_i_kleinberg_data2_t; + +/* ARPACK auxiliary routine for the unweighted HITS algorithm */ +static int igraph_i_kleinberg_unweighted(igraph_real_t *to, + const igraph_real_t *from, + int n, void *extra) { + igraph_i_kleinberg_data_t *data = (igraph_i_kleinberg_data_t*)extra; + igraph_adjlist_t *in = data->in; + igraph_adjlist_t *out = data->out; + igraph_vector_t *tmp = data->tmp; + igraph_vector_int_t *neis; + long int i, j, nlen; + + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(in, i); + nlen = igraph_vector_int_size(neis); + VECTOR(*tmp)[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + VECTOR(*tmp)[i] += from[nei]; + } + } + + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(out, i); + nlen = igraph_vector_int_size(neis); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + to[i] += VECTOR(*tmp)[nei]; + } + } + + return 0; +} + +/* ARPACK auxiliary routine for the weighted HITS algorithm */ +static int igraph_i_kleinberg_weighted(igraph_real_t *to, + const igraph_real_t *from, + int n, void *extra) { + + igraph_i_kleinberg_data2_t *data = (igraph_i_kleinberg_data2_t*)extra; + igraph_inclist_t *in = data->in; + igraph_inclist_t *out = data->out; + igraph_vector_t *tmp = data->tmp; + const igraph_vector_t *weights = data->weights; + const igraph_t *g = data->graph; + igraph_vector_int_t *neis; + long int i, j, nlen; + + for (i = 0; i < n; i++) { + neis = igraph_inclist_get(in, i); + nlen = igraph_vector_int_size(neis); + VECTOR(*tmp)[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int nei_edge = (long int) VECTOR(*neis)[j]; + long int nei = IGRAPH_OTHER(g, nei_edge, i); + VECTOR(*tmp)[i] += from[nei] * VECTOR(*weights)[nei_edge]; + } + } + + for (i = 0; i < n; i++) { + neis = igraph_inclist_get(out, i); + nlen = igraph_vector_int_size(neis); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int nei_edge = (long int) VECTOR(*neis)[j]; + long int nei = IGRAPH_OTHER(g, nei_edge, i); + to[i] += VECTOR(*tmp)[nei] * VECTOR(*weights)[nei_edge]; + } + } + + return 0; +} + +static int igraph_i_kleinberg(const igraph_t *graph, igraph_vector_t *vector, + igraph_real_t *value, igraph_bool_t scale, + const igraph_vector_t *weights, + igraph_arpack_options_t *options, int inout) { + + igraph_adjlist_t myinadjlist, myoutadjlist; + igraph_inclist_t myininclist, myoutinclist; + igraph_adjlist_t *inadjlist, *outadjlist; + igraph_inclist_t *ininclist, *outinclist; + igraph_vector_t tmp; + igraph_vector_t values; + igraph_matrix_t vectors; + igraph_i_kleinberg_data_t extra; + igraph_i_kleinberg_data2_t extra2; + long int i; + + if (igraph_ecount(graph) == 0 || igraph_vcount(graph) == 1) { + /* special case: empty graph or single vertex */ + if (value) { + *value = igraph_ecount(graph) ? 1.0 : IGRAPH_NAN; + } + if (vector) { + igraph_vector_resize(vector, igraph_vcount(graph)); + igraph_vector_fill(vector, 1); + } + return IGRAPH_SUCCESS; + } + + if (weights) { + igraph_real_t min, max; + + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid length of weights vector when calculating " + "hub or authority scores", IGRAPH_EINVAL); + } + /* Safe to call minmax, ecount == 0 case was caught earlier */ + IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max)); + if (min == 0 && max == 0) { + /* special case: all weights are zeros */ + if (value) { + *value = IGRAPH_NAN; + } + if (vector) { + igraph_vector_resize(vector, igraph_vcount(graph)); + igraph_vector_fill(vector, 1); + } + return IGRAPH_SUCCESS; + } + } + + options->n = igraph_vcount(graph); + options->start = 1; /* no random start vector */ + + IGRAPH_VECTOR_INIT_FINALLY(&values, 0); + IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, options->n); + + if (inout == 0) { + inadjlist = &myinadjlist; + outadjlist = &myoutadjlist; + ininclist = &myininclist; + outinclist = &myoutinclist; + } else if (inout == 1) { + inadjlist = &myoutadjlist; + outadjlist = &myinadjlist; + ininclist = &myoutinclist; + outinclist = &myininclist; + } else { + /* This should not happen */ + IGRAPH_ERROR("Invalid 'inout' argument, please do not call " + "this function directly", IGRAPH_FAILURE); + } + + if (weights == 0) { + IGRAPH_CHECK(igraph_adjlist_init(graph, &myinadjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &myinadjlist); + IGRAPH_CHECK(igraph_adjlist_init(graph, &myoutadjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &myoutadjlist); + } else { + IGRAPH_CHECK(igraph_inclist_init(graph, &myininclist, IGRAPH_IN, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &myininclist); + IGRAPH_CHECK(igraph_inclist_init(graph, &myoutinclist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &myoutinclist); + } + + IGRAPH_CHECK(igraph_degree(graph, &tmp, igraph_vss_all(), IGRAPH_ALL, 0)); + for (i = 0; i < options->n; i++) { + if (VECTOR(tmp)[i] != 0) { + MATRIX(vectors, i, 0) = VECTOR(tmp)[i]; + } else { + MATRIX(vectors, i, 0) = 1.0; + } + } + + extra.in = inadjlist; extra.out = outadjlist; extra.tmp = &tmp; + extra2.in = ininclist; extra2.out = outinclist; extra2.tmp = &tmp; + extra2.graph = graph; extra2.weights = weights; + + options->nev = 1; + options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rssolve */ + options->which[0] = 'L'; options->which[1] = 'M'; + + if (weights == 0) { + IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_kleinberg_unweighted, &extra, + options, 0, &values, &vectors)); + igraph_adjlist_destroy(&myoutadjlist); + igraph_adjlist_destroy(&myinadjlist); + IGRAPH_FINALLY_CLEAN(2); + } else { + IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_kleinberg_weighted, &extra2, + options, 0, &values, &vectors)); + igraph_inclist_destroy(&myoutinclist); + igraph_inclist_destroy(&myininclist); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + if (value) { + *value = VECTOR(values)[0]; + } + + if (vector) { + igraph_real_t amax = 0; + long int which = 0; + long int i; + IGRAPH_CHECK(igraph_vector_resize(vector, options->n)); + for (i = 0; i < options->n; i++) { + igraph_real_t tmp; + VECTOR(*vector)[i] = MATRIX(vectors, i, 0); + tmp = fabs(VECTOR(*vector)[i]); + if (tmp > amax) { + amax = tmp; + which = i; + } + } + if (scale && amax != 0) { + igraph_vector_scale(vector, 1 / VECTOR(*vector)[which]); + } else if (igraph_i_vector_mostly_negative(vector)) { + igraph_vector_scale(vector, -1.0); + } + + /* Correction for numeric inaccuracies (eliminating -0.0) */ + for (i = 0; i < options->n; i++) { + if (VECTOR(*vector)[i] < 0) { + VECTOR(*vector)[i] = 0; + } + } + } + + if (options->info) { + IGRAPH_WARNING("Non-zero return code from ARPACK routine!"); + } + igraph_matrix_destroy(&vectors); + igraph_vector_destroy(&values); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \function igraph_hub_score + * \brief Kleinberg's hub scores. + * + * The hub scores of the vertices are defined as the principal + * eigenvector of A*A^T, where A is the adjacency + * matrix of the graph, A^T is its transposed. + * + * See the following reference on the meaning of this score: + * J. Kleinberg. Authoritative sources in a hyperlinked + * environment. \emb Proc. 9th ACM-SIAM Symposium on Discrete + * Algorithms, \eme 1998. Extended version in \emb Journal of the + * ACM \eme 46(1999). Also appears as IBM Research Report RJ 10076, May + * 1997. + * \param graph The input graph. Can be directed and undirected. + * \param vector Pointer to an initialized vector, the result is + * stored here. If a null pointer then it is ignored. + * \param value If not a null pointer then the eigenvalue + * corresponding to the calculated eigenvector is stored here. + * \param scale If not zero then the result will be scaled such that + * the absolute value of the maximum centrality is one. + * \param weights A null pointer (=no edge weights), or a vector + * giving the weights of the edges. + * \param options Options to ARPACK. See \ref igraph_arpack_options_t + * for details. Note that the function overwrites the + * n (number of vertices) parameter and + * it always starts the calculation from a non-random vector + * calculated based on the degree of the vertices. + * \return Error code. + * + * Time complexity: depends on the input graph, usually it is O(|V|), + * the number of vertices. + * + * \sa \ref igraph_authority_score() for the companion measure, + * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(), + * \ref igraph_eigenvector_centrality() for similar measures. + */ + +int igraph_hub_score(const igraph_t *graph, igraph_vector_t *vector, + igraph_real_t *value, igraph_bool_t scale, + const igraph_vector_t *weights, + igraph_arpack_options_t *options) { + + return igraph_i_kleinberg(graph, vector, value, scale, weights, options, 0); +} + +/** + * \function igraph_authority_score + * \brief Kleinerg's authority scores. + * + * The authority scores of the vertices are defined as the principal + * eigenvector of A^T*A, where A is the adjacency + * matrix of the graph, A^T is its transposed. + * + * See the following reference on the meaning of this score: + * J. Kleinberg. Authoritative sources in a hyperlinked + * environment. \emb Proc. 9th ACM-SIAM Symposium on Discrete + * Algorithms, \eme 1998. Extended version in \emb Journal of the + * ACM \eme 46(1999). Also appears as IBM Research Report RJ 10076, May + * 1997. + * \param graph The input graph. Can be directed and undirected. + * \param vector Pointer to an initialized vector, the result is + * stored here. If a null pointer then it is ignored. + * \param value If not a null pointer then the eigenvalue + * corresponding to the calculated eigenvector is stored here. + * \param scale If not zero then the result will be scaled such that + * the absolute value of the maximum centrality is one. + * \param weights A null pointer (=no edge weights), or a vector + * giving the weights of the edges. + * \param options Options to ARPACK. See \ref igraph_arpack_options_t + * for details. Note that the function overwrites the + * n (number of vertices) parameter and + * it always starts the calculation from a non-random vector + * calculated based on the degree of the vertices. + * \return Error code. + * + * Time complexity: depends on the input graph, usually it is O(|V|), + * the number of vertices. + * + * \sa \ref igraph_hub_score() for the companion measure, + * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(), + * \ref igraph_eigenvector_centrality() for similar measures. + */ + +int igraph_authority_score(const igraph_t *graph, igraph_vector_t *vector, + igraph_real_t *value, igraph_bool_t scale, + const igraph_vector_t *weights, + igraph_arpack_options_t *options) { + + return igraph_i_kleinberg(graph, vector, value, scale, weights, options, 1); +} + +typedef struct igraph_i_pagerank_data_t { + const igraph_t *graph; + igraph_adjlist_t *adjlist; + igraph_real_t damping; + igraph_vector_t *outdegree; + igraph_vector_t *tmp; + igraph_vector_t *reset; +} igraph_i_pagerank_data_t; + +typedef struct igraph_i_pagerank_data2_t { + const igraph_t *graph; + igraph_inclist_t *inclist; + const igraph_vector_t *weights; + igraph_real_t damping; + igraph_vector_t *outdegree; + igraph_vector_t *tmp; + igraph_vector_t *reset; +} igraph_i_pagerank_data2_t; + +static int igraph_i_pagerank(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + igraph_i_pagerank_data_t *data = extra; + igraph_adjlist_t *adjlist = data->adjlist; + igraph_vector_t *outdegree = data->outdegree; + igraph_vector_t *tmp = data->tmp; + igraph_vector_t *reset = data->reset; + igraph_vector_int_t *neis; + long int i, j, nlen; + igraph_real_t sumfrom = 0.0; + igraph_real_t fact = 1 - data->damping; + + /* Calculate p(x) / outdegree(x) in advance for all the vertices. + * Note that we may divide by zero here; this is intentional since + * we won't use those values and we save a comparison this way. + * At the same time, we calculate the global probability of a + * random jump in `sumfrom`. For vertices with no outgoing edges, + * we will surely jump from there if we are there, hence those + * vertices contribute p(x) to the teleportation probability. + * For vertices with some outgoing edges, we jump from there with + * probability `fact` if we are there, hence they contribute + * p(x)*fact */ + for (i = 0; i < n; i++) { + sumfrom += VECTOR(*outdegree)[i] != 0 ? from[i] * fact : from[i]; + VECTOR(*tmp)[i] = from[i] / VECTOR(*outdegree)[i]; + } + + /* Here we calculate the part of the `to` vector that results from + * moving along links (and not from teleportation) */ + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(adjlist, i); + nlen = igraph_vector_int_size(neis); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + to[i] += VECTOR(*tmp)[nei]; + } + to[i] *= data->damping; + } + + /* Now we add the contribution from random jumps. `reset` is a vector + * that defines the probability of ending up in vertex i after a jump. + * `sumfrom` is the global probability of jumping as mentioned above. */ + /* printf("sumfrom = %.6f\n", (float)sumfrom); */ + + if (reset) { + /* Running personalized PageRank */ + for (i = 0; i < n; i++) { + to[i] += sumfrom * VECTOR(*reset)[i]; + } + } else { + /* Traditional PageRank with uniform reset vector */ + sumfrom /= n; + for (i = 0; i < n; i++) { + to[i] += sumfrom; + } + } + + return 0; +} + +static int igraph_i_pagerank2(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + igraph_i_pagerank_data2_t *data = extra; + const igraph_t *graph = data->graph; + igraph_inclist_t *inclist = data->inclist; + const igraph_vector_t *weights = data->weights; + igraph_vector_t *outdegree = data->outdegree; + igraph_vector_t *tmp = data->tmp; + igraph_vector_t *reset = data->reset; + long int i, j, nlen; + igraph_real_t sumfrom = 0.0; + igraph_vector_int_t *neis; + igraph_real_t fact = 1 - data->damping; + + /* + printf("PageRank weighted: multiplying vector: "); + for (i=0; idamping; + } + + /* printf("sumfrom = %.6f\n", (float)sumfrom); */ + + if (reset) { + /* Running personalized PageRank */ + for (i = 0; i < n; i++) { + to[i] += sumfrom * VECTOR(*reset)[i]; + } + } else { + /* Traditional PageRank with uniform reset vector */ + sumfrom /= n; + for (i = 0; i < n; i++) { + to[i] += sumfrom; + } + } + + /* + printf("PageRank weighted: multiplied vector: "); + for (i=0; i1 - damping. + * If the random walker gets stuck in a sink vertex, it will also restart + * from a random vertex. + * + * + * The PageRank centrality is mainly useful for directed graphs. In undirected + * graphs it converges to trivial values proportional to degrees as the damping + * factor approaches 1. + * + * + * Starting from version 0.9, igraph has two PageRank implementations, + * and the user can choose between them. The first implementation is + * \c IGRAPH_PAGERANK_ALGO_ARPACK, based on the ARPACK library. This + * was the default before igraph version 0.7. The second and recommended + * implementation is \c IGRAPH_PAGERANK_ALGO_PRPACK. This is using the + * PRPACK package, see https://github.com/dgleich/prpack . + * + * + * Note that the PageRank of a given vertex depends on the PageRank + * of all other vertices, so even if you want to calculate the PageRank for + * only some of the vertices, all of them must be calculated. Requesting + * the PageRank for only some of the vertices does not result in any + * performance increase at all. + * + * + * References: + * + * + * Sergey Brin and Larry Page: The Anatomy of a Large-Scale Hypertextual + * Web Search Engine. Proceedings of the 7th World-Wide Web Conference, + * Brisbane, Australia, April 1998. + * + * \param graph The graph object. + * \param algo The PageRank implementation to use. Possible values: + * \c IGRAPH_PAGERANK_ALGO_ARPACK, \c IGRAPH_PAGERANK_ALGO_PRPACK. + * \param vector Pointer to an initialized vector, the result is + * stored here. It is resized as needed. + * \param value Pointer to a real variable, the eigenvalue + * corresponding to the PageRank vector is stored here. It should + * be always exactly one. + * \param vids The vertex ids for which the PageRank is returned. + * \param directed Boolean, whether to consider the directedness of + * the edges. This is ignored for undirected graphs. + * \param damping The damping factor ("d" in the original paper). + * Must be a probability in the range [0, 1]. A commonly used value is 0.85. + * \param weights Optional edge weights. May be a \c NULL pointer, + * meaning unweighted edges, or a vector of non-negative values + * of the same length as the number of edges. + * \param options Options for the ARPACK method. See \ref igraph_arpack_options_t + * for details. Note that the function overwrites the n (number + * of vertices), nev (1), ncv (3) and which + * (LM) parameters and it always starts the calculation from a non-random vector + * calculated based on the degree of the vertices. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for temporary data. + * \c IGRAPH_EINVVID, invalid vertex id in \p vids. + * + * Time complexity: depends on the input graph, usually it is O(|E|), + * the number of edges. + * + * \sa \ref igraph_personalized_pagerank() and \ref igraph_personalized_pagerank_vs() + * for the personalized PageRank measure. See \ref igraph_arpack_rssolve() and + * \ref igraph_arpack_rnsolve() for the underlying machinery used by + * \c IGRAPH_PAGERANK_ALGO_ARPACK. + * + * \example examples/simple/igraph_pagerank.c + */ + +int igraph_pagerank(const igraph_t *graph, igraph_pagerank_algo_t algo, + igraph_vector_t *vector, + igraph_real_t *value, const igraph_vs_t vids, + igraph_bool_t directed, igraph_real_t damping, + const igraph_vector_t *weights, igraph_arpack_options_t *options) { + return igraph_personalized_pagerank(graph, algo, vector, value, vids, + directed, damping, NULL, weights, + options); +} + +/** + * \function igraph_personalized_pagerank_vs + * \brief Calculates the personalized Google PageRank for the specified vertices. + * + * The personalized PageRank is similar to the original PageRank measure, but + * when the random walk is restarted, a new starting vertex is chosen according to + * a specified distribution. + * This distribution is used both when restarting randomly with probability + * 1 - damping, and when the walker is forced to restart due to being + * stuck in a sink vertex (a vertex with no outgoing edges). + * + * + * This simplified interface takes a vertex sequence and resets the random walk to + * one of the vertices in the specified vertex sequence, chosen uniformly. A typical + * application of personalized PageRank is when the random walk is reset to the same + * vertex every time - this can easily be achieved using \ref igraph_vss_1() which + * generates a vertex sequence containing only a single vertex. + * + * + * Note that the personalized PageRank of a given vertex depends on the + * personalized PageRank of all other vertices, so even if you want to calculate + * the personalized PageRank for only some of the vertices, all of them must be + * calculated. Requesting the personalized PageRank for only some of the vertices + * does not result in any performance increase at all. + * + * + * + * \param graph The graph object. + * \param algo The PageRank implementation to use. Possible values: + * \c IGRAPH_PAGERANK_ALGO_ARPACK, \c IGRAPH_PAGERANK_ALGO_PRPACK. + * \param vector Pointer to an initialized vector, the result is + * stored here. It is resized as needed. + * \param value Pointer to a real variable, the eigenvalue + * corresponding to the PageRank vector is stored here. It should + * be always exactly one. + * \param vids The vertex ids for which the PageRank is returned. + * \param directed Boolean, whether to consider the directedness of + * the edges. This is ignored for undirected graphs. + * \param damping The damping factor ("d" in the original paper). + * Must be a probability in the range [0, 1]. A commonly used value is 0.85. + * \param reset_vids IDs of the vertices used when resetting the random walk. + * \param weights Optional edge weights, it is either a null pointer, + * then the edges are not weighted, or a vector of the same length + * as the number of edges. + * \param options Options for the ARPACK method. See \ref igraph_arpack_options_t + * for details. Note that the function overwrites the n (number + * of vertices), nev (1), ncv (3) and which + * (LM) parameters and it always starts the calculation from a non-random vector + * calculated based on the degree of the vertices. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * \c IGRAPH_EINVVID, invalid vertex id in + * \p vids or an empty reset vertex sequence in + * \p vids_reset. + * + * Time complexity: depends on the input graph, usually it is O(|E|), + * the number of edges. + * + * \sa \ref igraph_pagerank() for the non-personalized implementation. + */ + +int igraph_personalized_pagerank_vs(const igraph_t *graph, + igraph_pagerank_algo_t algo, igraph_vector_t *vector, + igraph_real_t *value, const igraph_vs_t vids, + igraph_bool_t directed, igraph_real_t damping, + igraph_vs_t reset_vids, + const igraph_vector_t *weights, + igraph_arpack_options_t *options) { + igraph_vector_t reset; + igraph_vit_t vit; + + IGRAPH_VECTOR_INIT_FINALLY(&reset, igraph_vcount(graph)); + IGRAPH_CHECK(igraph_vit_create(graph, reset_vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + while (!IGRAPH_VIT_END(vit)) { + VECTOR(reset)[(long int)IGRAPH_VIT_GET(vit)]++; + IGRAPH_VIT_NEXT(vit); + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_personalized_pagerank(graph, algo, vector, + value, vids, directed, + damping, &reset, weights, + options)); + + igraph_vector_destroy(&reset); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_personalized_pagerank + * \brief Calculates the personalized Google PageRank for the specified vertices. + * + * The personalized PageRank is similar to the original PageRank measure, but + * when the random walk is restarted, a new starting vertex is chosen non-uniformly, + * according to the distribution specified in \p reset + * (instead of the uniform distribution in the original PageRank measure). + * The \p reset distribution is used both when restarting randomly with probability + * 1 - damping, and when the walker is forced to restart due to being + * stuck in a sink vertex (a vertex with no outgoing edges). + * + * + * Note that the personalized PageRank of a given vertex depends on the + * personalized PageRank of all other vertices, so even if you want to calculate + * the personalized PageRank for only some of the vertices, all of them must be + * calculated. Requesting the personalized PageRank for only some of the vertices + * does not result in any performance increase at all. + * + * + * + * \param graph The graph object. + * \param algo The PageRank implementation to use. Possible values: + * \c IGRAPH_PAGERANK_ALGO_ARPACK, \c IGRAPH_PAGERANK_ALGO_PRPACK. + * \param vector Pointer to an initialized vector, the result is + * stored here. It is resized as needed. + * \param value Pointer to a real variable, the eigenvalue + * corresponding to the PageRank vector is stored here. It should + * be always exactly one. + * \param vids The vertex ids for which the PageRank is returned. + * \param directed Boolean, whether to consider the directedness of + * the edges. This is ignored for undirected graphs. + * \param damping The damping factor ("d" in the original paper). + * Must be a probability in the range [0, 1]. A commonly used value is 0.85. + * \param reset The probability distribution over the vertices used when + * resetting the random walk. It is either a \c NULL pointer (denoting + * a uniform choice that results in the original PageRank measure) + * or a vector of the same length as the number of vertices. + * \param weights Optional edge weights. May be a \c NULL pointer, + * meaning unweighted edges, or a vector of non-negative values + * of the same length as the number of edges. + * \param options Options for the ARPACK method. See \ref igraph_arpack_options_t + * for details. Note that the function overwrites the n (number + * of vertices), nev (1), ncv (3) and which + * (LM) parameters and it always starts the calculation from a non-random vector + * calculated based on the degree of the vertices. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * \c IGRAPH_EINVVID, invalid vertex id in + * \p vids or an invalid reset vector in \p reset. + * + * Time complexity: depends on the input graph, usually it is O(|E|), + * the number of edges. + * + * \sa \ref igraph_pagerank() for the non-personalized implementation, + * \ref igraph_personalized_pagerank_vs() for a personalized implementation + * with resetting to specific vertices. + */ +int igraph_personalized_pagerank(const igraph_t *graph, + igraph_pagerank_algo_t algo, igraph_vector_t *vector, + igraph_real_t *value, const igraph_vs_t vids, + igraph_bool_t directed, igraph_real_t damping, + const igraph_vector_t *reset, + const igraph_vector_t *weights, + igraph_arpack_options_t *options) { + + if (damping < 0.0 || damping > 1.0) { + IGRAPH_ERROR("The PageRank damping factor must be in the range [0,1].", IGRAPH_EINVAL); + } + + if (algo == IGRAPH_PAGERANK_ALGO_ARPACK) { + return igraph_i_personalized_pagerank_arpack(graph, vector, value, vids, + directed, damping, reset, + weights, options); + } else if (algo == IGRAPH_PAGERANK_ALGO_PRPACK) { + return igraph_i_personalized_pagerank_prpack(graph, vector, value, vids, + directed, damping, reset, + weights); + } + + IGRAPH_ERROR("Unknown PageRank algorithm", IGRAPH_EINVAL); +} + +/* + * ARPACK-based implementation of \c igraph_personalized_pagerank. + * + * See \c igraph_personalized_pagerank for the documentation of the parameters. + */ +static int igraph_i_personalized_pagerank_arpack(const igraph_t *graph, igraph_vector_t *vector, + igraph_real_t *value, const igraph_vs_t vids, + igraph_bool_t directed, igraph_real_t damping, + const igraph_vector_t *reset, + const igraph_vector_t *weights, + igraph_arpack_options_t *options) { + igraph_matrix_t values; + igraph_matrix_t vectors; + igraph_neimode_t dirmode; + igraph_vector_t outdegree; + igraph_vector_t indegree; + igraph_vector_t tmp; + igraph_vector_t normalized_reset; + + long int i; + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + + if (reset && igraph_vector_size(reset) != no_of_nodes) { + IGRAPH_ERROR("Invalid length of reset vector when calculating personalized PageRank scores.", IGRAPH_EINVAL); + } + + if (no_of_edges == 0) { + /* Special case: graph with no edges. Result is the same as the personalization vector. */ + if (value) { + *value = 1.0; + } + if (vector) { + IGRAPH_CHECK(igraph_vector_resize(vector, no_of_nodes)); + if (reset && no_of_nodes > 0) { + for (i=0; i < no_of_nodes; ++i) { + VECTOR(*vector)[i] = VECTOR(*reset)[i]; + } + igraph_vector_scale(vector, 1.0 / igraph_vector_sum(vector)); + } else { + igraph_vector_fill(vector, 1.0 / no_of_nodes); + } + } + return IGRAPH_SUCCESS; + } + + options->n = (int) no_of_nodes; + options->nev = 1; + options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rnsolve */ + options->which[0] = 'L'; options->which[1] = 'M'; + options->start = 1; /* no random start vector */ + + directed = directed && igraph_is_directed(graph); + + if (weights) { + igraph_real_t min, max; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid length of weights vector when calculating PageRank scores.", IGRAPH_EINVAL); + } + + /* Safe to call minmax, ecount == 0 case was caught earlier */ + IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max)); + if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + if (min == 0 && max == 0) { + /* Special case: all weights are zeros. Result is the same as the personalization vector. */ + if (value) { + *value = 1.0; + } + if (vector) { + IGRAPH_CHECK(igraph_vector_resize(vector, no_of_nodes)); + if (reset) { + for (i=0; i < no_of_nodes; ++i) { + VECTOR(*vector)[i] = VECTOR(*reset)[i]; + } + igraph_vector_scale(vector, 1.0 / igraph_vector_sum(vector)); + } else { + igraph_vector_fill(vector, 1.0 / no_of_nodes); + } + } + return IGRAPH_SUCCESS; + } + } + + IGRAPH_MATRIX_INIT_FINALLY(&values, 0, 0); + IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1); + + if (directed) { + dirmode = IGRAPH_IN; + } else { + dirmode = IGRAPH_ALL; + } + + IGRAPH_VECTOR_INIT_FINALLY(&indegree, options->n); + IGRAPH_VECTOR_INIT_FINALLY(&outdegree, options->n); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, options->n); + + RNG_BEGIN(); + + if (reset) { + /* Normalize reset vector so the sum is 1 */ + double reset_sum, reset_min; + reset_min = igraph_vector_min(reset); + if (reset_min < 0) { + IGRAPH_ERROR("The reset vector must not contain negative elements.", IGRAPH_EINVAL); + } + if (igraph_is_nan(reset_min)) { + IGRAPH_ERROR("The reset vector must not contain NaN values.", IGRAPH_EINVAL); + } + reset_sum = igraph_vector_sum(reset); + if (reset_sum == 0) { + IGRAPH_ERROR("The sum of the elements in the reset vector must not be zero.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_copy(&normalized_reset, reset)); + IGRAPH_FINALLY(igraph_vector_destroy, &normalized_reset); + + igraph_vector_scale(&normalized_reset, 1.0 / reset_sum); + } + + if (!weights) { + + igraph_adjlist_t adjlist; + igraph_i_pagerank_data_t data; + + data.graph = graph; + data.adjlist = &adjlist; + data.damping = damping; + data.outdegree = &outdegree; + data.tmp = &tmp; + data.reset = reset ? &normalized_reset : NULL; + + IGRAPH_CHECK(igraph_degree(graph, &outdegree, igraph_vss_all(), + directed ? IGRAPH_OUT : IGRAPH_ALL, IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree(graph, &indegree, igraph_vss_all(), + directed ? IGRAPH_IN : IGRAPH_ALL, IGRAPH_LOOPS)); + /* Set up an appropriate starting vector. We start from the in-degrees + * plus some small random noise to avoid convergence problems */ + for (i = 0; i < options->n; i++) { + if (VECTOR(indegree)[i]) { + MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(-1e-4, 1e-4); + } else { + MATRIX(vectors, i, 0) = 1; + } + } + + IGRAPH_CHECK(igraph_adjlist_init( + graph, &adjlist, dirmode, IGRAPH_LOOPS, IGRAPH_MULTIPLE + )); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + IGRAPH_CHECK(igraph_arpack_rnsolve(igraph_i_pagerank, + &data, options, 0, &values, &vectors)); + + igraph_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + + } else { + + igraph_inclist_t inclist; + igraph_bool_t negative_weight_warned = 0; + igraph_i_pagerank_data2_t data; + + data.graph = graph; + data.inclist = &inclist; + data.weights = weights; + data.damping = damping; + data.outdegree = &outdegree; + data.tmp = &tmp; + data.reset = reset ? &normalized_reset : NULL; + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, dirmode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + + /* Weighted degree */ + for (i = 0; i < no_of_edges; i++) { + long int from = IGRAPH_FROM(graph, i); + long int to = IGRAPH_TO(graph, i); + igraph_real_t weight = VECTOR(*weights)[i]; + if (weight < 0 && !negative_weight_warned) { + IGRAPH_WARNING("Replacing negative weights with zeros during PageRank calculation."); + weight = 0; + negative_weight_warned = 1; + } + VECTOR(outdegree)[from] += weight; + VECTOR(indegree) [to] += weight; + if (!directed) { + VECTOR(outdegree)[to] += weight; + VECTOR(indegree) [from] += weight; + } + } + /* Set up an appropriate starting vector. We start from the in-degrees + * plus some small random noise to avoid convergence problems */ + for (i = 0; i < options->n; i++) { + if (VECTOR(indegree)[i]) { + MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(-1e-4, 1e-4); + } else { + MATRIX(vectors, i, 0) = 1; + } + } + + IGRAPH_CHECK(igraph_arpack_rnsolve(igraph_i_pagerank2, + &data, options, 0, &values, &vectors)); + + igraph_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(1); + } + + RNG_END(); + + if (reset) { + igraph_vector_destroy(&normalized_reset); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_destroy(&tmp); + igraph_vector_destroy(&outdegree); + igraph_vector_destroy(&indegree); + IGRAPH_FINALLY_CLEAN(3); + + if (value) { + *value = MATRIX(values, 0, 0); + } + + if (vector) { + long int i; + igraph_vit_t vit; + long int nodes_to_calc; + igraph_real_t sum = 0; + + for (i = 0; i < no_of_nodes; i++) { + sum += MATRIX(vectors, i, 0); + } + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + nodes_to_calc = IGRAPH_VIT_SIZE(vit); + + IGRAPH_CHECK(igraph_vector_resize(vector, nodes_to_calc)); + for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) { + VECTOR(*vector)[i] = MATRIX(vectors, (long int)IGRAPH_VIT_GET(vit), 0); + VECTOR(*vector)[i] /= sum; + } + + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + } + + if (options->info) { + IGRAPH_WARNING("Non-zero return code from ARPACK routine!"); + } + + igraph_matrix_destroy(&vectors); + igraph_matrix_destroy(&values); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/centrality/centralization.c b/src/rigraph/core/centrality/centralization.c new file mode 100644 index 0000000..9516c12 --- /dev/null +++ b/src/rigraph/core/centrality/centralization.c @@ -0,0 +1,658 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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 . +*/ + +#include "igraph_centrality.h" + +#include "igraph_interface.h" +#include "igraph_vector.h" + +#include "../core/math.h" + +/** + * \function igraph_centralization + * Calculate the centralization score from the node level scores + * + * For a centrality score defined on the vertices of a graph, it is + * possible to define a graph level centralization index, by + * calculating the sum of the deviation from the maximum centrality + * score. Consequently, the higher the centralization index of the + * graph, the more centralized the structure is. + * + * In order to make graphs of different sizes comparable, + * the centralization index is usually normalized to a number between + * zero and one, by dividing the (unnormalized) centralization score + * of the most centralized structure with the same number of vertices. + * + * For most centrality indices the most centralized + * structure is the star graph, a single center connected to all other + * nodes in the network. There are some variation depending on whether + * the graph is directed or not, whether loop edges are allowed, etc. + * + * + * This function simply calculates the graph level index, if the node + * level scores and the theoretical maximum are given. It is called by + * all the measure-specific centralization functions. + * + * \param scores A vector containing the node-level centrality + * scores. + * \param theoretical_max The graph level centrality score of the most + * centralized graph with the same number of vertices. Only used + * if \c normalized set to true. + * \param normalized Boolean, whether to normalize the centralization + * by dividing the supplied theoretical maximum. + * \return The graph level index. + * + * \sa \ref igraph_centralization_degree(), \ref + * igraph_centralization_betweenness(), \ref + * igraph_centralization_closeness(), and \ref + * igraph_centralization_eigenvector_centrality() for specific + * centralization functions. + * + * Time complexity: O(n), the length of the score vector. + * + * \example examples/simple/centralization.c + */ + +igraph_real_t igraph_centralization(const igraph_vector_t *scores, + igraph_real_t theoretical_max, + igraph_bool_t normalized) { + + long int no_of_nodes = igraph_vector_size(scores); + igraph_real_t maxscore = 0.0; + igraph_real_t cent = 0.0; + + if (no_of_nodes != 0) { + maxscore = igraph_vector_max(scores); + cent = no_of_nodes * maxscore - igraph_vector_sum(scores); + if (normalized) { + cent = cent / theoretical_max; + } + } else { + cent = IGRAPH_NAN; + } + + return cent; +} + +/** + * \function igraph_centralization_degree + * Calculate vertex degree and graph centralization + * + * This function calculates the degree of the vertices by passing its + * arguments to \ref igraph_degree(); and it calculates the graph + * level centralization index based on the results by calling \ref + * igraph_centralization(). + * \param graph The input graph. + * \param res A vector if you need the node-level degree scores, or a + * null pointer otherwise. + * \param mode Constant the specifies the type of degree for directed + * graphs. Possible values: \c IGRAPH_IN, \c IGRAPH_OUT and \c + * IGRAPH_ALL. This argument is ignored for undirected graphs. + * \param loops Boolean, whether to consider loop edges when + * calculating the degree (and the centralization). + * \param centralization Pointer to a real number, the centralization + * score is placed here. + * \param theoretical_max Pointer to real number or a null pointer. If + * not a null pointer, then the theoretical maximum graph + * centrality score for a graph with the same number vertices is + * stored here. + * \param normalized Boolean, whether to calculate a normalized + * centralization score. See \ref igraph_centralization() for how + * the normalization is done. + * \return Error code. + * + * \sa \ref igraph_centralization(), \ref igraph_degree(). + * + * Time complexity: the complexity of \ref igraph_degree() plus O(n), + * the number of vertices queried, for calculating the centralization + * score. + */ + +int igraph_centralization_degree(const igraph_t *graph, igraph_vector_t *res, + igraph_neimode_t mode, igraph_bool_t loops, + igraph_real_t *centralization, + igraph_real_t *theoretical_max, + igraph_bool_t normalized) { + + igraph_vector_t myscores; + igraph_vector_t *scores = res; + igraph_real_t *tmax = theoretical_max, mytmax; + + if (!tmax) { + tmax = &mytmax; + } + + if (!res) { + scores = &myscores; + IGRAPH_VECTOR_INIT_FINALLY(scores, 0); + } + + IGRAPH_CHECK(igraph_degree(graph, scores, igraph_vss_all(), mode, loops)); + + IGRAPH_CHECK(igraph_centralization_degree_tmax(graph, 0, mode, loops, + tmax)); + + *centralization = igraph_centralization(scores, *tmax, normalized); + + if (!res) { + igraph_vector_destroy(scores); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_centralization_degree_tmax + * Theoretical maximum for graph centralization based on degree + * + * This function returns the theoretical maximum graph centrality + * based on vertex degree. + * + * + * There are two ways to call this function, the first is to supply a + * graph as the graph argument, and then the number of + * vertices is taken from this object, and its directedness is + * considered as well. The nodes argument is ignored in + * this case. The mode argument is also ignored if the + * supplied graph is undirected. + * + * + * The other way is to supply a null pointer as the graph + * argument. In this case the nodes and mode + * arguments are considered. + * + * + * The most centralized structure is the star. More specifically, for + * undirected graphs it is the star, for directed graphs it is the + * in-star or the out-star. + * \param graph A graph object or a null pointer, see the description + * above. + * \param nodes The number of nodes. This is ignored if the + * graph argument is not a null pointer. + * \param mode Constant, whether the calculation is based on in-degree + * (IGRAPH_IN), out-degree (IGRAPH_OUT) + * or total degree (IGRAPH_ALL). This is ignored if + * the graph argument is not a null pointer and the + * given graph is undirected. + * \param loops Boolean scalar, whether to consider loop edges in the + * calculation. + * \param res Pointer to a real variable, the result is stored here. + * \return Error code. + * + * Time complexity: O(1). + * + * \sa \ref igraph_centralization_degree() and \ref + * igraph_centralization(). + */ + +int igraph_centralization_degree_tmax(const igraph_t *graph, + igraph_integer_t nodes, + igraph_neimode_t mode, + igraph_bool_t loops, + igraph_real_t *res) { + + igraph_bool_t directed = mode != IGRAPH_ALL; + igraph_real_t real_nodes; + + if (graph) { + directed = igraph_is_directed(graph); + nodes = igraph_vcount(graph); + } + + real_nodes = nodes; /* implicit cast to igraph_real_t */ + + if (directed) { + switch (mode) { + case IGRAPH_IN: + case IGRAPH_OUT: + if (!loops) { + *res = (real_nodes - 1) * (real_nodes - 1); + } else { + *res = (real_nodes - 1) * real_nodes; + } + break; + case IGRAPH_ALL: + if (!loops) { + *res = 2 * (real_nodes - 1) * (real_nodes - 2); + } else { + *res = 2 * (real_nodes - 1) * (real_nodes - 1); + } + break; + } + } else { + if (!loops) { + *res = (real_nodes - 1) * (real_nodes - 2); + } else { + *res = (real_nodes - 1) * real_nodes; + } + } + + return 0; +} + +/** + * \function igraph_centralization_betweenness + * Calculate vertex betweenness and graph centralization + * + * This function calculates the betweenness centrality of the vertices + * by passing its arguments to \ref igraph_betweenness(); and it + * calculates the graph level centralization index based on the + * results by calling \ref igraph_centralization(). + * \param graph The input graph. + * \param res A vector if you need the node-level betweenness scores, or a + * null pointer otherwise. + * \param directed Boolean, whether to consider directed paths when + * calculating betweenness. + * \param centralization Pointer to a real number, the centralization + * score is placed here. + * \param theoretical_max Pointer to real number or a null pointer. If + * not a null pointer, then the theoretical maximum graph + * centrality score for a graph with the same number vertices is + * stored here. + * \param normalized Boolean, whether to calculate a normalized + * centralization score. See \ref igraph_centralization() for how + * the normalization is done. + * \return Error code. + * + * \sa \ref igraph_centralization(), \ref igraph_betweenness(). + * + * Time complexity: the complexity of \ref igraph_betweenness() plus + * O(n), the number of vertices queried, for calculating the + * centralization score. + */ + +int igraph_centralization_betweenness(const igraph_t *graph, + igraph_vector_t *res, + igraph_bool_t directed, + igraph_real_t *centralization, + igraph_real_t *theoretical_max, + igraph_bool_t normalized) { + + igraph_vector_t myscores; + igraph_vector_t *scores = res; + igraph_real_t *tmax = theoretical_max, mytmax; + + if (!tmax) { + tmax = &mytmax; + } + + if (!res) { + scores = &myscores; + IGRAPH_VECTOR_INIT_FINALLY(scores, 0); + } + + IGRAPH_CHECK(igraph_betweenness(graph, scores, igraph_vss_all(), directed, /*weights=*/ 0)); + + IGRAPH_CHECK(igraph_centralization_betweenness_tmax(graph, 0, directed, tmax)); + + *centralization = igraph_centralization(scores, *tmax, normalized); + + if (!res) { + igraph_vector_destroy(scores); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_centralization_betweenness_tmax + * Theoretical maximum for graph centralization based on betweenness + * + * This function returns the theoretical maximum graph centrality + * based on vertex betweenness. + * + * + * There are two ways to call this function, the first is to supply a + * graph as the graph argument, and then the number of + * vertices is taken from this object, and its directedness is + * considered as well. The nodes argument is ignored in + * this case. The directed argument is also ignored if the + * supplied graph is undirected. + * + * + * The other way is to supply a null pointer as the graph + * argument. In this case the nodes and directed + * arguments are considered. + * + * + * The most centralized structure is the star. + * \param graph A graph object or a null pointer, see the description + * above. + * \param nodes The number of nodes. This is ignored if the + * graph argument is not a null pointer. + * \param directed Boolean scalar, whether to use directed paths in + * the betweenness calculation. This argument is ignored if + * graph is not a null pointer and it is undirected. + * \param res Pointer to a real variable, the result is stored here. + * \return Error code. + * + * Time complexity: O(1). + * + * \sa \ref igraph_centralization_betweenness() and \ref + * igraph_centralization(). + */ + +int igraph_centralization_betweenness_tmax(const igraph_t *graph, + igraph_integer_t nodes, + igraph_bool_t directed, + igraph_real_t *res) { + igraph_real_t real_nodes; + + if (graph) { + directed = directed && igraph_is_directed(graph); + nodes = igraph_vcount(graph); + } + + real_nodes = nodes; /* implicit cast to igraph_real_t */ + + if (directed) { + *res = (real_nodes - 1) * (real_nodes - 1) * (real_nodes - 2); + } else { + *res = (real_nodes - 1) * (real_nodes - 1) * (real_nodes - 2) / 2.0; + } + + return 0; +} + +/** + * \function igraph_centralization_closeness + * Calculate vertex closeness and graph centralization + * + * This function calculates the closeness centrality of the vertices + * by passing its arguments to \ref igraph_closeness(); and it + * calculates the graph level centralization index based on the + * results by calling \ref igraph_centralization(). + * \param graph The input graph. + * \param res A vector if you need the node-level closeness scores, or a + * null pointer otherwise. + * \param mode Constant the specifies the type of closeness for directed + * graphs. Possible values: \c IGRAPH_IN, \c IGRAPH_OUT and \c + * IGRAPH_ALL. This argument is ignored for undirected graphs. See + * \ref igraph_closeness() argument with the same name for more. + * \param centralization Pointer to a real number, the centralization + * score is placed here. + * \param theoretical_max Pointer to real number or a null pointer. If + * not a null pointer, then the theoretical maximum graph + * centrality score for a graph with the same number vertices is + * stored here. + * \param normalized Boolean, whether to calculate a normalized + * centralization score. See \ref igraph_centralization() for how + * the normalization is done. + * \return Error code. + * + * \sa \ref igraph_centralization(), \ref igraph_closeness(). + * + * Time complexity: the complexity of \ref igraph_closeness() plus + * O(n), the number of vertices queried, for calculating the + * centralization score. + */ + +int igraph_centralization_closeness(const igraph_t *graph, + igraph_vector_t *res, + igraph_neimode_t mode, + igraph_real_t *centralization, + igraph_real_t *theoretical_max, + igraph_bool_t normalized) { + + igraph_vector_t myscores; + igraph_vector_t *scores = res; + igraph_real_t *tmax = theoretical_max, mytmax; + + if (!tmax) { + tmax = &mytmax; + } + + if (!res) { + scores = &myscores; + IGRAPH_VECTOR_INIT_FINALLY(scores, 0); + } + + IGRAPH_CHECK(igraph_closeness(graph, scores, NULL, NULL, igraph_vss_all(), mode, + /*weights=*/ 0, /*normalize=*/ 1)); + + IGRAPH_CHECK(igraph_centralization_closeness_tmax(graph, 0, mode, + tmax)); + + *centralization = igraph_centralization(scores, *tmax, normalized); + + if (!res) { + igraph_vector_destroy(scores); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_centralization_closeness_tmax + * Theoretical maximum for graph centralization based on closeness + * + * This function returns the theoretical maximum graph centrality + * based on vertex closeness. + * + * + * There are two ways to call this function, the first is to supply a + * graph as the graph argument, and then the number of + * vertices is taken from this object, and its directedness is + * considered as well. The nodes argument is ignored in + * this case. The mode argument is also ignored if the + * supplied graph is undirected. + * + * + * The other way is to supply a null pointer as the graph + * argument. In this case the nodes and mode + * arguments are considered. + * + * + * The most centralized structure is the star. + * \param graph A graph object or a null pointer, see the description + * above. + * \param nodes The number of nodes. This is ignored if the + * graph argument is not a null pointer. + * \param mode Constant, specifies what kinf of distances to consider + * to calculate closeness. See the mode argument of + * \ref igraph_closeness() for details. This argument is ignored + * if graph is not a null pointer and it is + * undirected. + * \param res Pointer to a real variable, the result is stored here. + * \return Error code. + * + * Time complexity: O(1). + * + * \sa \ref igraph_centralization_closeness() and \ref + * igraph_centralization(). + */ + +int igraph_centralization_closeness_tmax(const igraph_t *graph, + igraph_integer_t nodes, + igraph_neimode_t mode, + igraph_real_t *res) { + igraph_real_t real_nodes; + + if (graph) { + nodes = igraph_vcount(graph); + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + } + + real_nodes = nodes; /* implicit cast to igraph_real_t */ + + if (mode != IGRAPH_ALL) { + *res = (real_nodes - 1) * (1.0 - 1.0 / real_nodes); + } else { + *res = (real_nodes - 1) * (real_nodes - 2) / (2.0 * real_nodes - 3); + } + + return 0; +} + +/** + * \function igraph_centralization_eigenvector_centrality + * Calculate eigenvector centrality scores and graph centralization + * + * This function calculates the eigenvector centrality of the vertices + * by passing its arguments to \ref igraph_eigenvector_centrality); + * and it calculates the graph level centralization index based on the + * results by calling \ref igraph_centralization(). + * \param graph The input graph. + * \param vector A vector if you need the node-level eigenvector + * centrality scores, or a null pointer otherwise. + * \param value If not a null pointer, then the leading eigenvalue is + * stored here. + * \param scale If not zero then the result will be scaled, such that + * the absolute value of the maximum centrality is one. + * \param options Options to ARPACK. See \ref igraph_arpack_options_t + * for details. Note that the function overwrites the + * n (number of vertices) parameter and + * it always starts the calculation from a non-random vector + * calculated based on the degree of the vertices. + * \param centralization Pointer to a real number, the centralization + * score is placed here. + * \param theoretical_max Pointer to real number or a null pointer. If + * not a null pointer, then the theoretical maximum graph + * centrality score for a graph with the same number vertices is + * stored here. + * \param normalized Boolean, whether to calculate a normalized + * centralization score. See \ref igraph_centralization() for how + * the normalization is done. + * \return Error code. + * + * \sa \ref igraph_centralization(), \ref igraph_eigenvector_centrality(). + * + * Time complexity: the complexity of \ref + * igraph_eigenvector_centrality() plus O(|V|), the number of vertices + * for the calculating the centralization. + */ + +int igraph_centralization_eigenvector_centrality( + const igraph_t *graph, + igraph_vector_t *vector, + igraph_real_t *value, + igraph_bool_t directed, + igraph_bool_t scale, + igraph_arpack_options_t *options, + igraph_real_t *centralization, + igraph_real_t *theoretical_max, + igraph_bool_t normalized) { + + igraph_vector_t myscores; + igraph_vector_t *scores = vector; + igraph_real_t realvalue, *myvalue = value; + igraph_real_t *tmax = theoretical_max, mytmax; + + if (!tmax) { + tmax = &mytmax; + } + + if (!vector) { + scores = &myscores; + IGRAPH_VECTOR_INIT_FINALLY(scores, 0); + } + if (!value) { + myvalue = &realvalue; + } + + IGRAPH_CHECK(igraph_eigenvector_centrality(graph, scores, myvalue, directed, + scale, /*weights=*/ 0, + options)); + + IGRAPH_CHECK(igraph_centralization_eigenvector_centrality_tmax( + graph, 0, directed, + scale, + tmax)); + + *centralization = igraph_centralization(scores, *tmax, normalized); + + if (!vector) { + igraph_vector_destroy(scores); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_centralization_eigenvector_centrality_tmax + * Theoretical maximum centralization for eigenvector centrality + * + * This function returns the theoretical maximum graph centrality + * based on vertex eigenvector centrality. + * + * + * There are two ways to call this function, the first is to supply a + * graph as the graph argument, and then the number of + * vertices is taken from this object, and its directedness is + * considered as well. The nodes argument is ignored in + * this case. The directed argument is also ignored if the + * supplied graph is undirected. + * + * + * The other way is to supply a null pointer as the graph + * argument. In this case the nodes and directed + * arguments are considered. + * + * + * The most centralized directed structure is the in-star. The most + * centralized undirected structure is the graph with a single edge. + * \param graph A graph object or a null pointer, see the description + * above. + * \param nodes The number of nodes. This is ignored if the + * graph argument is not a null pointer. + * \param directed Boolean scalar, whether to consider edge + * directions. This argument is ignored if + * graph is not a null pointer and it is undirected. + * \param scale Whether to rescale the node-level centrality scores to + * have a maximum of one. + * \param res Pointer to a real variable, the result is stored here. + * \return Error code. + * + * Time complexity: O(1). + * + * \sa \ref igraph_centralization_closeness() and \ref + * igraph_centralization(). + */ + +int igraph_centralization_eigenvector_centrality_tmax( + const igraph_t *graph, + igraph_integer_t nodes, + igraph_bool_t directed, + igraph_bool_t scale, + igraph_real_t *res) { + + if (graph) { + nodes = igraph_vcount(graph); + directed = directed && igraph_is_directed(graph); + } + + if (directed) { + *res = nodes - 1; + } else { + if (scale) { + *res = nodes - 2; + } else { + *res = (nodes - 2.0) / M_SQRT2; + } + } + + return 0; +} diff --git a/src/rigraph/core/centrality/closeness.c b/src/rigraph/core/centrality/closeness.c new file mode 100644 index 0000000..68ecb7e --- /dev/null +++ b/src/rigraph/core/centrality/closeness.c @@ -0,0 +1,877 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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 . +*/ + +#include "igraph_centrality.h" + +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_dqueue.h" + +#include "../core/indheap.h" +#include "../core/interruption.h" +#include "../core/math.h" + +/***** Closeness centrality *****/ + +/** + * \ingroup structural + * \function igraph_closeness + * \brief Closeness centrality calculations for some vertices. + * + * The closeness centrality of a vertex measures how easily other + * vertices can be reached from it (or the other way: how easily it + * can be reached from the other vertices). It is defined as + * the inverse of the mean distance to (or from) all other vertices. + * + * + * Closeness centrality is meaningful only for connected graphs. + * If the graph is not connected, igraph computes the inverse of the + * mean distance to (or from) all \em reachable vertices. In undirected + * graphs, this is equivalent to computing the closeness separately in + * each connected component. The optional \p all_reachable output + * parameter is provided to help detect when the graph is disconnected. + * + * + * While there is no universally adopted definition of closeness centrality + * for disconnected graphs, there have been some attempts for generalizing + * the concept to the disconnected case. One type of approach considers the mean distance + * only to reachable vertices, then re-scales the obtained certrality score + * by a factor that depends on the number of reachable vertices + * (i.e. the size of the component in the undirected case). + * To facilitate computing these generalizations of closeness centrality, + * the number of reachable vertices (not including the starting vertex) + * is returned in \p reachable_count. + * + * + * In disconnected graphs, consider using the harmonic centrality, + * computable using \ref igraph_harmonic_centrality(). + * + * + * For isolated vertices, i.e. those having no associated paths, NaN is returned. + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * closeness centrality scores for the given vertices. + * \param reachable_count If not \c NULL, this vector will contain the number of + * vertices reachable from each vertex for which the closeness is calculated + * (not including that vertex). + * \param all_reachable Pointer to a Boolean. If not \c NULL, it indicates if all + * vertices of the graph were reachable from each vertex in \p vids. + * If false, the graph is non-connected. If true, and the graph is undirected, + * or if the graph is directed and \p vids contains all vertices, then the + * graph is connected. + * \param vids The vertices for which the closeness centrality will be computed. + * \param mode The type of shortest paths to be used for the + * calculation in directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the lengths of the outgoing paths are calculated. + * \cli IGRAPH_IN + * the lengths of the incoming paths are calculated. + * \cli IGRAPH_ALL + * the directed graph is considered as an + * undirected one for the computation. + * \endclist + * \param weights An optional vector containing edge weights for + * weighted closeness. No edge weight may be NaN. Supply a null + * pointer here for traditional, unweighted closeness. + * \param normalized If true, the inverse of the mean distance to reachable + * vetices is returned. If false, the inverse of the sum of distances + * is returned. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * invalid vertex id passed. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(n|E|), + * n is the number + * of vertices for which the calculation is done and + * |E| is the number + * of edges in the graph. + * + * \sa Other centrality types: \ref igraph_degree(), \ref igraph_betweenness(), + * \ref igraph_harmonic_centrality(). + * See \ref igraph_closeness_cutoff() for the range-limited closeness centrality. + */ +int igraph_closeness(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_t *reachable_count, igraph_bool_t *all_reachable, + const igraph_vs_t vids, igraph_neimode_t mode, + const igraph_vector_t *weights, + igraph_bool_t normalized) { + return igraph_closeness_cutoff(graph, res, reachable_count, all_reachable, vids, mode, weights, normalized, -1); +} + +static int igraph_i_closeness_cutoff_weighted(const igraph_t *graph, + igraph_vector_t *res, + igraph_vector_t *reachable_count, + igraph_bool_t *all_reachable, + const igraph_vs_t vids, + igraph_neimode_t mode, + igraph_real_t cutoff, + const igraph_vector_t *weights, + igraph_bool_t normalized) { + + /* See igraph_shortest_paths_dijkstra() for the implementation + details and the dirty tricks. */ + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + + igraph_2wheap_t Q; + igraph_vit_t vit; + long int nodes_to_calc; + + igraph_lazy_inclist_t inclist; + long int i, j; + + igraph_vector_t dist; + igraph_vector_long_t which; + long int nodes_reached; + + igraph_real_t mindist = 0; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight <= 0) { + IGRAPH_ERROR("Weight vector must be positive.", IGRAPH_EINVAL); + } else if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + nodes_to_calc = IGRAPH_VIT_SIZE(vit); + + if (reachable_count) { + igraph_vector_resize(reachable_count, nodes_to_calc); + } + + if (all_reachable) { + *all_reachable = 1; /* be optimistic */ + } + + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + IGRAPH_CHECK(igraph_vector_long_init(&which, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &which); + + IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); + igraph_vector_null(res); + + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + + long int source = IGRAPH_VIT_GET(vit); + igraph_2wheap_clear(&Q); + igraph_2wheap_push_with_index(&Q, source, -1.0); + VECTOR(which)[source] = i + 1; + VECTOR(dist)[source] = 1.0; /* actual distance is zero but we need to store distance + 1 */ + nodes_reached = 0; + + while (!igraph_2wheap_empty(&Q)) { + igraph_integer_t minnei = (igraph_integer_t) igraph_2wheap_max_index(&Q); + /* Now check all neighbors of minnei for a shorter path */ + igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, minnei); + long int nlen = igraph_vector_int_size(neis); + + mindist = -igraph_2wheap_delete_max(&Q); + + if (cutoff >= 0 && (mindist - 1.0) > cutoff) { + continue; /* NOT break!!! */ + } + + VECTOR(*res)[i] += (mindist - 1.0); + nodes_reached++; + + for (j = 0; j < nlen; j++) { + long int edge = (long int) VECTOR(*neis)[j]; + long int to = IGRAPH_OTHER(graph, edge, minnei); + igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; + igraph_real_t curdist = VECTOR(dist)[to]; + + if (VECTOR(which)[to] != i + 1) { + /* First non-infinite distance */ + VECTOR(which)[to] = i + 1; + VECTOR(dist)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, to, -altdist)); + } else if (curdist == 0 /* this means curdist is infinity */ || altdist < curdist) { + /* This is a shorter path */ + VECTOR(dist)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_modify(&Q, to, -altdist)); + } + } + + } /* !igraph_2wheap_empty(&Q) */ + + if (reachable_count) { + VECTOR(*reachable_count)[i] = nodes_reached - 1; + } + + if (normalized) { + /* compute the inverse of the average distance, considering only reachable nodes */ + VECTOR(*res)[i] = VECTOR(*res)[i] == 0 ? IGRAPH_NAN : ((igraph_real_t) (nodes_reached-1)) / VECTOR(*res)[i]; + } else { + /* compute the inverse of the sum of distances */ + VECTOR(*res)[i] = VECTOR(*res)[i] == 0 ? IGRAPH_NAN : 1.0 / VECTOR(*res)[i]; + } + + if (all_reachable) { + if (nodes_reached < no_of_nodes) { + *all_reachable = 0 /* false */; + } + } + } /* !IGRAPH_VIT_END(vit) */ + + igraph_vector_long_destroy(&which); + igraph_vector_destroy(&dist); + igraph_lazy_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(5); + + return 0; +} + +/** + * \ingroup structural + * \function igraph_closeness_estimate + * \brief Closeness centrality estimations for some vertices. + * + * \deprecated-by igraph_closeness_cutoff 0.9 + * + * + * The closeness centrality of a vertex measures how easily other + * vertices can be reached from it (or the other way: how easily it + * can be reached from the other vertices). It is defined as + * the number of vertices minus one divided by the sum of the + * lengths of all geodesics from/to the given vertex. When estimating + * closeness centrality, igraph considers paths having a length less than + * or equal to a prescribed cutoff value. + * + * + * If the graph is not connected, and there is no such path between two + * vertices, the number of vertices is used instead the length of the + * geodesic. This is always longer than the longest possible geodesic. + * + * + * Since the estimation considers vertex pairs with a distance greater than + * the given value as disconnected, the resulting estimation will always be + * lower than the actual closeness centrality. + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * closeness centrality scores for the given vertices. + * \param vids The vertices for which the closeness centrality will be estimated. + * \param mode The type of shortest paths to be used for the + * calculation in directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the lengths of the outgoing paths are calculated. + * \cli IGRAPH_IN + * the lengths of the incoming paths are calculated. + * \cli IGRAPH_ALL + * the directed graph is considered as an + * undirected one for the computation. + * \endclist + * \param cutoff The maximal length of paths that will be considered. + * If negative, the exact closeness will be calculated (no upper + * limit on path lengths). + * \param weights An optional vector containing edge weights for + * weighted closeness. No edge weight may be NaN. Supply a + * null pointer here for traditional, unweighted closeness. + * \param normalized Boolean, whether to normalize results by multiplying + * by the number of vertices minus one. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * invalid vertex id passed. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(n|E|), + * n is the number + * of vertices for which the calculation is done and + * |E| is the number + * of edges in the graph. + * + * \sa Other centrality types: \ref igraph_degree(), \ref igraph_betweenness(). + */ + +int igraph_closeness_estimate(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_neimode_t mode, + igraph_real_t cutoff, + const igraph_vector_t *weights, + igraph_bool_t normalized) { + IGRAPH_WARNING("igraph_closeness_estimate is deprecated, use igraph_closeness_cutoff."); + return igraph_closeness_cutoff(graph, res, NULL, NULL, vids, mode, weights, normalized, cutoff); +} + + +/** + * \ingroup structural + * \function igraph_closeness_cutoff + * \brief Range limited closeness centrality. + * + * This function computes a range-limited version of closeness centrality + * by considering only those shortest paths whose length is no greater + * then the given cutoff value. + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * range-limited closeness centrality scores for the given vertices. + * \param reachable_count If not \c NULL, this vector will contain the number of + * vertices reachable within the cutoff distance from each vertex for which + * the range-limited closeness is calculated (not including that vertex). + * \param all_reachable Pointer to a Boolean. If not \c NULL, it indicates if all + * vertices of the graph were reachable from each vertex in \p vids within + * the given cutoff distance. + * \param vids The vertices for which the range limited closeness centrality + * will be computed. + * \param mode The type of shortest paths to be used for the + * calculation in directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the lengths of the outgoing paths are calculated. + * \cli IGRAPH_IN + * the lengths of the incoming paths are calculated. + * \cli IGRAPH_ALL + * the directed graph is considered as an + * undirected one for the computation. + * \endclist + * \param weights An optional vector containing edge weights for + * weighted closeness. No edge weight may be NaN. Supply a null + * pointer here for traditional, unweighted closeness. + * \param normalized If true, the inverse of the mean distance to vertices + * reachable within the cutoff is returned. If false, the inverse + * of the sum of distances is returned. + * \param cutoff The maximal length of paths that will be considered. + * If negative, the exact closeness will be calculated (no upper + * limit on path lengths). + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * invalid vertex id passed. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(n|E|), + * n is the number + * of vertices for which the calculation is done and + * |E| is the number + * of edges in the graph. + * + * \sa \ref igraph_closeness() to calculate the exact closeness centrality. + */ + +int igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_t *reachable_count, igraph_bool_t *all_reachable, + const igraph_vs_t vids, igraph_neimode_t mode, + const igraph_vector_t *weights, + igraph_bool_t normalized, + igraph_real_t cutoff) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t already_counted; + igraph_vector_int_t *neis; + long int i, j; + long int nodes_reached; + igraph_adjlist_t allneis; + + long int actdist = 0; + + igraph_dqueue_t q; + + long int nodes_to_calc; + igraph_vit_t vit; + + if (weights) { + return igraph_i_closeness_cutoff_weighted(graph, res, reachable_count, all_reachable, vids, mode, cutoff, + weights, normalized); + } + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + nodes_to_calc = IGRAPH_VIT_SIZE(vit); + + if (reachable_count) { + igraph_vector_resize(reachable_count, nodes_to_calc); + } + + if (all_reachable) { + *all_reachable = 1; /* be optimistic */ + } + + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode for closeness.", IGRAPH_EINVMODE); + } + + IGRAPH_VECTOR_INIT_FINALLY(&already_counted, no_of_nodes); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + + IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); + igraph_vector_null(res); + + for (IGRAPH_VIT_RESET(vit), i = 0; + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) { + nodes_reached = 0; + + igraph_dqueue_clear(&q); + IGRAPH_CHECK(igraph_dqueue_push(&q, IGRAPH_VIT_GET(vit))); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + VECTOR(already_counted)[(long int)IGRAPH_VIT_GET(vit)] = i + 1; + + IGRAPH_PROGRESS("Closeness: ", 100.0 * i / nodes_to_calc, NULL); + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_empty(&q)) { + long int act = (long int) igraph_dqueue_pop(&q); + actdist = (long int) igraph_dqueue_pop(&q); + + if (cutoff >= 0 && actdist > cutoff) { + continue; /* NOT break!!! */ + } + + VECTOR(*res)[i] += actdist; + nodes_reached++; + + /* check the neighbors */ + neis = igraph_adjlist_get(&allneis, act); + for (j = 0; j < igraph_vector_int_size(neis); j++) { + long int neighbor = (long int) VECTOR(*neis)[j]; + if (VECTOR(already_counted)[neighbor] == i + 1) { + continue; + } + VECTOR(already_counted)[neighbor] = i + 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + } + } + + if (reachable_count) { + VECTOR(*reachable_count)[i] = nodes_reached - 1; + } + + if (normalized) { + /* compute the inverse of the average distance, considering only reachable nodes */ + VECTOR(*res)[i] = VECTOR(*res)[i] == 0 ? IGRAPH_NAN : ((igraph_real_t) (nodes_reached-1)) / VECTOR(*res)[i]; + } else { + /* compute the inverse of the sum of distances */ + VECTOR(*res)[i] = VECTOR(*res)[i] == 0 ? IGRAPH_NAN : 1.0 / VECTOR(*res)[i]; + } + + if (all_reachable) { + if (nodes_reached < no_of_nodes) { + *all_reachable = 0 /* false */; + } + } + } + + IGRAPH_PROGRESS("Closeness: ", 100.0, NULL); + + /* Clean */ + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&already_counted); + igraph_vit_destroy(&vit); + igraph_adjlist_destroy(&allneis); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + + +/***** Harmonic centrality *****/ + +static int igraph_i_harmonic_centrality_unweighted(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_neimode_t mode, + igraph_bool_t normalized, + igraph_real_t cutoff) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t already_counted; + igraph_vector_int_t *neis; + long int i, j; + igraph_adjlist_t allneis; + + long int actdist = 0; + + igraph_dqueue_t q; + + long int nodes_to_calc; + igraph_vit_t vit; + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + nodes_to_calc = IGRAPH_VIT_SIZE(vit); + + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && + mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode for harmonic centrality.", IGRAPH_EINVMODE); + } + + IGRAPH_VECTOR_INIT_FINALLY(&already_counted, no_of_nodes); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + + IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); + igraph_vector_null(res); + + for (IGRAPH_VIT_RESET(vit), i = 0; + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) + { + long int source = IGRAPH_VIT_GET(vit); + + igraph_dqueue_clear(&q); + IGRAPH_CHECK(igraph_dqueue_push(&q, source)); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + VECTOR(already_counted)[source] = i + 1; + + IGRAPH_PROGRESS("Harmonic centrality: ", 100.0 * i / nodes_to_calc, NULL); + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_empty(&q)) { + long int act = (long int) igraph_dqueue_pop(&q); + actdist = (long int) igraph_dqueue_pop(&q); + + if (cutoff >= 0 && actdist > cutoff) { + continue; /* NOT break!!! */ + } + + /* Exclude self-distance, which is zero. */ + if (source != act) { + VECTOR(*res)[i] += 1.0/actdist; + } + + /* check the neighbors */ + neis = igraph_adjlist_get(&allneis, act); + for (j = 0; j < igraph_vector_int_size(neis); j++) { + long int neighbor = (long int) VECTOR(*neis)[j]; + if (VECTOR(already_counted)[neighbor] == i + 1) { + continue; + } + VECTOR(already_counted)[neighbor] = i + 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + } + } + } + + if (normalized && no_of_nodes > 1 /* not a null graph or singleton graph */) { + igraph_vector_scale(res, 1.0 / (no_of_nodes - 1)); + } + + IGRAPH_PROGRESS("Harmonic centrality: ", 100.0, NULL); + + /* Clean */ + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&already_counted); + igraph_vit_destroy(&vit); + igraph_adjlist_destroy(&allneis); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + + +static int igraph_i_harmonic_centrality_weighted(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + igraph_neimode_t mode, + const igraph_vector_t *weights, + igraph_bool_t normalized, + igraph_real_t cutoff) { + + /* See igraph_shortest_paths_dijkstra() for the implementation + details and the dirty tricks. */ + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + + igraph_2wheap_t Q; + igraph_vit_t vit; + long int nodes_to_calc; + + igraph_lazy_inclist_t inclist; + long int i, j; + + igraph_vector_t dist; + igraph_vector_long_t which; + + igraph_real_t mindist = 0; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight <= 0) { + IGRAPH_ERROR("Weight vector must be positive.", IGRAPH_EINVAL); + } else if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + nodes_to_calc = IGRAPH_VIT_SIZE(vit); + + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + IGRAPH_CHECK(igraph_vector_long_init(&which, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &which); + + IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); + igraph_vector_null(res); + + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + + long int source = IGRAPH_VIT_GET(vit); + igraph_2wheap_clear(&Q); + igraph_2wheap_push_with_index(&Q, source, -1.0); + VECTOR(which)[source] = i + 1; + VECTOR(dist)[source] = 1.0; /* actual distance is zero but we need to store distance + 1 */ + + while (!igraph_2wheap_empty(&Q)) { + igraph_integer_t minnei = (igraph_integer_t) igraph_2wheap_max_index(&Q); + /* Now check all neighbors of minnei for a shorter path */ + igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, minnei); + long int nlen = igraph_vector_int_size(neis); + + mindist = -igraph_2wheap_delete_max(&Q); + + if (cutoff >= 0 && (mindist - 1.0) > cutoff) { + continue; /* NOT break!!! */ + } + + /* Exclude self-distance, which is zero. */ + if (source != minnei) { + VECTOR(*res)[i] += 1.0 / (mindist - 1.0); + } + + for (j = 0; j < nlen; j++) { + long int edge = (long int) VECTOR(*neis)[j]; + long int to = IGRAPH_OTHER(graph, edge, minnei); + igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; + igraph_real_t curdist = VECTOR(dist)[to]; + + if (VECTOR(which)[to] != i + 1) { + /* First non-infinite distance */ + VECTOR(which)[to] = i + 1; + VECTOR(dist)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, to, -altdist)); + } else if (curdist == 0 /* this means curdist is infinity */ || altdist < curdist) { + /* This is a shorter path */ + VECTOR(dist)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_modify(&Q, to, -altdist)); + } + } + + } /* !igraph_2wheap_empty(&Q) */ + + } /* !IGRAPH_VIT_END(vit) */ + + if (normalized && no_of_nodes > 1 /* not a null graph or singleton graph */) { + igraph_vector_scale(res, 1.0 / (no_of_nodes - 1)); + } + + igraph_vector_long_destroy(&which); + igraph_vector_destroy(&dist); + igraph_lazy_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup structural + * \function igraph_harmonic_centrality_cutoff + * \brief Range limited harmonic centrality. + * + * This function computes the range limited version of harmonic centrality: + * only those shortest paths are considered whose length is not above the given cutoff. + * The inverse distance to vertices not reachable within the cutoff is considered + * to be zero. + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * range limited harmonic centrality scores for the given vertices. + * \param vids The vertices for which the harmonic centrality will be computed. + * \param mode The type of shortest paths to be used for the + * calculation in directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the lengths of the outgoing paths are calculated. + * \cli IGRAPH_IN + * the lengths of the incoming paths are calculated. + * \cli IGRAPH_ALL + * the directed graph is considered as an + * undirected one for the computation. + * \endclist + * \param weights An optional vector containing edge weights for + * weighted harmonic centrality. No edge weight may be NaN. + * If \c NULL, all weights are considered to be one. + * \param normalized Boolean, whether to normalize the result. If true, + * the result is the mean inverse path length to other vertices. + * i.e. it is normalized by the number of vertices minus one. + * If false, the result is the sum of inverse path lengths to other + * vertices. + * \param cutoff The maximal length of paths that will be considered. + * The inverse distance to vertices that are not reachable within + * the cutoff path length is considered to be zero. + * Supply a negative value to compute the exact harmonic centrality, + * without any upper limit on the length of paths. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * invalid vertex id passed. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(n|E|), where + * n is the number of vertices for which the calculation is done and + * |E| is the number of edges in the graph. + * + * \sa Other centrality types: \ref igraph_closeness(), \ref igraph_betweenness(). + */ + +int igraph_harmonic_centrality_cutoff(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_neimode_t mode, + const igraph_vector_t *weights, + igraph_bool_t normalized, + igraph_real_t cutoff) { + if (weights) { + return igraph_i_harmonic_centrality_weighted(graph, res, vids, mode, weights, normalized, cutoff); + } else { + return igraph_i_harmonic_centrality_unweighted(graph, res, vids, mode, normalized, cutoff); + } +} + + +/** + * \ingroup structural + * \function igraph_harmonic_centrality + * \brief Harmonic centrality for some vertices. + * + * The harmonic centrality of a vertex is the mean inverse distance to + * all other vertices. The inverse distance to an unreachable vertex + * is considered to be zero. + * + * + * References: + * + * + * M. Marchiori and V. Latora, Harmony in the small-world, Physica A 285, pp. 539-546 (2000). + * https://doi.org/10.1016/S0378-4371%2800%2900311-3 + * + * + * Y. Rochat, Closeness Centrality Extended to Unconnected Graphs: the Harmonic Centrality Index, ASNA 2009. + * https://infoscience.epfl.ch/record/200525 + * + * + * S. Vigna and P. Boldi, Axioms for Centrality, Internet Mathematics 10, (2014). + * https://doi.org/10.1080/15427951.2013.865686 + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * harmonic centrality scores for the given vertices. + * \param vids The vertices for which the harmonic centrality will be computed. + * \param mode The type of shortest paths to be used for the + * calculation in directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the lengths of the outgoing paths are calculated. + * \cli IGRAPH_IN + * the lengths of the incoming paths are calculated. + * \cli IGRAPH_ALL + * the directed graph is considered as an + * undirected one for the computation. + * \endclist + * \param weights An optional vector containing edge weights for + * weighted harmonic centrality. No edge weight may be NaN. + * If \c NULL, all weights are considered to be one. + * \param normalized Boolean, whether to normalize the result. If true, + * the result is the mean inverse path length to other vertices, + * i.e. it is normalized by the number of vertices minus one. + * If false, the result is the sum of inverse path lengths to other + * vertices. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * invalid vertex id passed. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(n|E|), where + * n is the numberof vertices for which the calculation is done and + * |E| is the number of edges in the graph. + * + * \sa Other centrality types: \ref igraph_closeness(), \ref igraph_degree(), \ref igraph_betweenness(). + */ + +int igraph_harmonic_centrality(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_neimode_t mode, + const igraph_vector_t *weights, + igraph_bool_t normalized) { + return igraph_harmonic_centrality_cutoff(graph, res, vids, mode, weights, normalized, /* cutoff= */ -1); +} diff --git a/src/rigraph/core/centrality/coreness.c b/src/rigraph/core/centrality/coreness.c new file mode 100644 index 0000000..28ac504 --- /dev/null +++ b/src/rigraph/core/centrality/coreness.c @@ -0,0 +1,164 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_memory.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" + +/** + * \function igraph_coreness + * \brief Finding the coreness of the vertices in a network. + * + * The k-core of a graph is a maximal subgraph in which each vertex + * has at least degree k. (Degree here means the degree in the + * subgraph of course.). The coreness of a vertex is the highest order + * of a k-core containing the vertex. + * + * + * This function implements the algorithm presented in Vladimir + * Batagelj, Matjaz Zaversnik: An O(m) Algorithm for Cores + * Decomposition of Networks. + * \param graph The input graph. + * \param cores Pointer to an initialized vector, the result of the + * computation will be stored here. It will be resized as + * needed. For each vertex it contains the highest order of a + * core containing the vertex. + * \param mode For directed graph it specifies whether to calculate + * in-cores, out-cores or the undirected version. It is ignored + * for undirected graphs. Possible values: \c IGRAPH_ALL + * undirected version, \c IGRAPH_IN in-cores, \c IGRAPH_OUT + * out-cores. + * \return Error code. + * + * Time complexity: O(|E|), the number of edges. + */ + +int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores, + igraph_neimode_t mode) { + + long int no_of_nodes = igraph_vcount(graph); + long int *bin, *vert, *pos; + long int maxdeg; + long int i, j = 0; + igraph_vector_t neis; + igraph_neimode_t omode; + + if (mode != IGRAPH_ALL && mode != IGRAPH_OUT && mode != IGRAPH_IN) { + IGRAPH_ERROR("Invalid mode in k-cores", IGRAPH_EINVAL); + } + if (!igraph_is_directed(graph) || mode == IGRAPH_ALL) { + mode = omode = IGRAPH_ALL; + } else if (mode == IGRAPH_IN) { + omode = IGRAPH_OUT; + } else { + omode = IGRAPH_IN; + } + + if (no_of_nodes == 0) { + igraph_vector_clear(cores); + return IGRAPH_SUCCESS; + } + + vert = IGRAPH_CALLOC(no_of_nodes, long int); + if (vert == 0) { + IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, vert); + pos = IGRAPH_CALLOC(no_of_nodes, long int); + if (pos == 0) { + IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, pos); + + /* maximum degree + degree of vertices */ + IGRAPH_CHECK(igraph_degree(graph, cores, igraph_vss_all(), mode, + IGRAPH_LOOPS)); + maxdeg = (long int) igraph_vector_max(cores); + + bin = IGRAPH_CALLOC(maxdeg + 1, long int); + if (bin == 0) { + IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, bin); + + /* degree histogram */ + for (i = 0; i < no_of_nodes; i++) { + bin[ (long int)VECTOR(*cores)[i] ] += 1; + } + + /* start pointers */ + j = 0; + for (i = 0; i <= maxdeg; i++) { + long int k = bin[i]; + bin[i] = j; + j += k; + } + + /* sort in vert (and corrupt bin) */ + for (i = 0; i < no_of_nodes; i++) { + pos[i] = bin[(long int)VECTOR(*cores)[i]]; + vert[pos[i]] = i; + bin[(long int)VECTOR(*cores)[i]] += 1; + } + + /* correct bin */ + for (i = maxdeg; i > 0; i--) { + bin[i] = bin[i - 1]; + } + bin[0] = 0; + + /* this is the main algorithm */ + IGRAPH_VECTOR_INIT_FINALLY(&neis, maxdeg); + for (i = 0; i < no_of_nodes; i++) { + long int v = vert[i]; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, omode)); + for (j = 0; j < igraph_vector_size(&neis); j++) { + long int u = (long int) VECTOR(neis)[j]; + if (VECTOR(*cores)[u] > VECTOR(*cores)[v]) { + long int du = (long int) VECTOR(*cores)[u]; + long int pu = pos[u]; + long int pw = bin[du]; + long int w = vert[pw]; + if (u != w) { + pos[u] = pw; + pos[w] = pu; + vert[pu] = w; + vert[pw] = u; + } + bin[du] += 1; + VECTOR(*cores)[u] -= 1; + } + } + } + + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + + igraph_free(bin); + igraph_free(pos); + igraph_free(vert); + IGRAPH_FINALLY_CLEAN(3); + return 0; +} diff --git a/src/rigraph/core/centrality/prpack.cpp b/src/rigraph/core/centrality/prpack.cpp new file mode 100644 index 0000000..bd70178 --- /dev/null +++ b/src/rigraph/core/centrality/prpack.cpp @@ -0,0 +1,130 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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 . +*/ + +#include "igraph_error.h" + +//#include "centrality/prpack_internal.h" +//#include "centrality/prpack/prpack_igraph_graph.h" +//#include "centrality/prpack/prpack_solver.h" +// #include "core/exceptions.h" + +#include "prpack_internal.h" +#include "prpack/prpack_igraph_graph.h" +#include "prpack/prpack_solver.h" +#include "../core/exceptions.h" + + +using namespace prpack; +using namespace std; + +/* + * PRPACK-based implementation of \c igraph_personalized_pagerank. + * + * See \c igraph_personalized_pagerank for the documentation of the parameters. + */ +int igraph_i_personalized_pagerank_prpack(const igraph_t *graph, igraph_vector_t *vector, + igraph_real_t *value, const igraph_vs_t vids, + igraph_bool_t directed, igraph_real_t damping, + const igraph_vector_t *reset, + const igraph_vector_t *weights) { + long int i, no_of_nodes = igraph_vcount(graph), nodes_to_calc; + igraph_vit_t vit; + double *u = nullptr; + double *v = nullptr; + const prpack_result *res; + + IGRAPH_HANDLE_EXCEPTIONS( + if (reset) { + if (igraph_vector_size(reset) != no_of_nodes) { + IGRAPH_ERROR("Invalid length of reset vector when calculating personalized PageRank scores.", IGRAPH_EINVAL); + } + + /* Normalize reset vector so the sum is 1 */ + double reset_min = igraph_vector_min(reset); + if (reset_min < 0) { + IGRAPH_ERROR("The reset vector must not contain negative elements.", IGRAPH_EINVAL); + } + if (igraph_is_nan(reset_min)) { + IGRAPH_ERROR("The reset vector must not contain NaN values.", IGRAPH_EINVAL); + } + + double reset_sum = igraph_vector_sum(reset); + if (reset_sum == 0) { + IGRAPH_ERROR("The sum of the elements in the reset vector must not be zero.", IGRAPH_EINVAL); + } + + // Construct the personalization vector + v = new double[no_of_nodes]; + for (i = 0; i < no_of_nodes; i++) { + v[i] = VECTOR(*reset)[i] / reset_sum; + } + + // u is the distribution used when restarting the walk due to being stuck in a sink + // v is the distribution used when restarting due to damping + // Here we use the same distribution for both + u = v; + } + + // Since PRPACK uses the algebraic method to solve PageRank, damping factors very close to 1.0 + // may lead to numerical instability, the apperance of non-finite values, or the iteration + // never terminating. + if (damping > 0.999) { + IGRAPH_WARNINGF( + "Damping factor is %g. " + "Damping values close to 1 may lead to numerical instability when using PRPACK.", + damping); + } + + // Construct and run the solver + prpack_igraph_graph prpack_graph(graph, weights, directed); + prpack_solver solver(&prpack_graph, false); + res = solver.solve(damping, 1e-10, u, v, ""); + + // Delete the personalization vector + delete [] v; + ); + + // Check whether the solver converged + // TODO: this is commented out because some of the solvers do not implement it yet + /* + if (!res->converged) { + IGRAPH_WARNING("PRPACK solver failed to converge. Results may be inaccurate."); + } + */ + + // Fill the result vector + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + nodes_to_calc = IGRAPH_VIT_SIZE(vit); + IGRAPH_CHECK(igraph_vector_resize(vector, nodes_to_calc)); + for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) { + VECTOR(*vector)[i] = res->x[(long int)IGRAPH_VIT_GET(vit)]; + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + // TODO: can we get the eigenvalue? We'll just fake it until we can. + if (value) { + *value = 1.0; + } + delete res; + + return IGRAPH_SUCCESS; +} \ No newline at end of file diff --git a/src/rigraph/core/centrality/prpack/prpack.h b/src/rigraph/core/centrality/prpack/prpack.h new file mode 100644 index 0000000..bcddf37 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack.h @@ -0,0 +1,11 @@ +#ifndef PRPACK +#define PRPACK + +#include "prpack_csc.h" +#include "prpack_csr.h" +#include "prpack_edge_list.h" +#include "prpack_base_graph.h" +#include "prpack_solver.h" +#include "prpack_result.h" + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_base_graph.cpp b/src/rigraph/core/centrality/prpack/prpack_base_graph.cpp new file mode 100644 index 0000000..b78c6fe --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_base_graph.cpp @@ -0,0 +1,341 @@ +#include "prpack_base_graph.h" +#include "prpack_utils.h" +#include +#include +#include +#include +#include +#include +#include +using namespace prpack; +using namespace std; + +void prpack_base_graph::initialize() { + heads = NULL; + tails = NULL; + vals = NULL; +} + +prpack_base_graph::prpack_base_graph() { + initialize(); + num_vs = num_es = 0; +} + +prpack_base_graph::prpack_base_graph(const prpack_csc* g) { + initialize(); + num_vs = g->num_vs; + num_es = g->num_es; + // fill in heads and tails + num_self_es = 0; + int* hs = g->heads; + int* ts = g->tails; + tails = new int[num_vs]; + memset(tails, 0, num_vs*sizeof(tails[0])); + for (int h = 0; h < num_vs; ++h) { + const int start_ti = hs[h]; + const int end_ti = (h + 1 != num_vs) ? hs[h + 1] : num_es; + for (int ti = start_ti; ti < end_ti; ++ti) { + const int t = ts[ti]; + ++tails[t]; + if (h == t) + ++num_self_es; + } + } + for (int i = 0, sum = 0; i < num_vs; ++i) { + const int temp = sum; + sum += tails[i]; + tails[i] = temp; + } + heads = new int[num_es]; + int* osets = new int[num_vs]; + memset(osets, 0, num_vs*sizeof(osets[0])); + for (int h = 0; h < num_vs; ++h) { + const int start_ti = hs[h]; + const int end_ti = (h + 1 != num_vs) ? hs[h + 1] : num_es; + for (int ti = start_ti; ti < end_ti; ++ti) { + const int t = ts[ti]; + heads[tails[t] + osets[t]++] = h; + } + } + // clean up + delete[] osets; +} + +prpack_base_graph::prpack_base_graph(const prpack_int64_csc* g) { + initialize(); + // TODO remove the assert and add better behavior + assert(num_vs <= std::numeric_limits::max()); + num_vs = (int)g->num_vs; + num_es = (int)g->num_es; + // fill in heads and tails + num_self_es = 0; + int64_t* hs = g->heads; + int64_t* ts = g->tails; + tails = new int[num_vs]; + memset(tails, 0, num_vs*sizeof(tails[0])); + for (int h = 0; h < num_vs; ++h) { + const int start_ti = (int)hs[h]; + const int end_ti = (h + 1 != num_vs) ? (int)hs[h + 1] : num_es; + for (int ti = start_ti; ti < end_ti; ++ti) { + const int t = (int)ts[ti]; + ++tails[t]; + if (h == t) + ++num_self_es; + } + } + for (int i = 0, sum = 0; i < num_vs; ++i) { + const int temp = sum; + sum += tails[i]; + tails[i] = temp; + } + heads = new int[num_es]; + int* osets = new int[num_vs]; + memset(osets, 0, num_vs*sizeof(osets[0])); + for (int h = 0; h < num_vs; ++h) { + const int start_ti = (int)hs[h]; + const int end_ti = (h + 1 != num_vs) ? (int)hs[h + 1] : num_es; + for (int ti = start_ti; ti < end_ti; ++ti) { + const int t = (int)ts[ti]; + heads[tails[t] + osets[t]++] = h; + } + } + // clean up + delete[] osets; +} + +prpack_base_graph::prpack_base_graph(const prpack_csr* g) { + (void)g; // to silence an unused argument warning + initialize(); + throw std::runtime_error("not implemented yet"); +} + +prpack_base_graph::prpack_base_graph(const prpack_edge_list* g) { + initialize(); + num_vs = g->num_vs; + num_es = g->num_es; + // fill in heads and tails + num_self_es = 0; + int* hs = g->heads; + int* ts = g->tails; + tails = new int[num_vs]; + memset(tails, 0, num_vs*sizeof(tails[0])); + for (int i = 0; i < num_es; ++i) { + ++tails[ts[i]]; + if (hs[i] == ts[i]) + ++num_self_es; + } + for (int i = 0, sum = 0; i < num_vs; ++i) { + const int temp = sum; + sum += tails[i]; + tails[i] = temp; + } + heads = new int[num_es]; + int* osets = new int[num_vs]; + memset(osets, 0, num_vs*sizeof(osets[0])); + for (int i = 0; i < num_es; ++i) + heads[tails[ts[i]] + osets[ts[i]]++] = hs[i]; + // clean up + delete[] osets; +} + +prpack_base_graph::prpack_base_graph(const char* filename, const char* format, const bool weighted) { + initialize(); + FILE* f = fopen(filename, "r"); + const string s(filename); + const string t(format); + const string ext = (t == "") ? s.substr(s.rfind('.') + 1) : t; + if (ext == "smat") { + read_smat(f, weighted); + } else { + prpack_utils::validate(!weighted, + "Error: graph format is not compatible with weighted option."); + if (ext == "edges" || ext == "eg2") { + read_edges(f); + } else if (ext == "graph-txt") { + read_ascii(f); + } else { + prpack_utils::validate(false, "Error: invalid graph format."); + } + } + fclose(f); +} + +prpack_base_graph::~prpack_base_graph() { + delete[] heads; + delete[] tails; + delete[] vals; +} + +void prpack_base_graph::read_smat(FILE* f, const bool weighted) { + // read in header + double ignore = 0.0; + int retval = fscanf(f, "%d %lf %d", &num_vs, &ignore, &num_es); + if (retval != 3) { + throw std::runtime_error("error while parsing smat file"); + } + // fill in heads and tails + num_self_es = 0; + int* hs = new int[num_es]; + int* ts = new int[num_es]; + heads = new int[num_es]; + tails = new int[num_vs]; + double* vs = NULL; + if (weighted) { + vs = new double[num_es]; + vals = new double[num_es]; + } + memset(tails, 0, num_vs*sizeof(tails[0])); + for (int i = 0; i < num_es; ++i) { + retval = fscanf(f, "%d %d %lf", &hs[i], &ts[i], &((weighted) ? vs[i] : ignore)); + if (retval != 3) { + throw std::runtime_error("error while parsing smat file"); + } + ++tails[ts[i]]; + if (hs[i] == ts[i]) + ++num_self_es; + } + for (int i = 0, sum = 0; i < num_vs; ++i) { + const int temp = sum; + sum += tails[i]; + tails[i] = temp; + } + int* osets = new int[num_vs]; + memset(osets, 0, num_vs*sizeof(osets[0])); + for (int i = 0; i < num_es; ++i) { + const int idx = tails[ts[i]] + osets[ts[i]]++; + heads[idx] = hs[i]; + if (weighted) + vals[idx] = vs[i]; + } + // clean up + delete[] hs; + delete[] ts; + delete[] vs; + delete[] osets; +} + +void prpack_base_graph::read_edges(FILE* f) { + vector > al; + int h, t; + num_es = num_self_es = 0; + while (fscanf(f, "%d %d", &h, &t) == 2) { + const int m = (h < t) ? t : h; + if ((int) al.size() < m + 1) + al.resize(m + 1); + al[t].push_back(h); + ++num_es; + if (h == t) + ++num_self_es; + } + num_vs = al.size(); + heads = new int[num_es]; + tails = new int[num_vs]; + for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) { + tails[tails_i] = heads_i; + for (int j = 0; j < (int) al[tails_i].size(); ++j) + heads[heads_i++] = al[tails_i][j]; + } +} + +void prpack_base_graph::read_ascii(FILE* f) { + int retval = fscanf(f, "%d", &num_vs); + if (retval != 1) { + throw std::runtime_error("error while parsing ascii file"); + } + while (getc(f) != '\n'); + vector* al = new vector[num_vs]; + num_es = num_self_es = 0; + char s[32]; + for (int h = 0; h < num_vs; ++h) { + bool line_ended = false; + while (!line_ended) { + for (int i = 0; ; ++i) { + s[i] = getc(f); + if ('9' < s[i] || s[i] < '0') { + line_ended = s[i] == '\n'; + if (i != 0) { + s[i] = '\0'; + const int t = atoi(s); + al[t].push_back(h); + ++num_es; + if (h == t) + ++num_self_es; + } + break; + } + } + } + } + heads = new int[num_es]; + tails = new int[num_vs]; + for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) { + tails[tails_i] = heads_i; + for (int j = 0; j < (int) al[tails_i].size(); ++j) + heads[heads_i++] = al[tails_i][j]; + } + delete[] al; +} + +prpack_base_graph::prpack_base_graph(int nverts, int nedges, + std::pair* edges) { + initialize(); + num_vs = nverts; + num_es = nedges; + + // fill in heads and tails + num_self_es = 0; + int* hs = new int[num_es]; + int* ts = new int[num_es]; + tails = new int[num_vs]; + memset(tails, 0, num_vs*sizeof(tails[0])); + for (int i = 0; i < num_es; ++i) { + assert(edges[i].first >= 0 && edges[i].first < num_vs); + assert(edges[i].second >= 0 && edges[i].second < num_vs); + hs[i] = edges[i].first; + ts[i] = edges[i].second; + ++tails[ts[i]]; + if (hs[i] == ts[i]) + ++num_self_es; + } + for (int i = 0, sum = 0; i < num_vs; ++i) { + int temp = sum; + sum += tails[i]; + tails[i] = temp; + } + heads = new int[num_es]; + int* osets = new int[num_vs]; + memset(osets, 0, num_vs*sizeof(osets[0])); + for (int i = 0; i < num_es; ++i) + heads[tails[ts[i]] + osets[ts[i]]++] = hs[i]; + // clean up + delete[] hs; + delete[] ts; + delete[] osets; +} + +/** Normalize the edge weights to sum to one. + */ +void prpack_base_graph::normalize_weights() { + if (!vals) { + // skip normalizing weights if not using values + return; + } + std::vector rowsums(num_vs,0.); + // the graph is in a compressed in-edge list. + for (int i=0; i +#include + +namespace prpack { + + class prpack_base_graph { + private: + // helper methods + void initialize(); + void read_smat(std::FILE* f, const bool weighted); + void read_edges(std::FILE* f); + void read_ascii(std::FILE* f); + public: + // instance variables + int num_vs; + int num_es; + int num_self_es; + int* heads; + int* tails; + double* vals; + // constructors + prpack_base_graph(); // only to support inheritance + prpack_base_graph(const prpack_csc* g); + prpack_base_graph(const prpack_int64_csc* g); + prpack_base_graph(const prpack_csr* g); + prpack_base_graph(const prpack_edge_list* g); + prpack_base_graph(const char* filename, const char* format, const bool weighted); + prpack_base_graph(int nverts, int nedges, std::pair* edges); + // destructor + ~prpack_base_graph(); + // operations + void normalize_weights(); + }; + +} + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_csc.h b/src/rigraph/core/centrality/prpack/prpack_csc.h new file mode 100644 index 0000000..91f352d --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_csc.h @@ -0,0 +1,30 @@ +#ifndef PRPACK_CSC +#define PRPACK_CSC + +#if !defined(_MSC_VER) && !defined (__MINGW32__) && !defined (__MINGW64__) +# include +#else +# include +typedef __int64 int64_t; +#endif + +namespace prpack { + + class prpack_csc { + public: + int num_vs; + int num_es; + int* heads; + int* tails; + }; + + class prpack_int64_csc { + public: + int64_t num_vs; + int64_t num_es; + int64_t* heads; + int64_t* tails; + }; +} + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_csr.h b/src/rigraph/core/centrality/prpack/prpack_csr.h new file mode 100644 index 0000000..3c8c7b7 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_csr.h @@ -0,0 +1,16 @@ +#ifndef PRPACK_CSR +#define PRPACK_CSR + +namespace prpack { + + class prpack_csr { + public: + int num_vs; + int num_es; + int* heads; + int* tails; + }; + +} + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_edge_list.h b/src/rigraph/core/centrality/prpack/prpack_edge_list.h new file mode 100644 index 0000000..a84466f --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_edge_list.h @@ -0,0 +1,16 @@ +#ifndef PRPACK_EDGE_LIST +#define PRPACK_EDGE_LIST + +namespace prpack { + + class prpack_edge_list { + public: + int num_vs; + int num_es; + int* heads; + int* tails; + }; + +} + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_igraph_graph.cpp b/src/rigraph/core/centrality/prpack/prpack_igraph_graph.cpp new file mode 100644 index 0000000..0b78cff --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_igraph_graph.cpp @@ -0,0 +1,147 @@ +#include "prpack_igraph_graph.h" +#include +#include + +#include "igraph_interface.h" + +//using namespace prpack; +using namespace std; + +#ifdef PRPACK_IGRAPH_SUPPORT + +prpack_igraph_graph::prpack_igraph_graph(const igraph_t* g, const igraph_vector_t* weights, + bool directed) { + const igraph_bool_t treat_as_directed = igraph_is_directed(g) && directed; + igraph_es_t es; + igraph_eit_t eit; + igraph_vector_t neis; + long int i, j, eid, sum, temp, num_ignored_es; + int *p_head, *p_head_copy; + double* p_weight = 0; + + // Get the number of vertices and edges. For undirected graphs, we add + // an edge in both directions. + num_vs = igraph_vcount(g); + num_es = igraph_ecount(g); + num_self_es = 0; + if (!treat_as_directed) { + num_es *= 2; + } + + // Allocate memory for heads and tails + p_head = heads = new int[num_es]; + tails = new int[num_vs]; + memset(tails, 0, num_vs * sizeof(tails[0])); + + // Allocate memory for weights if needed + if (weights != 0) { + p_weight = vals = new double[num_es]; + } + + // Count the number of ignored edges (those with negative or zero weight) + num_ignored_es = 0; + + if (treat_as_directed) { + // Select all the edges and iterate over them by the source vertices + es = igraph_ess_all(IGRAPH_EDGEORDER_TO); + + // Add the edges + igraph_eit_create(g, es, &eit); + while (!IGRAPH_EIT_END(eit)) { + eid = IGRAPH_EIT_GET(eit); + IGRAPH_EIT_NEXT(eit); + + // Handle the weight + if (weights != 0) { + // Does this edge have zero or negative weight? + if (VECTOR(*weights)[eid] <= 0) { + // Ignore it. + num_ignored_es++; + continue; + } + + *p_weight = VECTOR(*weights)[eid]; + ++p_weight; + } + + *p_head = IGRAPH_FROM(g, eid); + ++p_head; + ++tails[IGRAPH_TO(g, eid)]; + + if (IGRAPH_FROM(g, eid) == IGRAPH_TO(g, eid)) { + ++num_self_es; + } + } + igraph_eit_destroy(&eit); + } else { + // Select all the edges and iterate over them by the target vertices + igraph_vector_init(&neis, 0); + + for (i = 0; i < num_vs; i++) { + igraph_incident(g, &neis, i, IGRAPH_ALL); + temp = igraph_vector_size(&neis); + + // TODO: should loop edges be added in both directions? + p_head_copy = p_head; + for (j = 0; j < temp; j++) { + if (weights != 0) { + if (VECTOR(*weights)[(long int)VECTOR(neis)[j]] <= 0) { + // Ignore + num_ignored_es++; + continue; + } + + *p_weight = VECTOR(*weights)[(long int)VECTOR(neis)[j]]; + ++p_weight; + } + + *p_head = IGRAPH_OTHER(g, VECTOR(neis)[j], i); + if (i == *p_head) { + num_self_es++; + } + ++p_head; + } + tails[i] = p_head - p_head_copy; + } + + igraph_vector_destroy(&neis); + } + + // Decrease num_es by the number of ignored edges + num_es -= num_ignored_es; + + // Finalize the tails vector + for (i = 0, sum = 0; i < num_vs; ++i) { + temp = sum; + sum += tails[i]; + tails[i] = temp; + } + + // Normalize the weights + normalize_weights(); + + // Debug + /* + printf("Heads:"); + for (i = 0; i < num_es; ++i) { + printf(" %d", heads[i]); + } + printf("\n"); + printf("Tails:"); + for (i = 0; i < num_vs; ++i) { + printf(" %d", tails[i]); + } + printf("\n"); + if (vals) { + printf("Vals:"); + for (i = 0; i < num_es; ++i) { + printf(" %.4f", vals[i]); + } + printf("\n"); + } + printf("===========================\n"); + */ +} + +// PRPACK_IGRAPH_SUPPORT +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_igraph_graph.h b/src/rigraph/core/centrality/prpack/prpack_igraph_graph.h new file mode 100644 index 0000000..1bab84d --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_igraph_graph.h @@ -0,0 +1,28 @@ +#ifndef PRPACK_IGRAPH_GRAPH +#define PRPACK_IGRAPH_GRAPH + +#ifdef PRPACK_IGRAPH_SUPPORT + +#include "prpack_base_graph.h" + +struct igraph_s; +struct igraph_vector_t; + +namespace prpack { + + class prpack_igraph_graph : public prpack_base_graph { + + public: + // constructors + explicit prpack_igraph_graph(const struct igraph_s* g, + const struct igraph_vector_t* weights = 0, + bool directed = true); + }; + +} + +// PRPACK_IGRAPH_SUPPORT +#endif + +// PRPACK_IGRAPH_GRAPH +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_preprocessed_ge_graph.cpp b/src/rigraph/core/centrality/prpack/prpack_preprocessed_ge_graph.cpp new file mode 100644 index 0000000..303e30d --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_preprocessed_ge_graph.cpp @@ -0,0 +1,65 @@ +#include "prpack_preprocessed_ge_graph.h" +#include +using namespace prpack; +using namespace std; + +void prpack_preprocessed_ge_graph::initialize() { + matrix = NULL; + d = NULL; +} + +void prpack_preprocessed_ge_graph::initialize_weighted(const prpack_base_graph* bg) { + // initialize d + fill(d, d + num_vs, 1); + // fill in the matrix + for (int i = 0, inum_vs = 0; i < num_vs; ++i, inum_vs += num_vs) { + const int start_j = bg->tails[i]; + const int end_j = (i + 1 != num_vs) ? bg->tails[i + 1] : bg->num_es; + for (int j = start_j; j < end_j; ++j) { + matrix[inum_vs + bg->heads[j]] += bg->vals[j]; + d[bg->heads[j]] -= bg->vals[j]; + } + } +} + +void prpack_preprocessed_ge_graph::initialize_unweighted(const prpack_base_graph* bg) { + // fill in the matrix + for (int i = 0, inum_vs = 0; i < num_vs; ++i, inum_vs += num_vs) { + const int start_j = bg->tails[i]; + const int end_j = (i + 1 != num_vs) ? bg->tails[i + 1] : bg->num_es; + for (int j = start_j; j < end_j; ++j) + ++matrix[inum_vs + bg->heads[j]]; + } + // normalize the columns + for (int j = 0; j < num_vs; ++j) { + double sum = 0; + for (int inum_vs = 0; inum_vs < num_vs*num_vs; inum_vs += num_vs) + sum += matrix[inum_vs + j]; + if (sum > 0) { + d[j] = 0; + const double coeff = 1/sum; + for (int inum_vs = 0; inum_vs < num_vs*num_vs; inum_vs += num_vs) + matrix[inum_vs + j] *= coeff; + } else { + d[j] = 1; + } + } +} + +prpack_preprocessed_ge_graph::prpack_preprocessed_ge_graph(const prpack_base_graph* bg) { + initialize(); + num_vs = bg->num_vs; + num_es = bg->num_es; + matrix = new double[num_vs*num_vs]; + d = new double[num_vs]; + fill(matrix, matrix + num_vs*num_vs, 0); + if (bg->vals != NULL) + initialize_weighted(bg); + else + initialize_unweighted(bg); +} + +prpack_preprocessed_ge_graph::~prpack_preprocessed_ge_graph() { + delete[] matrix; + delete[] d; +} diff --git a/src/rigraph/core/centrality/prpack/prpack_preprocessed_ge_graph.h b/src/rigraph/core/centrality/prpack/prpack_preprocessed_ge_graph.h new file mode 100644 index 0000000..678568f --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_preprocessed_ge_graph.h @@ -0,0 +1,26 @@ +#ifndef PRPACK_PREPROCESSED_GE_GRAPH +#define PRPACK_PREPROCESSED_GE_GRAPH +#include "prpack_preprocessed_graph.h" +#include "prpack_base_graph.h" + +namespace prpack { + + // Pre-processed graph class + class prpack_preprocessed_ge_graph : public prpack_preprocessed_graph { + private: + // helper methods + void initialize(); + void initialize_weighted(const prpack_base_graph* bg); + void initialize_unweighted(const prpack_base_graph* bg); + public: + // instance variables + double* matrix; + // constructors + prpack_preprocessed_ge_graph(const prpack_base_graph* bg); + // destructor + ~prpack_preprocessed_ge_graph(); + }; + +} + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_preprocessed_graph.h b/src/rigraph/core/centrality/prpack/prpack_preprocessed_graph.h new file mode 100644 index 0000000..4f5a318 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_preprocessed_graph.h @@ -0,0 +1,17 @@ +#ifndef PRPACK_PREPROCESSED_GRAPH +#define PRPACK_PREPROCESSED_GRAPH + +namespace prpack { + + // TODO: this class should not be seeable by the users of the library. + // Super graph class. + class prpack_preprocessed_graph { + public: + int num_vs; + int num_es; + double* d; + }; + +} + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_preprocessed_gs_graph.cpp b/src/rigraph/core/centrality/prpack/prpack_preprocessed_gs_graph.cpp new file mode 100644 index 0000000..2cefd5d --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_preprocessed_gs_graph.cpp @@ -0,0 +1,80 @@ +#include "prpack_preprocessed_gs_graph.h" +#include +using namespace prpack; +using namespace std; + +void prpack_preprocessed_gs_graph::initialize() { + heads = NULL; + tails = NULL; + vals = NULL; + ii = NULL; + d = NULL; + num_outlinks = NULL; +} + +void prpack_preprocessed_gs_graph::initialize_weighted(const prpack_base_graph* bg) { + vals = new double[num_es]; + d = new double[num_vs]; + fill(d, d + num_vs, 1); + for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) { + tails[tails_i] = heads_i; + ii[tails_i] = 0; + const int start_j = bg->tails[tails_i]; + const int end_j = (tails_i + 1 != num_vs) ? bg->tails[tails_i + 1]: bg->num_es; + for (int j = start_j; j < end_j; ++j) { + if (tails_i == bg->heads[j]) + ii[tails_i] += bg->vals[j]; + else { + heads[heads_i] = bg->heads[j]; + vals[heads_i] = bg->vals[j]; + ++heads_i; + } + d[bg->heads[j]] -= bg->vals[j]; + } + } +} + +void prpack_preprocessed_gs_graph::initialize_unweighted(const prpack_base_graph* bg) { + num_outlinks = new double[num_vs]; + fill(num_outlinks, num_outlinks + num_vs, 0); + for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) { + tails[tails_i] = heads_i; + ii[tails_i] = 0; + const int start_j = bg->tails[tails_i]; + const int end_j = (tails_i + 1 != num_vs) ? bg->tails[tails_i + 1]: bg->num_es; + for (int j = start_j; j < end_j; ++j) { + if (tails_i == bg->heads[j]) + ++ii[tails_i]; + else + heads[heads_i++] = bg->heads[j]; + ++num_outlinks[bg->heads[j]]; + } + } + for (int i = 0; i < num_vs; ++i) { + if (num_outlinks[i] == 0) + num_outlinks[i] = -1; + ii[i] /= num_outlinks[i]; + } +} + +prpack_preprocessed_gs_graph::prpack_preprocessed_gs_graph(const prpack_base_graph* bg) { + initialize(); + num_vs = bg->num_vs; + num_es = bg->num_es - bg->num_self_es; + heads = new int[num_es]; + tails = new int[num_vs]; + ii = new double[num_vs]; + if (bg->vals != NULL) + initialize_weighted(bg); + else + initialize_unweighted(bg); +} + +prpack_preprocessed_gs_graph::~prpack_preprocessed_gs_graph() { + delete[] heads; + delete[] tails; + delete[] vals; + delete[] ii; + delete[] d; + delete[] num_outlinks; +} diff --git a/src/rigraph/core/centrality/prpack/prpack_preprocessed_gs_graph.h b/src/rigraph/core/centrality/prpack/prpack_preprocessed_gs_graph.h new file mode 100644 index 0000000..eec3fe2 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_preprocessed_gs_graph.h @@ -0,0 +1,30 @@ +#ifndef PRPACK_PREPROCESSED_GS_GRAPH +#define PRPACK_PREPROCESSED_GS_GRAPH +#include "prpack_preprocessed_graph.h" +#include "prpack_base_graph.h" + +namespace prpack { + + // Pre-processed graph class + class prpack_preprocessed_gs_graph : public prpack_preprocessed_graph { + private: + // helper methods + void initialize(); + void initialize_weighted(const prpack_base_graph* bg); + void initialize_unweighted(const prpack_base_graph* bg); + public: + // instance variables + int* heads; + int* tails; + double* vals; + double* ii; + double* num_outlinks; + // constructors + prpack_preprocessed_gs_graph(const prpack_base_graph* bg); + // destructor + ~prpack_preprocessed_gs_graph(); + }; + +} + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_preprocessed_scc_graph.cpp b/src/rigraph/core/centrality/prpack/prpack_preprocessed_scc_graph.cpp new file mode 100644 index 0000000..a34ad7e --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_preprocessed_scc_graph.cpp @@ -0,0 +1,201 @@ +#include "prpack_preprocessed_scc_graph.h" +#include +#include +#include +using namespace prpack; +using namespace std; + +void prpack_preprocessed_scc_graph::initialize() { + heads_inside = NULL; + tails_inside = NULL; + vals_inside = NULL; + heads_outside = NULL; + tails_outside = NULL; + vals_outside = NULL; + ii = NULL; + d = NULL; + num_outlinks = NULL; + divisions = NULL; + encoding = NULL; + decoding = NULL; +} + +void prpack_preprocessed_scc_graph::initialize_weighted(const prpack_base_graph* bg) { + vals_inside = new double[num_es]; + vals_outside = new double[num_es]; + d = new double[num_vs]; + fill(d, d + num_vs, 1); + for (int comp_i = 0; comp_i < num_comps; ++comp_i) { + const int start_i = divisions[comp_i]; + const int end_i = (comp_i + 1 != num_comps) ? divisions[comp_i + 1] : num_vs; + for (int i = start_i; i < end_i; ++i) { + ii[i] = 0; + const int decoded = decoding[i]; + const int start_j = bg->tails[decoded]; + const int end_j = (decoded + 1 != num_vs) ? bg->tails[decoded + 1] : bg->num_es; + tails_inside[i] = num_es_inside; + tails_outside[i] = num_es_outside; + for (int j = start_j; j < end_j; ++j) { + const int h = encoding[bg->heads[j]]; + if (h == i) { + ii[i] += bg->vals[j]; + } else { + if (start_i <= h && h < end_i) { + heads_inside[num_es_inside] = h; + vals_inside[num_es_inside] = bg->vals[j]; + ++num_es_inside; + } else { + heads_outside[num_es_outside] = h; + vals_outside[num_es_outside] = bg->vals[j]; + ++num_es_outside; + } + } + d[h] -= bg->vals[j]; + } + } + } +} + +void prpack_preprocessed_scc_graph::initialize_unweighted(const prpack_base_graph* bg) { + num_outlinks = new double[num_vs]; + fill(num_outlinks, num_outlinks + num_vs, 0); + for (int comp_i = 0; comp_i < num_comps; ++comp_i) { + const int start_i = divisions[comp_i]; + const int end_i = (comp_i + 1 != num_comps) ? divisions[comp_i + 1] : num_vs; + for (int i = start_i; i < end_i; ++i) { + ii[i] = 0; + const int decoded = decoding[i]; + const int start_j = bg->tails[decoded]; + const int end_j = (decoded + 1 != num_vs) ? bg->tails[decoded + 1] : bg->num_es; + tails_inside[i] = num_es_inside; + tails_outside[i] = num_es_outside; + for (int j = start_j; j < end_j; ++j) { + const int h = encoding[bg->heads[j]]; + if (h == i) { + ++ii[i]; + } else { + if (start_i <= h && h < end_i) + heads_inside[num_es_inside++] = h; + else + heads_outside[num_es_outside++] = h; + } + ++num_outlinks[h]; + } + } + } + for (int i = 0; i < num_vs; ++i) { + if (num_outlinks[i] == 0) + num_outlinks[i] = -1; + ii[i] /= num_outlinks[i]; + } +} + +prpack_preprocessed_scc_graph::prpack_preprocessed_scc_graph(const prpack_base_graph* bg) { + initialize(); + // initialize instance variables + num_vs = bg->num_vs; + num_es = bg->num_es - bg->num_self_es; + // initialize Tarjan's algorithm variables + num_comps = 0; + int mn = 0; // the number of vertices seen so far + int sz = 0; // size of st + int decoding_i = 0; // size of decoding currently filled in + decoding = new int[num_vs]; + int* scc = new int[num_vs]; // the strongly connected component this vertex is in + int* low = new int[num_vs]; // the lowest index this vertex can reach + int* num = new int[num_vs]; // the index of this vertex in the dfs traversal + int* st = new int[num_vs]; // a stack for the dfs + memset(num, -1, num_vs*sizeof(num[0])); + memset(scc, -1, num_vs*sizeof(scc[0])); + int* cs1 = new int[num_vs]; // call stack variable for dfs + int* cs2 = new int[num_vs]; // call stack variable for dfs + // run iterative Tarjan's algorithm + for (int root = 0; root < num_vs; ++root) { + if (num[root] != -1) + continue; + int csz = 1; + cs1[0] = root; + cs2[0] = bg->tails[root]; + // dfs + while (csz) { + const int p = cs1[csz - 1]; // node we're dfs-ing on + int& it = cs2[csz - 1]; // iteration of the for loop + if (it == bg->tails[p]) { + low[p] = num[p] = mn++; + st[sz++] = p; + } else { + low[p] = min(low[p], low[bg->heads[it - 1]]); + } + bool done = false; + int end_it = (p + 1 != num_vs) ? bg->tails[p + 1] : bg->num_es; + for (; it < end_it; ++it) { + int h = bg->heads[it]; + if (scc[h] == -1) { + if (num[h] == -1) { + // dfs(h, p); + cs1[csz] = h; + cs2[csz++] = bg->tails[h]; + ++it; + done = true; + break; + } + low[p] = min(low[p], low[h]); + } + } + if (done) + continue; + // if p is the first explored vertex of a scc + if (low[p] == num[p]) { + cs1[num_vs - 1 - num_comps] = decoding_i; + while (scc[p] != num_comps) { + scc[st[--sz]] = num_comps; + decoding[decoding_i++] = st[sz]; + } + ++num_comps; + } + --csz; + } + } + // set up other instance variables + divisions = new int[num_comps]; + divisions[0] = 0; + for (int i = 1; i < num_comps; ++i) + divisions[i] = cs1[num_vs - 1 - i]; + encoding = num; + for (int i = 0; i < num_vs; ++i) + encoding[decoding[i]] = i; + // fill in inside and outside instance variables + ii = new double[num_vs]; + tails_inside = cs1; + heads_inside = new int[num_es]; + tails_outside = cs2; + heads_outside = new int[num_es]; + num_es_inside = num_es_outside = 0; + // continue initialization based off of weightedness + if (bg->vals != NULL) + initialize_weighted(bg); + else + initialize_unweighted(bg); + // free memory + // do not free num <==> encoding + // do not free cs1 <==> tails_inside + // do not free cs2 <==> tails_outside + delete[] scc; + delete[] low; + delete[] st; +} + +prpack_preprocessed_scc_graph::~prpack_preprocessed_scc_graph() { + delete[] heads_inside; + delete[] tails_inside; + delete[] vals_inside; + delete[] heads_outside; + delete[] tails_outside; + delete[] vals_outside; + delete[] ii; + delete[] d; + delete[] num_outlinks; + delete[] divisions; + delete[] encoding; + delete[] decoding; +} diff --git a/src/rigraph/core/centrality/prpack/prpack_preprocessed_scc_graph.h b/src/rigraph/core/centrality/prpack/prpack_preprocessed_scc_graph.h new file mode 100644 index 0000000..6584c3f --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_preprocessed_scc_graph.h @@ -0,0 +1,39 @@ +#ifndef PRPACK_PREPROCESSED_SCC_GRAPH +#define PRPACK_PREPROCESSED_SCC_GRAPH +#include "prpack_preprocessed_graph.h" +#include "prpack_base_graph.h" + +namespace prpack { + + // Pre-processed graph class + class prpack_preprocessed_scc_graph : public prpack_preprocessed_graph { + private: + // helper methods + void initialize(); + void initialize_weighted(const prpack_base_graph* bg); + void initialize_unweighted(const prpack_base_graph* bg); + public: + // instance variables + int num_es_inside; + int* heads_inside; + int* tails_inside; + double* vals_inside; + int num_es_outside; + int* heads_outside; + int* tails_outside; + double* vals_outside; + double* ii; + double* num_outlinks; + int num_comps; + int* divisions; + int* encoding; + int* decoding; + // constructors + prpack_preprocessed_scc_graph(const prpack_base_graph* bg); + // destructor + ~prpack_preprocessed_scc_graph(); + }; + +} + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_preprocessed_schur_graph.cpp b/src/rigraph/core/centrality/prpack/prpack_preprocessed_schur_graph.cpp new file mode 100644 index 0000000..2a422d0 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_preprocessed_schur_graph.cpp @@ -0,0 +1,120 @@ +#include "prpack_preprocessed_schur_graph.h" +#include +#include +using namespace prpack; +using namespace std; + +void prpack_preprocessed_schur_graph::initialize() { + heads = NULL; + tails = NULL; + vals = NULL; + ii = NULL; + d = NULL; + num_outlinks = NULL; + encoding = NULL; + decoding = NULL; +} + +void prpack_preprocessed_schur_graph::initialize_weighted(const prpack_base_graph* bg) { + // permute d + ii = d; + d = new double[num_vs]; + for (int i = 0; i < num_vs; ++i) + d[encoding[i]] = ii[i]; + // convert bg to head/tail format + for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) { + ii[tails_i] = 0; + tails[tails_i] = heads_i; + const int decoded = decoding[tails_i]; + const int start_i = bg->tails[decoded]; + const int end_i = (decoded + 1 != num_vs) ? bg->tails[decoded + 1] : bg->num_es; + for (int i = start_i; i < end_i; ++i) { + if (decoded == bg->heads[i]) + ii[tails_i] += bg->vals[i]; + else { + heads[heads_i] = encoding[bg->heads[i]]; + vals[heads_i] = bg->vals[i]; + ++heads_i; + } + } + } +} + +void prpack_preprocessed_schur_graph::initialize_unweighted(const prpack_base_graph* bg) { + // permute num_outlinks + ii = num_outlinks; + num_outlinks = new double[num_vs]; + for (int i = 0; i < num_vs; ++i) + num_outlinks[encoding[i]] = (ii[i] == 0) ? -1 : ii[i]; + // convert bg to head/tail format + for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) { + ii[tails_i] = 0; + tails[tails_i] = heads_i; + const int decoded = decoding[tails_i]; + const int start_i = bg->tails[decoded]; + const int end_i = (decoded + 1 != num_vs) ? bg->tails[decoded + 1] : bg->num_es; + for (int i = start_i; i < end_i; ++i) { + if (decoded == bg->heads[i]) + ++ii[tails_i]; + else + heads[heads_i++] = encoding[bg->heads[i]]; + } + if (ii[tails_i] > 0) + ii[tails_i] /= num_outlinks[tails_i]; + } +} + +prpack_preprocessed_schur_graph::prpack_preprocessed_schur_graph(const prpack_base_graph* bg) { + initialize(); + // initialize instance variables + num_vs = bg->num_vs; + num_es = bg->num_es - bg->num_self_es; + tails = new int[num_vs]; + heads = new int[num_es]; + const bool weighted = bg->vals != NULL; + if (weighted) { + vals = new double[num_vs]; + d = new double[num_vs]; + fill(d, d + num_vs, 1); + for (int i = 0; i < bg->num_es; ++i) + d[bg->heads[i]] -= bg->vals[i]; + } else { + num_outlinks = new double[num_vs]; + fill(num_outlinks, num_outlinks + num_vs, 0); + for (int i = 0; i < bg->num_es; ++i) + ++num_outlinks[bg->heads[i]]; + } + // permute no-inlink vertices to the beginning, and no-outlink vertices to the end + encoding = new int[num_vs]; + decoding = new int[num_vs]; + num_no_in_vs = num_no_out_vs = 0; + for (int i = 0; i < num_vs; ++i) { + if (bg->tails[i] == ((i + 1 != num_vs) ? bg->tails[i + 1] : bg->num_es)) { + decoding[encoding[i] = num_no_in_vs] = i; + ++num_no_in_vs; + } else if ((weighted) ? (d[i] == 1) : (num_outlinks[i] == 0)) { + decoding[encoding[i] = num_vs - 1 - num_no_out_vs] = i; + ++num_no_out_vs; + } + } + // permute everything else + for (int i = 0, p = num_no_in_vs; i < num_vs; ++i) + if (bg->tails[i] < ((i + 1 != num_vs) ? bg->tails[i + 1] : bg->num_es) && ((weighted) ? (d[i] < 1) : (num_outlinks[i] > 0))) + decoding[encoding[i] = p++] = i; + // continue initialization based off of weightedness + if (weighted) + initialize_weighted(bg); + else + initialize_unweighted(bg); +} + +prpack_preprocessed_schur_graph::~prpack_preprocessed_schur_graph() { + delete[] heads; + delete[] tails; + delete[] vals; + delete[] ii; + delete[] d; + delete[] num_outlinks; + delete[] encoding; + delete[] decoding; +} diff --git a/src/rigraph/core/centrality/prpack/prpack_preprocessed_schur_graph.h b/src/rigraph/core/centrality/prpack/prpack_preprocessed_schur_graph.h new file mode 100644 index 0000000..a0593b1 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_preprocessed_schur_graph.h @@ -0,0 +1,33 @@ +#ifndef PRPACK_PREPROCESSED_SCHUR_GRAPH +#define PRPACK_PREPROCESSED_SCHUR_GRAPH +#include "prpack_preprocessed_graph.h" +#include "prpack_base_graph.h" + +namespace prpack { + + class prpack_preprocessed_schur_graph : public prpack_preprocessed_graph { + private: + // helper methods + void initialize(); + void initialize_weighted(const prpack_base_graph* bg); + void initialize_unweighted(const prpack_base_graph* bg); + public: + // instance variables + int num_no_in_vs; + int num_no_out_vs; + int* heads; + int* tails; + double* vals; + double* ii; + double* num_outlinks; + int* encoding; + int* decoding; + // constructors + prpack_preprocessed_schur_graph(const prpack_base_graph* bg); + // destructor + ~prpack_preprocessed_schur_graph(); + }; + +} + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_result.cpp b/src/rigraph/core/centrality/prpack/prpack_result.cpp new file mode 100644 index 0000000..1fef884 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_result.cpp @@ -0,0 +1,11 @@ +#include "prpack_result.h" +#include +using namespace prpack; + +prpack_result::prpack_result() { + x = NULL; +} + +prpack_result::~prpack_result() { + delete[] x; +} diff --git a/src/rigraph/core/centrality/prpack/prpack_result.h b/src/rigraph/core/centrality/prpack/prpack_result.h new file mode 100644 index 0000000..87da0f7 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_result.h @@ -0,0 +1,29 @@ +#ifndef PRPACK_RESULT +#define PRPACK_RESULT + +#include + +namespace prpack { + + // Result class. + class prpack_result { + public: + // instance variables + int num_vs; + int num_es; + double* x; + double read_time; + double preprocess_time; + double compute_time; + long num_es_touched; + std::string method; + int converged; + // constructor + prpack_result(); + // destructor + ~prpack_result(); + }; + +} + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_solver.cpp b/src/rigraph/core/centrality/prpack/prpack_solver.cpp new file mode 100644 index 0000000..b1de4af --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_solver.cpp @@ -0,0 +1,884 @@ +#include "prpack_solver.h" +#include "prpack_utils.h" +#include +#include +#include +#include +#include +using namespace prpack; +using namespace std; + +void prpack_solver::initialize() { + geg = NULL; + gsg = NULL; + sg = NULL; + sccg = NULL; + owns_bg = true; +} + +prpack_solver::prpack_solver(const prpack_csc* g) { + initialize(); + TIME(read_time, bg = new prpack_base_graph(g)); +} + +prpack_solver::prpack_solver(const prpack_int64_csc* g) { + initialize(); + TIME(read_time, bg = new prpack_base_graph(g)); +} + +prpack_solver::prpack_solver(const prpack_csr* g) { + initialize(); + TIME(read_time, bg = new prpack_base_graph(g)); +} + +prpack_solver::prpack_solver(const prpack_edge_list* g) { + initialize(); + TIME(read_time, bg = new prpack_base_graph(g)); +} + +prpack_solver::prpack_solver(prpack_base_graph* g, bool owns_bg) { + initialize(); + this->owns_bg = owns_bg; + TIME(read_time, bg = g); +} + +prpack_solver::prpack_solver(const char* filename, const char* format, const bool weighted) { + initialize(); + TIME(read_time, bg = new prpack_base_graph(filename, format, weighted)); +} + +prpack_solver::~prpack_solver() { + if (owns_bg) { + delete bg; + } + delete geg; + delete gsg; + delete sg; + delete sccg; +} + +int prpack_solver::get_num_vs() { + return bg->num_vs; +} + +prpack_result* prpack_solver::solve(const double alpha, const double tol, const char* method) { + return solve(alpha, tol, NULL, NULL, method); +} + +prpack_result* prpack_solver::solve( + const double alpha, + const double tol, + const double* u, + const double* v, + const char* method) { + double preprocess_time = 0; + double compute_time = 0; + prpack_result* ret = NULL; + // decide which method to run + string m; + if (strcmp(method, "") != 0) + m = string(method); + else { + if (bg->num_vs < 128) + m = "ge"; + else if (sccg != NULL) + m = "sccgs"; + else if (sg != NULL) + m = "sg"; + else + m = "sccgs"; + if (u != v) + m += "_uv"; + } + // run the appropriate method + if (m == "ge") { + if (geg == NULL) { + TIME(preprocess_time, geg = new prpack_preprocessed_ge_graph(bg)); + } + TIME(compute_time, ret = solve_via_ge( + alpha, + tol, + geg->num_vs, + geg->matrix, + u)); + } else if (m == "ge_uv") { + if (geg == NULL) { + TIME(preprocess_time, geg = new prpack_preprocessed_ge_graph(bg)); + } + TIME(compute_time, ret = solve_via_ge_uv( + alpha, + tol, + geg->num_vs, + geg->matrix, + geg->d, + u, + v)); + } else if (m == "gs") { + if (gsg == NULL) { + TIME(preprocess_time, gsg = new prpack_preprocessed_gs_graph(bg)); + } + TIME(compute_time, ret = solve_via_gs( + alpha, + tol, + gsg->num_vs, + gsg->num_es, + gsg->heads, + gsg->tails, + gsg->vals, + gsg->ii, + gsg->d, + gsg->num_outlinks, + u, + v)); + } else if (m == "gserr") { + if (gsg == NULL) { + TIME(preprocess_time, gsg = new prpack_preprocessed_gs_graph(bg)); + } + TIME(compute_time, ret = solve_via_gs_err( + alpha, + tol, + gsg->num_vs, + gsg->num_es, + gsg->heads, + gsg->tails, + gsg->ii, + gsg->num_outlinks, + u, + v)); + } else if (m == "sgs") { + if (sg == NULL) { + TIME(preprocess_time, sg = new prpack_preprocessed_schur_graph(bg)); + } + TIME(compute_time, ret = solve_via_schur_gs( + alpha, + tol, + sg->num_vs, + sg->num_no_in_vs, + sg->num_no_out_vs, + sg->num_es, + sg->heads, + sg->tails, + sg->vals, + sg->ii, + sg->d, + sg->num_outlinks, + u, + sg->encoding, + sg->decoding)); + } else if (m == "sgs_uv") { + if (sg == NULL) { + TIME(preprocess_time, sg = new prpack_preprocessed_schur_graph(bg)); + } + TIME(compute_time, ret = solve_via_schur_gs_uv( + alpha, + tol, + sg->num_vs, + sg->num_no_in_vs, + sg->num_no_out_vs, + sg->num_es, + sg->heads, + sg->tails, + sg->vals, + sg->ii, + sg->d, + sg->num_outlinks, + u, + v, + sg->encoding, + sg->decoding)); + } else if (m == "sccgs") { + if (sccg == NULL) { + TIME(preprocess_time, sccg = new prpack_preprocessed_scc_graph(bg)); + } + TIME(compute_time, ret = solve_via_scc_gs( + alpha, + tol, + sccg->num_vs, + sccg->num_es_inside, + sccg->heads_inside, + sccg->tails_inside, + sccg->vals_inside, + sccg->num_es_outside, + sccg->heads_outside, + sccg->tails_outside, + sccg->vals_outside, + sccg->ii, + sccg->d, + sccg->num_outlinks, + u, + sccg->num_comps, + sccg->divisions, + sccg->encoding, + sccg->decoding)); + } else if (m == "sccgs_uv") { + if (sccg == NULL) { + TIME(preprocess_time, sccg = new prpack_preprocessed_scc_graph(bg)); + } + TIME(compute_time, ret = solve_via_scc_gs_uv( + alpha, + tol, + sccg->num_vs, + sccg->num_es_inside, + sccg->heads_inside, + sccg->tails_inside, + sccg->vals_inside, + sccg->num_es_outside, + sccg->heads_outside, + sccg->tails_outside, + sccg->vals_outside, + sccg->ii, + sccg->d, + sccg->num_outlinks, + u, + v, + sccg->num_comps, + sccg->divisions, + sccg->encoding, + sccg->decoding)); + } else { + throw invalid_argument("Unknown method specified for PRPACK: '" + m + "'."); + } + ret->method = m; + ret->read_time = read_time; + ret->preprocess_time = preprocess_time; + ret->compute_time = compute_time; + ret->num_vs = bg->num_vs; + ret->num_es = bg->num_es; + return ret; +} + +// VARIOUS SOLVING METHODS //////////////////////////////////////////////////////////////////////// + +prpack_result* prpack_solver::solve_via_ge( + const double alpha, + const double tol, + const int num_vs, + const double* matrix, + const double* uv) { + prpack_result* ret = new prpack_result(); + // initialize uv values + const double uv_const = 1.0/num_vs; + const int uv_exists = (uv) ? 1 : 0; + uv = (uv) ? uv : &uv_const; + // create matrix A + double* A = new double[num_vs*num_vs]; + for (int i = 0; i < num_vs*num_vs; ++i) + A[i] = -alpha*matrix[i]; + for (int i = 0; i < num_vs*num_vs; i += num_vs + 1) + ++A[i]; + // create vector b + double* b = new double[num_vs]; + for (int i = 0; i < num_vs; ++i) + b[i] = uv[uv_exists*i]; + // solve and normalize + ge(num_vs, A, b); + normalize(num_vs, b); + // clean up and return + delete[] A; + ret->num_es_touched = -1; + ret->x = b; + return ret; +} + +prpack_result* prpack_solver::solve_via_ge_uv( + const double alpha, + const double tol, + const int num_vs, + const double* matrix, + const double* d, + const double* u, + const double* v) { + prpack_result* ret = new prpack_result(); + // initialize u and v values + const double u_const = 1.0/num_vs; + const double v_const = 1.0/num_vs; + const int u_exists = (u) ? 1 : 0; + const int v_exists = (v) ? 1 : 0; + u = (u) ? u : &u_const; + v = (v) ? v : &v_const; + // create matrix A + double* A = new double[num_vs*num_vs]; + for (int i = 0; i < num_vs*num_vs; ++i) + A[i] = -alpha*matrix[i]; + for (int i = 0, inum_vs = 0; i < num_vs; ++i, inum_vs += num_vs) + for (int j = 0; j < num_vs; ++j) + A[inum_vs + j] -= alpha*u[u_exists*i]*d[j]; + for (int i = 0; i < num_vs*num_vs; i += num_vs + 1) + ++A[i]; + // create vector b + double* b = new double[num_vs]; + for (int i = 0; i < num_vs; ++i) + b[i] = (1 - alpha)*v[v_exists*i]; + // solve + ge(num_vs, A, b); + // clean up and return + delete[] A; + ret->num_es_touched = -1; + ret->x = b; + return ret; +} + +// Vanilla Gauss-Seidel. +prpack_result* prpack_solver::solve_via_gs( + const double alpha, + const double tol, + const int num_vs, + const int num_es, + const int* heads, + const int* tails, + const double* vals, + const double* ii, + const double* d, + const double* num_outlinks, + const double* u, + const double* v) { + prpack_result* ret = new prpack_result(); + const bool weighted = vals != NULL; + // initialize u and v values + const double u_const = 1.0/num_vs; + const double v_const = 1.0/num_vs; + const int u_exists = (u) ? 1 : 0; + const int v_exists = (v) ? 1 : 0; + u = (u) ? u : &u_const; + v = (v) ? v : &v_const; + // initialize the eigenvector (and use personalization vector) + double* x = new double[num_vs]; + for (int i = 0; i < num_vs; ++i) + x[i] = 0; + // initialize delta + double delta = 0; + // run Gauss-Seidel + ret->num_es_touched = 0; + double err = 1, c = 0; + do { + if (weighted) { + for (int i = 0; i < num_vs; ++i) { + double new_val = 0; + const int start_j = tails[i]; + const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es; + for (int j = start_j; j < end_j; ++j) + // TODO: might want to use compensation summation for large: end_j - start_j + new_val += x[heads[j]]*vals[j]; + new_val = alpha*new_val + (1 - alpha)*v[v_exists*i]; + delta -= alpha*x[i]*d[i]; + new_val += delta*u[u_exists*i]; + new_val /= 1 - alpha*(d[i]*u[u_exists*i] + (1 - d[i])*ii[i]); + delta += alpha*new_val*d[i]; + COMPENSATED_SUM(err, x[i] - new_val, c); + x[i] = new_val; + } + } else { + for (int i = 0; i < num_vs; ++i) { + const double old_val = x[i]*num_outlinks[i]; + double new_val = 0; + const int start_j = tails[i]; + const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es; + for (int j = start_j; j < end_j; ++j) + // TODO: might want to use compensation summation for large: end_j - start_j + new_val += x[heads[j]]; + new_val = alpha*new_val + (1 - alpha)*v[v_exists*i]; + if (num_outlinks[i] < 0) { + delta -= alpha*old_val; + new_val += delta*u[u_exists*i]; + new_val /= 1 - alpha*u[u_exists*i]; + delta += alpha*new_val; + } else { + new_val += delta*u[u_exists*i]; + new_val /= 1 - alpha*ii[i]; + } + COMPENSATED_SUM(err, old_val - new_val, c); + x[i] = new_val/num_outlinks[i]; + } + } + // update iteration index + ret->num_es_touched += num_es; + } while (err >= tol); + // undo num_outlinks transformation + if (!weighted) + for (int i = 0; i < num_vs; ++i) + x[i] *= num_outlinks[i]; + // return results + ret->x = x; + return ret; +} + +// Implement a gauss-seidel-like process with a strict error bound +// we return a solution with 1-norm error less than tol. +prpack_result* prpack_solver::solve_via_gs_err( + const double alpha, + const double tol, + const int num_vs, + const int num_es, + const int* heads, + const int* tails, + const double* ii, + const double* num_outlinks, + const double* u, + const double* v) { + prpack_result* ret = new prpack_result(); + // initialize u and v values + const double u_const = 1.0/num_vs; + const double v_const = 1.0/num_vs; + const int u_exists = (u) ? 1 : 0; + const int v_exists = (v) ? 1 : 0; + u = (u) ? u : &u_const; + v = (v) ? v : &v_const; + // Note to Dave, we can't rescale v because we could be running this + // same routine from multiple threads. + // initialize the eigenvector (and use personalization vector) + double* x = new double[num_vs]; + for (int i = 0; i < num_vs; ++i) { + x[i] = 0.; + } + // initialize delta + double delta = 0.; + // run Gauss-Seidel, note that we store x/deg[i] throughout this + // iteration. + int64_t maxedges = (int64_t)((double)num_es*std::min( + log(tol)/log(alpha), + (double)PRPACK_SOLVER_MAX_ITERS)); + ret->num_es_touched = 0; + double err=1., c = 0.; + do { + // iterate through vertices + for (int i = 0; i < num_vs; ++i) { + double old_val = x[i]*num_outlinks[i]; // adjust back to the "true" value. + double new_val = 0.; + int start_j = tails[i], end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es; + for (int j = start_j; j < end_j; ++j) { + // TODO: might want to use compensation summation for large: end_j - start_j + new_val += x[heads[j]]; + } + new_val = alpha*new_val + alpha*ii[i]*old_val + (1.0-alpha)*v[v_exists*i]; + new_val += delta*u[u_exists*i]; // add the dangling node adjustment + if (num_outlinks[i] < 0) { + delta += alpha*(new_val - old_val); + } + // note that new_val > old_val, but the fabs is just for + COMPENSATED_SUM(err, -(new_val - old_val), c); + x[i] = new_val/num_outlinks[i]; + } + // update iteration index + ret->num_es_touched += num_es; + } while (err >= tol && ret->num_es_touched < maxedges); + if (err >= tol) { + ret->converged = 0; + } else { + ret->converged = 1; + } + // undo num_outlinks transformation + for (int i = 0; i < num_vs; ++i) + x[i] *= num_outlinks[i]; + // return results + ret->x = x; + return ret; +} + +// Gauss-Seidel using the Schur complement to separate dangling nodes. +prpack_result* prpack_solver::solve_via_schur_gs( + const double alpha, + const double tol, + const int num_vs, + const int num_no_in_vs, + const int num_no_out_vs, + const int num_es, + const int* heads, + const int* tails, + const double* vals, + const double* ii, + const double* d, + const double* num_outlinks, + const double* uv, + const int* encoding, + const int* decoding, + const bool should_normalize) { + prpack_result* ret = new prpack_result(); + const bool weighted = vals != NULL; + // initialize uv values + const double uv_const = 1.0/num_vs; + const int uv_exists = (uv) ? 1 : 0; + uv = (uv) ? prpack_utils::permute(num_vs, uv, encoding) : &uv_const; + // initialize the eigenvector (and use personalization vector) + double* x = new double[num_vs]; + for (int i = 0; i < num_vs - num_no_out_vs; ++i) + x[i] = uv[uv_exists*i]/(1 - alpha*ii[i])/((weighted) ? 1 : num_outlinks[i]); + // run Gauss-Seidel for the top left part of (I - alpha*P)*x = uv + ret->num_es_touched = 0; + double err, c; + do { + // iterate through vertices + int num_es_touched = 0; + err = c = 0; +#ifdef _OPENMP + #pragma omp parallel for firstprivate(c) reduction(+:err, num_es_touched) schedule(dynamic, 64) +#endif + for (int i = num_no_in_vs; i < num_vs - num_no_out_vs; ++i) { + double new_val = 0; + const int start_j = tails[i]; + const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es; + if (weighted) { + for (int j = start_j; j < end_j; ++j) + // TODO: might want to use compensation summation for large: end_j - start_j + new_val += x[heads[j]]*vals[j]; + COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]), c); + new_val = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]); + x[i] = new_val; + } else { + for (int j = start_j; j < end_j; ++j) + // TODO: might want to use compensation summation for large: end_j - start_j + new_val += x[heads[j]]; + COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]*num_outlinks[i]), c); + new_val = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]); + x[i] = new_val/num_outlinks[i]; + } + num_es_touched += end_j - start_j; + } + // update iteration index + ret->num_es_touched += num_es_touched; + } while (err/(1 - alpha) >= tol); + // solve for the dangling nodes + int num_es_touched = 0; +#ifdef _OPENMP + #pragma omp parallel for reduction(+:num_es_touched) schedule(dynamic, 64) +#endif + for (int i = num_vs - num_no_out_vs; i < num_vs; ++i) { + x[i] = 0; + const int start_j = tails[i]; + const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es; + for (int j = start_j; j < end_j; ++j) + x[i] += x[heads[j]]*((weighted) ? vals[j] : 1); + x[i] = (alpha*x[i] + uv[uv_exists*i])/(1 - alpha*ii[i]); + num_es_touched += end_j - start_j; + } + ret->num_es_touched += num_es_touched; + // undo num_outlinks transformation + if (!weighted) + for (int i = 0; i < num_vs - num_no_out_vs; ++i) + x[i] *= num_outlinks[i]; + // normalize x to get the solution for: (I - alpha*P - alpha*u*d')*x = (1 - alpha)*v + if (should_normalize) + normalize(num_vs, x); + // return results + ret->x = prpack_utils::permute(num_vs, x, decoding); + delete[] x; + if (uv_exists) + delete[] uv; + return ret; +} + +prpack_result* prpack_solver::solve_via_schur_gs_uv( + const double alpha, + const double tol, + const int num_vs, + const int num_no_in_vs, + const int num_no_out_vs, + const int num_es, + const int* heads, + const int* tails, + const double* vals, + const double* ii, + const double* d, + const double* num_outlinks, + const double* u, + const double* v, + const int* encoding, + const int* decoding) { + // solve uv = u + prpack_result* ret_u = solve_via_schur_gs( + alpha, + tol, + num_vs, + num_no_in_vs, + num_no_out_vs, + num_es, + heads, + tails, + vals, + ii, + d, + num_outlinks, + u, + encoding, + decoding, + false); + // solve uv = v + prpack_result* ret_v = solve_via_schur_gs( + alpha, + tol, + num_vs, + num_no_in_vs, + num_no_out_vs, + num_es, + heads, + tails, + vals, + ii, + d, + num_outlinks, + v, + encoding, + decoding, + false); + // combine the u and v cases + return combine_uv(num_vs, d, num_outlinks, encoding, alpha, ret_u, ret_v); +} + +/** Gauss-Seidel using strongly connected components. + * Notes: + * If not weighted, then we store x[i] = "x[i]/outdegree" to + * avoid additional arithmetic. We don't do this for the weighted + * case because the adjustment may not be constant. + */ +prpack_result* prpack_solver::solve_via_scc_gs( + const double alpha, + const double tol, + const int num_vs, + const int num_es_inside, + const int* heads_inside, + const int* tails_inside, + const double* vals_inside, + const int num_es_outside, + const int* heads_outside, + const int* tails_outside, + const double* vals_outside, + const double* ii, + const double* d, + const double* num_outlinks, + const double* uv, + const int num_comps, + const int* divisions, + const int* encoding, + const int* decoding, + const bool should_normalize) { + prpack_result* ret = new prpack_result(); + const bool weighted = vals_inside != NULL; + // initialize uv values + const double uv_const = 1.0/num_vs; + const int uv_exists = (uv) ? 1 : 0; + uv = (uv) ? prpack_utils::permute(num_vs, uv, encoding) : &uv_const; + // CHECK initialize the solution with one iteration of GS from x=0. + double* x = new double[num_vs]; + for (int i = 0; i < num_vs; ++i) + x[i] = uv[uv_exists*i]/(1 - alpha*ii[i])/((weighted) ? 1 : num_outlinks[i]); + // create x_outside + double* x_outside = new double[num_vs]; + // run Gauss-Seidel for (I - alpha*P)*x = uv + ret->num_es_touched = 0; + for (int comp_i = 0; comp_i < num_comps; ++comp_i) { + const int start_comp = divisions[comp_i]; + const int end_comp = (comp_i + 1 != num_comps) ? divisions[comp_i + 1] : num_vs; + const bool parallelize = end_comp - start_comp > 512; + // initialize relevant x_outside values + for (int i = start_comp; i < end_comp; ++i) { + x_outside[i] = 0; + const int start_j = tails_outside[i]; + const int end_j = (i + 1 != num_vs) ? tails_outside[i + 1] : num_es_outside; + for (int j = start_j; j < end_j; ++j) + x_outside[i] += x[heads_outside[j]]*((weighted) ? vals_outside[j] : 1.); + ret->num_es_touched += end_j - start_j; + } + double err, c; + do { + int num_es_touched = 0; + err = c = 0; + if (parallelize) { + // iterate through vertices +#ifdef _OPENMP + #pragma omp parallel for firstprivate(c) reduction(+:err, num_es_touched) schedule(dynamic, 64) +#endif + for (int i = start_comp; i < end_comp; ++i) { + double new_val = x_outside[i]; + const int start_j = tails_inside[i]; + const int end_j = (i + 1 != num_vs) ? tails_inside[i + 1] : num_es_inside; + if (weighted) { + for (int j = start_j; j < end_j; ++j) { + // TODO: might want to use compensation summation for large: end_j - start_j + new_val += x[heads_inside[j]]*vals_inside[j]; + } + COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]), c); + x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]); + } else { + for (int j = start_j; j < end_j; ++j) { + // TODO: might want to use compensation summation for large: end_j - start_j + new_val += x[heads_inside[j]]; + } + COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]*num_outlinks[i]), c); + x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i])/num_outlinks[i]; + } + num_es_touched += end_j - start_j; + } + } else { + for (int i = start_comp; i < end_comp; ++i) { + double new_val = x_outside[i]; + const int start_j = tails_inside[i]; + const int end_j = (i + 1 != num_vs) ? tails_inside[i + 1] : num_es_inside; + if (weighted) { + for (int j = start_j; j < end_j; ++j) { + // TODO: might want to use compensation summation for large: end_j - start_j + new_val += x[heads_inside[j]]*vals_inside[j]; + } + COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]), c); + x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]); + } else { + for (int j = start_j; j < end_j; ++j) { + // TODO: might want to use compensation summation for large: end_j - start_j + new_val += x[heads_inside[j]]; + } + COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]*num_outlinks[i]), c); + x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i])/num_outlinks[i]; + } + num_es_touched += end_j - start_j; + } + } + // update iteration index + ret->num_es_touched += num_es_touched; + } while (err/(1 - alpha) >= tol*(end_comp - start_comp)/num_vs); + } + // undo num_outlinks transformation + if (!weighted) + for (int i = 0; i < num_vs; ++i) + x[i] *= num_outlinks[i]; + // normalize x to get the solution for: (I - alpha*P - alpha*u*d')*x = (1 - alpha)*v + if (should_normalize) + normalize(num_vs, x); + // return results + ret->x = prpack_utils::permute(num_vs, x, decoding); + delete[] x; + delete[] x_outside; + if (uv_exists) + delete[] uv; + return ret; +} + +prpack_result* prpack_solver::solve_via_scc_gs_uv( + const double alpha, + const double tol, + const int num_vs, + const int num_es_inside, + const int* heads_inside, + const int* tails_inside, + const double* vals_inside, + const int num_es_outside, + const int* heads_outside, + const int* tails_outside, + const double* vals_outside, + const double* ii, + const double* d, + const double* num_outlinks, + const double* u, + const double* v, + const int num_comps, + const int* divisions, + const int* encoding, + const int* decoding) { + // solve uv = u + prpack_result* ret_u = solve_via_scc_gs( + alpha, + tol, + num_vs, + num_es_inside, + heads_inside, + tails_inside, + vals_inside, + num_es_outside, + heads_outside, + tails_outside, + vals_outside, + ii, + d, + num_outlinks, + u, + num_comps, + divisions, + encoding, + decoding, + false); + // solve uv = v + prpack_result* ret_v = solve_via_scc_gs( + alpha, + tol, + num_vs, + num_es_inside, + heads_inside, + tails_inside, + vals_inside, + num_es_outside, + heads_outside, + tails_outside, + vals_outside, + ii, + d, + num_outlinks, + v, + num_comps, + divisions, + encoding, + decoding, + false); + // combine u and v + return combine_uv(num_vs, d, num_outlinks, encoding, alpha, ret_u, ret_v); +} + +// VARIOUS HELPER METHODS ///////////////////////////////////////////////////////////////////////// + +// Run Gaussian-Elimination (note: this changes A and returns the solution in b) +void prpack_solver::ge(const int sz, double* A, double* b) { + // put into triangular form + for (int i = 0, isz = 0; i < sz; ++i, isz += sz) + for (int k = 0, ksz = 0; k < i; ++k, ksz += sz) + if (A[isz + k] != 0) { + const double coeff = A[isz + k]/A[ksz + k]; + A[isz + k] = 0; + for (int j = k + 1; j < sz; ++j) + A[isz + j] -= coeff*A[ksz + j]; + b[i] -= coeff*b[k]; + } + // backwards substitution + for (int i = sz - 1, isz = (sz - 1)*sz; i >= 0; --i, isz -= sz) { + for (int j = i + 1; j < sz; ++j) + b[i] -= A[isz + j]*b[j]; + b[i] /= A[isz + i]; + } +} + +// Normalize a vector to sum to 1. +void prpack_solver::normalize(const int length, double* x) { + double norm = 0, c = 0; + for (int i = 0; i < length; ++i) { + COMPENSATED_SUM(norm, x[i], c); + } + norm = 1/norm; + for (int i = 0; i < length; ++i) + x[i] *= norm; +} + +// Combine u and v results. +prpack_result* prpack_solver::combine_uv( + const int num_vs, + const double* d, + const double* num_outlinks, + const int* encoding, + const double alpha, + const prpack_result* ret_u, + const prpack_result* ret_v) { + prpack_result* ret = new prpack_result(); + const bool weighted = d != NULL; + double delta_u = 0; + double delta_v = 0; + for (int i = 0; i < num_vs; ++i) { + if ((weighted) ? (d[encoding[i]] == 1) : (num_outlinks[encoding[i]] < 0)) { + delta_u += ret_u->x[i]; + delta_v += ret_v->x[i]; + } + } + const double s = ((1 - alpha)*alpha*delta_v)/(1 - alpha*delta_u); + const double t = 1 - alpha; + ret->x = new double[num_vs]; + for (int i = 0; i < num_vs; ++i) + ret->x[i] = s*ret_u->x[i] + t*ret_v->x[i]; + ret->num_es_touched = ret_u->num_es_touched + ret_v->num_es_touched; + // clean up and return + delete ret_u; + delete ret_v; + return ret; +} diff --git a/src/rigraph/core/centrality/prpack/prpack_solver.h b/src/rigraph/core/centrality/prpack/prpack_solver.h new file mode 100644 index 0000000..c89d4f3 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_solver.h @@ -0,0 +1,178 @@ +#ifndef PRPACK_SOLVER +#define PRPACK_SOLVER +#include "prpack_base_graph.h" +#include "prpack_csc.h" +#include "prpack_csr.h" +#include "prpack_edge_list.h" +#include "prpack_preprocessed_ge_graph.h" +#include "prpack_preprocessed_gs_graph.h" +#include "prpack_preprocessed_scc_graph.h" +#include "prpack_preprocessed_schur_graph.h" +#include "prpack_result.h" + +// TODO Make this a user configurable variable +#define PRPACK_SOLVER_MAX_ITERS 1000000 + +namespace prpack { + + // Solver class. + class prpack_solver { + private: + // instance variables + double read_time; + prpack_base_graph* bg; + prpack_preprocessed_ge_graph* geg; + prpack_preprocessed_gs_graph* gsg; + prpack_preprocessed_schur_graph* sg; + prpack_preprocessed_scc_graph* sccg; + bool owns_bg; + // methods + void initialize(); + static prpack_result* solve_via_ge( + const double alpha, + const double tol, + const int num_vs, + const double* matrix, + const double* uv); + static prpack_result* solve_via_ge_uv( + const double alpha, + const double tol, + const int num_vs, + const double* matrix, + const double* d, + const double* u, + const double* v); + static prpack_result* solve_via_gs( + const double alpha, + const double tol, + const int num_vs, + const int num_es, + const int* heads, + const int* tails, + const double* vals, + const double* ii, + const double* d, + const double* num_outlinks, + const double* u, + const double* v); + static prpack_result* solve_via_gs_err( + const double alpha, + const double tol, + const int num_vs, + const int num_es, + const int* heads, + const int* tails, + const double* ii, + const double* num_outlinks, + const double* u, + const double* v); + static prpack_result* solve_via_schur_gs( + const double alpha, + const double tol, + const int num_vs, + const int num_no_in_vs, + const int num_no_out_vs, + const int num_es, + const int* heads, + const int* tails, + const double* vals, + const double* ii, + const double* d, + const double* num_outlinks, + const double* uv, + const int* encoding, + const int* decoding, + const bool should_normalize = true); + static prpack_result* solve_via_schur_gs_uv( + const double alpha, + const double tol, + const int num_vs, + const int num_no_in_vs, + const int num_no_out_vs, + const int num_es, + const int* heads, + const int* tails, + const double* vals, + const double* ii, + const double* d, + const double* num_outlinks, + const double* u, + const double* v, + const int* encoding, + const int* decoding); + static prpack_result* solve_via_scc_gs( + const double alpha, + const double tol, + const int num_vs, + const int num_es_inside, + const int* heads_inside, + const int* tails_inside, + const double* vals_inside, + const int num_es_outside, + const int* heads_outside, + const int* tails_outside, + const double* vals_outside, + const double* ii, + const double* d, + const double* num_outlinks, + const double* uv, + const int num_comps, + const int* divisions, + const int* encoding, + const int* decoding, + const bool should_normalize = true); + static prpack_result* solve_via_scc_gs_uv( + const double alpha, + const double tol, + const int num_vs, + const int num_es_inside, + const int* heads_inside, + const int* tails_inside, + const double* vals_inside, + const int num_es_outside, + const int* heads_outside, + const int* tails_outside, + const double* vals_outside, + const double* ii, + const double* d, + const double* num_outlinks, + const double* u, + const double* v, + const int num_comps, + const int* divisions, + const int* encoding, + const int* decoding); + static void ge(const int sz, double* A, double* b); + static void normalize(const int length, double* x); + static prpack_result* combine_uv( + const int num_vs, + const double* d, + const double* num_outlinks, + const int* encoding, + const double alpha, + const prpack_result* ret_u, + const prpack_result* ret_v); + public: + // constructors + prpack_solver(const prpack_csc* g); + prpack_solver(const prpack_int64_csc* g); + prpack_solver(const prpack_csr* g); + prpack_solver(const prpack_edge_list* g); + prpack_solver(prpack_base_graph* g, bool owns_bg=true); + prpack_solver(const char* filename, const char* format, const bool weighted); + // destructor + ~prpack_solver(); + // methods + int get_num_vs(); + prpack_result* solve(const double alpha, const double tol, const char* method); + prpack_result* solve( + const double alpha, + const double tol, + const double* u, + const double* v, + const char* method); + }; + +} + +#endif diff --git a/src/rigraph/core/centrality/prpack/prpack_utils.cpp b/src/rigraph/core/centrality/prpack/prpack_utils.cpp new file mode 100644 index 0000000..1d425b1 --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_utils.cpp @@ -0,0 +1,59 @@ +/** + * @file prpack_utils.cpp + * An assortment of utility functions for reporting errors, checking time, + * and working with vectors. + */ + +#include +#include "prpack_utils.h" +#include +#include +#include +using namespace prpack; +using namespace std; + +#ifdef PRPACK_IGRAPH_SUPPORT +#include "igraph_error.h" +#endif + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#include +#endif +double prpack_utils::get_time() { + LARGE_INTEGER t, freq; + QueryPerformanceCounter(&t); + QueryPerformanceFrequency(&freq); + return double(t.QuadPart)/double(freq.QuadPart); +} +#else +#include +#include +double prpack_utils::get_time() { + struct timeval t; + gettimeofday(&t, 0); + return (t.tv_sec*1.0 + t.tv_usec/1000000.0); +} +#endif + +// Fails and outputs 'msg' if 'condition' is false. +void prpack_utils::validate(const bool condition, const string& msg) { + if (!condition) { +#ifdef PRPACK_IGRAPH_SUPPORT + igraph_error("Internal error in PRPACK", IGRAPH_FILE_BASENAME, __LINE__, + IGRAPH_EINTERNAL); +#else + cerr << msg << endl; + exit(-1); +#endif + } +} + +// Permute a vector. +double* prpack_utils::permute(const int length, const double* a, const int* coding) { + double* ret = new double[length]; + for (int i = 0; i < length; ++i) + ret[coding[i]] = a[i]; + return ret; +} diff --git a/src/rigraph/core/centrality/prpack/prpack_utils.h b/src/rigraph/core/centrality/prpack/prpack_utils.h new file mode 100644 index 0000000..261370d --- /dev/null +++ b/src/rigraph/core/centrality/prpack/prpack_utils.h @@ -0,0 +1,33 @@ +#ifndef PRPACK_UTILS +#define PRPACK_UTILS +#ifdef MATLAB_MEX_FILE +#include "mex.h" +#endif +#include + +// Computes the time taken to do X and stores it in T. +#define TIME(T, X) \ + (T) = prpack_utils::get_time(); \ + (X); \ + (T) = prpack_utils::get_time() - (T) + +// Computes S += A using C as a carry-over. +// This is a macro over a function as it is faster this way. +#define COMPENSATED_SUM(S, A, C) \ + double compensated_sum_y = (A) - (C); \ + double compensated_sum_t = (S) + compensated_sum_y; \ + (C) = compensated_sum_t - (S) - compensated_sum_y; \ + (S) = compensated_sum_t + +namespace prpack { + + class prpack_utils { + public: + static double get_time(); + static void validate(const bool condition, const std::string& msg); + static double* permute(const int length, const double* a, const int* coding); + }; + +} + +#endif diff --git a/src/rigraph/core/centrality/prpack_internal.h b/src/rigraph/core/centrality/prpack_internal.h new file mode 100644 index 0000000..01a85f6 --- /dev/null +++ b/src/rigraph/core/centrality/prpack_internal.h @@ -0,0 +1,44 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_PRPACK +#define IGRAPH_PRPACK + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_datatype.h" +#include "igraph_iterators.h" + +#include "igraph_interface.h" + +__BEGIN_DECLS + +int igraph_i_personalized_pagerank_prpack(const igraph_t *graph, igraph_vector_t *vector, + igraph_real_t *value, const igraph_vs_t vids, + igraph_bool_t directed, igraph_real_t damping, + const igraph_vector_t *reset, + const igraph_vector_t *weights); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/cliques/cliquer/cliquer.c b/src/rigraph/core/cliques/cliquer/cliquer.c new file mode 100644 index 0000000..ab9f861 --- /dev/null +++ b/src/rigraph/core/cliques/cliquer/cliquer.c @@ -0,0 +1,1768 @@ + +/* + * This file contains the clique searching routines. + * + * Copyright (C) 2002 Sampo Niskanen, Patric Östergård. + * Licensed under the GNU GPL, read the file LICENSE for details. + */ + +#include +#include +#include +/* +#include +#include +#include +*/ + +#include "cliquer.h" + +#include "config.h" + +/* Default cliquer options */ +clique_options clique_default_options = { + reorder_by_default, NULL, /*clique_print_time*/ NULL, NULL, NULL, NULL, NULL, 0 +}; + + +/* Calculate d/q, rounding result upwards/downwards. */ +#define DIV_UP(d,q) (((d)+(q)-1)/(q)) +#define DIV_DOWN(d,q) ((int)((d)/(q))) + + +/* Global variables used: */ +/* These must be saved and restored in re-entrance. */ +static int *clique_size; /* c[i] == max. clique size in {0,1,...,i-1} */ +static set_t current_clique; /* Current clique being searched. */ +static set_t best_clique; /* Largest/heaviest clique found so far. */ +/*static struct tms cputimer;*/ /* Timer for opts->time_function() */ +/*static struct timeval realtimer;*/ /* Timer for opts->time_function() */ +static int clique_list_count=0; /* No. of cliques in opts->clique_list[] */ +static int weight_multiplier=1; /* Weights multiplied by this when passing + * to time_function(). */ + +/* List cache (contains memory blocks of size g->n * sizeof(int)) */ +static int **temp_list=NULL; +static int temp_count=0; + + +/* + * Macros for re-entrance. ENTRANCE_SAVE() must be called immediately + * after variable definitions, ENTRANCE_RESTORE() restores global + * variables to original values. entrance_level should be increased + * and decreased accordingly. + */ +static int entrance_level=0; /* How many levels for entrance have occurred? */ + +#define ENTRANCE_SAVE() \ +int *old_clique_size = clique_size; \ +set_t old_current_clique = current_clique; \ +set_t old_best_clique = best_clique; \ +int old_clique_list_count = clique_list_count; \ +int old_weight_multiplier = weight_multiplier; \ +int **old_temp_list = temp_list; \ +int old_temp_count = temp_count; \ +/*struct tms old_cputimer; \ +struct timeval old_realtimer; \ +memcpy(&old_cputimer,&cputimer,sizeof(struct tms)); \ +memcpy(&old_realtimer,&realtimer,sizeof(struct timeval));*/ + +#define ENTRANCE_RESTORE() \ +clique_size = old_clique_size; \ +current_clique = old_current_clique; \ +best_clique = old_best_clique; \ +clique_list_count = old_clique_list_count; \ +weight_multiplier = old_weight_multiplier; \ +temp_list = old_temp_list; \ +temp_count = old_temp_count; \ +/*memcpy(&cputimer,&old_cputimer,sizeof(struct tms)); \ +memcpy(&realtimer,&old_realtimer,sizeof(struct timeval));*/ + + +/* Number of clock ticks per second (as returned by sysconf(_SC_CLK_TCK)) */ +/*static int clocks_per_sec=0;*/ + + + + +/* Recursion and helper functions */ +static boolean sub_unweighted_single(int *table, int size, int min_size, + graph_t *g); +static CLIQUER_LARGE_INT sub_unweighted_all(int *table, int size, int min_size, int max_size, + boolean maximal, graph_t *g, + clique_options *opts); +static int sub_weighted_all(int *table, int size, int weight, + int current_weight, int prune_low, int prune_high, + int min_weight, int max_weight, boolean maximal, + graph_t *g, clique_options *opts); + + +static boolean store_clique(set_t clique, graph_t *g, clique_options *opts); +static boolean is_maximal(set_t clique, graph_t *g); +static boolean false_function(set_t clique,graph_t *g,clique_options *opts); + + + + + +/***** Unweighted searches *****/ +/* + * Unweighted searches are done separately from weighted searches because + * some effective pruning methods can be used when the vertex weights + * are all 1. Single and all clique finding routines are separated, + * because the single clique finding routine can store the found clique + * while it is returning from the recursion, thus requiring no implicit + * storing of the current clique. When searching for all cliques the + * current clique must be stored. + */ + + +/* + * unweighted_clique_search_single() + * + * Searches for a single clique of size min_size. Stores maximum clique + * sizes into clique_size[]. + * + * table - the order of the vertices in g to use + * min_size - minimum size of clique to search for. If min_size==0, + * searches for a maximum clique. + * g - the graph + * opts - time printing options + * + * opts->time_function is called after each base-level recursion, if + * non-NULL. + * + * Returns the size of the clique found, or 0 if min_size>0 and a clique + * of that size was not found (or if time_function aborted the search). + * The largest clique found is stored in current_clique. + * + * Note: Does NOT use opts->user_function of opts->clique_list. + */ +static int unweighted_clique_search_single(int *table, int min_size, + graph_t *g, clique_options *opts) { + /* + struct tms tms; + struct timeval timeval; + */ + int i,j; + int v,w; + int *newtable; + int newsize; + + v=table[0]; + clique_size[v]=1; + set_empty(current_clique); + SET_ADD_ELEMENT(current_clique,v); + if (min_size==1) + return 1; + + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + for (i=1; i < g->n; i++) { + w=v; + v=table[i]; + + newsize=0; + for (j=0; jtime_function) { + gettimeofday(&timeval,NULL); + times(&tms); + if (!opts->time_function(entrance_level, + i+1,g->n,clique_size[v] * + weight_multiplier, + (double)(tms.tms_utime- + cputimer.tms_utime)/ + clocks_per_sec, + timeval.tv_sec- + realtimer.tv_sec+ + (double)(timeval.tv_usec- + realtimer.tv_usec)/ + 1000000,opts)) { + temp_list[temp_count++]=newtable; + return 0; + } + } + */ + + if (min_size) { + if (clique_size[v]>=min_size) { + temp_list[temp_count++]=newtable; + return clique_size[v]; + } + if (clique_size[v]+g->n-i-1 < min_size) { + temp_list[temp_count++]=newtable; + return 0; + } + } + } + + temp_list[temp_count++]=newtable; + + if (min_size) + return 0; + return clique_size[v]; +} + +/* + * sub_unweighted_single() + * + * Recursion function for searching for a single clique of size min_size. + * + * table - subset of the vertices in graph + * size - size of table + * min_size - size of clique to look for within the subgraph + * (decreased with every recursion) + * g - the graph + * + * Returns TRUE if a clique of size min_size is found, FALSE otherwise. + * If a clique of size min_size is found, it is stored in current_clique. + * + * clique_size[] for all values in table must be defined and correct, + * otherwise inaccurate results may occur. + */ +static boolean sub_unweighted_single(int *table, int size, int min_size, + graph_t *g) { + int i; + int v; + int *newtable; + int *p1, *p2; + + /* Zero or one vertices needed anymore. */ + if (min_size <= 1) { + if (size>0 && min_size==1) { + set_empty(current_clique); + SET_ADD_ELEMENT(current_clique,table[0]); + return TRUE; + } + if (min_size==0) { + set_empty(current_clique); + return TRUE; + } + return FALSE; + } + if (size < min_size) + return FALSE; + + /* Dynamic memory allocation with cache */ + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + + for (i = size-1; i >= 0; i--) { + v = table[i]; + + if (clique_size[v] < min_size) + break; + /* This is faster when compiling with gcc than placing + * this in the for-loop condition. */ + if (i+1 < min_size) + break; + + /* Very ugly code, but works faster than "for (i=...)" */ + p1 = newtable; + for (p2=table; p2 < table+i; p2++) { + int w = *p2; + if (GRAPH_IS_EDGE(g, v, w)) { + *p1 = w; + p1++; + } + } + + /* Avoid unneccessary loops (next size == p1-newtable) */ + if (p1-newtable < min_size-1) + continue; + /* Now p1-newtable >= min_size-1 >= 2-1 == 1, so we can use + * p1-newtable-1 safely. */ + if (clique_size[newtable[p1-newtable-1]] < min_size-1) + continue; + + if (sub_unweighted_single(newtable,p1-newtable, + min_size-1,g)) { + /* Clique found. */ + SET_ADD_ELEMENT(current_clique,v); + temp_list[temp_count++]=newtable; + return TRUE; + } + } + temp_list[temp_count++]=newtable; + return FALSE; +} + + +/* + * unweighted_clique_search_all() + * + * Searches for all cliques with size at least min_size and at most + * max_size. Stores the cliques as opts declares. + * + * table - the order of the vertices in g to search + * start - first index where the subgraph table[0], ..., table[start] + * might include a requested kind of clique + * min_size - minimum size of clique to search for. min_size > 0 ! + * max_size - maximum size of clique to search for. If no upper limit + * is desired, use eg. INT_MAX + * maximal - requires cliques to be maximal + * g - the graph + * opts - time printing and clique storage options + * + * Cliques found are stored as defined by opts->user_function and + * opts->clique_list. opts->time_function is called after each + * base-level recursion, if non-NULL. + * + * clique_size[] must be defined and correct for all values of + * table[0], ..., table[start-1]. + * + * Returns the number of cliques stored (not neccessarily number of cliques + * in graph, if user/time_function aborts). + */ +static CLIQUER_LARGE_INT unweighted_clique_search_all(int *table, int start, + int min_size, int max_size, + boolean maximal, graph_t *g, + clique_options *opts) { + /* + struct timeval timeval; + struct tms tms; + */ + int i, j; + int v; + int *newtable; + int newsize; + CLIQUER_LARGE_INT r; + CLIQUER_LARGE_INT count=0; + + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + + clique_list_count=0; + set_empty(current_clique); + for (i=start; i < g->n; i++) { + v=table[i]; + clique_size[v]=min_size; /* Do not prune here. */ + + newsize=0; + for (j=0; jtime_function) { + gettimeofday(&timeval,NULL); + times(&tms); + if (!opts->time_function(entrance_level, + i+1,g->n,min_size * + weight_multiplier, + (double)(tms.tms_utime- + cputimer.tms_utime)/ + clocks_per_sec, + timeval.tv_sec- + realtimer.tv_sec+ + (double)(timeval.tv_usec- + realtimer.tv_usec)/ + 1000000,opts)) { + /* Abort. */ + break; + } + } +#endif + } + temp_list[temp_count++]=newtable; + return count; +} + +/* + * sub_unweighted_all() + * + * Recursion function for searching for all cliques of given size. + * + * table - subset of vertices of graph g + * size - size of table + * min_size - minimum size of cliques to search for (decreased with + * every recursion) + * max_size - maximum size of cliques to search for (decreased with + * every recursion). If no upper limit is desired, use + * eg. INT_MAX + * maximal - require cliques to be maximal (passed through) + * g - the graph + * opts - storage options + * + * All cliques of suitable size found are stored according to opts. + * + * Returns the number of cliques found. If user_function returns FALSE, + * then the number of cliques is returned negative. + * + * Uses current_clique to store the currently-being-searched clique. + * clique_size[] for all values in table must be defined and correct, + * otherwise inaccurate results may occur. + */ +static CLIQUER_LARGE_INT sub_unweighted_all(int *table, int size, int min_size, int max_size, + boolean maximal, graph_t *g, + clique_options *opts) { + int i; + int v; + int *newtable; + int *p1, *p2; + CLIQUER_LARGE_INT n; + CLIQUER_LARGE_INT count=0; /* Amount of cliques found */ + + if (min_size <= 0) { + if ((!maximal) || is_maximal(current_clique,g)) { + /* We've found one. Store it. */ + count++; + if (!store_clique(current_clique,g,opts)) { + return -count; + } + } + if (max_size <= 0) { + /* If we add another element, size will be too big. */ + return count; + } + } + + if (size < min_size) { + return count; + } + + /* Dynamic memory allocation with cache */ + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + + for (i=size-1; i>=0; i--) { + v = table[i]; + if (clique_size[v] < min_size) { + break; + } + if (i+1 < min_size) { + break; + } + + /* Very ugly code, but works faster than "for (i=...)" */ + p1 = newtable; + for (p2=table; p2 < table+i; p2++) { + int w = *p2; + if (GRAPH_IS_EDGE(g, v, w)) { + *p1 = w; + p1++; + } + } + + /* Avoid unneccessary loops (next size == p1-newtable) */ + if (p1-newtable < min_size-1) { + continue; + } + + SET_ADD_ELEMENT(current_clique,v); + n=sub_unweighted_all(newtable,p1-newtable, + min_size-1,max_size-1,maximal,g,opts); + SET_DEL_ELEMENT(current_clique,v); + if (n < 0) { + /* Abort. */ + count -= n; + count = -count; + break; + } + count+=n; + } + temp_list[temp_count++]=newtable; + return count; +} + + + + +/***** Weighted clique searches *****/ +/* + * Weighted clique searches can use the same recursive routine, because + * in both cases (single/all) they have to search through all potential + * permutations searching for heavier cliques. + */ + + +/* + * weighted_clique_search_single() + * + * Searches for a single clique of weight at least min_weight, and at + * most max_weight. Stores maximum clique sizes into clique_size[] + * (or min_weight-1, whichever is smaller). + * + * table - the order of the vertices in g to use + * min_weight - minimum weight of clique to search for. If min_weight==0, + * then searches for a maximum weight clique + * max_weight - maximum weight of clique to search for. If no upper limit + * is desired, use eg. INT_MAX + * g - the graph + * opts - time printing options + * + * opts->time_function is called after each base-level recursion, if + * non-NULL. + * + * Returns 0 if a clique of requested weight was not found (also if + * time_function requested an abort), otherwise returns >= 1. + * If min_weight==0 (search for maximum-weight clique), then the return + * value is the weight of the clique found. The found clique is stored + * in best_clique. + * + * Note: Does NOT use opts->user_function of opts->clique_list. + */ +static int weighted_clique_search_single(int *table, int min_weight, + int max_weight, graph_t *g, + clique_options *opts) { + /* + struct timeval timeval; + struct tms tms; + */ + int i,j; + int v; + int *newtable; + int newsize; + int newweight; + int search_weight; + int min_w; + clique_options localopts; + + if (min_weight==0) + min_w=INT_MAX; + else + min_w=min_weight; + + + if (min_weight==1) { + /* min_weight==1 may cause trouble in the routine, and + * it's trivial to check as it's own case. + * We write nothing to clique_size[]. */ + for (i=0; i < g->n; i++) { + if (g->weights[table[i]] <= max_weight) { + set_empty(best_clique); + SET_ADD_ELEMENT(best_clique,table[i]); + return g->weights[table[i]]; + } + } + return 0; + } + + localopts.time_function=NULL; + localopts.reorder_function=NULL; + localopts.reorder_map=NULL; + localopts.user_function=false_function; + localopts.user_data=NULL; + localopts.clique_list=&best_clique; + localopts.clique_list_length=1; + clique_list_count=0; + + v=table[0]; + set_empty(best_clique); + SET_ADD_ELEMENT(best_clique,v); + search_weight=g->weights[v]; + if (min_weight && (search_weight >= min_weight)) { + if (search_weight <= max_weight) { + /* Found suitable clique. */ + return search_weight; + } + search_weight=min_weight-1; + } + clique_size[v]=search_weight; + set_empty(current_clique); + + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + + for (i = 1; i < g->n; i++) { + v=table[i]; + + newsize=0; + newweight=0; + for (j=0; jweights[table[j]]; + newtable[newsize]=table[j]; + newsize++; + } + } + + + SET_ADD_ELEMENT(current_clique,v); + search_weight=sub_weighted_all(newtable,newsize,newweight, + g->weights[v],search_weight, + clique_size[table[i-1]] + + g->weights[v], + min_w,max_weight,FALSE, + g,&localopts); + SET_DEL_ELEMENT(current_clique,v); + if (search_weight < 0) { + break; + } + + clique_size[v]=search_weight; + + /* + if (opts->time_function) { + gettimeofday(&timeval,NULL); + times(&tms); + if (!opts->time_function(entrance_level, + i+1,g->n,clique_size[v] * + weight_multiplier, + (double)(tms.tms_utime- + cputimer.tms_utime)/ + clocks_per_sec, + timeval.tv_sec- + realtimer.tv_sec+ + (double)(timeval.tv_usec- + realtimer.tv_usec)/ + 1000000,opts)) { + set_free(current_clique); + current_clique=NULL; + break; + } + } + */ + } + temp_list[temp_count++]=newtable; + if (min_weight && (search_weight > 0)) { + /* Requested clique has not been found. */ + return 0; + } + return clique_size[table[i-1]]; +} + + +/* + * weighted_clique_search_all() + * + * Searches for all cliques with weight at least min_weight and at most + * max_weight. Stores the cliques as opts declares. + * + * table - the order of the vertices in g to search + * start - first index where the subgraph table[0], ..., table[start] + * might include a requested kind of clique + * min_weight - minimum weight of clique to search for. min_weight > 0 ! + * max_weight - maximum weight of clique to search for. If no upper limit + * is desired, use eg. INT_MAX + * maximal - search only for maximal cliques + * g - the graph + * opts - time printing and clique storage options + * + * Cliques found are stored as defined by opts->user_function and + * opts->clique_list. opts->time_function is called after each + * base-level recursion, if non-NULL. + * + * clique_size[] must be defined and correct for all values of + * table[0], ..., table[start-1]. + * + * Returns the number of cliques stored (not neccessarily number of cliques + * in graph, if user/time_function aborts). + */ +static int weighted_clique_search_all(int *table, int start, + int min_weight, int max_weight, + boolean maximal, graph_t *g, + clique_options *opts) { + /* + struct timeval timeval; + struct tms tms; + */ + int i,j; + int v; + int *newtable; + int newsize; + int newweight; + + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + + clique_list_count=0; + set_empty(current_clique); + for (i=start; i < g->n; i++) { + v=table[i]; + clique_size[v]=min_weight; /* Do not prune here. */ + + newsize=0; + newweight=0; + for (j=0; jweights[table[j]]; + newsize++; + } + } + + SET_ADD_ELEMENT(current_clique,v); + j=sub_weighted_all(newtable,newsize,newweight, + g->weights[v],min_weight-1,INT_MAX, + min_weight,max_weight,maximal,g,opts); + SET_DEL_ELEMENT(current_clique,v); + + if (j<0) { + /* Abort. */ + break; + } + + /* + if (opts->time_function) { + gettimeofday(&timeval,NULL); + times(&tms); + if (!opts->time_function(entrance_level, + i+1,g->n,clique_size[v] * + weight_multiplier, + (double)(tms.tms_utime- + cputimer.tms_utime)/ + clocks_per_sec, + timeval.tv_sec- + realtimer.tv_sec+ + (double)(timeval.tv_usec- + realtimer.tv_usec)/ + 1000000,opts)) { + set_free(current_clique); + current_clique=NULL; + break; + } + } + */ + } + temp_list[temp_count++]=newtable; + + return clique_list_count; +} + +/* + * sub_weighted_all() + * + * Recursion function for searching for all cliques of given weight. + * + * table - subset of vertices of graph g + * size - size of table + * weight - total weight of vertices in table + * current_weight - weight of clique found so far + * prune_low - ignore all cliques with weight less or equal to this value + * (often heaviest clique found so far) (passed through) + * prune_high - maximum weight possible for clique in this subgraph + * (passed through) + * min_size - minimum weight of cliques to search for (passed through) + * Must be greater than 0. + * max_size - maximum weight of cliques to search for (passed through) + * If no upper limit is desired, use eg. INT_MAX + * maximal - search only for maximal cliques + * g - the graph + * opts - storage options + * + * All cliques of suitable weight found are stored according to opts. + * + * Returns weight of heaviest clique found (prune_low if a heavier clique + * hasn't been found); if a clique with weight at least min_size is found + * then min_size-1 is returned. If clique storage failed, -1 is returned. + * + * The largest clique found smaller than max_weight is stored in + * best_clique, if non-NULL. + * + * Uses current_clique to store the currently-being-searched clique. + * clique_size[] for all values in table must be defined and correct, + * otherwise inaccurate results may occur. + * + * To search for a single maximum clique, use min_weight==max_weight==INT_MAX, + * with best_clique non-NULL. To search for a single given-weight clique, + * use opts->clique_list and opts->user_function=false_function. When + * searching for all cliques, min_weight should be given the minimum weight + * desired. + */ +static int sub_weighted_all(int *table, int size, int weight, + int current_weight, int prune_low, int prune_high, + int min_weight, int max_weight, boolean maximal, + graph_t *g, clique_options *opts) { + int i; + int v,w; + int *newtable; + int *p1, *p2; + int newweight; + + if (current_weight >= min_weight) { + if ((current_weight <= max_weight) && + ((!maximal) || is_maximal(current_clique,g))) { + /* We've found one. Store it. */ + if (!store_clique(current_clique,g,opts)) { + return -1; + } + } + if (current_weight >= max_weight) { + /* Clique too heavy. */ + return min_weight-1; + } + } + if (size <= 0) { + /* current_weight < min_weight, prune_low < min_weight, + * so return value is always < min_weight. */ + if (current_weight>prune_low) { + if (best_clique) { + best_clique = set_copy(best_clique,current_clique); + } + if (current_weight < min_weight) + return current_weight; + else + return min_weight-1; + } else { + return prune_low; + } + } + + /* Dynamic memory allocation with cache */ + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + + for (i = size-1; i >= 0; i--) { + v = table[i]; + if (current_weight+clique_size[v] <= prune_low) { + /* Dealing with subset without heavy enough clique. */ + break; + } + if (current_weight+weight <= prune_low) { + /* Even if all elements are added, won't do. */ + break; + } + + /* Very ugly code, but works faster than "for (i=...)" */ + p1 = newtable; + newweight = 0; + for (p2=table; p2 < table+i; p2++) { + w = *p2; + if (GRAPH_IS_EDGE(g, v, w)) { + *p1 = w; + newweight += g->weights[w]; + p1++; + } + } + + w=g->weights[v]; + weight-=w; + /* Avoid a few unneccessary loops */ + if (current_weight+w+newweight <= prune_low) { + continue; + } + + SET_ADD_ELEMENT(current_clique,v); + prune_low=sub_weighted_all(newtable,p1-newtable, + newweight, + current_weight+w, + prune_low,prune_high, + min_weight,max_weight,maximal, + g,opts); + SET_DEL_ELEMENT(current_clique,v); + if ((prune_low<0) || (prune_low>=prune_high)) { + /* Impossible to find larger clique. */ + break; + } + } + temp_list[temp_count++]=newtable; + return prune_low; +} + + + + +/***** Helper functions *****/ + + +/* + * store_clique() + * + * Stores a clique according to given user options. + * + * clique - the clique to store + * opts - storage options + * + * Returns FALSE if opts->user_function() returned FALSE; otherwise + * returns TRUE. + */ +static boolean store_clique(set_t clique, graph_t *g, clique_options *opts) { + + clique_list_count++; + + /* clique_list[] */ + if (opts->clique_list) { + /* + * This has been a major source of bugs: + * Has clique_list_count been set to 0 before calling + * the recursions? + */ + if (clique_list_count <= 0) { + IGRAPH_FATAL("CLIQUER INTERNAL ERROR: clique_list_count has negative value! Please report as a bug."); + } + if (clique_list_count <= opts->clique_list_length) + opts->clique_list[clique_list_count-1] = + set_copy(opts->clique_list[clique_list_count-1], clique); + } + + /* user_function() */ + if (opts->user_function) { + if (!opts->user_function(clique,g,opts)) { + /* User function requested abort. */ + return FALSE; + } + } + + return TRUE; +} + +/* + * maximalize_clique() + * + * Adds greedily all possible vertices in g to set s to make it a maximal + * clique. + * + * s - clique of vertices to make maximal + * g - graph + * + * Note: Not very optimized (uses a simple O(n^2) routine), but is called + * at maximum once per clique_xxx() call, so it shouldn't matter. + */ +static void maximalize_clique(set_t s,graph_t *g) { + int i,j; + boolean add; + + for (i=0; i < g->n; i++) { + add=TRUE; + for (j=0; j < g->n; j++) { + if (SET_CONTAINS_FAST(s,j) && !GRAPH_IS_EDGE(g,i,j)) { + add=FALSE; + break; + } + } + if (add) { + SET_ADD_ELEMENT(s,i); + } + } + return; +} + + +/* + * is_maximal() + * + * Check whether a clique is maximal or not. + * + * clique - set of vertices in clique + * g - graph + * + * Returns TRUE is clique is a maximal clique of g, otherwise FALSE. + */ +static boolean is_maximal(set_t clique, graph_t *g) { + int i,j; + int *table; + int len; + boolean addable; + + if (temp_count) { + temp_count--; + table=temp_list[temp_count]; + } else { + table=malloc(g->n * sizeof(int)); + } + + len=0; + for (i=0; i < g->n; i++) + if (SET_CONTAINS_FAST(clique,i)) + table[len++]=i; + + for (i=0; i < g->n; i++) { + addable=TRUE; + for (j=0; jtime_function() requests abort). + * + * The returned clique is newly allocated and can be freed by set_free(). + * + * Note: Does NOT use opts->user_function() or opts->clique_list[]. + */ +set_t clique_unweighted_find_single(graph_t *g,int min_size,int max_size, + boolean maximal, clique_options *opts) { + int i; + int *table; + set_t s; + + ENTRANCE_SAVE(); + entrance_level++; + + if (opts==NULL) + opts=&clique_default_options; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(g!=NULL); + ASSERT(min_size>=0); + ASSERT(max_size>=0); + ASSERT((max_size==0) || (min_size <= max_size)); + ASSERT(!((min_size==0) && (max_size>0))); + ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL)); + + if ((max_size>0) && (min_size>max_size)) { + /* state was not changed */ + entrance_level--; + return NULL; + } + + /* + if (clocks_per_sec==0) + clocks_per_sec=sysconf(_SC_CLK_TCK); + ASSERT(clocks_per_sec>0); + */ + + /* Dynamic allocation */ + current_clique=set_new(g->n); + clique_size=malloc(g->n * sizeof(int)); + /* table allocated later */ + temp_list=malloc((g->n+2)*sizeof(int *)); + temp_count=0; + + /* "start clock" */ + /* + gettimeofday(&realtimer,NULL); + times(&cputimer); + */ + + /* reorder */ + if (opts->reorder_function) { + table=opts->reorder_function(g,FALSE); + } else if (opts->reorder_map) { + table=reorder_duplicate(opts->reorder_map,g->n); + } else { + table=reorder_ident(g->n); + } + ASSERT(reorder_is_bijection(table,g->n)); + + + if (unweighted_clique_search_single(table,min_size,g,opts)==0) { + set_free(current_clique); + current_clique=NULL; + goto cleanreturn; + } + if (maximal && (min_size>0)) { + maximalize_clique(current_clique,g); + + if ((max_size > 0) && (set_size(current_clique) > max_size)) { + clique_options localopts; + + s = set_new(g->n); + localopts.time_function = opts->time_function; + localopts.output = opts->output; + localopts.user_function = false_function; + localopts.clique_list = &s; + localopts.clique_list_length = 1; + + for (i=0; i < g->n-1; i++) + if (clique_size[table[i]]>=min_size) + break; + if (unweighted_clique_search_all(table,i,min_size, + max_size,maximal, + g,&localopts)) { + set_free(current_clique); + current_clique=s; + } else { + set_free(current_clique); + current_clique=NULL; + } + } + } + + cleanreturn: + s=current_clique; + + /* Free resources */ + for (i=0; i < temp_count; i++) + free(temp_list[i]); + free(temp_list); + free(table); + free(clique_size); + + ENTRANCE_RESTORE(); + entrance_level--; + + return s; +} + + +/* + * clique_unweighted_find_all() + * + * Find all cliques with size at least min_size and at most max_size. + * + * g - the graph + * min_size - minimum size of cliques to search for. If min_size==0, + * searches for maximum cliques. + * max_size - maximum size of cliques to search for. If max_size==0, no + * upper limit is used. If min_size==0, this must also be 0. + * maximal - require cliques to be maximal cliques + * opts - time printing and clique storage options + * + * Returns the number of cliques found. This can be less than the number + * of cliques in the graph iff opts->time_function() or opts->user_function() + * returns FALSE (request abort). + * + * The cliques found are stored in opts->clique_list[] and + * opts->user_function() is called with them (if non-NULL). The cliques + * stored in opts->clique_list[] are newly allocated, and can be freed + * by set_free(). + */ +CLIQUER_LARGE_INT clique_unweighted_find_all(graph_t *g, int min_size, int max_size, + boolean maximal, clique_options *opts) { + int i; + int *table; + CLIQUER_LARGE_INT count; + + ENTRANCE_SAVE(); + entrance_level++; + + if (opts==NULL) + opts=&clique_default_options; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(g!=NULL); + ASSERT(min_size>=0); + ASSERT(max_size>=0); + ASSERT((max_size==0) || (min_size <= max_size)); + ASSERT(!((min_size==0) && (max_size>0))); + ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL)); + + if ((max_size>0) && (min_size>max_size)) { + /* state was not changed */ + entrance_level--; + return 0; + } + + /* + if (clocks_per_sec==0) + clocks_per_sec=sysconf(_SC_CLK_TCK); + ASSERT(clocks_per_sec>0); + */ + + /* Dynamic allocation */ + current_clique=set_new(g->n); + clique_size=malloc(g->n * sizeof(int)); + /* table allocated later */ + temp_list=malloc((g->n+2)*sizeof(int *)); + temp_count=0; + + clique_list_count=0; + memset(clique_size,0,g->n * sizeof(int)); + + /* "start clock" */ + /* + gettimeofday(&realtimer,NULL); + times(&cputimer); + */ + + /* reorder */ + if (opts->reorder_function) { + table=opts->reorder_function(g,FALSE); + } else if (opts->reorder_map) { + table=reorder_duplicate(opts->reorder_map,g->n); + } else { + table=reorder_ident(g->n); + } + ASSERT(reorder_is_bijection(table,g->n)); + + + /* Search as normal until there is a chance to find a suitable + * clique. */ + if (unweighted_clique_search_single(table,min_size,g,opts)==0) { + count=0; + goto cleanreturn; + } + + if (min_size==0 && max_size==0) { + min_size=max_size=clique_size[table[g->n-1]]; + maximal=FALSE; /* No need to test, since we're searching + * for maximum cliques. */ + } + if (max_size==0) { + max_size=INT_MAX; + } + + for (i=0; i < g->n-1; i++) + if (clique_size[table[i]] >= min_size) + break; + count=unweighted_clique_search_all(table,i,min_size,max_size, + maximal,g,opts); + + cleanreturn: + /* Free resources */ + for (i=0; itime_function() requests abort). + * + * The returned clique is newly allocated and can be freed by set_free(). + * + * Note: Does NOT use opts->user_function() or opts->clique_list[]. + * Note: Automatically uses clique_unweighted_find_single if all vertex + * weights are the same. + */ +set_t clique_find_single(graph_t *g,int min_weight,int max_weight, + boolean maximal, clique_options *opts) { + int i; + int *table; + set_t s; + + ENTRANCE_SAVE(); + entrance_level++; + + if (opts==NULL) + opts=&clique_default_options; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(g!=NULL); + ASSERT(min_weight>=0); + ASSERT(max_weight>=0); + ASSERT((max_weight==0) || (min_weight <= max_weight)); + ASSERT(!((min_weight==0) && (max_weight>0))); + ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL)); + + if ((max_weight>0) && (min_weight>max_weight)) { + /* state was not changed */ + entrance_level--; + return NULL; + } + + /* + if (clocks_per_sec==0) + clocks_per_sec=sysconf(_SC_CLK_TCK); + ASSERT(clocks_per_sec>0); + */ + + /* Check whether we can use unweighted routines. */ + if (!graph_weighted(g)) { + min_weight=DIV_UP(min_weight,g->weights[0]); + if (max_weight) { + max_weight=DIV_DOWN(max_weight,g->weights[0]); + if (max_weight < min_weight) { + /* state was not changed */ + entrance_level--; + return NULL; + } + } + + weight_multiplier = g->weights[0]; + entrance_level--; + s=clique_unweighted_find_single(g,min_weight,max_weight, + maximal,opts); + ENTRANCE_RESTORE(); + return s; + } + + /* Dynamic allocation */ + current_clique=set_new(g->n); + best_clique=set_new(g->n); + clique_size=malloc(g->n * sizeof(int)); + memset(clique_size, 0, g->n * sizeof(int)); + /* table allocated later */ + temp_list=malloc((g->n+2)*sizeof(int *)); + temp_count=0; + + clique_list_count=0; + + /* "start clock" */ + /* + gettimeofday(&realtimer,NULL); + times(&cputimer); + */ + + /* reorder */ + if (opts->reorder_function) { + table=opts->reorder_function(g,TRUE); + } else if (opts->reorder_map) { + table=reorder_duplicate(opts->reorder_map,g->n); + } else { + table=reorder_ident(g->n); + } + ASSERT(reorder_is_bijection(table,g->n)); + + if (max_weight==0) + max_weight=INT_MAX; + + if (weighted_clique_search_single(table,min_weight,max_weight, + g,opts)==0) { + /* Requested clique has not been found. */ + set_free(best_clique); + best_clique=NULL; + goto cleanreturn; + } + if (maximal && (min_weight>0)) { + maximalize_clique(best_clique,g); + if (graph_subgraph_weight(g,best_clique) > max_weight) { + clique_options localopts; + + localopts.time_function = opts->time_function; + localopts.output = opts->output; + localopts.user_function = false_function; + localopts.clique_list = &best_clique; + localopts.clique_list_length = 1; + + for (i=0; i < g->n-1; i++) + if ((clique_size[table[i]] >= min_weight) || + (clique_size[table[i]] == 0)) + break; + if (!weighted_clique_search_all(table,i,min_weight, + max_weight,maximal, + g,&localopts)) { + set_free(best_clique); + best_clique=NULL; + } + } + } + + cleanreturn: + s=best_clique; + + /* Free resources */ + for (i=0; i < temp_count; i++) + free(temp_list[i]); + free(temp_list); + temp_list=NULL; + temp_count=0; + free(table); + set_free(current_clique); + current_clique=NULL; + free(clique_size); + clique_size=NULL; + + ENTRANCE_RESTORE(); + entrance_level--; + + return s; +} + + + + + +/* + * clique_find_all() + * + * Find all cliques with weight at least min_weight and at most max_weight. + * + * g - the graph + * min_weight - minimum weight of cliques to search for. If min_weight==0, + * searches for maximum weight cliques. + * max_weight - maximum weight of cliques to search for. If max_weight==0, + * no upper limit is used. If min_weight==0, max_weight must + * also be 0. + * maximal - require cliques to be maximal cliques + * opts - time printing and clique storage options + * + * Returns the number of cliques found. This can be less than the number + * of cliques in the graph iff opts->time_function() or opts->user_function() + * returns FALSE (request abort). + * + * The cliques found are stored in opts->clique_list[] and + * opts->user_function() is called with them (if non-NULL). The cliques + * stored in opts->clique_list[] are newly allocated, and can be freed + * by set_free(). + * + * Note: Automatically uses clique_unweighted_find_all if all vertex + * weights are the same. + */ +int clique_find_all(graph_t *g, int min_weight, int max_weight, + boolean maximal, clique_options *opts) { + int i,n; + int *table; + CLIQUER_LARGE_INT r; + + ENTRANCE_SAVE(); + entrance_level++; + + if (opts==NULL) + opts=&clique_default_options; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(g!=NULL); + ASSERT(min_weight>=0); + ASSERT(max_weight>=0); + ASSERT((max_weight==0) || (min_weight <= max_weight)); + ASSERT(!((min_weight==0) && (max_weight>0))); + ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL)); + + if ((max_weight>0) && (min_weight>max_weight)) { + /* state was not changed */ + entrance_level--; + return 0; + } + + /* + if (clocks_per_sec==0) + clocks_per_sec=sysconf(_SC_CLK_TCK); + ASSERT(clocks_per_sec>0); + */ + + if (!graph_weighted(g)) { + min_weight=DIV_UP(min_weight,g->weights[0]); + if (max_weight) { + max_weight=DIV_DOWN(max_weight,g->weights[0]); + if (max_weight < min_weight) { + /* state was not changed */ + entrance_level--; + return 0; + } + } + + weight_multiplier = g->weights[0]; + entrance_level--; + r=clique_unweighted_find_all(g,min_weight,max_weight,maximal, + opts); + ENTRANCE_RESTORE(); + return r; + } + + /* Dynamic allocation */ + current_clique=set_new(g->n); + best_clique=set_new(g->n); + clique_size=malloc(g->n * sizeof(int)); + memset(clique_size, 0, g->n * sizeof(int)); + /* table allocated later */ + temp_list=malloc((g->n+2)*sizeof(int *)); + temp_count=0; + + /* "start clock" */ + /* + gettimeofday(&realtimer,NULL); + times(&cputimer); + */ + + /* reorder */ + if (opts->reorder_function) { + table=opts->reorder_function(g,TRUE); + } else if (opts->reorder_map) { + table=reorder_duplicate(opts->reorder_map,g->n); + } else { + table=reorder_ident(g->n); + } + ASSERT(reorder_is_bijection(table,g->n)); + + /* First phase */ + n=weighted_clique_search_single(table,min_weight,INT_MAX,g,opts); + if (n==0) { + /* Requested clique has not been found. */ + goto cleanreturn; + } + + if (min_weight==0) { + min_weight=n; + max_weight=n; + maximal=FALSE; /* They're maximum cliques already. */ + } + if (max_weight==0) + max_weight=INT_MAX; + + for (i=0; i < g->n; i++) + if ((clique_size[table[i]] >= min_weight) || + (clique_size[table[i]] == 0)) + break; + + /* Second phase */ + n=weighted_clique_search_all(table,i,min_weight,max_weight,maximal, + g,opts); + + cleanreturn: + /* Free resources */ + for (i=0; i < temp_count; i++) + free(temp_list[i]); + free(temp_list); + free(table); + set_free(current_clique); + set_free(best_clique); + free(clique_size); + + ENTRANCE_RESTORE(); + entrance_level--; + + return n; +} + + + + + + + + + + + + + + + + +#if 0 +/* + * clique_print_time() + * + * Reports current running information every 0.1 seconds or when values + * change. + * + * level - re-entrance level + * i - current recursion level + * n - maximum recursion level + * max - weight of heaviest clique found + * cputime - CPU time used in algorithm so far + * realtime - real time used in algorithm so far + * opts - prints information to (FILE *)opts->output (or stdout if NULL) + * + * Returns always TRUE (ie. never requests abort). + */ +boolean clique_print_time(int level, int i, int n, int max, + double cputime, double realtime, + clique_options *opts) { + static float prev_time=100; + static int prev_i=100; + static int prev_max=100; + static int prev_level=0; + FILE *fp=opts->output; + int j; + + if (fp==NULL) + fp=stdout; + + if (ABS(prev_time-realtime)>0.1 || i==n || ioutput (or stdout if NULL) + * + * Returns always TRUE (ie. never requests abort). + */ +boolean clique_print_time_always(int level, int i, int n, int max, + double cputime, double realtime, + clique_options *opts) { + static float prev_time=100; + static int prev_i=100; + FILE *fp=opts->output; + int j; + + if (fp==NULL) + fp=stdout; + + for (j=1; j + +/* This is an igraph-specific modification to cliquer. + * We use a 64-bit CLIQUER_LARGE_INT (even on 32-bit systems) in places + * which are prone to overflow. Since cliquer indicates interruption by + * returning -1 times the clique count, the effect of overflow is that + * it returns a partial (i.e. incorrect) result without warning. */ +#include +#ifndef CLIQUER_LARGE_INT +#define CLIQUER_LARGE_INT int64_t +#endif + +#include "set.h" +#include "graph.h" +#include "reorder.h" + +typedef struct _clique_options clique_options; +struct _clique_options { + int *(*reorder_function)(graph_t *, boolean); + int *reorder_map; + + /* arguments: level, n, max, user_time, system_time, opts */ + boolean (*time_function)(int,int,int,int,double,double, + clique_options *); + FILE *output; + + boolean (*user_function)(set_t,graph_t *,clique_options *); + void *user_data; + set_t *clique_list; + int clique_list_length; +}; + +/* Weighted clique functions */ +extern int clique_max_weight(graph_t *g,clique_options *opts); +extern set_t clique_find_single(graph_t *g,int min_weight,int max_weight, + boolean maximal, clique_options *opts); +extern int clique_find_all(graph_t *g, int req_weight, boolean exact, + boolean maximal, clique_options *opts); + +/* Unweighted clique functions */ +#define clique_unweighted_max_size clique_unweighted_max_weight +extern int clique_unweighted_max_weight(graph_t *g, clique_options *opts); +extern set_t clique_unweighted_find_single(graph_t *g,int min_size, + int max_size,boolean maximal, + clique_options *opts); +extern CLIQUER_LARGE_INT clique_unweighted_find_all(graph_t *g, int min_size, int max_size, + boolean maximal, clique_options *opts); + +/* Time printing functions */ +/* +extern boolean clique_print_time(int level, int i, int n, int max, + double cputime, double realtime, + clique_options *opts); +extern boolean clique_print_time_always(int level, int i, int n, int max, + double cputime, double realtime, + clique_options *opts); +*/ + +/* Alternate spelling (let's be a little forgiving): */ +#define cliquer_options clique_options +#define cliquer_default_options clique_default_options + +#endif /* !CLIQUER_H */ diff --git a/src/rigraph/core/cliques/cliquer/cliquer_graph.c b/src/rigraph/core/cliques/cliquer/cliquer_graph.c new file mode 100644 index 0000000..c9284fd --- /dev/null +++ b/src/rigraph/core/cliques/cliquer/cliquer_graph.c @@ -0,0 +1,765 @@ + +/* + * This file contains the graph handling routines. + * + * Copyright (C) 2002 Sampo Niskanen, Patric Östergård. + * Licensed under the GNU GPL, read the file LICENSE for details. + */ + + +#include +#include +#include +#include "graph.h" + + +/* +static graph_t *graph_read_dimacs_binary(FILE *fp,char *firstline); +static graph_t *graph_read_dimacs_ascii(FILE *fp,char *firstline); +*/ + + +/* + * graph_new() + * + * Returns a newly allocated graph with n vertices all with weight 1, + * and no edges. + */ +graph_t *graph_new(int n) { + graph_t *g; + int i; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(n>0); + + g=malloc(sizeof(graph_t)); + g->n=n; + g->edges=malloc(g->n * sizeof(set_t)); + g->weights=malloc(g->n * sizeof(int)); + for (i=0; i < g->n; i++) { + g->edges[i]=set_new(n); + g->weights[i]=1; + } + return g; +} + +/* + * graph_free() + * + * Frees the memory associated with the graph g. + */ +void graph_free(graph_t *g) { + int i; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(g!=NULL); + ASSERT(g->n > 0); + + for (i=0; i < g->n; i++) { + set_free(g->edges[i]); + } + free(g->weights); + free(g->edges); + free(g); + return; +} + + +/* + * graph_resize() + * + * Resizes graph g to given size. If size > g->n, the new vertices are + * not connected to any others and their weights are set to 1. + * If size < g->n, the last g->n - size vertices are removed. + */ +void graph_resize(graph_t *g, int size) { + int i; + + ASSERT(g!=NULL); + ASSERT(g->n > 0); + ASSERT(size > 0); + + if (g->n == size) + return; + + /* Free/alloc extra edge-sets */ + for (i=size; i < g->n; i++) + set_free(g->edges[i]); + g->edges=realloc(g->edges, size * sizeof(set_t)); + for (i=g->n; i < size; i++) + g->edges[i]=set_new(size); + + /* Resize original sets */ + for (i=0; i < MIN(g->n,size); i++) { + g->edges[i]=set_resize(g->edges[i],size); + } + + /* Weights */ + g->weights=realloc(g->weights,size * sizeof(int)); + for (i=g->n; iweights[i]=1; + + g->n=size; + return; +} + +/* + * graph_crop() + * + * Resizes the graph so as to remove all highest-valued isolated vertices. + */ +void graph_crop(graph_t *g) { + int i; + + for (i=g->n-1; i>=1; i--) + if (set_size(g->edges[i])>0) + break; + graph_resize(g,i+1); + return; +} + + +/* + * graph_weighted() + * + * Returns TRUE if all vertex weights of graph g are all the same. + * + * Note: Does NOT require weights to be 1. + */ +boolean graph_weighted(graph_t *g) { + int i,w; + + w=g->weights[0]; + for (i=1; i < g->n; i++) + if (g->weights[i] != w) + return TRUE; + return FALSE; +} + +/* + * graph_edge_count() + * + * Returns the number of edges in graph g. + */ +int graph_edge_count(graph_t *g) { + int i; + int count=0; + + for (i=0; i < g->n; i++) { + count += set_size(g->edges[i]); + } + return count/2; +} + + +#if 0 +/* + * graph_write_dimacs_ascii_file() + * + * Writes an ASCII dimacs-format file of graph g, with comment, to + * given file. + * + * Returns TRUE if successful, FALSE if an error occurred. + */ +boolean graph_write_dimacs_ascii_file(graph_t *g, char *comment, char *file) { + FILE *fp; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(file!=NULL); + + if ((fp=fopen(file,"wb"))==NULL) + return FALSE; + if (!graph_write_dimacs_ascii(g,comment,fp)) { + fclose(fp); + return FALSE; + } + fclose(fp); + return TRUE; +} + +/* + * graph_write_dimacs_ascii() + * + * Writes an ASCII dimacs-format file of graph g, with comment, to the + * file stream fp. + * + * Returns TRUE if successful, FALSE if an error occurred. + */ +boolean graph_write_dimacs_ascii(graph_t *g, char *comment, FILE *fp) { + int i,j; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(graph_test(g,NULL)); + ASSERT(fp!=NULL); + + if (comment) + fprintf(fp,"c %s\n",comment); + fprintf(fp,"p edge %d %d\n",g->n,graph_edge_count(g)); + for (i=0; i < g->n; i++) + if (g->weights[i]!=1) + fprintf(fp,"n %d %d\n",i+1,g->weights[i]); + for (i=0; i < g->n; i++) + for (j=0; j= headersize) { \ + headersize+=1024; \ + header=realloc(header,headersize); \ +} \ +strncat(header,s,1000); \ +headerlength+=strlen(s); + +boolean graph_write_dimacs_binary(graph_t *g, char *comment,FILE *fp) { + char *buf; + char *header=NULL; + int headersize=0; + int headerlength=0; + int i,j; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(graph_test(g,NULL)); + ASSERT(fp!=NULL); + + buf=malloc(MAX(1024,g->n/8+1)); + header=malloc(1024); + header[0]=0; + headersize=1024; + if (comment) { + strcpy(buf,"c "); + strncat(buf,comment,1000); + strcat(buf,"\n"); + STR_APPEND(buf); + } + sprintf(buf,"p edge %d %d\n",g->n,graph_edge_count(g)); + STR_APPEND(buf); + for (i=0; i < g->n; i++) { + if (g->weights[i]!=1) { + sprintf(buf,"n %d %d\n",i+1,g->weights[i]); + STR_APPEND(buf); + } + } + + fprintf(fp,"%d\n",(int)strlen(header)); + fprintf(fp,"%s",header); + free(header); + + for (i=0; i < g->n; i++) { + memset(buf,0,i/8+1); + for (j=0; j=strlen(str)) /* blank line */ + return TRUE; + if (str[i+1]!=0 && !isspace(str[i+1])) /* not 1-char field */ + return FALSE; + + switch (str[i]) { + case 'c': + return TRUE; + case 'p': + if (g->n != 0) + return FALSE; + if (sscanf(str," p %15s %d %d %2s",tmp,&(g->n),&i,tmp)!=3) + return FALSE; + if (g->n <= 0) + return FALSE; + g->edges=calloc(g->n,sizeof(set_t)); + for (i=0; in; i++) + g->edges[i]=set_new(g->n); + g->weights=calloc(g->n,sizeof(int)); + for (i=0; in; i++) + g->weights[i]=1; + return TRUE; + case 'n': + if ((g->n <= 0) || (g->weights == NULL)) + return FALSE; + if (sscanf(str," n %d %d %2s",&i,&w,tmp)!=2) + return FALSE; + if (i<1 || i>g->n) + return FALSE; + if (w<=0) + return FALSE; + g->weights[i-1]=w; + return TRUE; + case 'e': + if ((g->n <= 0) || (g->edges == NULL)) + return FALSE; + if (sscanf(str," e %d %d %2s",&i,&j,tmp)!=2) + return FALSE; + if (i<1 || j<1 || i>g->n || j>g->n) + return FALSE; + if (i==j) /* We want antireflexive graphs. */ + return TRUE; + GRAPH_ADD_EDGE(g,i-1,j-1); + return TRUE; + case 'd': + case 'v': + case 'x': + return TRUE; + default: + fprintf(stderr,"Warning: ignoring field '%c' in " + "input.\n",str[i]); + return TRUE; + } +} + + +/* + * graph_read_dimacs_binary() + * + * Reads a dimacs-format binary file from file stream fp with the first + * line being firstline. + * + * Returns the newly-allocated graph or NULL if an error occurred. + * + * TODO: This function leaks memory when reading erroneous files. + */ +static graph_t *graph_read_dimacs_binary(FILE *fp,char *firstline) { + int length=0; + graph_t *g; + int i,j; + char *buffer; + char *start; + char *end; + char **buf; + char tmp[10]; + + if (sscanf(firstline," %d %2s",&length,tmp)!=1) + return NULL; + if (length<=0) { + fprintf(stderr,"Malformed preamble: preamble size < 0.\n"); + return NULL; + } + buffer=malloc(length+2); + if (fread(buffer,1,length,fp)n <= 0) { + fprintf(stderr,"Malformed preamble: number of " + "vertices <= 0\n"); + free(g); + return NULL; + } + + /* Binary part. */ + buf=calloc(g->n,sizeof(char*)); + for (i=0; i < g->n; i++) { + buf[i]=calloc(g->n,1); + if (fread(buf[i],1,i/8+1,fp) < (i/8+1)) { + fprintf(stderr,"Unexpected end of file when " + "reading graph.\n"); + return NULL; + } + } + + for (i=0; i < g->n; i++) { + for (j=0; jn <= 0) { + free(g); + fprintf(stderr,"Unexpected end of file when reading graph.\n"); + return NULL; + } + + return g; +} +#endif + + +#if 0 +/* + * graph_print() + * + * Prints a representation of the graph g to stdout (along with any errors + * noticed). Mainly useful for debugging purposes and trivial output. + * + * The output consists of a first line describing the dimensions and then + * one line per vertex containing the vertex number (numbered 0,...,n-1), + * the vertex weight (if the graph is weighted), "->" and then a list + * of all vertices it is adjacent to. + */ +void graph_print(graph_t *g) { + int i,j; + int asymm=0; + int refl=0; + int nonpos=0; + int extra=0; + unsigned int weight=0; + boolean weighted; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + + if (g==NULL) { + printf(" WARNING: Graph pointer is NULL!\n"); + return; + } + if (g->n <= 0) { + printf(" WARNING: Graph has %d vertices " + "(should be positive)!\n",g->n); + return; + } + + weighted=graph_weighted(g); + + printf("%s graph has %d vertices, %d edges (density %.2f).\n", + weighted?"Weighted":((g->weights[0]==1)? + "Unweighted":"Semi-weighted"), + g->n,graph_edge_count(g), + (float)graph_edge_count(g)/((float)(g->n - 1)*(g->n)/2)); + + for (i=0; i < g->n; i++) { + printf("%2d",i); + if (weighted) { + printf(" w=%d",g->weights[i]); + if (g->weights[i] <= 0) { + printf("*NON-POSITIVE*"); + nonpos++; + } + } + if (weight < INT_MAX) + weight+=g->weights[i]; + printf(" ->"); + for (j=0; j < g->n; j++) { + if (SET_CONTAINS_FAST(g->edges[i],j)) { + printf(" %d",j); + if (i==j) { + printf("*REFLEXIVE*"); + refl++; + } + if (!SET_CONTAINS_FAST(g->edges[j],i)) { + printf("*ASYMMERTIC*"); + asymm++; + } + } + } + for (j=g->n; j < SET_ARRAY_LENGTH(g->edges[i])*ELEMENTSIZE; + j++) { + if (SET_CONTAINS_FAST(g->edges[i],j)) { + printf(" %d*NON-EXISTENT*",j); + extra++; + } + } + printf("\n"); + } + + if (asymm) + printf(" WARNING: Graph contained %d asymmetric edges!\n", + asymm); + if (refl) + printf(" WARNING: Graph contained %d reflexive edges!\n", + refl); + if (nonpos) + printf(" WARNING: Graph contained %d non-positive vertex " + "weights!\n",nonpos); + if (extra) + printf(" WARNING: Graph contained %d edges to " + "non-existent vertices!\n",extra); + if (weight>=INT_MAX) + printf(" WARNING: Total graph weight >= INT_MAX!\n"); + return; +} + + +/* + * graph_test() + * + * Tests graph g to be valid. Checks that g is non-NULL, the edges are + * symmetric and anti-reflexive, and that all vertex weights are positive. + * If output is non-NULL, prints a few lines telling the status of the graph + * to file descriptor output. + * + * Returns TRUE if the graph is valid, FALSE otherwise. + */ +boolean graph_test(graph_t *g,FILE *output) { + int i,j; + int edges=0; + int asymm=0; + int nonpos=0; + int refl=0; + int extra=0; + unsigned int weight=0; + boolean weighted; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + + if (g==NULL) { + if (output) + fprintf(output," WARNING: Graph pointer is NULL!\n"); + return FALSE; + } + + weighted=graph_weighted(g); + + for (i=0; i < g->n; i++) { + if (g->edges[i]==NULL) { + if (output) + fprintf(output," WARNING: Graph edge set " + "NULL!\n" + " (further warning suppressed)\n"); + return FALSE; + } + if (SET_MAX_SIZE(g->edges[i]) < g->n) { + if (output) + fprintf(output," WARNING: Graph edge set " + "too small!\n" + " (further warnings suppressed)\n"); + return FALSE; + } + for (j=0; j < g->n; j++) { + if (SET_CONTAINS_FAST(g->edges[i],j)) { + edges++; + if (i==j) { + refl++; + } + if (!SET_CONTAINS_FAST(g->edges[j],i)) { + asymm++; + } + } + } + for (j=g->n; j < SET_ARRAY_LENGTH(g->edges[i])*ELEMENTSIZE; + j++) { + if (SET_CONTAINS_FAST(g->edges[i],j)) + extra++; + } + if (g->weights[i] <= 0) + nonpos++; + if (weightweights[i]; + } + + edges/=2; /* Each is counted twice. */ + + if (output) { + /* Semi-weighted means all weights are equal, but not 1. */ + fprintf(output,"%s graph has %d vertices, %d edges " + "(density %.2f).\n", + weighted?"Weighted": + ((g->weights[0]==1)?"Unweighted":"Semi-weighted"), + g->n,edges,(float)edges/((float)(g->n - 1)*(g->n)/2)); + + if (asymm) + fprintf(output," WARNING: Graph contained %d " + "asymmetric edges!\n",asymm); + if (refl) + fprintf(output," WARNING: Graph contained %d " + "reflexive edges!\n",refl); + if (nonpos) + fprintf(output," WARNING: Graph contained %d " + "non-positive vertex weights!\n",nonpos); + if (extra) + fprintf(output," WARNING: Graph contained %d edges " + "to non-existent vertices!\n",extra); + if (weight>=INT_MAX) + fprintf(output," WARNING: Total graph weight >= " + "INT_MAX!\n"); + if (asymm==0 && refl==0 && nonpos==0 && extra==0 && + weight=INT_MAX) + return FALSE; + + return TRUE; +} + + +/* + * graph_test_regular() + * + * Returns the vertex degree for regular graphs, or -1 if the graph is + * not regular. + */ +int graph_test_regular(graph_t *g) { + int i,n; + + n=set_size(g->edges[0]); + + for (i=1; i < g->n; i++) { + if (set_size(g->edges[i]) != n) + return -1; + } + return n; +} + +#endif diff --git a/src/rigraph/core/cliques/cliquer/cliquerconf.h b/src/rigraph/core/cliques/cliquer/cliquerconf.h new file mode 100644 index 0000000..47d923b --- /dev/null +++ b/src/rigraph/core/cliques/cliquer/cliquerconf.h @@ -0,0 +1,68 @@ + +#ifndef CLIQUERCONF_H +#define CLIQUERCONF_H + +/* + * setelement is the basic memory type used in sets. It is often fastest + * to be as large as can fit into the CPU registers. + * + * ELEMENTSIZE is the size of one setelement, measured in bits. It must + * be either 16, 32 or 64 (otherwise additional changes must be made to + * the source). + * + * The default is to use "unsigned long int" and attempt to guess the + * size using , which should work pretty well. Check functioning + * with "make test". + */ + +/* typedef unsigned long int setelement; */ +/* #define ELEMENTSIZE 64 */ + + +/* + * INLINE is a command prepended to function declarations to instruct the + * compiler to inline the function. If inlining is not desired, define blank. + * + * The default is to use "inline", which is recognized by most compilers. + */ + +/* #define INLINE */ +/* #define INLINE __inline__ */ +#if __STDC_VERSION__ >= 199901L + #define INLINE inline +#else + #if defined(_MSC_VER) + #define INLINE __inline + #elif defined(__GNUC__) + #define INLINE __inline__ + #else + #define INLINE + #endif +#endif + + +/* + * Set handling functions are defined as static functions in set.h for + * performance reasons. This may cause unnecessary warnings from the + * compiler. Some compilers (such as GCC) have the possibility to turn + * off the warnings on a per-function basis using a flag prepended to + * the function declaration. + * + * The default is to use the correct attribute when compiling with GCC, + * or no flag otherwise. + */ + +/* #define UNUSED_FUNCTION __attribute__((unused)) */ +/* #define UNUSED_FUNCTION */ + + +/* + * Uncommenting the following will disable all assertions (checks that + * function arguments and other variables are correct). This is highly + * discouraged, as it allows bugs to go unnoticed easier. The assertions + * are set so that they do not slow down programs notably. + */ + +/* #define ASSERT(x) */ + +#endif /* !CLIQUERCONF_H */ diff --git a/src/rigraph/core/cliques/cliquer/graph.h b/src/rigraph/core/cliques/cliquer/graph.h new file mode 100644 index 0000000..56f92af --- /dev/null +++ b/src/rigraph/core/cliques/cliquer/graph.h @@ -0,0 +1,75 @@ + +#ifndef CLIQUER_GRAPH_H +#define CLIQUER_GRAPH_H + +#include "set.h" + +typedef struct _graph_t graph_t; +struct _graph_t { + int n; /* Vertices numbered 0...n-1 */ + set_t *edges; /* A list of n sets (the edges). */ + int *weights; /* A list of n vertex weights. */ +}; + + +#define GRAPH_IS_EDGE_FAST(g,i,j) (SET_CONTAINS_FAST((g)->edges[(i)],(j))) +#define GRAPH_IS_EDGE(g,i,j) (((i)<((g)->n))?SET_CONTAINS((g)->edges[(i)], \ + (j)):FALSE) +#define GRAPH_ADD_EDGE(g,i,j) do { \ + SET_ADD_ELEMENT((g)->edges[(i)],(j)); \ + SET_ADD_ELEMENT((g)->edges[(j)],(i)); \ +} while (FALSE) +#define GRAPH_DEL_EDGE(g,i,j) do { \ + SET_DEL_ELEMENT((g)->edges[(i)],(j)); \ + SET_DEL_ELEMENT((g)->edges[(j)],(i)); \ +} while (FALSE) + + +extern graph_t *graph_new(int n); +extern void graph_free(graph_t *g); +extern void graph_resize(graph_t *g, int size); +extern void graph_crop(graph_t *g); + +extern boolean graph_weighted(graph_t *g); +extern int graph_edge_count(graph_t *g); + +/* +extern graph_t *graph_read_dimacs(FILE *fp); +extern graph_t *graph_read_dimacs_file(char *file); +extern boolean graph_write_dimacs_ascii(graph_t *g, char *comment,FILE *fp); +extern boolean graph_write_dimacs_ascii_file(graph_t *g,char *comment, + char *file); +extern boolean graph_write_dimacs_binary(graph_t *g, char *comment,FILE *fp); +extern boolean graph_write_dimacs_binary_file(graph_t *g, char *comment, + char *file); + +extern void graph_print(graph_t *g); +extern boolean graph_test(graph_t *g, FILE *output); +extern int graph_test_regular(graph_t *g); +*/ + +UNUSED_FUNCTION INLINE +static int graph_subgraph_weight(graph_t *g,set_t s) { + unsigned int i,j; + int count=0; + setelement e; + + for (i=0; iweights[i*ELEMENTSIZE+j]; + e = e>>1; + } + } + } + return count; +} + +UNUSED_FUNCTION INLINE +static int graph_vertex_degree(graph_t *g, int v) { + return set_size(g->edges[v]); +} + +#endif /* !CLIQUER_GRAPH_H */ diff --git a/src/rigraph/core/cliques/cliquer/misc.h b/src/rigraph/core/cliques/cliquer/misc.h new file mode 100644 index 0000000..618b5c1 --- /dev/null +++ b/src/rigraph/core/cliques/cliquer/misc.h @@ -0,0 +1,59 @@ + +#ifndef CLIQUER_MISC_H +#define CLIQUER_MISC_H + +#include "igraph_error.h" +#include "cliquerconf.h" + +/* + * We #define boolean instead of using a typedef because nauty.h uses it + * also. AFAIK, there is no way to check for an existing typedef, and + * re-typedefing is illegal (even when using exactly the same datatype!). + */ +#ifndef boolean +#define boolean int +#endif + + +/* + * The original cliquer source has some functions incorrectly marked as unused, + * thus leave this undefined. + */ +#define UNUSED_FUNCTION + + +/* + * Default inlining directive: "inline" + */ +#ifndef INLINE +#define INLINE inline +#endif + + +#include +#include + +#ifndef ASSERT +#define ASSERT IGRAPH_ASSERT +#endif /* !ASSERT */ + + +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif +#ifndef ABS +#define ABS(v) (((v)<0)?(-(v)):(v)) +#endif + +#endif /* !CLIQUER_MISC_H */ diff --git a/src/rigraph/core/cliques/cliquer/reorder.c b/src/rigraph/core/cliques/cliquer/reorder.c new file mode 100644 index 0000000..15d197a --- /dev/null +++ b/src/rigraph/core/cliques/cliquer/reorder.c @@ -0,0 +1,424 @@ + +/* + * This file contains the vertex reordering routines. + * + * Copyright (C) 2002 Sampo Niskanen, Patric Östergård. + * Licensed under the GNU GPL, read the file LICENSE for details. + */ + +#include "reorder.h" + +#include + +#include + +#include + + +/* + * reorder_set() + * + * Reorders the set s with a function i -> order[i]. + * + * Note: Assumes that order is the same size as SET_MAX_SIZE(s). + */ +void reorder_set(set_t s,int *order) { + set_t tmp; + int i,j; + setelement e; + + ASSERT(reorder_is_bijection(order,SET_MAX_SIZE(s))); + + tmp=set_new(SET_MAX_SIZE(s)); + + for (i=0; i<(SET_MAX_SIZE(s)/ELEMENTSIZE); i++) { + e=s[i]; + if (e==0) + continue; + for (j=0; j>1; + } + } + if (SET_MAX_SIZE(s)%ELEMENTSIZE) { + e=s[i]; + for (j=0; j<(SET_MAX_SIZE(s)%ELEMENTSIZE); j++) { + if (e&1) { + SET_ADD_ELEMENT(tmp,order[i*ELEMENTSIZE+j]); + } + e = e>>1; + } + } + set_copy(s,tmp); + set_free(tmp); + return; +} + + +/* + * reorder_graph() + * + * Reorders the vertices in the graph with function i -> order[i]. + * + * Note: Assumes that order is of size g->n. + */ +void reorder_graph(graph_t *g, int *order) { + int i; + set_t *tmp_e; + int *tmp_w; + + ASSERT(reorder_is_bijection(order,g->n)); + + tmp_e=malloc(g->n * sizeof(set_t)); + tmp_w=malloc(g->n * sizeof(int)); + for (i=0; in; i++) { + reorder_set(g->edges[i],order); + tmp_e[order[i]]=g->edges[i]; + tmp_w[order[i]]=g->weights[i]; + } + for (i=0; in; i++) { + g->edges[i]=tmp_e[i]; + g->weights[i]=tmp_w[i]; + } + free(tmp_e); + free(tmp_w); + return; +} + + + +/* + * reorder_duplicate() + * + * Returns a newly allocated duplicate of the given ordering. + */ +int *reorder_duplicate(int *order,int n) { + int *new; + + new=malloc(n*sizeof(int)); + memcpy(new,order,n*sizeof(int)); + return new; +} + +/* + * reorder_invert() + * + * Inverts the given ordering so that new[old[i]]==i. + * + * Note: Asserts that order is a bijection. + */ +void reorder_invert(int *order,int n) { + int *new; + int i; + + ASSERT(reorder_is_bijection(order,n)); + + new=malloc(n*sizeof(int)); + for (i=0; i {0,...,n-1}. + * + * Returns TRUE if it is a bijection, FALSE otherwise. + */ +boolean reorder_is_bijection(int *order,int n) { + boolean *used; + int i; + + used=calloc(n,sizeof(boolean)); + for (i=0; i=n) { + free(used); + return FALSE; + } + if (used[order[i]]) { + free(used); + return FALSE; + } + used[order[i]]=TRUE; + } + for (i=0; in); +} + +/* + * reorder_by_reverse() + * + * Returns a reverse identity ordering. + */ +int *reorder_by_reverse(graph_t *g,boolean weighted) { + int i; + int *order; + + order=malloc(g->n * sizeof(int)); + for (i=0; i < g->n; i++) + order[i]=g->n-i-1; + return order; +} + +/* + * reorder_by_greedy_coloring() + * + * Equivalent to reorder_by_weighted_greedy_coloring or + * reorder_by_unweighted_greedy_coloring according to the value of weighted. + */ +int *reorder_by_greedy_coloring(graph_t *g,boolean weighted) { + if (weighted) + return reorder_by_weighted_greedy_coloring(g,weighted); + else + return reorder_by_unweighted_greedy_coloring(g,weighted); +} + + +/* + * reorder_by_unweighted_greedy_coloring() + * + * Returns an ordering for the graph g by coloring the clique one + * color at a time, always adding the vertex of largest degree within + * the uncolored graph, and numbering these vertices 0, 1, ... + * + * Experimentally efficient for use with unweighted graphs. + */ +int *reorder_by_unweighted_greedy_coloring(graph_t *g,boolean weighted) { + int i,j,v; + boolean *tmp_used; + int *degree; /* -1 for used vertices */ + int *order; + int maxdegree,maxvertex=0; + boolean samecolor; + + tmp_used=calloc(g->n,sizeof(boolean)); + degree=calloc(g->n,sizeof(int)); + order=calloc(g->n,sizeof(int)); + + for (i=0; i < g->n; i++) { + for (j=0; j < g->n; j++) { + ASSERT(!((i==j) && GRAPH_IS_EDGE(g,i,j))); + if (GRAPH_IS_EDGE(g,i,j)) + degree[i]++; + } + } + + v=0; + while (v < g->n) { + /* Reset tmp_used. */ + memset(tmp_used,0,g->n * sizeof(boolean)); + + do { + /* Find vertex to be colored. */ + maxdegree=0; + samecolor=FALSE; + for (i=0; i < g->n; i++) { + if (!tmp_used[i] && degree[i] >= maxdegree) { + maxvertex=i; + maxdegree=degree[i]; + samecolor=TRUE; + } + } + if (samecolor) { + order[v]=maxvertex; + degree[maxvertex]=-1; + v++; + + /* Mark neighbors not to color with same + * color and update neighbor degrees. */ + for (i=0; i < g->n; i++) { + if (GRAPH_IS_EDGE(g,maxvertex,i)) { + tmp_used[i]=TRUE; + degree[i]--; + } + } + } + } while (samecolor); + } + + free(tmp_used); + free(degree); + return order; +} + +/* + * reorder_by_weighted_greedy_coloring() + * + * Returns an ordering for the graph g by coloring the clique one + * color at a time, always adding the vertex that (in order of importance): + * 1. has the minimum weight in the remaining graph + * 2. has the largest sum of weights surrounding the vertex + * + * Experimentally efficient for use with weighted graphs. + */ +int *reorder_by_weighted_greedy_coloring(graph_t *g, boolean weighted) { + int i,j,p=0; + int cnt; + int *nwt; /* Sum of surrounding vertices' weights */ + int min_wt,max_nwt; + boolean *used; + int *order; + + nwt=malloc(g->n * sizeof(int)); + order=malloc(g->n * sizeof(int)); + used=calloc(g->n,sizeof(boolean)); + + for (i=0; i < g->n; i++) { + nwt[i]=0; + for (j=0; j < g->n; j++) + if (GRAPH_IS_EDGE(g, i, j)) + nwt[i] += g->weights[j]; + } + + for (cnt=0; cnt < g->n; cnt++) { + min_wt=INT_MAX; + max_nwt=-1; + for (i=g->n-1; i>=0; i--) + if ((!used[i]) && (g->weights[i] < min_wt)) + min_wt=g->weights[i]; + for (i=g->n-1; i>=0; i--) { + if (used[i] || (g->weights[i] > min_wt)) + continue; + if (nwt[i] > max_nwt) { + max_nwt=nwt[i]; + p=i; + } + } + order[cnt]=p; + used[p]=TRUE; + for (j=0; j < g->n; j++) + if ((!used[j]) && (GRAPH_IS_EDGE(g, p, j))) + nwt[j] -= g->weights[p]; + } + + free(nwt); + free(used); + + ASSERT(reorder_is_bijection(order,g->n)); + + return order; +} + +/* + * reorder_by_degree() + * + * Returns a reordering of the graph g so that the vertices with largest + * degrees (most neighbors) are first. + */ +int *reorder_by_degree(graph_t *g, boolean weighted) { + int i,j,v; + int *degree; + int *order; + int maxdegree,maxvertex=0; + + degree=calloc(g->n,sizeof(int)); + order=calloc(g->n,sizeof(int)); + + for (i=0; i < g->n; i++) { + for (j=0; j < g->n; j++) { + ASSERT(!((i==j) && GRAPH_IS_EDGE(g,i,j))); + if (GRAPH_IS_EDGE(g,i,j)) + degree[i]++; + } + } + + for (v=0; v < g->n; v++) { + maxdegree=0; + for (i=0; i < g->n; i++) { + if (degree[i] >= maxdegree) { + maxvertex=i; + maxdegree=degree[i]; + } + } + order[v]=maxvertex; + degree[maxvertex]=-1; /* used */ +/*** Max. degree withing unselected graph: + for (i=0; i < g->n; i++) { + if (GRAPH_IS_EDGE(g,maxvertex,i)) + degree[i]--; + } +***/ + } + + free(degree); + return order; +} + +/* + * reorder_by_random() + * + * Returns a random reordering for graph g. + * Note: Used the functions rand() and srand() to generate the random + * numbers. srand() is re-initialized every time reorder_by_random() + * is called using the system time. + */ +int *reorder_by_random(graph_t *g, boolean weighted) { + int i,r; + int *new; + boolean *used; + + new=calloc(g->n, sizeof(int)); + used=calloc(g->n, sizeof(boolean)); + for (i=0; i < g->n; i++) { + do { + r = igraph_rng_get_integer(igraph_rng_default(), 0, g->n - 1); + } while (used[r]); + new[i]=r; + used[r]=TRUE; + } + free(used); + return new; +} diff --git a/src/rigraph/core/cliques/cliquer/reorder.h b/src/rigraph/core/cliques/cliquer/reorder.h new file mode 100644 index 0000000..5c06d31 --- /dev/null +++ b/src/rigraph/core/cliques/cliquer/reorder.h @@ -0,0 +1,26 @@ + +#ifndef CLIQUER_REORDER_H +#define CLIQUER_REORDER_H + +#include "set.h" +#include "graph.h" + +extern void reorder_set(set_t s,int *order); +extern void reorder_graph(graph_t *g, int *order); +extern int *reorder_duplicate(int *order,int n); +extern void reorder_invert(int *order,int n); +extern void reorder_reverse(int *order,int n); +extern int *reorder_ident(int n); +extern boolean reorder_is_bijection(int *order,int n); + + +#define reorder_by_default reorder_by_greedy_coloring +extern int *reorder_by_greedy_coloring(graph_t *g, boolean weighted); +extern int *reorder_by_weighted_greedy_coloring(graph_t *g, boolean weighted); +extern int *reorder_by_unweighted_greedy_coloring(graph_t *g,boolean weighted); +extern int *reorder_by_degree(graph_t *g, boolean weighted); +extern int *reorder_by_random(graph_t *g, boolean weighted); +extern int *reorder_by_ident(graph_t *g, boolean weighted); +extern int *reorder_by_reverse(graph_t *g, boolean weighted); + +#endif /* !CLIQUER_REORDER_H */ diff --git a/src/rigraph/core/cliques/cliquer/set.h b/src/rigraph/core/cliques/cliquer/set.h new file mode 100644 index 0000000..9727d30 --- /dev/null +++ b/src/rigraph/core/cliques/cliquer/set.h @@ -0,0 +1,386 @@ + +/* + * This file contains the set handling routines. + * + * Copyright (C) 2002 Sampo Niskanen, Patric Östergård. + * Licensed under the GNU GPL, read the file LICENSE for details. + */ + +#ifndef CLIQUER_SET_H +#define CLIQUER_SET_H + +#include +#include +#include +#include +#include "misc.h" + +/* + * Sets are arrays of setelement's (typically unsigned long int's) with + * representative bits for each value they can contain. The values + * are numbered 0,...,n-1. + */ + + +/*** Variable types and constants. ***/ + + +/* + * If setelement hasn't been declared: + * - use "unsigned long int" as setelement + * - try to deduce size from ULONG_MAX + */ + +#ifndef ELEMENTSIZE +typedef unsigned long int setelement; +# if (ULONG_MAX == 65535) +# define ELEMENTSIZE 16 +# elif (ULONG_MAX == 4294967295) +# define ELEMENTSIZE 32 +# else +# define ELEMENTSIZE 64 +# endif +#endif /* !ELEMENTSIZE */ + +typedef setelement * set_t; + + +/*** Counting amount of 1 bits in a setelement ***/ + +/* Array for amount of 1 bits in a byte. */ +static int set_bit_count[256] = { + 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 }; + +/* The following macros assume that all higher bits are 0. + * They may in some cases be useful also on with other ELEMENTSIZE's, + * so we define them all. */ +#define SET_ELEMENT_BIT_COUNT_8(a) (set_bit_count[(a)]) +#define SET_ELEMENT_BIT_COUNT_16(a) (set_bit_count[(a)>>8] + \ + set_bit_count[(a)&0xFF]) +#define SET_ELEMENT_BIT_COUNT_32(a) (set_bit_count[(a)>>24] + \ + set_bit_count[((a)>>16)&0xFF] + \ + set_bit_count[((a)>>8)&0xFF] + \ + set_bit_count[(a)&0xFF]) +#define SET_ELEMENT_BIT_COUNT_64(a) (set_bit_count[(a)>>56] + \ + set_bit_count[((a)>>48)&0xFF] + \ + set_bit_count[((a)>>40)&0xFF] + \ + set_bit_count[((a)>>32)&0xFF] + \ + set_bit_count[((a)>>24)&0xFF] + \ + set_bit_count[((a)>>16)&0xFF] + \ + set_bit_count[((a)>>8)&0xFF] + \ + set_bit_count[(a)&0xFF]) +#if (ELEMENTSIZE==64) +# define SET_ELEMENT_BIT_COUNT(a) SET_ELEMENT_BIT_COUNT_64(a) +# define FULL_ELEMENT ((setelement)0xFFFFFFFFFFFFFFFF) +#elif (ELEMENTSIZE==32) +# define SET_ELEMENT_BIT_COUNT(a) SET_ELEMENT_BIT_COUNT_32(a) +# define FULL_ELEMENT ((setelement)0xFFFFFFFF) +#elif (ELEMENTSIZE==16) +# define SET_ELEMENT_BIT_COUNT(a) SET_ELEMENT_BIT_COUNT_16(a) +# define FULL_ELEMENT ((setelement)0xFFFF) +#else +# error "SET_ELEMENT_BIT_COUNT(a) not defined for current ELEMENTSIZE" +#endif + + + +/*** Macros and functions ***/ + +/* + * Gives a value with bit x (counting from lsb up) set. + * + * Making this as a table might speed up things on some machines + * (though on most modern machines it's faster to shift instead of + * using memory). Making it a macro makes it easy to change. + */ +#define SET_BIT_MASK(x) ((setelement)1<<(x)) + + + +/* Set element handling macros */ + +#define SET_ELEMENT_INTERSECT(a,b) ((a)&(b)) +#define SET_ELEMENT_UNION(a,b) ((a)|(b)) +#define SET_ELEMENT_DIFFERENCE(a,b) ((a)&(~(b))) +#define SET_ELEMENT_CONTAINS(e,v) ((e)&SET_BIT_MASK(v)) + + +/* Set handling macros */ + +#define SET_ADD_ELEMENT(s,a) \ + ((s)[(a)/ELEMENTSIZE] |= SET_BIT_MASK((a)%ELEMENTSIZE)) +#define SET_DEL_ELEMENT(s,a) \ + ((s)[(a)/ELEMENTSIZE] &= ~SET_BIT_MASK((a)%ELEMENTSIZE)) +#define SET_CONTAINS_FAST(s,a) (SET_ELEMENT_CONTAINS((s)[(a)/ELEMENTSIZE], \ + (a)%ELEMENTSIZE)) +#define SET_CONTAINS(s,a) (((a)0); + + n=(size/ELEMENTSIZE+1)+1; + s=calloc(n,sizeof(setelement)); + s[0]=size; + + return &(s[1]); +} + +/* + * set_free() + * + * Free the memory associated with set s. + */ +UNUSED_FUNCTION INLINE +static void set_free(set_t s) { + ASSERT(s!=NULL); + free(&(s[-1])); +} + +/* + * set_resize() + * + * Resizes set s to given size. If the size is less than SET_MAX_SIZE(s), + * the last elements are dropped. + * + * Returns a pointer to the new set. + */ +UNUSED_FUNCTION INLINE +static set_t set_resize(set_t s, unsigned int size) { + unsigned int n; + + ASSERT(size>0); + + n=(size/ELEMENTSIZE+1); + s=((setelement *)realloc(s-1,(n+1)*sizeof(setelement)))+1; + + if (n>SET_ARRAY_LENGTH(s)) + memset(s+SET_ARRAY_LENGTH(s),0, + (n-SET_ARRAY_LENGTH(s))*sizeof(setelement)); + if (size < SET_MAX_SIZE(s)) + s[(size-1)/ELEMENTSIZE] &= (FULL_ELEMENT >> + (ELEMENTSIZE-size%ELEMENTSIZE)); + s[-1]=size; + + return s; +} + +/* + * set_size() + * + * Returns the number of elements in set s. + */ +UNUSED_FUNCTION INLINE +static int set_size(set_t s) { + int count=0; + setelement *c; + + for (c=s; c < s+SET_ARRAY_LENGTH(s); c++) + count+=SET_ELEMENT_BIT_COUNT(*c); + return count; +} + +/* + * set_duplicate() + * + * Returns a newly allocated duplicate of set s. + */ +UNUSED_FUNCTION INLINE +static set_t set_duplicate(set_t s) { + set_t new; + + new=set_new(SET_MAX_SIZE(s)); + memcpy(new,s,SET_ARRAY_LENGTH(s)*sizeof(setelement)); + return new; +} + +/* + * set_copy() + * + * Copies set src to dest. If dest is NULL, is equal to set_duplicate. + * If dest smaller than src, it is freed and a new set of the same size as + * src is returned. + */ +UNUSED_FUNCTION INLINE +static set_t set_copy(set_t dest,set_t src) { + if (dest==NULL) + return set_duplicate(src); + if (SET_MAX_SIZE(dest)=0) { + * // i is in set s + * } + */ +UNUSED_FUNCTION INLINE +static int set_return_next(set_t s, unsigned int n) { + n++; + if (n >= SET_MAX_SIZE(s)) + return -1; + + while (n%ELEMENTSIZE) { + if (SET_CONTAINS(s,n)) + return n; + n++; + if (n >= SET_MAX_SIZE(s)) + return -1; + } + + while (s[n/ELEMENTSIZE]==0) { + n+=ELEMENTSIZE; + if (n >= SET_MAX_SIZE(s)) + return -1; + } + while (!SET_CONTAINS(s,n)) { + n++; + if (n >= SET_MAX_SIZE(s)) + return -1; + } + return n; +} + + +/* + * set_print() + * + * Prints the size and contents of set s to stdout. + * Mainly useful for debugging purposes and trivial output. + */ +/* +UNUSED_FUNCTION +static void set_print(set_t s) { + int i; + printf("size=%d(max %d)",set_size(s),(int)SET_MAX_SIZE(s)); + for (i=0; in) { + IGRAPH_ERROR("Invalid vertex weight vector length", IGRAPH_EINVAL); + } + + for (i = 0; i < g->n; ++i) { + g->weights[i] = VECTOR(*vertex_weights)[i]; + if (g->weights[i] != VECTOR(*vertex_weights)[i]) { + IGRAPH_WARNING("Only integer vertex weights are supported; weights will be truncated to their integer parts"); + } + if (g->weights[i] <= 0) { + IGRAPH_ERROR("Vertex weights must be positive", IGRAPH_EINVAL); + } + } + + return IGRAPH_SUCCESS; +} + + +/* Find all cliques. */ + +static boolean collect_cliques_callback(set_t s, graph_t *g, clique_options *opt) { + igraph_vector_ptr_t *list; + igraph_vector_t *clique; + int i, j; + + IGRAPH_UNUSED(g); + + CLIQUER_ALLOW_INTERRUPTION(); + + list = (igraph_vector_ptr_t *) opt->user_data; + clique = (igraph_vector_t *) malloc(sizeof(igraph_vector_t)); + igraph_vector_init(clique, set_size(s)); + + i = -1; j = 0; + while ((i = set_return_next(s, i)) >= 0) { + VECTOR(*clique)[j++] = i; + } + + igraph_vector_ptr_push_back(list, clique); + + return TRUE; +} + +int igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, + igraph_integer_t min_size, igraph_integer_t max_size) { + graph_t *g; + igraph_integer_t vcount = igraph_vcount(graph); + + if (vcount == 0) { + igraph_vector_ptr_clear(res); + return IGRAPH_SUCCESS; + } + + if (min_size <= 0) { + min_size = 1; + } + if (max_size <= 0) { + max_size = 0; + } + + if (max_size > 0 && max_size < min_size) { + IGRAPH_ERROR("max_size must not be smaller than min_size", IGRAPH_EINVAL); + } + + igraph_to_cliquer(graph, &g); + IGRAPH_FINALLY(graph_free, g); + + igraph_vector_ptr_clear(res); + igraph_cliquer_opt.user_data = res; + igraph_cliquer_opt.user_function = &collect_cliques_callback; + + IGRAPH_FINALLY(free_clique_list, res); + CLIQUER_INTERRUPTABLE(clique_unweighted_find_all(g, min_size, max_size, /* maximal= */ FALSE, &igraph_cliquer_opt)); + IGRAPH_FINALLY_CLEAN(1); + + graph_free(g); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/* Count cliques of each size. */ + +static boolean count_cliques_callback(set_t s, graph_t *g, clique_options *opt) { + igraph_vector_t *hist; + + IGRAPH_UNUSED(g); + + CLIQUER_ALLOW_INTERRUPTION(); + + hist = (igraph_vector_t *) opt->user_data; + VECTOR(*hist)[set_size(s) - 1] += 1; + + return TRUE; +} + +int igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist, + igraph_integer_t min_size, igraph_integer_t max_size) { + graph_t *g; + int i; + igraph_integer_t vcount = igraph_vcount(graph); + + if (vcount == 0) { + igraph_vector_clear(hist); + return IGRAPH_SUCCESS; + } + + if (min_size <= 0) { + min_size = 1; + } + if (max_size <= 0) { + max_size = vcount; /* also used for initial hist vector size, do not set to zero */ + } + + if (max_size < min_size) { + IGRAPH_ERRORF("Maximum clique size (%" IGRAPH_PRId ") must not be " + "smaller than minimum clique size (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, max_size, min_size); + } + + igraph_to_cliquer(graph, &g); + IGRAPH_FINALLY(graph_free, g); + + igraph_vector_resize(hist, max_size); + igraph_vector_null(hist); + igraph_cliquer_opt.user_data = hist; + igraph_cliquer_opt.user_function = &count_cliques_callback; + + CLIQUER_INTERRUPTABLE(clique_unweighted_find_all(g, min_size, max_size, /* maximal= */ FALSE, &igraph_cliquer_opt)); + + for (i = max_size; i > 0; --i) + if (VECTOR(*hist)[i - 1] > 0) { + break; + } + igraph_vector_resize(hist, i); + igraph_vector_resize_min(hist); + + graph_free(g); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/* Call function for each clique. */ + +struct callback_data { + igraph_clique_handler_t *handler; + void *arg; +}; + +static boolean callback_callback(set_t s, graph_t *g, clique_options *opt) { + igraph_vector_t *clique; + struct callback_data *cd; + int i, j; + + IGRAPH_UNUSED(g); + + CLIQUER_ALLOW_INTERRUPTION(); + + cd = (struct callback_data *) opt->user_data; + + clique = (igraph_vector_t *) malloc(sizeof(igraph_vector_t)); + igraph_vector_init(clique, set_size(s)); + + i = -1; j = 0; + while ((i = set_return_next(s, i)) >= 0) { + VECTOR(*clique)[j++] = i; + } + + return (*(cd->handler))(clique, cd->arg); +} + +int igraph_i_cliquer_callback(const igraph_t *graph, + igraph_integer_t min_size, igraph_integer_t max_size, + igraph_clique_handler_t *cliquehandler_fn, void *arg) { + graph_t *g; + struct callback_data cd; + igraph_integer_t vcount = igraph_vcount(graph); + + if (vcount == 0) { + return IGRAPH_SUCCESS; + } + + if (min_size <= 0) { + min_size = 1; + } + if (max_size <= 0) { + max_size = 0; + } + + if (max_size > 0 && max_size < min_size) { + IGRAPH_ERROR("max_size must not be smaller than min_size", IGRAPH_EINVAL); + } + + igraph_to_cliquer(graph, &g); + IGRAPH_FINALLY(graph_free, g); + + cd.handler = cliquehandler_fn; + cd.arg = arg; + igraph_cliquer_opt.user_data = &cd; + igraph_cliquer_opt.user_function = &callback_callback; + + CLIQUER_INTERRUPTABLE(clique_unweighted_find_all(g, min_size, max_size, /* maximal= */ FALSE, &igraph_cliquer_opt)); + + graph_free(g); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/* Find weighted cliques in given weight range. */ + +int igraph_i_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res, + igraph_real_t min_weight, igraph_real_t max_weight, igraph_bool_t maximal) { + graph_t *g; + igraph_integer_t vcount = igraph_vcount(graph); + + if (vcount == 0) { + igraph_vector_ptr_clear(res); + return IGRAPH_SUCCESS; + } + + if (min_weight != (int) min_weight) { + IGRAPH_WARNING("Only integer vertex weights are supported; the minimum weight will be truncated to its integer part"); + min_weight = (int) min_weight; + } + + if (max_weight != (int) max_weight) { + IGRAPH_WARNING("Only integer vertex weights are supported; the maximum weight will be truncated to its integer part"); + max_weight = (int) max_weight; + } + + if (min_weight <= 0) { + min_weight = 1; + } + if (max_weight <= 0) { + max_weight = 0; + } + + if (max_weight > 0 && max_weight < min_weight) { + IGRAPH_ERROR("max_weight must not be smaller than min_weight", IGRAPH_EINVAL); + } + + igraph_to_cliquer(graph, &g); + IGRAPH_FINALLY(graph_free, g); + + IGRAPH_CHECK(set_weights(vertex_weights, g)); + + igraph_vector_ptr_clear(res); + igraph_cliquer_opt.user_data = res; + igraph_cliquer_opt.user_function = &collect_cliques_callback; + + IGRAPH_FINALLY(free_clique_list, res); + CLIQUER_INTERRUPTABLE(clique_find_all(g, min_weight, max_weight, maximal, &igraph_cliquer_opt)); + IGRAPH_FINALLY_CLEAN(1); + + graph_free(g); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/* Find largest weighted cliques. */ + +int igraph_i_largest_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res) { + graph_t *g; + igraph_integer_t vcount = igraph_vcount(graph); + + if (vcount == 0) { + igraph_vector_ptr_clear(res); + return IGRAPH_SUCCESS; + } + + igraph_to_cliquer(graph, &g); + IGRAPH_FINALLY(graph_free, g); + + IGRAPH_CHECK(set_weights(vertex_weights, g)); + + igraph_vector_ptr_clear(res); + igraph_cliquer_opt.user_data = res; + igraph_cliquer_opt.user_function = &collect_cliques_callback; + + IGRAPH_FINALLY(free_clique_list, res); + CLIQUER_INTERRUPTABLE(clique_find_all(g, 0, 0, FALSE, &igraph_cliquer_opt)); + IGRAPH_FINALLY_CLEAN(1); + + graph_free(g); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/* Find weight of largest weight clique. */ + +int igraph_i_weighted_clique_number(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_real_t *res) { + graph_t *g; + igraph_integer_t vcount = igraph_vcount(graph); + + if (vcount == 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + + igraph_to_cliquer(graph, &g); + IGRAPH_FINALLY(graph_free, g); + + IGRAPH_CHECK(set_weights(vertex_weights, g)); + + igraph_cliquer_opt.user_function = NULL; + + /* we are not using a callback function, thus this is not interruptable */ + *res = clique_max_weight(g, &igraph_cliquer_opt); + + graph_free(g); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/cliques/cliques.c b/src/rigraph/core/cliques/cliques.c new file mode 100644 index 0000000..bbb950f --- /dev/null +++ b/src/rigraph/core/cliques/cliques.c @@ -0,0 +1,1139 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_cliques.h" + +#include "igraph_error.h" +#include "igraph_memory.h" +#include "igraph_constants.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_stack.h" + +#include "cliquer_internal.h" +#include "../core/interruption.h" +#include "../core/set.h" + +#include /* memset */ + +static void igraph_i_cliques_free_res(igraph_vector_ptr_t *res) { + long i, n; + + n = igraph_vector_ptr_size(res); + for (i = 0; i < n; i++) { + if (VECTOR(*res)[i] != 0) { + igraph_vector_destroy(VECTOR(*res)[i]); + igraph_free(VECTOR(*res)[i]); + } + } + igraph_vector_ptr_clear(res); +} + +static int igraph_i_find_k_cliques( + const igraph_t *graph, + long int size, + const igraph_real_t *member_storage, + igraph_real_t **new_member_storage, + long int old_clique_count, + long int *clique_count, + igraph_vector_t *neis, + igraph_bool_t independent_vertices) { + + long int j, k, l, m, n, new_member_storage_size; + const igraph_real_t *c1, *c2; + igraph_real_t v1, v2; + igraph_bool_t ok; + + /* Allocate the storage */ + *new_member_storage = IGRAPH_REALLOC(*new_member_storage, + (size_t) (size * old_clique_count), + igraph_real_t); + if (*new_member_storage == 0) { + IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM); + } + new_member_storage_size = size * old_clique_count; + IGRAPH_FINALLY(igraph_free, *new_member_storage); + + m = n = 0; + + /* Now consider all pairs of i-1-cliques and see if they can be merged */ + for (j = 0; j < old_clique_count; j++) { + for (k = j + 1; k < old_clique_count; k++) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* Since cliques are represented by their vertex indices in increasing + * order, two cliques can be merged iff they have exactly the same + * indices excluding one AND there is an edge between the two different + * vertices */ + c1 = member_storage + j * (size - 1); + c2 = member_storage + k * (size - 1); + /* Find the longest prefixes of c1 and c2 that are equal */ + for (l = 0; l < size - 1 && c1[l] == c2[l]; l++) { + (*new_member_storage)[m++] = c1[l]; + } + /* Now, if l == size-1, the two vectors are totally equal. + This is a bug */ + if (l == size - 1) { + IGRAPH_WARNING("possible bug in igraph_cliques"); + m = n; + } else { + /* Assuming that j (*new_member_storage)[m - 1]) { + (*new_member_storage)[m++] = v2; + n = m; + } else { + m = n; + } + } else { + m = n; + } + } + /* See if new_member_storage is full. If so, reallocate */ + if (m == new_member_storage_size) { + IGRAPH_FINALLY_CLEAN(1); + *new_member_storage = IGRAPH_REALLOC(*new_member_storage, + (size_t) new_member_storage_size * 2, + igraph_real_t); + if (*new_member_storage == 0) { + IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM); + } + new_member_storage_size *= 2; + IGRAPH_FINALLY(igraph_free, *new_member_storage); + } + } + } + } + + /* Calculate how many cliques we have found */ + *clique_count = n / size; + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/* Internal function for calculating cliques or independent vertex sets. + * They are practically the same except that the complementer of the graph + * should be used in the latter case. + */ +static int igraph_i_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, + igraph_integer_t min_size, igraph_integer_t max_size, + igraph_bool_t independent_vertices) { + + igraph_integer_t no_of_nodes; + igraph_vector_t neis; + igraph_real_t *member_storage = 0, *new_member_storage, *c1; + long int i, j, k, clique_count, old_clique_count; + + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); + } + + no_of_nodes = igraph_vcount(graph); + + if (min_size < 0) { + min_size = 0; + } + if (max_size > no_of_nodes || max_size <= 0) { + max_size = no_of_nodes; + } + + igraph_vector_ptr_clear(res); + + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_FINALLY(igraph_i_cliques_free_res, res); + + /* Will be resized later, if needed. */ + member_storage = IGRAPH_CALLOC(1, igraph_real_t); + if (member_storage == 0) { + IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, member_storage); + + /* Find all 1-cliques: every vertex will be a clique */ + new_member_storage = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + if (new_member_storage == 0) { + IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, new_member_storage); + + for (i = 0; i < no_of_nodes; i++) { + new_member_storage[i] = i; + } + clique_count = no_of_nodes; + old_clique_count = 0; + + /* Add size 1 cliques if requested */ + if (min_size <= 1) { + IGRAPH_CHECK(igraph_vector_ptr_resize(res, no_of_nodes)); + igraph_vector_ptr_null(res); + for (i = 0; i < no_of_nodes; i++) { + igraph_vector_t *p = IGRAPH_CALLOC(1, igraph_vector_t); + if (p == 0) { + IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, p); + IGRAPH_CHECK(igraph_vector_init(p, 1)); + VECTOR(*p)[0] = i; + VECTOR(*res)[i] = p; + IGRAPH_FINALLY_CLEAN(1); + } + } + + for (i = 2; i <= max_size && clique_count > 1; i++) { + + /* Here new_member_storage contains the cliques found in the previous + iteration. Save this into member_storage, might be needed later */ + + c1 = member_storage; + member_storage = new_member_storage; + new_member_storage = c1; + old_clique_count = clique_count; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Calculate the cliques */ + + IGRAPH_FINALLY_CLEAN(2); + IGRAPH_CHECK(igraph_i_find_k_cliques(graph, i, member_storage, + &new_member_storage, + old_clique_count, + &clique_count, + &neis, + independent_vertices)); + IGRAPH_FINALLY(igraph_free, member_storage); + IGRAPH_FINALLY(igraph_free, new_member_storage); + + /* Add the cliques just found to the result if requested */ + if (i >= min_size && i <= max_size) { + for (j = 0, k = 0; j < clique_count; j++, k += i) { + igraph_vector_t *p = IGRAPH_CALLOC(1, igraph_vector_t); + if (p == 0) { + IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, p); + IGRAPH_CHECK(igraph_vector_init_copy(p, &new_member_storage[k], i)); + IGRAPH_FINALLY(igraph_vector_destroy, p); + IGRAPH_CHECK(igraph_vector_ptr_push_back(res, p)); + IGRAPH_FINALLY_CLEAN(2); + } + } + + } /* i <= max_size && clique_count != 0 */ + + igraph_free(member_storage); + igraph_free(new_member_storage); + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(4); /* 3 here, +1 is igraph_i_cliques_free_res */ + + return 0; +} + +/** + * \function igraph_cliques + * \brief Finds all or some cliques in a graph. + * + * + * Cliques are fully connected subgraphs of a graph. + * + * + * If you are only interested in the size of the largest clique in the graph, + * use \ref igraph_clique_number() instead. + * + * The current implementation of this function + * uses version 1.21 of the Cliquer library by Sampo Niskanen and + * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html + * + * \param graph The input graph. + * \param res Pointer to a pointer vector, the result will be stored + * here, i.e. \p res will contain pointers to \ref igraph_vector_t + * objects which contain the indices of vertices involved in a clique. + * The pointer vector will be resized if needed but note that the + * objects in the pointer vector will not be freed. + * \param min_size Integer giving the minimum size of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer giving the maximum size of the cliques to be + * returned. If negative or zero, no upper bound will be used. + * \return Error code. + * + * \sa \ref igraph_largest_cliques() and \ref igraph_clique_number(). + * + * Time complexity: Exponential + * + * \example examples/simple/igraph_cliques.c + */ +int igraph_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, + igraph_integer_t min_size, igraph_integer_t max_size) { + return igraph_i_cliquer_cliques(graph, res, min_size, max_size); +} + + +/** + * \function igraph_clique_size_hist + * \brief Counts cliques of each size in the graph. + * + * + * Cliques are fully connected subgraphs of a graph. + * + * The current implementation of this function + * uses version 1.21 of the Cliquer library by Sampo Niskanen and + * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html + * + * \param graph The input graph. + * \param hist Pointer to an initialized vector. The result will be stored + * here. The first element will store the number of size-1 cliques, the second + * element the number of size-2 cliques, etc. For cliques smaller than \p min_size, + * zero counts will be returned. + * \param min_size Integer giving the minimum size of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer giving the maximum size of the cliques to be + * returned. If negative or zero, no upper bound will be used. + * \return Error code. + * + * \sa \ref igraph_cliques() and \ref igraph_cliques_callback() + * + * Time complexity: Exponential + * + */ +int igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist, + igraph_integer_t min_size, igraph_integer_t max_size) { + return igraph_i_cliquer_histogram(graph, hist, min_size, max_size); +} + + +/** + * \function igraph_cliques_callback + * \brief Calls a function for each clique in the graph. + * + * + * Cliques are fully connected subgraphs of a graph. This function + * enumerates all cliques within the given size range and calls + * \p cliquehandler_fn for each of them. The cliques are passed to the + * callback function as a pointer to an \ref igraph_vector_t. Destroying and + * freeing this vector is left up to the user. Use \ref igraph_vector_destroy() + * to destroy it first, then free it using \ref igraph_free(). + * + * The current implementation of this function + * uses version 1.21 of the Cliquer library by Sampo Niskanen and + * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html + * + * \param graph The input graph. + * \param min_size Integer giving the minimum size of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer giving the maximum size of the cliques to be + * returned. If negative or zero, no upper bound will be used. + * \param cliquehandler_fn Callback function to be called for each clique. + * See also \ref igraph_clique_handler_t. + * \param arg Extra argument to supply to \p cliquehandler_fn. + * \return Error code. + * + * \sa \ref igraph_cliques() + * + * Time complexity: Exponential + * + */ +int igraph_cliques_callback(const igraph_t *graph, + igraph_integer_t min_size, igraph_integer_t max_size, + igraph_clique_handler_t *cliquehandler_fn, void *arg) { + return igraph_i_cliquer_callback(graph, min_size, max_size, cliquehandler_fn, arg); +} + + +/** + * \function igraph_weighted_cliques + * \brief Finds all cliques in a given weight range in a vertex weighted graph. + * + * + * Cliques are fully connected subgraphs of a graph. + * The weight of a clique is the sum of the weights + * of individual vertices within the clique. + * + * The current implementation of this function + * uses version 1.21 of the Cliquer library by Sampo Niskanen and + * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html + * + * Only positive integer vertex weights are supported. + * + * \param graph The input graph. + * \param vertex_weights A vector of vertex weights. The current implementation + * will truncate all weights to their integer parts. You may pass \c NULL + * here to make each vertex have a weight of 1. + * \param res Pointer to a pointer vector, the result will be stored + * here, i.e. \p res will contain pointers to \ref igraph_vector_t + * objects which contain the indices of vertices involved in a clique. + * The pointer vector will be resized if needed but note that the + * objects in the pointer vector will not be freed. + * \param min_weight Integer giving the minimum weight of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_weight Integer giving the maximum weight of the cliques to be + * returned. If negative or zero, no upper bound will be used. + * \param maximal If true, only maximal cliques will be returned + * \return Error code. + * + * \sa \ref igraph_cliques(), \ref igraph_maximal_cliques() + * + * Time complexity: Exponential + * + */ +int igraph_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res, + igraph_real_t min_weight, igraph_real_t max_weight, igraph_bool_t maximal) { + if (vertex_weights) { + return igraph_i_weighted_cliques(graph, vertex_weights, res, min_weight, max_weight, maximal); + } else if (maximal) { + return igraph_maximal_cliques(graph, res, min_weight, max_weight); + } else { + return igraph_cliques(graph, res, min_weight, max_weight); + } +} + + +/** + * \function igraph_largest_weighted_cliques + * \brief Finds the largest weight clique(s) in a graph. + * + * + * Finds the clique(s) having the largest weight in the graph. + * + * The current implementation of this function + * uses version 1.21 of the Cliquer library by Sampo Niskanen and + * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html + * + * Only positive integer vertex weights are supported. + * + * \param graph The input graph. + * \param vertex_weights A vector of vertex weights. The current implementation + * will truncate all weights to their integer parts. You may pass \c NULL + * here to make each vertex have a weight of 1. + * \param res Pointer to a pointer vector, the result will be stored + * here, i.e. \p res will contain pointers to \ref igraph_vector_t + * objects which contain the indices of vertices involved in a clique. + * The pointer vector will be resized if needed but note that the + * objects in the pointer vector will not be freed. + * \return Error code. + * + * \sa \ref igraph_weighted_cliques(), \ref igraph_weighted_clique_number(), \ref igraph_largest_cliques() + * + * Time complexity: TODO + */ +int igraph_largest_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res) { + if (vertex_weights) { + return igraph_i_largest_weighted_cliques(graph, vertex_weights, res); + } else { + return igraph_largest_cliques(graph, res); + } +} + + +/** + * \function igraph_weighted_clique_number + * \brief Finds the weight of the largest weight clique in the graph. + * + * The current implementation of this function + * uses version 1.21 of the Cliquer library by Sampo Niskanen and + * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html + * + * Only positive integer vertex weights are supported. + * + * \param graph The input graph. + * \param vertex_weights A vector of vertex weights. The current implementation + * will truncate all weights to their integer parts. You may pass \c NULL + * here to make each vertex have a weight of 1. + * \param res The largest weight will be returned to the \c igraph_real_t + * pointed to by this variable. + * \return Error code. + * + * \sa \ref igraph_weighted_cliques(), \ref igraph_largest_weighted_cliques(), \ref igraph_clique_number() + * + * Time complexity: TODO + * + */ +int igraph_weighted_clique_number(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_real_t *res) { + if (vertex_weights) { + return igraph_i_weighted_clique_number(graph, vertex_weights, res); + } else { + igraph_integer_t res_int; + IGRAPH_CHECK(igraph_clique_number(graph, &res_int)); + if (res) { + *res = res_int; + } + return IGRAPH_SUCCESS; + } +} + +typedef int(*igraph_i_maximal_clique_func_t)(const igraph_vector_t*, void*, igraph_bool_t*); +typedef struct { + igraph_vector_ptr_t* result; + igraph_integer_t min_size; + igraph_integer_t max_size; +} igraph_i_maximal_clique_data_t; + +static int igraph_i_maximal_or_largest_cliques_or_indsets( + const igraph_t *graph, + igraph_vector_ptr_t *res, + igraph_integer_t *clique_number, + igraph_bool_t keep_only_largest, + igraph_bool_t complementer); + +/** + * \function igraph_independent_vertex_sets + * \brief Finds all independent vertex sets in a graph. + * + * + * A vertex set is considered independent if there are no edges between + * them. + * + * + * If you are interested in the size of the largest independent vertex set, + * use \ref igraph_independence_number() instead. + * + * + * The current implementation was ported to igraph from the Very Nauty Graph + * Library by Keith Briggs and uses the algorithm from the paper + * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm + * for generating all the maximal independent sets. SIAM J Computing, + * 6:505--517, 1977. + * + * \param graph The input graph. + * \param res Pointer to a pointer vector, the result will be stored + * here, i.e. \p res will contain pointers to \ref igraph_vector_t + * objects which contain the indices of vertices involved in an independent + * vertex set. The pointer vector will be resized if needed but note that the + * objects in the pointer vector will not be freed. + * \param min_size Integer giving the minimum size of the sets to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer giving the maximum size of the sets to be + * returned. If negative or zero, no upper bound will be used. + * \return Error code. + * + * \sa \ref igraph_largest_independent_vertex_sets(), + * \ref igraph_independence_number(). + * + * Time complexity: TODO + * + * \example examples/simple/igraph_independent_sets.c + */ +int igraph_independent_vertex_sets(const igraph_t *graph, + igraph_vector_ptr_t *res, + igraph_integer_t min_size, + igraph_integer_t max_size) { + return igraph_i_cliques(graph, res, min_size, max_size, 1); +} + +/** + * \function igraph_largest_independent_vertex_sets + * \brief Finds the largest independent vertex set(s) in a graph. + * + * + * An independent vertex set is largest if there is no other + * independent vertex set with more vertices in the graph. + * + * + * The current implementation was ported to igraph from the Very Nauty Graph + * Library by Keith Briggs and uses the algorithm from the paper + * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm + * for generating all the maximal independent sets. SIAM J Computing, + * 6:505--517, 1977. + * + * \param graph The input graph. + * \param res Pointer to a pointer vector, the result will be stored + * here. It will be resized as needed. + * \return Error code. + * + * \sa \ref igraph_independent_vertex_sets(), \ref + * igraph_maximal_independent_vertex_sets(). + * + * Time complexity: TODO + */ + +int igraph_largest_independent_vertex_sets(const igraph_t *graph, + igraph_vector_ptr_t *res) { + return igraph_i_maximal_or_largest_cliques_or_indsets(graph, res, 0, 1, 0); +} + +typedef struct igraph_i_max_ind_vsets_data_t { + igraph_integer_t matrix_size; + igraph_adjlist_t adj_list; /* Adjacency list of the graph */ + igraph_vector_t deg; /* Degrees of individual nodes */ + igraph_set_t* buckets; /* Bucket array */ + /* The IS value for each node. Still to be explained :) */ + igraph_integer_t* IS; + igraph_integer_t largest_set_size; /* Size of the largest set encountered */ + igraph_bool_t keep_only_largest; /* True if we keep only the largest sets */ +} igraph_i_max_ind_vsets_data_t; + +static int igraph_i_maximal_independent_vertex_sets_backtrack( + const igraph_t *graph, + igraph_vector_ptr_t *res, + igraph_i_max_ind_vsets_data_t *clqdata, + igraph_integer_t level) { + long int v1, v2, v3, c, j, k; + igraph_vector_int_t *neis1, *neis2; + igraph_bool_t f; + igraph_integer_t j1; + long int it_state; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (level >= clqdata->matrix_size - 1) { + igraph_integer_t size = 0; + if (res) { + igraph_vector_t *vec; + vec = IGRAPH_CALLOC(1, igraph_vector_t); + if (vec == 0) { + IGRAPH_ERROR("igraph_i_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM); + } + IGRAPH_VECTOR_INIT_FINALLY(vec, 0); + for (v1 = 0; v1 < clqdata->matrix_size; v1++) + if (clqdata->IS[v1] == 0) { + IGRAPH_CHECK(igraph_vector_push_back(vec, v1)); + } + size = (igraph_integer_t) igraph_vector_size(vec); + if (!clqdata->keep_only_largest) { + IGRAPH_CHECK(igraph_vector_ptr_push_back(res, vec)); + } else { + if (size > clqdata->largest_set_size) { + /* We are keeping only the largest sets, and we've found one that's + * larger than all previous sets, so we have to clear the list */ + j = igraph_vector_ptr_size(res); + for (v1 = 0; v1 < j; v1++) { + igraph_vector_destroy(VECTOR(*res)[v1]); + free(VECTOR(*res)[v1]); + } + igraph_vector_ptr_clear(res); + IGRAPH_CHECK(igraph_vector_ptr_push_back(res, vec)); + } else if (size == clqdata->largest_set_size) { + IGRAPH_CHECK(igraph_vector_ptr_push_back(res, vec)); + } else { + igraph_vector_destroy(vec); + free(vec); + } + } + IGRAPH_FINALLY_CLEAN(1); + } else { + for (v1 = 0, size = 0; v1 < clqdata->matrix_size; v1++) + if (clqdata->IS[v1] == 0) { + size++; + } + } + if (size > clqdata->largest_set_size) { + clqdata->largest_set_size = size; + } + } else { + v1 = level + 1; + /* Count the number of vertices with an index less than v1 that have + * an IS value of zero */ + neis1 = igraph_adjlist_get(&clqdata->adj_list, v1); + c = 0; + j = 0; + while (j < VECTOR(clqdata->deg)[v1] && + (v2 = (long int) VECTOR(*neis1)[j]) <= level) { + if (clqdata->IS[v2] == 0) { + c++; + } + j++; + } + + if (c == 0) { + /* If there are no such nodes... */ + j = 0; + while (j < VECTOR(clqdata->deg)[v1] && + (v2 = (long int) VECTOR(*neis1)[j]) <= level) { + clqdata->IS[v2]++; + j++; + } + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, (igraph_integer_t) v1)); + j = 0; + while (j < VECTOR(clqdata->deg)[v1] && + (v2 = (long int) VECTOR(*neis1)[j]) <= level) { + clqdata->IS[v2]--; + j++; + } + } else { + /* If there are such nodes, store the count in the IS value of v1 */ + clqdata->IS[v1] = (igraph_integer_t) c; + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, (igraph_integer_t) v1)); + clqdata->IS[v1] = 0; + + f = 1; + j = 0; + while (j < VECTOR(clqdata->deg)[v1] && + (v2 = (long int) VECTOR(*neis1)[j]) <= level) { + if (clqdata->IS[v2] == 0) { + IGRAPH_CHECK(igraph_set_add(&clqdata->buckets[v1], + (igraph_integer_t) j)); + neis2 = igraph_adjlist_get(&clqdata->adj_list, v2); + k = 0; + while (k < VECTOR(clqdata->deg)[v2] && + (v3 = (long int) VECTOR(*neis2)[k]) <= level) { + clqdata->IS[v3]--; + if (clqdata->IS[v3] == 0) { + f = 0; + } + k++; + } + } + clqdata->IS[v2]++; + j++; + } + + if (f) { + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, (igraph_integer_t) v1)); + } + + j = 0; + while (j < VECTOR(clqdata->deg)[v1] && + (v2 = (long int) VECTOR(*neis1)[j]) <= level) { + clqdata->IS[v2]--; + j++; + } + + it_state = 0; + while (igraph_set_iterate(&clqdata->buckets[v1], &it_state, &j1)) { + j = (long)j1; + v2 = (long int) VECTOR(*neis1)[j]; + neis2 = igraph_adjlist_get(&clqdata->adj_list, v2); + k = 0; + while (k < VECTOR(clqdata->deg)[v2] && + (v3 = (long int) VECTOR(*neis2)[k]) <= level) { + clqdata->IS[v3]++; + k++; + } + } + igraph_set_clear(&clqdata->buckets[v1]); + } + } + + return 0; +} + +static void igraph_i_free_set_array(igraph_set_t* array) { + long int i = 0; + while (igraph_set_inited(array + i)) { + igraph_set_destroy(array + i); + i++; + } + IGRAPH_FREE(array); +} + +/** + * \function igraph_maximal_independent_vertex_sets + * \brief Finds all maximal independent vertex sets of a graph. + * + * + * A maximal independent vertex set is an independent vertex set which + * can't be extended any more by adding a new vertex to it. + * + * + * The algorithm used here is based on the following paper: + * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm for + * generating all the maximal independent sets. SIAM J Computing, + * 6:505--517, 1977. + * + * + * The implementation was originally written by Kevin O'Neill and modified + * by K M Briggs in the Very Nauty Graph Library. I simply re-wrote it to + * use igraph's data structures. + * + * + * If you are interested in the size of the largest independent vertex set, + * use \ref igraph_independence_number() instead. + * + * \param graph The input graph. + * \param res Pointer to a pointer vector, the result will be stored + * here, i.e. \p res will contain pointers to \ref igraph_vector_t + * objects which contain the indices of vertices involved in an independent + * vertex set. The pointer vector will be resized if needed but note that the + * objects in the pointer vector will not be freed. + * \return Error code. + * + * \sa \ref igraph_maximal_cliques(), \ref + * igraph_independence_number() + * + * Time complexity: TODO. + */ +int igraph_maximal_independent_vertex_sets(const igraph_t *graph, + igraph_vector_ptr_t *res) { + igraph_i_max_ind_vsets_data_t clqdata; + igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph), i; + + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); + } + + clqdata.matrix_size = no_of_nodes; + clqdata.keep_only_largest = 0; + + IGRAPH_CHECK(igraph_adjlist_init( + graph, &clqdata.adj_list, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE + )); + IGRAPH_FINALLY(igraph_adjlist_destroy, &clqdata.adj_list); + + clqdata.IS = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + if (clqdata.IS == 0) { + IGRAPH_ERROR("igraph_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, clqdata.IS); + + IGRAPH_VECTOR_INIT_FINALLY(&clqdata.deg, no_of_nodes); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(clqdata.deg)[i] = igraph_vector_int_size(igraph_adjlist_get(&clqdata.adj_list, i)); + } + + clqdata.buckets = IGRAPH_CALLOC(no_of_nodes + 1, igraph_set_t); + if (clqdata.buckets == 0) { + IGRAPH_ERROR("igraph_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_i_free_set_array, clqdata.buckets); + + for (i = 0; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_set_init(&clqdata.buckets[i], 0)); + } + + igraph_vector_ptr_clear(res); + + /* Do the show */ + clqdata.largest_set_size = 0; + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, &clqdata, 0)); + + /* Cleanup */ + for (i = 0; i < no_of_nodes; i++) { + igraph_set_destroy(&clqdata.buckets[i]); + } + igraph_adjlist_destroy(&clqdata.adj_list); + igraph_vector_destroy(&clqdata.deg); + igraph_free(clqdata.IS); + igraph_free(clqdata.buckets); + IGRAPH_FINALLY_CLEAN(4); + return 0; +} + +/** + * \function igraph_independence_number + * \brief Finds the independence number of the graph. + * + * + * The independence number of a graph is the cardinality of the largest + * independent vertex set. + * + * + * The current implementation was ported to igraph from the Very Nauty Graph + * Library by Keith Briggs and uses the algorithm from the paper + * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm + * for generating all the maximal independent sets. SIAM J Computing, + * 6:505--517, 1977. + * + * \param graph The input graph. + * \param no The independence number will be returned to the \c + * igraph_integer_t pointed by this variable. + * \return Error code. + * + * \sa \ref igraph_independent_vertex_sets(). + * + * Time complexity: TODO. + */ +int igraph_independence_number(const igraph_t *graph, igraph_integer_t *no) { + igraph_i_max_ind_vsets_data_t clqdata; + igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph), i; + + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); + } + + clqdata.matrix_size = no_of_nodes; + clqdata.keep_only_largest = 0; + + IGRAPH_CHECK(igraph_adjlist_init( + graph, &clqdata.adj_list, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE + )); + IGRAPH_FINALLY(igraph_adjlist_destroy, &clqdata.adj_list); + + clqdata.IS = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + if (clqdata.IS == 0) { + IGRAPH_ERROR("igraph_independence_number failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, clqdata.IS); + + IGRAPH_VECTOR_INIT_FINALLY(&clqdata.deg, no_of_nodes); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(clqdata.deg)[i] = igraph_vector_int_size(igraph_adjlist_get(&clqdata.adj_list, i)); + } + + clqdata.buckets = IGRAPH_CALLOC(no_of_nodes + 1, igraph_set_t); + if (clqdata.buckets == 0) { + IGRAPH_ERROR("igraph_independence_number failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_i_free_set_array, clqdata.buckets); + + for (i = 0; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_set_init(&clqdata.buckets[i], 0)); + } + + /* Do the show */ + clqdata.largest_set_size = 0; + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, 0, &clqdata, 0)); + *no = clqdata.largest_set_size; + + /* Cleanup */ + for (i = 0; i < no_of_nodes; i++) { + igraph_set_destroy(&clqdata.buckets[i]); + } + igraph_adjlist_destroy(&clqdata.adj_list); + igraph_vector_destroy(&clqdata.deg); + igraph_free(clqdata.IS); + igraph_free(clqdata.buckets); + IGRAPH_FINALLY_CLEAN(4); + + return 0; +} + +/*************************************************************************/ +/* MAXIMAL CLIQUES, LARGEST CLIQUES */ +/*************************************************************************/ + +static igraph_bool_t igraph_i_maximal_cliques_store_max_size(igraph_vector_t* clique, void* data) { + igraph_integer_t* result = (igraph_integer_t*)data; + if (*result < igraph_vector_size(clique)) { + *result = (igraph_integer_t) igraph_vector_size(clique); + } + igraph_vector_destroy(clique); + igraph_Free(clique); + return 1; +} + +static igraph_bool_t igraph_i_largest_cliques_store(igraph_vector_t* clique, void* data) { + igraph_vector_ptr_t* result = (igraph_vector_ptr_t*)data; + long int i, n; + + /* Is the current clique at least as large as the others that we have found? */ + if (!igraph_vector_ptr_empty(result)) { + n = igraph_vector_size(clique); + if (n < igraph_vector_size(VECTOR(*result)[0])) { + igraph_vector_destroy(clique); + igraph_Free(clique); + return 1; + } + + if (n > igraph_vector_size(VECTOR(*result)[0])) { + for (i = 0; i < igraph_vector_ptr_size(result); i++) { + igraph_vector_destroy(VECTOR(*result)[i]); + } + igraph_vector_ptr_free_all(result); + igraph_vector_ptr_resize(result, 0); + } + } + + IGRAPH_CHECK(igraph_vector_ptr_push_back(result, clique)); + + return 1; +} + +/** + * \function igraph_largest_cliques + * \brief Finds the largest clique(s) in a graph. + * + * + * A clique is largest (quite intuitively) if there is no other clique + * in the graph which contains more vertices. + * + * + * Note that this is not necessarily the same as a maximal clique, + * i.e. the largest cliques are always maximal but a maximal clique is + * not always largest. + * + * The current implementation of this function searches + * for maximal cliques using \ref igraph_maximal_cliques_callback() and drops + * those that are not the largest. + * + * The implementation of this function changed between + * igraph 0.5 and 0.6, so the order of the cliques and the order of + * vertices within the cliques will almost surely be different between + * these two versions. + * + * \param graph The input graph. + * \param res Pointer to an initialized pointer vector, the result + * will be stored here. It will be resized as needed. Note that + * vertices of a clique may be returned in arbitrary order. + * \return Error code. + * + * \sa \ref igraph_cliques(), \ref igraph_maximal_cliques() + * + * Time complexity: O(3^(|V|/3)) worst case. + */ + +int igraph_largest_cliques(const igraph_t *graph, igraph_vector_ptr_t *res) { + igraph_vector_ptr_clear(res); + IGRAPH_FINALLY(igraph_i_cliques_free_res, res); + IGRAPH_CHECK(igraph_maximal_cliques_callback(graph, &igraph_i_largest_cliques_store, (void*)res, 0, 0)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_clique_number + * \brief Finds the clique number of the graph. + * + * + * The clique number of a graph is the size of the largest clique. + * + * The current implementation of this function searches + * for maximal cliques using \ref igraph_maximal_cliques_callback() and keeps + * track of the size of the largest clique that was found. + * + * \param graph The input graph. + * \param no The clique number will be returned to the \c igraph_integer_t + * pointed by this variable. + * \return Error code. + * + * \sa \ref igraph_cliques(), \ref igraph_largest_cliques(). + * + * Time complexity: O(3^(|V|/3)) worst case. + */ +int igraph_clique_number(const igraph_t *graph, igraph_integer_t *no) { + *no = 0; + return igraph_maximal_cliques_callback(graph, &igraph_i_maximal_cliques_store_max_size, (void*)no, 0, 0); +} + +static int igraph_i_maximal_or_largest_cliques_or_indsets(const igraph_t *graph, + igraph_vector_ptr_t *res, + igraph_integer_t *clique_number, + igraph_bool_t keep_only_largest, + igraph_bool_t complementer) { + igraph_i_max_ind_vsets_data_t clqdata; + igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph), i; + + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); + } + + clqdata.matrix_size = no_of_nodes; + clqdata.keep_only_largest = keep_only_largest; + + if (complementer) { + IGRAPH_CHECK(igraph_adjlist_init_complementer(graph, &clqdata.adj_list, IGRAPH_ALL, 0)); + } else { + IGRAPH_CHECK(igraph_adjlist_init( + graph, &clqdata.adj_list, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE + )); + } + IGRAPH_FINALLY(igraph_adjlist_destroy, &clqdata.adj_list); + + clqdata.IS = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + if (clqdata.IS == 0) { + IGRAPH_ERROR("igraph_i_maximal_or_largest_cliques_or_indsets failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, clqdata.IS); + + IGRAPH_VECTOR_INIT_FINALLY(&clqdata.deg, no_of_nodes); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(clqdata.deg)[i] = igraph_vector_int_size(igraph_adjlist_get(&clqdata.adj_list, i)); + } + + clqdata.buckets = IGRAPH_CALLOC(no_of_nodes + 1, igraph_set_t); + if (clqdata.buckets == 0) { + IGRAPH_ERROR("igraph_maximal_or_largest_cliques_or_indsets failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_i_free_set_array, clqdata.buckets); + + for (i = 0; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_set_init(&clqdata.buckets[i], 0)); + } + + if (res) { + igraph_vector_ptr_clear(res); + } + + /* Do the show */ + clqdata.largest_set_size = 0; + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, &clqdata, 0)); + + /* Cleanup */ + for (i = 0; i < no_of_nodes; i++) { + igraph_set_destroy(&clqdata.buckets[i]); + } + igraph_adjlist_destroy(&clqdata.adj_list); + igraph_vector_destroy(&clqdata.deg); + igraph_free(clqdata.IS); + igraph_free(clqdata.buckets); + IGRAPH_FINALLY_CLEAN(4); + + if (clique_number) { + *clique_number = clqdata.largest_set_size; + } + return 0; +} diff --git a/src/rigraph/core/cliques/glet.c b/src/rigraph/core/cliques/glet.c new file mode 100644 index 0000000..74d14ad --- /dev/null +++ b/src/rigraph/core/cliques/glet.c @@ -0,0 +1,867 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_graphlets.h" + +#include "igraph_conversion.h" +#include "igraph_constructors.h" +#include "igraph_cliques.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_qsort.h" +#include "igraph_structural.h" + +/** + * \section graphlets_intro Introduction + * + * + * Graphlet decomposition models a weighted undirected graph + * via the union of potentially overlapping dense social groups. + * This is done by a two-step algorithm. In the first step, a candidate + * set of groups (a candidate basis) is created by finding cliques + * in the thresholded input graph. In the second step, + * the graph is projected onto the candidate basis, resulting in a + * weight coefficient for each clique in the candidate basis. + * + * + * + * For more information on graphlet decomposition, see + * Hossein Azari Soufiani and Edoardo M Airoldi: "Graphlet decomposition of a weighted network", + * https://arxiv.org/abs/1203.2821 and http://proceedings.mlr.press/v22/azari12/azari12.pdf + * + * + * + * igraph contains three functions for performing the graphlet + * decomponsition of a graph. The first is \ref igraph_graphlets(), which + * performs both steps of the method and returns a list of subgraphs + * with their corresponding weights. The other two functions + * correspond to the first and second steps of the algorithm, and they are + * useful if the user wishes to perform them individually: + * \ref igraph_graphlets_candidate_basis() and + * \ref igraph_graphlets_project(). + * + * + * + * + * Note: The term "graphlet" is used for several unrelated concepts + * in the literature. If you are looking to count induced subgraphs, see + * \ref igraph_motifs_randesu() and \ref igraph_subisomorphic_lad(). + * + * + */ + +typedef struct { + igraph_vector_int_t *resultids; + igraph_t *result; + igraph_vector_t *resultweights; + int nc; +} igraph_i_subclique_next_free_t; + +static void igraph_i_subclique_next_free(void *ptr) { + igraph_i_subclique_next_free_t *data = ptr; + int i; + if (data->resultids) { + for (i = 0; i < data->nc; i++) { + igraph_vector_int_destroy(&data->resultids[i]); + } + IGRAPH_FREE(data->resultids); + } + if (data->result) { + for (i = 0; i < data->nc; i++) { + igraph_destroy(&data->result[i]); + } + IGRAPH_FREE(data->result); + } + if (data->resultweights) { + for (i = 0; i < data->nc; i++) { + igraph_vector_destroy(&data->resultweights[i]); + } + IGRAPH_FREE(data->resultweights); + } +} + +/** + * \function igraph_i_subclique_next + * Calculate subcliques of the cliques found at the previous level + * + * \param graph Input graph. + * \param weight Edge weights. + * \param ids The ids of the vertices in the input graph. + * \param cliques A list of vectors, vertex ids for cliques. + * \param result The result is stored here, a list of graphs is stored + * here. + * \param resultids The ids of the vertices in the result graphs is + * stored here. + * \param clique_thr The thresholds for the cliques are stored here, + * if not a null pointer. + * \param next_thr The next thresholds for the cliques are stored + * here, if not a null pointer. + * + */ + +static int igraph_i_subclique_next(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_vector_int_t *ids, + const igraph_vector_ptr_t *cliques, + igraph_t **result, + igraph_vector_t **resultweights, + igraph_vector_int_t **resultids, + igraph_vector_t *clique_thr, + igraph_vector_t *next_thr) { + + /* The input is a set of cliques, that were found at a previous level. + For each clique, we calculate the next threshold, drop the isolate + vertices, and create a new graph from them. */ + + igraph_vector_int_t mark, map; + igraph_vector_int_t edges; + igraph_vector_t neis, newedges; + igraph_integer_t c, nc = igraph_vector_ptr_size(cliques); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_i_subclique_next_free_t freedata = { 0, 0, 0, nc }; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid length of weight vector", IGRAPH_EINVAL); + } + + if (igraph_vector_int_size(ids) != no_of_nodes) { + IGRAPH_ERROR("Invalid length of ID vector", IGRAPH_EINVAL); + } + + IGRAPH_FINALLY(igraph_i_subclique_next_free, &freedata); + *resultids = IGRAPH_CALLOC(nc, igraph_vector_int_t); + if (!*resultids) { + IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM); + } + freedata.resultids = *resultids; + *resultweights = IGRAPH_CALLOC(nc, igraph_vector_t); + if (!*resultweights) { + IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM); + } + freedata.resultweights = *resultweights; + *result = IGRAPH_CALLOC(nc, igraph_t); + if (!*result) { + IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM); + } + freedata.result = *result; + + igraph_vector_init(&newedges, 100); + IGRAPH_FINALLY(igraph_vector_destroy, &newedges); + igraph_vector_int_init(&mark, no_of_nodes); + IGRAPH_FINALLY(igraph_vector_int_destroy, &mark); + igraph_vector_int_init(&map, no_of_nodes); + IGRAPH_FINALLY(igraph_vector_int_destroy, &map); + igraph_vector_int_init(&edges, 100); + IGRAPH_FINALLY(igraph_vector_int_destroy, &edges); + igraph_vector_init(&neis, 10); + IGRAPH_FINALLY(igraph_vector_destroy, &neis); + + if (clique_thr) { + igraph_vector_resize(clique_thr, nc); + } + if (next_thr) { + igraph_vector_resize(next_thr, nc); + } + + /* Iterate over all cliques. We will create graphs for all + subgraphs defined by the cliques. */ + + for (c = 0; c < nc; c++) { + igraph_vector_t *clique = VECTOR(*cliques)[c]; + igraph_real_t minweight = IGRAPH_INFINITY, nextweight = IGRAPH_INFINITY; + igraph_integer_t e, v, clsize = igraph_vector_size(clique); + igraph_integer_t noe, nov = 0; + igraph_vector_int_t *newids = (*resultids) + c; + igraph_vector_t *neww = (*resultweights) + c; + igraph_t *newgraph = (*result) + c; + igraph_vector_int_clear(&edges); + igraph_vector_clear(&newedges); + + /* --------------------------------------------------- */ + + /* Iterate over the vertices of a clique and find the + edges within the clique, put them in a list. + At the same time, search for the minimum edge weight within + the clique and the next edge weight if any. */ + + for (v = 0; v < clsize; v++) { + igraph_integer_t i, neilen, node = VECTOR(*clique)[v]; + igraph_incident(graph, &neis, node, IGRAPH_ALL); + neilen = igraph_vector_size(&neis); + VECTOR(mark)[node] = c + 1; + for (i = 0; i < neilen; i++) { + igraph_integer_t edge = VECTOR(neis)[i]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, node); + if (VECTOR(mark)[nei] == c + 1) { + igraph_real_t w = VECTOR(*weights)[edge]; + igraph_vector_int_push_back(&edges, edge); + if (w < minweight) { + nextweight = minweight; + minweight = w; + } else if (w > minweight && w < nextweight) { + nextweight = w; + } + } + } + } /* v < clsize */ + + /* --------------------------------------------------- */ + + /* OK, we have stored the edges and found the weight of + the clique and the next weight to consider */ + + if (clique_thr) { + VECTOR(*clique_thr)[c] = minweight; + } + if (next_thr) { + VECTOR(*next_thr )[c] = nextweight; + } + + /* --------------------------------------------------- */ + + /* Now we create the subgraph from the edges above the next + threshold, and their incident vertices. */ + + igraph_vector_int_init(newids, 0); + igraph_vector_init(neww, 0); + + /* We use mark[] to denote the vertices already mapped to + the new graph. If this is -(c+1), then the vertex was + mapped, otherwise it was not. The mapping itself is in + map[]. */ + + noe = igraph_vector_int_size(&edges); + for (e = 0; e < noe; e++) { + igraph_integer_t edge = VECTOR(edges)[e]; + igraph_integer_t from, to; + igraph_real_t w = VECTOR(*weights)[edge]; + igraph_edge(graph, edge, &from, &to); + if (w >= nextweight) { + if (VECTOR(mark)[from] == c + 1) { + VECTOR(map)[from] = nov++; + VECTOR(mark)[from] = -(c + 1); + igraph_vector_int_push_back(newids, VECTOR(*ids)[from]); + } + if (VECTOR(mark)[to] == c + 1) { + VECTOR(map)[to] = nov++; + VECTOR(mark)[to] = -(c + 1); + igraph_vector_int_push_back(newids, VECTOR(*ids)[to]); + } + igraph_vector_push_back(neww, w); + igraph_vector_push_back(&newedges, VECTOR(map)[from]); + igraph_vector_push_back(&newedges, VECTOR(map)[to]); + } + } + + igraph_create(newgraph, &newedges, nov, IGRAPH_UNDIRECTED); + + /* --------------------------------------------------- */ + + } /* c < nc */ + + igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&edges); + igraph_vector_int_destroy(&mark); + igraph_vector_int_destroy(&map); + igraph_vector_destroy(&newedges); + IGRAPH_FINALLY_CLEAN(6); /* + freedata */ + + return 0; +} + +static void igraph_i_graphlets_destroy_vectorlist(igraph_vector_ptr_t *vl) { + int i, n = igraph_vector_ptr_size(vl); + for (i = 0; i < n; i++) { + igraph_vector_t *v = (igraph_vector_t*) VECTOR(*vl)[i]; + if (v) { + igraph_vector_destroy(v); + } + } + igraph_vector_ptr_destroy(vl); +} + +static int igraph_i_graphlets(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_ptr_t *cliques, + igraph_vector_t *thresholds, + const igraph_vector_int_t *ids, + igraph_real_t startthr) { + + /* This version is different from the main function, and is + appropriate to use in recursive calls, because it _adds_ the + results to 'cliques' and 'thresholds' and uses the supplied + 'startthr' */ + + igraph_vector_ptr_t mycliques; + int no_of_edges = igraph_ecount(graph); + igraph_vector_t subv; + igraph_t subg; + int i, nographs, nocliques; + igraph_t *newgraphs = 0; + igraph_vector_t *newweights = 0; + igraph_vector_int_t *newids = 0; + igraph_vector_t clique_thr, next_thr; + igraph_i_subclique_next_free_t freedata = { 0, 0, 0, 0 }; + + IGRAPH_CHECK(igraph_vector_ptr_init(&mycliques, 0)); + IGRAPH_FINALLY(igraph_i_graphlets_destroy_vectorlist, &mycliques); + IGRAPH_VECTOR_INIT_FINALLY(&subv, 0); + + /* We start by finding cliques at the lowest threshold */ + for (i = 0; i < no_of_edges; i++) { + if (VECTOR(*weights)[i] >= startthr) { + IGRAPH_CHECK(igraph_vector_push_back(&subv, i)); + } + } + igraph_subgraph_edges(graph, &subg, igraph_ess_vector(&subv), + /*delete_vertices=*/ 0); + IGRAPH_FINALLY(igraph_destroy, &subg); + igraph_maximal_cliques(&subg, &mycliques, /*min_size=*/ 0, /*max_size=*/ 0); + igraph_destroy(&subg); + IGRAPH_FINALLY_CLEAN(1); + nocliques = igraph_vector_ptr_size(&mycliques); + + igraph_vector_destroy(&subv); + IGRAPH_FINALLY_CLEAN(1); + + /* Get the next cliques and thresholds */ + IGRAPH_VECTOR_INIT_FINALLY(&next_thr, 0); + IGRAPH_VECTOR_INIT_FINALLY(&clique_thr, 0); + + igraph_i_subclique_next(graph, weights, ids, &mycliques, + &newgraphs, &newweights, &newids, + &clique_thr, &next_thr); + + freedata.result = newgraphs; + freedata.resultids = newids; + freedata.resultweights = newweights; + freedata.nc = nocliques; + IGRAPH_FINALLY(igraph_i_subclique_next_free, &freedata); + + /* Store cliques at the current level */ + igraph_vector_append(thresholds, &clique_thr); + for (i = 0; i < nocliques; i++) { + igraph_vector_t *cl = (igraph_vector_t*) VECTOR(mycliques)[i]; + int j, n = igraph_vector_size(cl); + for (j = 0; j < n; j++) { + int node = VECTOR(*cl)[j]; + VECTOR(*cl)[j] = VECTOR(*ids)[node]; + } + igraph_vector_sort(cl); + } + igraph_vector_ptr_append(cliques, &mycliques); + + /* Recursive calls for cliques found */ + nographs = igraph_vector_ptr_size(&mycliques); + for (i = 0; i < nographs; i++) { + igraph_t *g = newgraphs + i; + if (igraph_vcount(g) > 1) { + igraph_vector_t *w_sub = newweights + i; + igraph_vector_int_t *ids_sub = newids + i; + igraph_i_graphlets(g, w_sub, cliques, thresholds, ids_sub, VECTOR(next_thr)[i]); + } + } + + igraph_vector_destroy(&clique_thr); + igraph_vector_destroy(&next_thr); + igraph_i_subclique_next_free(&freedata); + igraph_vector_ptr_destroy(&mycliques); /* contents was copied over */ + IGRAPH_FINALLY_CLEAN(4); + + return 0; +} + +typedef struct { + const igraph_vector_ptr_t *cliques; + const igraph_vector_t *thresholds; +} igraph_i_graphlets_filter_t; + +static int igraph_i_graphlets_filter_cmp(void *data, const void *a, const void *b) { + igraph_i_graphlets_filter_t *ddata = (igraph_i_graphlets_filter_t *) data; + int *aa = (int*) a; + int *bb = (int*) b; + igraph_real_t t_a = VECTOR(*ddata->thresholds)[*aa]; + igraph_real_t t_b = VECTOR(*ddata->thresholds)[*bb]; + igraph_vector_t *v_a, *v_b; + int s_a, s_b; + + if (t_a < t_b) { + return -1; + } else if (t_a > t_b) { + return 1; + } + + v_a = (igraph_vector_t*) VECTOR(*ddata->cliques)[*aa]; + v_b = (igraph_vector_t*) VECTOR(*ddata->cliques)[*bb]; + s_a = igraph_vector_size(v_a); + s_b = igraph_vector_size(v_b); + + if (s_a < s_b) { + return -1; + } else if (s_a > s_b) { + return 1; + } else { + return 0; + } +} + +static int igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, + igraph_vector_t *thresholds) { + + /* Filter out non-maximal cliques. Every non-maximal clique is + part of a maximal clique, at the same threshold. + + First we order the cliques, according to their threshold, and + then according to their size. So when we look for a candidate + superset, we only need to check the cliques next in the list, + until their threshold is different. */ + + int i, iptr, nocliques = igraph_vector_ptr_size(cliques); + igraph_vector_int_t order; + igraph_i_graphlets_filter_t sortdata = { cliques, thresholds }; + + igraph_vector_int_init(&order, nocliques); + IGRAPH_FINALLY(igraph_vector_int_destroy, &order); + for (i = 0; i < nocliques; i++) { + VECTOR(order)[i] = i; + } + + igraph_qsort_r(VECTOR(order), nocliques, sizeof(int), &sortdata, + igraph_i_graphlets_filter_cmp); + + for (i = 0; i < nocliques - 1; i++) { + int ri = VECTOR(order)[i]; + igraph_vector_t *needle = VECTOR(*cliques)[ri]; + igraph_real_t thr_i = VECTOR(*thresholds)[ri]; + int n_i = igraph_vector_size(needle); + int j = i + 1; + + for (j = i + 1; j < nocliques; j++) { + int rj = VECTOR(order)[j]; + igraph_real_t thr_j = VECTOR(*thresholds)[rj]; + igraph_vector_t *hay; + int n_j, pi = 0, pj = 0; + + /* Done, not found */ + if (thr_j != thr_i) { + break; + } + + /* Check size of hay */ + hay = VECTOR(*cliques)[rj]; + n_j = igraph_vector_size(hay); + if (n_i > n_j) { + continue; + } + + /* Check if hay is a superset */ + while (pi < n_i && pj < n_j && n_i - pi <= n_j - pj) { + int ei = VECTOR(*needle)[pi]; + int ej = VECTOR(*hay)[pj]; + if (ei < ej) { + break; + } else if (ei > ej) { + pj++; + } else { + pi++; pj++; + } + } + if (pi == n_i) { + /* Found, delete immediately */ + igraph_vector_destroy(needle); + igraph_free(needle); + VECTOR(*cliques)[ri] = 0; + break; + } + } + } + + /* Remove null pointers from the list of cliques */ + for (i = 0, iptr = 0; i < nocliques; i++) { + igraph_vector_t *v = VECTOR(*cliques)[i]; + if (v) { + VECTOR(*cliques)[iptr] = v; + VECTOR(*thresholds)[iptr] = VECTOR(*thresholds)[i]; + iptr++; + } + } + igraph_vector_ptr_resize(cliques, iptr); + igraph_vector_resize(thresholds, iptr); + + igraph_vector_int_destroy(&order); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_graphlets_candidate_basis + * Calculate a candidate graphlets basis + * + * \param graph The input graph, it must be a simple graph, edge directions are + * ignored. + * \param weights Weights of the edges, a vector. + * \param cliques An initialized vector of pointers. + * The graphlet basis is stored here. Each element of the pointer + * vector will be a vector of vertex ids. Each elements must be + * destroyed using \ref igraph_vector_destroy() and \ref igraph_free(). + * \param thresholds An initialized vector, the (highest possible) + * weight thresholds for finding the basis subgraphs are stored + * here. + * \return Error code. + * + * See also: \ref igraph_graphlets() and \ref igraph_graphlets_project(). + */ + +int igraph_graphlets_candidate_basis(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_ptr_t *cliques, + igraph_vector_t *thresholds) { + + int no_of_nodes = igraph_vcount(graph); + int no_of_edges = igraph_ecount(graph); + igraph_real_t minthr; + igraph_vector_int_t ids; + igraph_bool_t simple; + int i; + + /* Some checks */ + if (weights == NULL) { + IGRAPH_ERROR("Graphlet functions require weighted graphs", IGRAPH_EINVAL); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_is_simple(graph, &simple)); + if (!simple) { + IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL); + } + + minthr = igraph_vector_min(weights); + igraph_vector_ptr_clear(cliques); + igraph_vector_clear(thresholds); + igraph_vector_int_init(&ids, no_of_nodes); + IGRAPH_FINALLY(igraph_vector_int_destroy, &ids); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(ids)[i] = i; + } + + igraph_i_graphlets(graph, weights, cliques, thresholds, &ids, minthr); + + igraph_vector_int_destroy(&ids); + IGRAPH_FINALLY_CLEAN(1); + + igraph_i_graphlets_filter(cliques, thresholds); + + return 0; +} + +/* TODO: not made static because it is used by the R interface */ +int igraph_i_graphlets_project(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_vector_ptr_t *cliques, + igraph_vector_t *Mu, igraph_bool_t startMu, + int niter, int vid1) { + + int no_of_nodes = igraph_vcount(graph); + int no_of_edges = igraph_ecount(graph); + int no_cliques = igraph_vector_ptr_size(cliques); + igraph_vector_int_t vcl, vclidx, ecl, eclidx, cel, celidx; + igraph_vector_t edgelist, newweights, normfact; + int i, total_vertices, e, ptr, total_edges; + igraph_bool_t simple; + + /* Check arguments */ + if (weights == NULL) { + IGRAPH_ERROR("Graphlet functions require weighted graphs", IGRAPH_EINVAL); + } + if (no_of_edges != igraph_vector_size(weights)) { + IGRAPH_ERROR("Invalid weight vector size", IGRAPH_EINVAL); + } + if (startMu && igraph_vector_size(Mu) != no_cliques) { + IGRAPH_ERROR("Invalid start coefficient vector size", IGRAPH_EINVAL); + } + if (niter < 0) { + IGRAPH_ERROR("Number of iterations must be non-negative", IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_is_simple(graph, &simple)); + if (!simple) { + IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL); + } + + if (!startMu) { + igraph_vector_resize(Mu, no_cliques); + igraph_vector_fill(Mu, 1); + } + + /* Count # cliques per vertex. Also, create an index + for the edges per clique. */ + IGRAPH_CHECK(igraph_vector_int_init(&vclidx, no_of_nodes + 2)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &vclidx); + IGRAPH_CHECK(igraph_vector_int_init(&celidx, no_cliques + 3)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &celidx); + for (i = 0, total_vertices = 0, total_edges = 0; i < no_cliques; i++) { + igraph_vector_t *v = VECTOR(*cliques)[i]; + int j, n = igraph_vector_size(v); + total_vertices += n; + total_edges += n * (n - 1) / 2; + VECTOR(celidx)[i + 2] = total_edges; + for (j = 0; j < n; j++) { + int vv = VECTOR(*v)[j] - vid1; + VECTOR(vclidx)[vv + 2] += 1; + } + } + VECTOR(celidx)[i + 2] = total_edges; + + /* Finalize index vector */ + for (i = 0; i < no_of_nodes; i++) { + VECTOR(vclidx)[i + 2] += VECTOR(vclidx)[i + 1]; + } + + /* Create vertex-clique list, the cliques for each vertex. */ + IGRAPH_CHECK(igraph_vector_int_init(&vcl, total_vertices)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &vcl); + for (i = 0; i < no_cliques; i++) { + igraph_vector_t *v = VECTOR(*cliques)[i]; + int j, n = igraph_vector_size(v); + for (j = 0; j < n; j++) { + int vv = VECTOR(*v)[j] - vid1; + int p = VECTOR(vclidx)[vv + 1]; + VECTOR(vcl)[p] = i; + VECTOR(vclidx)[vv + 1] += 1; + } + } + + /* Create an edge-clique list, the cliques of each edge */ + IGRAPH_CHECK(igraph_vector_int_init(&ecl, total_edges)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &ecl); + IGRAPH_CHECK(igraph_vector_int_init(&eclidx, no_of_edges + 1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &eclidx); + IGRAPH_CHECK(igraph_vector_init(&edgelist, no_of_edges * 2)); + IGRAPH_FINALLY(igraph_vector_destroy, &edgelist); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, /*by_col=*/ 0)); + for (i = 0, e = 0, ptr = 0; e < no_of_edges; e++) { + int from = VECTOR(edgelist)[i++]; + int to = VECTOR(edgelist)[i++]; + int from_s = VECTOR(vclidx)[from]; + int from_e = VECTOR(vclidx)[from + 1]; + int to_s = VECTOR(vclidx)[to]; + int to_e = VECTOR(vclidx)[to + 1]; + VECTOR(eclidx)[e] = ptr; + while (from_s < from_e && to_s < to_e) { + int from_v = VECTOR(vcl)[from_s]; + int to_v = VECTOR(vcl)[to_s]; + if (from_v == to_v) { + VECTOR(ecl)[ptr++] = from_v; + from_s++; to_s++; + } else if (from_v < to_v) { + from_s++; + } else { + to_s++; + } + } + } + VECTOR(eclidx)[e] = ptr; + + igraph_vector_destroy(&edgelist); + IGRAPH_FINALLY_CLEAN(1); + + /* Convert the edge-clique list to a clique-edge list */ + IGRAPH_CHECK(igraph_vector_int_init(&cel, total_edges)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &cel); + for (i = 0; i < no_of_edges; i++) { + int ecl_s = VECTOR(eclidx)[i], ecl_e = VECTOR(eclidx)[i + 1], j; + for (j = ecl_s; j < ecl_e; j++) { + int cl = VECTOR(ecl)[j]; + int epos = VECTOR(celidx)[cl + 1]; + VECTOR(cel)[epos] = i; + VECTOR(celidx)[cl + 1] += 1; + } + } + + /* Normalizing factors for the iteration */ + IGRAPH_CHECK(igraph_vector_init(&normfact, no_cliques)); + IGRAPH_FINALLY(igraph_vector_destroy, &normfact); + for (i = 0; i < no_cliques; i++) { + igraph_vector_t *v = VECTOR(*cliques)[i]; + int n = igraph_vector_size(v); + VECTOR(normfact)[i] = n * (n + 1) / 2; + } + + /* We have the clique-edge list, so do the projection now */ + IGRAPH_CHECK(igraph_vector_init(&newweights, no_of_edges)); + IGRAPH_FINALLY(igraph_vector_destroy, &newweights); + for (i = 0; i < niter; i++) { + for (e = 0; e < no_of_edges; e++) { + int start = VECTOR(eclidx)[e]; + int end = VECTOR(eclidx)[e + 1]; + VECTOR(newweights)[e] = 0.0001; + while (start < end) { + int clique = VECTOR(ecl)[start++]; + VECTOR(newweights)[e] += VECTOR(*Mu)[clique]; + } + } + for (e = 0; e < no_cliques; e++) { + igraph_real_t sumratio = 0; + int start = VECTOR(celidx)[e]; + int end = VECTOR(celidx)[e + 1]; + while (start < end) { + int edge = VECTOR(cel)[start++]; + sumratio += VECTOR(*weights)[edge] / VECTOR(newweights)[edge]; + } + VECTOR(*Mu)[e] *= sumratio / VECTOR(normfact)[e]; + } + } + + igraph_vector_destroy(&newweights); + igraph_vector_destroy(&normfact); + igraph_vector_int_destroy(&cel); + igraph_vector_int_destroy(&eclidx); + igraph_vector_int_destroy(&ecl); + igraph_vector_int_destroy(&vcl); + igraph_vector_int_destroy(&celidx); + igraph_vector_int_destroy(&vclidx); + IGRAPH_FINALLY_CLEAN(8); + + return 0; +} + +/** + * \function igraph_graphlets_project + * Project a graph on a graphlets basis + * + * Note that the graph projected does not have to be the same that + * was used to calculate the graphlet basis, but it is assumed that + * it has the same number of vertices, and the vertex ids of the two + * graphs match. + * \param graph The input graph, it must be a simple graph, edge directions are + * ignored. + * \param weights Weights of the edges in the input graph, a vector. + * \param cliques The graphlet basis, a pointer vector, in which each + * element is a vector of vertex ids. + * \param Mu An initialized vector, the weights of the graphlets will + * be stored here. This vector is also used to initialize the + * the weight vector for the iterative algorithm, if the + * \c startMu argument is true (non-zero). + * \param startMu If true (non-zero), then the supplied Mu vector is + * used as the starting point of the iteration. Otherwise a + * constant 1 vector is used. + * \param niter Integer scalar, the number of iterations to perform. + * \return Error code. + * + * See also: \ref igraph_graphlets() and + * \ref igraph_graphlets_candidate_basis(). + */ + +int igraph_graphlets_project(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_vector_ptr_t *cliques, + igraph_vector_t *Mu, igraph_bool_t startMu, + int niter) { + + return igraph_i_graphlets_project(graph, weights, cliques, Mu, startMu, + niter, /*vid1=*/ 0); +} + +typedef struct igraph_i_graphlets_order_t { + const igraph_vector_ptr_t *cliques; + const igraph_vector_t *Mu; +} igraph_i_graphlets_order_t; + +static int igraph_i_graphlets_order_cmp(void *data, const void *a, const void *b) { + igraph_i_graphlets_order_t *ddata = (igraph_i_graphlets_order_t*) data; + int *aa = (int*) a; + int *bb = (int*) b; + igraph_real_t Mu_a = VECTOR(*ddata->Mu)[*aa]; + igraph_real_t Mu_b = VECTOR(*ddata->Mu)[*bb]; + + if (Mu_a < Mu_b) { + return 1; + } else if (Mu_a > Mu_b) { + return -1; + } else { + return 0; + } +} + +/** + * \function igraph_graphlets + * Calculate graphlets basis and project the graph on it + * + * This function simply calls \ref igraph_graphlets_candidate_basis() + * and \ref igraph_graphlets_project(), and then orders the graphlets + * according to decreasing weights. + * \param graph The input graph, it must be a simple graph, edge directions are + * ignored. + * \param weights Weights of the edges, a vector. + * \param cliques An initialized vector of pointers. + * The graphlet basis is stored here. Each element of the pointer + * vector will be a vector of vertex ids. + * \param Mu An initialized vector, the weights of the graphlets will + * be stored here. + * \param niter Integer scalar, the number of iterations to perform + * for the projection step. + * \return Error code. + * + * See also: \ref igraph_graphlets_candidate_basis() and + * \ref igraph_graphlets_project(). + */ + +int igraph_graphlets(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_ptr_t *cliques, + igraph_vector_t *Mu, int niter) { + + int i, nocliques; + igraph_vector_t thresholds; + igraph_vector_int_t order; + igraph_i_graphlets_order_t sortdata = { cliques, Mu }; + + igraph_vector_init(&thresholds, 0); + IGRAPH_FINALLY(igraph_vector_destroy, &thresholds); + igraph_graphlets_candidate_basis(graph, weights, cliques, &thresholds); + igraph_vector_destroy(&thresholds); + IGRAPH_FINALLY_CLEAN(1); + + igraph_graphlets_project(graph, weights, cliques, Mu, /*startMu=*/ 0, niter); + + nocliques = igraph_vector_ptr_size(cliques); + igraph_vector_int_init(&order, nocliques); + IGRAPH_FINALLY(igraph_vector_int_destroy, &order); + for (i = 0; i < nocliques; i++) { + VECTOR(order)[i] = i; + } + igraph_qsort_r(VECTOR(order), nocliques, sizeof(int), &sortdata, + igraph_i_graphlets_order_cmp); + + igraph_vector_ptr_index_int(cliques, &order); + igraph_vector_index_int(Mu, &order); + + igraph_vector_int_destroy(&order); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} diff --git a/src/rigraph/core/cliques/maximal_cliques.c b/src/rigraph/core/cliques/maximal_cliques.c new file mode 100644 index 0000000..e949a46 --- /dev/null +++ b/src/rigraph/core/cliques/maximal_cliques.c @@ -0,0 +1,565 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_cliques.h" + +#include "igraph_adjlist.h" +#include "igraph_constants.h" +#include "igraph_community.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_progress.h" + +#include "../core/interruption.h" + +#define CONCAT2x(a,b) a ## b +#define CONCAT2(a,b) CONCAT2x(a,b) +#define FUNCTION(name,sfx) CONCAT2(name,sfx) + +static int igraph_i_maximal_cliques_reorder_adjlists( + const igraph_vector_int_t *PX, + int PS, int PE, int XS, int XE, + const igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist); + +static int igraph_i_maximal_cliques_select_pivot( + const igraph_vector_int_t *PX, + int PS, int PE, int XS, int XE, + const igraph_vector_int_t *pos, + const igraph_adjlist_t *adjlist, + int *pivot, + igraph_vector_int_t *nextv, + int oldPS, int oldXE); + +static int igraph_i_maximal_cliques_down( + igraph_vector_int_t *PX, + int PS, int PE, int XS, int XE, + igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist, int mynextv, + igraph_vector_int_t *R, + int *newPS, int *newXE); + +static int igraph_i_maximal_cliques_PX( + igraph_vector_int_t *PX, int PS, int *PE, + int *XS, int XE, igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist, int v, + igraph_vector_int_t *H); + +static int igraph_i_maximal_cliques_up( + igraph_vector_int_t *PX, int PS, int PE, + int XS, int XE, igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist, + igraph_vector_int_t *R, + igraph_vector_int_t *H); + +#define PRINT_PX do { \ + int j; \ + printf("PX="); \ + for (j=0; j= sPS && avneipos <= sPE) { + if (pp != avnei) { + int tmp = *avnei; + *avnei = *pp; + *pp = tmp; + } + pp++; + } + } + } + + return IGRAPH_SUCCESS; +} + +static int igraph_i_maximal_cliques_select_pivot( + const igraph_vector_int_t *PX, + int PS, int PE, int XS, int XE, + const igraph_vector_int_t *pos, + const igraph_adjlist_t *adjlist, + int *pivot, + igraph_vector_int_t *nextv, + int oldPS, int oldXE) { + igraph_vector_int_t *pivotvectneis; + int i, pivotvectlen, j, usize = -1; + int soldPS = oldPS + 1, soldXE = oldXE + 1, sPS = PS + 1, sPE = PE + 1; + + IGRAPH_UNUSED(XS); + + /* Choose a pivotvect, and bring up P vertices at the same time */ + for (i = PS; i <= XE; i++) { + int av = VECTOR(*PX)[i]; + igraph_vector_int_t *avneis = igraph_adjlist_get(adjlist, av); + int *avp = VECTOR(*avneis); + int avlen = igraph_vector_int_size(avneis); + int *ave = avp + avlen; + int *avnei = avp, *pp = avp; + + for (; avnei < ave; avnei++) { + int avneipos = VECTOR(*pos)[(int)(*avnei)]; + if (avneipos < soldPS || avneipos > soldXE) { + break; + } + if (avneipos >= sPS && avneipos <= sPE) { + if (pp != avnei) { + int tmp = *avnei; + *avnei = *pp; + *pp = tmp; + } + pp++; + } + } + if ((j = pp - avp) > usize) { + *pivot = av; + usize = j; + } + } + + IGRAPH_CHECK(igraph_vector_int_push_back(nextv, -1)); + pivotvectneis = igraph_adjlist_get(adjlist, *pivot); + pivotvectlen = igraph_vector_int_size(pivotvectneis); + + for (j = PS; j <= PE; j++) { + int vcand = VECTOR(*PX)[j]; + igraph_bool_t nei = 0; + int k = 0; + for (k = 0; k < pivotvectlen; k++) { + int unv = VECTOR(*pivotvectneis)[k]; + int unvpos = VECTOR(*pos)[unv]; + if (unvpos < sPS || unvpos > sPE) { + break; + } + if (unv == vcand) { + nei = 1; + break; + } + } + if (!nei) { + IGRAPH_CHECK(igraph_vector_int_push_back(nextv, vcand)); + } + } + + return IGRAPH_SUCCESS; +} + +#define SWAP(p1,p2) do { \ + int v1=VECTOR(*PX)[p1]; \ + int v2=VECTOR(*PX)[p2]; \ + VECTOR(*PX)[p1] = v2; \ + VECTOR(*PX)[p2] = v1; \ + VECTOR(*pos)[v1] = (p2)+1; \ + VECTOR(*pos)[v2] = (p1)+1; \ + } while (0) + +static int igraph_i_maximal_cliques_down(igraph_vector_int_t *PX, + int PS, int PE, int XS, int XE, + igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist, int mynextv, + igraph_vector_int_t *R, + int *newPS, int *newXE) { + + igraph_vector_int_t *vneis = igraph_adjlist_get(adjlist, mynextv); + int j, vneislen = igraph_vector_int_size(vneis); + int sPS = PS + 1, sPE = PE + 1, sXS = XS + 1, sXE = XE + 1; + + *newPS = PE + 1; *newXE = XS - 1; + for (j = 0; j < vneislen; j++) { + int vnei = VECTOR(*vneis)[j]; + int vneipos = VECTOR(*pos)[vnei]; + if (vneipos >= sPS && vneipos <= sPE) { + (*newPS)--; + SWAP(vneipos - 1, *newPS); + } else if (vneipos >= sXS && vneipos <= sXE) { + (*newXE)++; + SWAP(vneipos - 1, *newXE); + } + } + + IGRAPH_CHECK(igraph_vector_int_push_back(R, mynextv)); + + return IGRAPH_SUCCESS; +} + +#undef SWAP + +static int igraph_i_maximal_cliques_PX(igraph_vector_int_t *PX, int PS, int *PE, + int *XS, int XE, igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist, int v, + igraph_vector_int_t *H) { + + int vpos = VECTOR(*pos)[v] - 1; + int tmp = VECTOR(*PX)[*PE]; + + IGRAPH_UNUSED(PS); + IGRAPH_UNUSED(XE); + IGRAPH_UNUSED(adjlist); + + VECTOR(*PX)[vpos] = tmp; + VECTOR(*PX)[*PE] = v; + VECTOR(*pos)[v] = (*PE) + 1; + VECTOR(*pos)[tmp] = vpos + 1; + (*PE)--; (*XS)--; + IGRAPH_CHECK(igraph_vector_int_push_back(H, v)); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_maximal_cliques_up(igraph_vector_int_t *PX, int PS, int PE, + int XS, int XE, igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist, + igraph_vector_int_t *R, + igraph_vector_int_t *H) { + int vv; + + IGRAPH_UNUSED(PS); + IGRAPH_UNUSED(XE); + IGRAPH_UNUSED(adjlist); + + igraph_vector_int_pop_back(R); + + while ((vv = igraph_vector_int_pop_back(H)) != -1) { + int vvpos = VECTOR(*pos)[vv]; + int tmp = VECTOR(*PX)[XS]; + VECTOR(*PX)[XS] = vv; + VECTOR(*PX)[vvpos - 1] = tmp; + VECTOR(*pos)[vv] = XS + 1; + VECTOR(*pos)[tmp] = vvpos; + PE++; XS++; + } + + return 0; +} + +/** + * \function igraph_maximal_cliques + * \brief Finds all maximal cliques in a graph. + * + * + * A maximal clique is a clique which can't be extended any more by + * adding a new vertex to it. + * + * + * If you are only interested in the size of the largest clique in the + * graph, use \ref igraph_clique_number() instead. + * + * + * The current implementation uses a modified Bron-Kerbosch + * algorithm to find the maximal cliques, see: David Eppstein, + * Maarten Löffler, Darren Strash: Listing All Maximal Cliques in + * Sparse Graphs in Near-Optimal Time. Algorithms and Computation, + * Lecture Notes in Computer Science Volume 6506, 2010, pp 403-414. + * + * The implementation of this function changed between + * igraph 0.5 and 0.6 and also between 0.6 and 0.7, so the order of + * the cliques and the order of vertices within the cliques will + * almost surely be different between these three versions. + * + * \param graph The input graph. + * \param res Pointer to a pointer vector, the result will be stored + * here, i.e. \p res will contain pointers to \ref igraph_vector_t + * objects which contain the indices of vertices involved in a clique. + * The pointer vector will be resized if needed but note that the + * objects in the pointer vector will not be freed. Note that vertices + * of a clique may be returned in arbitrary order. + * \param min_size Integer giving the minimum size of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer giving the maximum size of the cliques to be + * returned. If negative or zero, no upper bound will be used. + * \return Error code. + * + * \sa \ref igraph_maximal_independent_vertex_sets(), \ref + * igraph_clique_number() + * + * Time complexity: O(d(n-d)3^(d/3)) worst case, d is the degeneracy + * of the graph, this is typically small for sparse graphs. + * + * \example examples/simple/igraph_maximal_cliques.c + */ + +int igraph_maximal_cliques(const igraph_t *graph, + igraph_vector_ptr_t *res, + igraph_integer_t min_size, + igraph_integer_t max_size); + +#define IGRAPH_MC_ORIG +#include "maximal_cliques_template.h" +#undef IGRAPH_MC_ORIG + +/** + * \function igraph_maximal_cliques_count + * Count the number of maximal cliques in a graph + * + * + * The current implementation uses a modified Bron-Kerbosch + * algorithm to find the maximal cliques, see: David Eppstein, + * Maarten Löffler, Darren Strash: Listing All Maximal Cliques in + * Sparse Graphs in Near-Optimal Time. Algorithms and Computation, + * Lecture Notes in Computer Science Volume 6506, 2010, pp 403-414. + * + * \param graph The input graph. + * \param res Pointer to an \c igraph_integer_t; the number of maximal + * cliques will be stored here. + * \param min_size Integer giving the minimum size of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer giving the maximum size of the cliques to be + * returned. If negative or zero, no upper bound will be used. + * \return Error code. + * + * \sa \ref igraph_maximal_cliques(). + * + * Time complexity: O(d(n-d)3^(d/3)) worst case, d is the degeneracy + * of the graph, this is typically small for sparse graphs. + * + * \example examples/simple/igraph_maximal_cliques.c + */ + +int igraph_maximal_cliques_count(const igraph_t *graph, + igraph_integer_t *res, + igraph_integer_t min_size, + igraph_integer_t max_size); + +#define IGRAPH_MC_COUNT +#include "maximal_cliques_template.h" +#undef IGRAPH_MC_COUNT + +/** + * \function igraph_maximal_cliques_file + * \brief Find maximal cliques and write them to a file. + * + * This function enumerates all maximal cliques and writes them to file. + * + * + * + * Edge directions are ignored. + * + * + * + * \param graph The input graph. + * \param outfile Pointer to the output file, it should be writable. + * \param min_size Integer giving the minimum size of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer giving the maximum size of the cliques to be + * returned. If negative or zero, no upper bound will be used. + * \return Error code. + * + * \sa \ref igraph_maximal_cliques(). + * + * Time complexity: O(d(n-d)3^(d/3)) worst case, d is the degeneracy + * of the graph, this is typically small for sparse graphs.* + * + */ + +int igraph_maximal_cliques_file(const igraph_t *graph, + FILE *outfile, + igraph_integer_t min_size, + igraph_integer_t max_size); + +#define IGRAPH_MC_FILE +#include "maximal_cliques_template.h" +#undef IGRAPH_MC_FILE + +/** + * \function igraph_maximal_cliques_subset + * Maximal cliques for a subset of initial vertices + * + * This function enumerates all maximal cliques for a subset of initial + * vertices and writes them to file. + * + * + * + * Edge directions are ignored. + * + * + * + * \param graph The input graph. + * \param subset Pointer to an \c igraph_vector_int_t containing the + * subset of initial vertices + * \param res Pointer to an \c igraph_ptr_t; the cliques will be + * stored here + * \param no Pointer to an \c igraph_integer_t; the number of maximal + * cliques will be stored here. + * \param outfile Pointer to an output file or \c NULL. + * When not \c NULL, the file should be writable. + * \param min_size Integer giving the minimum size of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer giving the maximum size of the cliques to be + * returned. If negative or zero, no upper bound will be used. + * \return Error code. + * + * \sa \ref igraph_maximal_cliques(). + * + * Time complexity: O(d(n-d)3^(d/3)) worst case, d is the degeneracy + * of the graph, this is typically small for sparse graphs. + * + */ + +int igraph_maximal_cliques_subset(const igraph_t *graph, + igraph_vector_int_t *subset, + igraph_vector_ptr_t *res, + igraph_integer_t *no, + FILE *outfile, + igraph_integer_t min_size, + igraph_integer_t max_size); + +#define IGRAPH_MC_FULL +#include "maximal_cliques_template.h" +#undef IGRAPH_MC_FULL + + +/** + * \function igraph_maximal_cliques_callback + * \brief Finds maximal cliques in a graph and calls a function for each one. + * + * This function enumerates all maximal cliques within the given size range + * and calls \p cliquehandler_fn for each of them. The cliques are passed to the + * callback function as a pointer to an \ref igraph_vector_t. Destroying and + * freeing this vector is left up to the user. Use \ref igraph_vector_destroy() + * to destroy it first, then free it using \ref igraph_free(). + * + * + * + * Edge directions are ignored. + * + * + * + * \param graph The input graph. + * \param cliquehandler_fn Callback function to be called for each clique. + * See also \ref igraph_clique_handler_t. + * \param arg Extra argument to supply to \p cliquehandler_fn. + * \param min_size Integer giving the minimum size of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer giving the maximum size of the cliques to be + * returned. If negative or zero, no upper bound will be used. + * \return Error code. + * + * \sa \ref igraph_maximal_cliques(). + * + * Time complexity: O(d(n-d)3^(d/3)) worst case, d is the degeneracy + * of the graph, this is typically small for sparse graphs. + * + */ + +int igraph_maximal_cliques_callback(const igraph_t *graph, + igraph_clique_handler_t *cliquehandler_fn, void *arg, + igraph_integer_t min_size, igraph_integer_t max_size); + +#define IGRAPH_MC_CALLBACK +#include "maximal_cliques_template.h" +#undef IGRAPH_MC_CALLBACK + + +/** + * \function igraph_maximal_cliques_hist + * \brief Counts the number of maximal cliques of each size in a graph. + * + * This function counts how many maximal cliques of each size are present in + * the graph. Size-1 maximal cliques are simply isolated vertices. + * + * + * + * Edge directions are ignored. + * + * + * + * \param graph The input graph. + * \param hist Pointer to an initialized vector. The result will be stored + * here. The first element will store the number of size-1 maximal cliques, + * the second element the number of size-2 maximal cliques, etc. + * For cliques smaller than \p min_size, zero counts will be returned. + * \param min_size Integer giving the minimum size of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer giving the maximum size of the cliques to be + * returned. If negative or zero, no upper bound will be used. + * \return Error code. + * + * \sa \ref igraph_maximal_cliques(). + * + * Time complexity: O(d(n-d)3^(d/3)) worst case, d is the degeneracy + * of the graph, this is typically small for sparse graphs. + * + */ + +int igraph_maximal_cliques_hist(const igraph_t *graph, + igraph_vector_t *hist, + igraph_integer_t min_size, + igraph_integer_t max_size); + +#define IGRAPH_MC_HIST +#include "maximal_cliques_template.h" +#undef IGRAPH_MC_HIST diff --git a/src/rigraph/core/cliques/maximal_cliques_template.h b/src/rigraph/core/cliques/maximal_cliques_template.h new file mode 100644 index 0000000..e3f3da7 --- /dev/null +++ b/src/rigraph/core/cliques/maximal_cliques_template.h @@ -0,0 +1,415 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifdef IGRAPH_MC_ORIG +#define RESTYPE igraph_vector_ptr_t *res +#define RESNAME res +#define SUFFIX +#define RECORD do { \ + igraph_vector_t *cl=IGRAPH_CALLOC(1, igraph_vector_t); \ + int j; \ + if (!cl) { \ + IGRAPH_ERROR("Cannot list maximal cliques", IGRAPH_ENOMEM); \ + } \ + IGRAPH_CHECK(igraph_vector_ptr_push_back(res, cl)); \ + IGRAPH_CHECK(igraph_vector_init(cl, clsize)); \ + for (j=0; j hsize) { \ + long hcapacity = igraph_vector_capacity(hist); \ + long j; \ + int err; \ + if (hcapacity < clsize && clsize < 2*hcapacity) \ + err = igraph_vector_reserve(hist, 2*hcapacity); \ + err = igraph_vector_resize(hist, clsize); \ + if (err != IGRAPH_SUCCESS) \ + IGRAPH_ERROR("Cannot count maximal cliques", IGRAPH_ENOMEM); \ + for (j=hsize; j < clsize; j++) \ + VECTOR(*hist)[j] = 0; \ + } \ + VECTOR(*hist)[clsize-1] += 1; \ + } while (0) +#define FINALLY \ + igraph_vector_clear(hist); \ + igraph_vector_reserve(hist, 50); /* initially reserve space for 50 elements */ +#define CLEANUP +#define FOR_LOOP_OVER_VERTICES for (i=0; i PE && XS > XE) { + /* Found a maximum clique, report it */ + int clsize = igraph_vector_int_size(R); + if (min_size <= clsize && (clsize <= max_size || max_size <= 0)) { + RECORD; + } + } else if (PS <= PE) { + /* Select a pivot element */ + int pivot, mynextv; + IGRAPH_CHECK(igraph_i_maximal_cliques_select_pivot( + PX, PS, PE, XS, XE, pos, adjlist, &pivot, nextv, oldPS, oldXE + )); + while ((mynextv = igraph_vector_int_pop_back(nextv)) != -1) { + int newPS, newXE; + + /* Going down, prepare */ + IGRAPH_CHECK(igraph_i_maximal_cliques_down( + PX, PS, PE, XS, XE, pos, adjlist, mynextv, R, &newPS, &newXE + )); + /* Recursive call */ + err = FUNCTION(igraph_i_maximal_cliques_bk, SUFFIX)( + PX, newPS, PE, XS, newXE, PS, XE, R, + pos, adjlist, RESNAME, nextv, H, + min_size, max_size); + + if (err == IGRAPH_STOP) { + return err; + } else { + IGRAPH_CHECK(err); + } + /* Putting v from P to X */ + if (igraph_vector_int_tail(nextv) != -1) { + IGRAPH_CHECK(igraph_i_maximal_cliques_PX( + PX, PS, &PE, &XS, XE, pos, adjlist, mynextv, H + )); + } + } + } + + /* Putting back vertices from X to P, see notes in H */ + IGRAPH_CHECK(igraph_i_maximal_cliques_up(PX, PS, PE, XS, XE, pos, adjlist, R, H)); + + return 0; +} + +int FUNCTION(igraph_maximal_cliques, SUFFIX)( + const igraph_t *graph, + RESTYPE, + igraph_integer_t min_size, + igraph_integer_t max_size) { + + /* Implementation details. TODO */ + + igraph_vector_int_t PX, R, H, pos, nextv; + igraph_vector_t coreness, order; + igraph_vector_int_t rank; /* TODO: this is not needed */ + int i, ii, nn, no_of_nodes = igraph_vcount(graph); + igraph_adjlist_t adjlist, fulladjlist; + igraph_real_t pgreset = round(no_of_nodes / 100.0), pg = pgreset, pgc = 0; + int err; + IGRAPH_UNUSED(nn); + + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("Edge directions are ignored for maximal clique " + "calculation"); + } + + IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&rank, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&coreness, no_of_nodes); + IGRAPH_CHECK(igraph_coreness(graph, &coreness, /*mode=*/ IGRAPH_ALL)); + IGRAPH_CHECK(igraph_vector_qsort_ind(&coreness, &order, /*descending=*/ 0)); + for (ii = 0; ii < no_of_nodes; ii++) { + int v = VECTOR(order)[ii]; + VECTOR(rank)[v] = ii; + } + + igraph_vector_destroy(&coreness); + IGRAPH_FINALLY_CLEAN(1); + + igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + igraph_adjlist_init(graph, &fulladjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + IGRAPH_FINALLY(igraph_adjlist_destroy, &fulladjlist); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&PX, 20); + IGRAPH_VECTOR_INT_INIT_FINALLY(&R, 20); + IGRAPH_VECTOR_INT_INIT_FINALLY(&H, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&pos, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&nextv, 100); + + FINALLY; + + FOR_LOOP_OVER_VERTICES { + int v; + int vrank; + igraph_vector_int_t *vneis; + int vdeg; + int Pptr, Xptr, PS, PE, XS, XE; + int j; + + FOR_LOOP_OVER_VERTICES_PREPARE; + + v = VECTOR(order)[i]; + vrank = VECTOR(rank)[v]; + vneis = igraph_adjlist_get(&fulladjlist, v); + vdeg = igraph_vector_int_size(vneis); + Pptr = 0; Xptr = vdeg - 1; PS = 0; XE = vdeg - 1; + + pg--; + if (pg <= 0) { + IGRAPH_PROGRESS("Maximal cliques: ", pgc++, NULL); + pg = pgreset; + } + + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_vector_int_resize(&PX, vdeg)); + IGRAPH_CHECK(igraph_vector_int_resize(&R, 1)); + IGRAPH_CHECK(igraph_vector_int_resize(&H, 1)); + igraph_vector_int_null(&pos); /* TODO: makes it quadratic? */ + IGRAPH_CHECK(igraph_vector_int_resize(&nextv, 1)); + + VECTOR(H)[0] = -1; /* marks the end of the recursion */ + VECTOR(nextv)[0] = -1; + + /* ================================================================*/ + /* P <- G(v[i]) intersect { v[i+1], ..., v[n-1] } + X <- G(v[i]) intersect { v[0], ..., v[i-1] } */ + + VECTOR(R)[0] = v; + for (j = 0; j < vdeg; j++) { + int vx = VECTOR(*vneis)[j]; + if (VECTOR(rank)[vx] > vrank) { + VECTOR(PX)[Pptr] = vx; + VECTOR(pos)[vx] = Pptr + 1; + Pptr++; + } else if (VECTOR(rank)[vx] < vrank) { + VECTOR(PX)[Xptr] = vx; + VECTOR(pos)[vx] = Xptr + 1; + Xptr--; + } + } + + PE = Pptr - 1; XS = Xptr + 1; /* end of P, start of X in PX */ + + /* Create an adjacency list that is specific to the + v vertex. It only contains 'v' and its neighbors. Moreover, we + only deal with the vertices in P and X (and R). */ + IGRAPH_CHECK(igraph_vector_int_update( + igraph_adjlist_get(&adjlist, v), + igraph_adjlist_get(&fulladjlist, v) + )); + for (j = 0; j <= vdeg - 1; j++) { + int vv = VECTOR(PX)[j]; + igraph_vector_int_t *fadj = igraph_adjlist_get(&fulladjlist, vv); + igraph_vector_int_t *radj = igraph_adjlist_get(&adjlist, vv); + int k, fn = igraph_vector_int_size(fadj); + igraph_vector_int_clear(radj); + for (k = 0; k < fn; k++) { + int nei = VECTOR(*fadj)[k]; + int neipos = VECTOR(pos)[nei] - 1; + if (neipos >= PS && neipos <= XE) { + IGRAPH_CHECK(igraph_vector_int_push_back(radj, nei)); + } + } + } + + /* Reorder the adjacency lists, according to P and X. */ + IGRAPH_CHECK(igraph_i_maximal_cliques_reorder_adjlists( + &PX, PS, PE, XS, XE, &pos, &adjlist + )); + + err = FUNCTION(igraph_i_maximal_cliques_bk, SUFFIX)( + &PX, PS, PE, XS, XE, PS, XE, &R, &pos, + &adjlist, RESNAME, &nextv, &H, min_size, + max_size); + if (err == IGRAPH_STOP) { + break; + } else { + IGRAPH_CHECK(err); + } + } + + IGRAPH_PROGRESS("Maximal cliques: ", 100.0, NULL); + + CLEANUP; + + igraph_vector_int_destroy(&nextv); + igraph_vector_int_destroy(&pos); + igraph_vector_int_destroy(&H); + igraph_vector_int_destroy(&R); + igraph_vector_int_destroy(&PX); + igraph_adjlist_destroy(&fulladjlist); + igraph_adjlist_destroy(&adjlist); + igraph_vector_int_destroy(&rank); + igraph_vector_destroy(&order); + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} + +#undef RESTYPE +#undef RESNAME +#undef SUFFIX +#undef RECORD +#undef FINALLY +#undef CLEANUP +#undef FOR_LOOP_OVER_VERTICES +#undef FOR_LOOP_OVER_VERTICES_PREPARE diff --git a/src/rigraph/core/community/community_misc.c b/src/rigraph/core/community/community_misc.c new file mode 100644 index 0000000..b519a02 --- /dev/null +++ b/src/rigraph/core/community/community_misc.c @@ -0,0 +1,854 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" +#include "igraph_constructors.h" +#include "igraph_memory.h" +#include "igraph_random.h" +#include "igraph_arpack.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_progress.h" +#include "igraph_stack.h" +#include "igraph_spmatrix.h" +#include "igraph_statusbar.h" +#include "igraph_conversion.h" +#include "igraph_centrality.h" +#include "igraph_structural.h" + +#include "../core/indheap.h" +#include "../core/interruption.h" + +#include "config.h" + +#include +#include + +/** + * \function igraph_community_to_membership + * \brief Create membership vector from community structure dendrogram + * + * This function creates a membership vector from a community + * structure dendrogram. A membership vector contains for each vertex + * the id of its graph component, the graph components are numbered + * from zero, see the same argument of \ref igraph_clusters() for an + * example of a membership vector. + * + * + * Many community detection algorithms return with a \em merges + * matrix, \ref igraph_community_walktrap() and \ref + * igraph_community_edge_betweenness() are two examples. The matrix + * contains the merge operations performed while mapping the + * hierarchical structure of a network. If the matrix has \c n-1 rows, + * where \c n is the number of vertices in the graph, then it contains + * the hierarchical structure of the whole network and it is called a + * dendrogram. + * + * + * This function performs \p steps merge operations as prescribed by + * the \p merges matrix and returns the current state of the network. + * + * + * If \p merges is not a complete dendrogram, it is possible to + * take \p steps steps if \p steps is not bigger than the number + * lines in \p merges. + * \param merges The two-column matrix containing the merge + * operations. See \ref igraph_community_walktrap() for the + * detailed syntax. + * \param nodes The number of leaf nodes in the dendrogram. + * \param steps Integer constant, the number of steps to take. + * \param membership Pointer to an initialized vector, the membership + * results will be stored here, if not NULL. The vector will be + * resized as needed. + * \param csize Pointer to an initialized vector, or NULL. If not NULL + * then the sizes of the components will be stored here, the vector + * will be resized as needed. + * + * \sa \ref igraph_community_walktrap(), \ref + * igraph_community_edge_betweenness(), \ref + * igraph_community_fastgreedy() for community structure detection + * algorithms. + * + * Time complexity: O(|V|), the number of vertices in the graph. + */ +int igraph_community_to_membership(const igraph_matrix_t *merges, + igraph_integer_t nodes, + igraph_integer_t steps, + igraph_vector_t *membership, + igraph_vector_t *csize) { + + long int no_of_nodes = nodes; + long int components = no_of_nodes - steps; + long int i, found = 0; + igraph_vector_t tmp; + igraph_vector_bool_t already_merged; + igraph_vector_t own_membership; + igraph_bool_t using_own_membership = 0; + + if (steps > igraph_matrix_nrow(merges)) { + IGRAPH_ERRORF("Number of steps is greater than number of rows in merges matrix: found %" + IGRAPH_PRId " steps, %ld rows.", IGRAPH_EINVAL, steps, igraph_matrix_nrow(merges)); + } + + if (igraph_matrix_ncol(merges) != 2) { + IGRAPH_ERRORF("The merges matrix should have two columns, but has %ld.", + IGRAPH_EINVAL, igraph_matrix_ncol(merges)); + } + if (steps < 0) { + IGRAPH_ERRORF("Number of steps should be non-negative, found %" IGRAPH_PRId ".", IGRAPH_EINVAL, steps); + } + + if (csize != 0 && membership == 0) { + /* we need a membership vector to calculate 'csize' but the user did + * not provide one; let's allocate one ourselves */ + IGRAPH_VECTOR_INIT_FINALLY(&own_membership, no_of_nodes); + using_own_membership = 1; + membership = &own_membership; + } + + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_null(membership); + } + if (csize) { + IGRAPH_CHECK(igraph_vector_resize(csize, components)); + igraph_vector_null(csize); + } + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&already_merged, steps + no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, steps); + + for (i = steps - 1; i >= 0; i--) { + long int c1 = (long int) MATRIX(*merges, i, 0); + long int c2 = (long int) MATRIX(*merges, i, 1); + + if (VECTOR(already_merged)[c1] == 0) { + VECTOR(already_merged)[c1] = 1; + } else { + IGRAPH_ERRORF("Merges matrix contains multiple merges of cluster %ld.", IGRAPH_EINVAL, c1); + } + if (VECTOR(already_merged)[c2] == 0) { + VECTOR(already_merged)[c2] = 1; + } else { + IGRAPH_ERRORF("Merges matrix contains multiple merges of cluster %ld.", IGRAPH_EINVAL, c2); + } + + /* new component? */ + if (VECTOR(tmp)[i] == 0) { + found++; + VECTOR(tmp)[i] = found; + } + + if (c1 < no_of_nodes) { + long int cid = (long int) VECTOR(tmp)[i] - 1; + if (membership) { + VECTOR(*membership)[c1] = cid + 1; + } + if (csize) { + VECTOR(*csize)[cid] += 1; + } + } else { + VECTOR(tmp)[c1 - no_of_nodes] = VECTOR(tmp)[i]; + } + + if (c2 < no_of_nodes) { + long int cid = (long int) VECTOR(tmp)[i] - 1; + if (membership) { + VECTOR(*membership)[c2] = cid + 1; + } + if (csize) { + VECTOR(*csize)[cid] += 1; + } + } else { + VECTOR(tmp)[c2 - no_of_nodes] = VECTOR(tmp)[i]; + } + + } + + if (membership || csize) { + /* it can never happen that csize != 0 and membership == 0; we have + * handled that case above */ + for (i = 0; i < no_of_nodes; i++) { + long int tmp = (long int) VECTOR(*membership)[i]; + if (tmp != 0) { + if (membership) { + VECTOR(*membership)[i] = tmp - 1; + } + } else { + if (csize) { + VECTOR(*csize)[found] += 1; + } + if (membership) { + VECTOR(*membership)[i] = found; + } + found++; + } + } + } + + igraph_vector_destroy(&tmp); + igraph_vector_bool_destroy(&already_merged); + IGRAPH_FINALLY_CLEAN(2); + + if (using_own_membership) { + igraph_vector_destroy(&own_membership); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_reindex_membership + * \brief Makes the IDs in a membership vector continuous + * + * This function reindexes component IDs in a membership vector + * in a way that the new IDs start from zero and go up to C-1, + * where C is the number of unique component IDs in the original + * vector. The supplied membership is expected to fall in the + * range 0, ..., n - 1. + * + * \param membership Numeric vector which gives the type of each + * vertex, i.e. the component to which it belongs. + * The vector will be altered in-place. + * \param new_to_old Pointer to a vector which will contain the + * old component ID for each new one, or NULL, + * in which case it is not returned. The vector + * will be resized as needed. + * \param nb_clusters Pointer to an integer for the number of + * distinct clusters. If not NULL, this will be + * updated to reflect the number of distinct + * clusters found in membership. + * + * Time complexity: should be O(n) for n elements. + */ +int igraph_reindex_membership(igraph_vector_t *membership, + igraph_vector_t *new_to_old, + igraph_integer_t *nb_clusters) { + + long int i, n = igraph_vector_size(membership); + igraph_vector_t new_cluster; + igraph_integer_t i_nb_clusters; + + /* We allow original cluster indices in the range 0, ..., n - 1 */ + IGRAPH_CHECK(igraph_vector_init(&new_cluster, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &new_cluster); + + if (new_to_old) { + igraph_vector_clear(new_to_old); + } + + /* Clean clusters. We will store the new cluster + 1 so that membership == 0 + * indicates that no cluster was assigned yet. */ + i_nb_clusters = 1; + for (i = 0; i < n; i++) { + long int c = (long int)VECTOR(*membership)[i]; + + if (c < 0) { + IGRAPH_ERRORF("Membership indices should be non-negative. " + "Found member of cluster %ld.", IGRAPH_EINVAL, c); + } + + if (c >= n) { + IGRAPH_ERRORF("Membership indices should be less than total number of vertices. " + "Found member of cluster %ld, but only %ld vertices.", IGRAPH_EINVAL, c, n); + } + + if (VECTOR(new_cluster)[c] == 0) { + VECTOR(new_cluster)[c] = (igraph_real_t)i_nb_clusters; + i_nb_clusters += 1; + if (new_to_old) { + IGRAPH_CHECK(igraph_vector_push_back(new_to_old, c)); + } + } + } + + /* Assign new membership */ + for (i = 0; i < n; i++) { + long int c = (long int)VECTOR(*membership)[i]; + VECTOR(*membership)[i] = VECTOR(new_cluster)[c] - 1; + } + if (nb_clusters) { + /* We used the cluster + 1, so correct */ + *nb_clusters = i_nb_clusters - 1; + } + + igraph_vector_destroy(&new_cluster); + + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_compare_communities_vi(const igraph_vector_t *v1, + const igraph_vector_t *v2, igraph_real_t* result); +static int igraph_i_compare_communities_nmi(const igraph_vector_t *v1, + const igraph_vector_t *v2, igraph_real_t* result); +static int igraph_i_compare_communities_rand(const igraph_vector_t *v1, + const igraph_vector_t *v2, igraph_real_t* result, igraph_bool_t adjust); +static int igraph_i_split_join_distance(const igraph_vector_t *v1, + const igraph_vector_t *v2, igraph_integer_t* distance12, + igraph_integer_t* distance21); + +/** + * \ingroup communities + * \function igraph_compare_communities + * \brief Compares community structures using various metrics + * + * This function assesses the distance between two community structures + * using the variation of information (VI) metric of Meila (2003), the + * normalized mutual information (NMI) of Danon et al (2005), the + * split-join distance of van Dongen (2000), the Rand index of Rand (1971) + * or the adjusted Rand index of Hubert and Arabie (1985). + * + * + * References: + * + * + * Meila M: Comparing clusterings by the variation of information. + * In: Schölkopf B, Warmuth MK (eds.). Learning Theory and Kernel Machines: + * 16th Annual Conference on Computational Learning Theory and 7th Kernel + * Workshop, COLT/Kernel 2003, Washington, DC, USA. Lecture Notes in Computer + * Science, vol. 2777, Springer, 2003. ISBN: 978-3-540-40720-1. + * + * + * Danon L, Diaz-Guilera A, Duch J, Arenas A: Comparing community structure + * identification. J Stat Mech P09008, 2005. + * + * + * van Dongen S: Performance criteria for graph clustering and Markov cluster + * experiments. Technical Report INS-R0012, National Research Institute for + * Mathematics and Computer Science in the Netherlands, Amsterdam, May 2000. + * + * + * Rand WM: Objective criteria for the evaluation of clustering methods. + * J Am Stat Assoc 66(336):846-850, 1971. + * + * + * Hubert L and Arabie P: Comparing partitions. Journal of Classification + * 2:193-218, 1985. + * + * \param comm1 the membership vector of the first community structure + * \param comm2 the membership vector of the second community structure + * \param result the result is stored here. + * \param method the comparison method to use. \c IGRAPH_COMMCMP_VI + * selects the variation of information (VI) metric of + * Meila (2003), \c IGRAPH_COMMCMP_NMI selects the + * normalized mutual information measure proposed by + * Danon et al (2005), \c IGRAPH_COMMCMP_SPLIT_JOIN + * selects the split-join distance of van Dongen (2000), + * \c IGRAPH_COMMCMP_RAND selects the unadjusted Rand + * index (1971) and \c IGRAPH_COMMCMP_ADJUSTED_RAND + * selects the adjusted Rand index. + * + * \return Error code. + * + * Time complexity: O(n log(n)). + */ +int igraph_compare_communities(const igraph_vector_t *comm1, + const igraph_vector_t *comm2, igraph_real_t* result, + igraph_community_comparison_t method) { + igraph_vector_t c1, c2; + + if (igraph_vector_size(comm1) != igraph_vector_size(comm2)) { + IGRAPH_ERROR("community membership vectors have different lengths", IGRAPH_EINVAL); + } + + /* Copy and reindex membership vectors to make sure they are continuous */ + IGRAPH_CHECK(igraph_vector_copy(&c1, comm1)); + IGRAPH_FINALLY(igraph_vector_destroy, &c1); + + IGRAPH_CHECK(igraph_vector_copy(&c2, comm2)); + IGRAPH_FINALLY(igraph_vector_destroy, &c2); + + IGRAPH_CHECK(igraph_reindex_membership(&c1, 0, NULL)); + IGRAPH_CHECK(igraph_reindex_membership(&c2, 0, NULL)); + + switch (method) { + case IGRAPH_COMMCMP_VI: + IGRAPH_CHECK(igraph_i_compare_communities_vi(&c1, &c2, result)); + break; + + case IGRAPH_COMMCMP_NMI: + IGRAPH_CHECK(igraph_i_compare_communities_nmi(&c1, &c2, result)); + break; + + case IGRAPH_COMMCMP_SPLIT_JOIN: { + igraph_integer_t d12, d21; + IGRAPH_CHECK(igraph_i_split_join_distance(&c1, &c2, &d12, &d21)); + *result = d12 + d21; + } + break; + + case IGRAPH_COMMCMP_RAND: + case IGRAPH_COMMCMP_ADJUSTED_RAND: + IGRAPH_CHECK(igraph_i_compare_communities_rand(&c1, &c2, result, + method == IGRAPH_COMMCMP_ADJUSTED_RAND)); + break; + + default: + IGRAPH_ERROR("unknown community comparison method", IGRAPH_EINVAL); + } + + /* Clean up everything */ + igraph_vector_destroy(&c1); + igraph_vector_destroy(&c2); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \ingroup communities + * \function igraph_split_join_distance + * \brief Calculates the split-join distance of two community structures + * + * The split-join distance between partitions A and B is the sum of the + * projection distance of A from B and the projection distance of B from + * A. The projection distance is an asymmetric measure and it is defined + * as follows: + * + * + * First, each set in partition A is evaluated against all sets in partition + * B. For each set in partition A, the best matching set in partition B is + * found and the overlap size is calculated. (Matching is quantified by the + * size of the overlap between the two sets). Then, the maximal overlap sizes + * for each set in A are summed together and subtracted from the number of + * elements in A. + * + * + * The split-join distance will be returned in two arguments, \c distance12 + * will contain the projection distance of the first partition from the + * second, while \c distance21 will be the projection distance of the second + * partition from the first. This makes it easier to detect whether a + * partition is a subpartition of the other, since in this case, the + * corresponding distance will be zero. + * + * + * Reference: + * + * + * van Dongen S: Performance criteria for graph clustering and Markov cluster + * experiments. Technical Report INS-R0012, National Research Institute for + * Mathematics and Computer Science in the Netherlands, Amsterdam, May 2000. + * + * \param comm1 the membership vector of the first community structure + * \param comm2 the membership vector of the second community structure + * \param distance12 pointer to an \c igraph_integer_t, the projection distance + * of the first community structure from the second one will be + * returned here. + * \param distance21 pointer to an \c igraph_integer_t, the projection distance + * of the second community structure from the first one will be + * returned here. + * \return Error code. + * + * \see \ref igraph_compare_communities() with the \c IGRAPH_COMMCMP_SPLIT_JOIN + * method if you are not interested in the individual distances but only the sum + * of them. + * + * Time complexity: O(n log(n)). + */ +int igraph_split_join_distance(const igraph_vector_t *comm1, + const igraph_vector_t *comm2, igraph_integer_t *distance12, + igraph_integer_t *distance21) { + igraph_vector_t c1, c2; + + if (igraph_vector_size(comm1) != igraph_vector_size(comm2)) { + IGRAPH_ERRORF("Community membership vectors have different lengths: %ld and %ld.", + IGRAPH_EINVAL, igraph_vector_size(comm1), igraph_vector_size(comm2)); + } + + /* Copy and reindex membership vectors to make sure they are continuous */ + IGRAPH_CHECK(igraph_vector_copy(&c1, comm1)); + IGRAPH_FINALLY(igraph_vector_destroy, &c1); + + IGRAPH_CHECK(igraph_vector_copy(&c2, comm2)); + IGRAPH_FINALLY(igraph_vector_destroy, &c2); + + IGRAPH_CHECK(igraph_reindex_membership(&c1, 0, NULL)); + IGRAPH_CHECK(igraph_reindex_membership(&c2, 0, NULL)); + + IGRAPH_CHECK(igraph_i_split_join_distance(&c1, &c2, distance12, distance21)); + + /* Clean up everything */ + igraph_vector_destroy(&c1); + igraph_vector_destroy(&c2); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * Calculates the entropy and the mutual information for two reindexed community + * membership vectors v1 and v2. This is needed by both Meila's and Danon's + * community comparison measure. + */ +static int igraph_i_entropy_and_mutual_information(const igraph_vector_t* v1, + const igraph_vector_t* v2, double* h1, double* h2, double* mut_inf) { + long int i, n; + long int k1; + long int k2; + double *p1, *p2; + igraph_spmatrix_t m; + igraph_spmatrix_iter_t mit; + + n = igraph_vector_size(v1); + if (n == 0) { + *h1 = 0; + *h2 = 0; + *mut_inf = 0; + return IGRAPH_SUCCESS; + } + k1 = (long int)igraph_vector_max(v1) + 1; + k2 = (long int)igraph_vector_max(v2) + 1; + p1 = IGRAPH_CALLOC(k1, double); + if (p1 == 0) { + IGRAPH_ERROR("igraph_i_entropy_and_mutual_information failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, p1); + p2 = IGRAPH_CALLOC(k2, double); + if (p2 == 0) { + IGRAPH_ERROR("igraph_i_entropy_and_mutual_information failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, p2); + + /* Calculate the entropy of v1 */ + *h1 = 0.0; + for (i = 0; i < n; i++) { + p1[(long int)VECTOR(*v1)[i]]++; + } + for (i = 0; i < k1; i++) { + p1[i] /= n; + *h1 -= p1[i] * log(p1[i]); + } + + /* Calculate the entropy of v2 */ + *h2 = 0.0; + for (i = 0; i < n; i++) { + p2[(long int)VECTOR(*v2)[i]]++; + } + for (i = 0; i < k2; i++) { + p2[i] /= n; + *h2 -= p2[i] * log(p2[i]); + } + + /* We will only need the logs of p1 and p2 from now on */ + for (i = 0; i < k1; i++) { + p1[i] = log(p1[i]); + } + for (i = 0; i < k2; i++) { + p2[i] = log(p2[i]); + } + + /* Calculate the mutual information of v1 and v2 */ + *mut_inf = 0.0; + IGRAPH_CHECK(igraph_spmatrix_init(&m, k1, k2)); + IGRAPH_FINALLY(igraph_spmatrix_destroy, &m); + for (i = 0; i < n; i++) { + IGRAPH_CHECK(igraph_spmatrix_add_e(&m, + (int)VECTOR(*v1)[i], (int)VECTOR(*v2)[i], 1)); + } + IGRAPH_CHECK(igraph_spmatrix_iter_create(&mit, &m)); + IGRAPH_FINALLY(igraph_spmatrix_iter_destroy, &mit); + while (!igraph_spmatrix_iter_end(&mit)) { + double p = mit.value / n; + *mut_inf += p * (log(p) - p1[mit.ri] - p2[mit.ci]); + igraph_spmatrix_iter_next(&mit); + } + + igraph_spmatrix_iter_destroy(&mit); + igraph_spmatrix_destroy(&m); + IGRAPH_FREE(p1); IGRAPH_FREE(p2); + + IGRAPH_FINALLY_CLEAN(4); + + return 0; +} + +/** + * Implementation of the normalized mutual information (NMI) measure of + * Danon et al. This function assumes that the community membership + * vectors have already been normalized using igraph_reindex_communities(). + * + * + * Reference: Danon L, Diaz-Guilera A, Duch J, Arenas A: Comparing community + * structure identification. J Stat Mech P09008, 2005. + * + * + * Time complexity: O(n log(n)) + */ +static int igraph_i_compare_communities_nmi(const igraph_vector_t *v1, const igraph_vector_t *v2, + igraph_real_t* result) { + double h1, h2, mut_inf; + + IGRAPH_CHECK(igraph_i_entropy_and_mutual_information(v1, v2, &h1, &h2, &mut_inf)); + + if (h1 == 0 && h2 == 0) { + *result = 1; + } else { + *result = 2 * mut_inf / (h1 + h2); + } + + return IGRAPH_SUCCESS; +} + +/** + * Implementation of the variation of information metric (VI) of + * Meila et al. This function assumes that the community membership + * vectors have already been normalized using igraph_reindex_communities(). + * + * + * Reference: Meila M: Comparing clusterings by the variation of information. + * In: Schölkopf B, Warmuth MK (eds.). Learning Theory and Kernel Machines: + * 16th Annual Conference on Computational Learning Theory and 7th Kernel + * Workshop, COLT/Kernel 2003, Washington, DC, USA. Lecture Notes in Computer + * Science, vol. 2777, Springer, 2003. ISBN: 978-3-540-40720-1. + * + * + * Time complexity: O(n log(n)) + */ +static int igraph_i_compare_communities_vi(const igraph_vector_t *v1, const igraph_vector_t *v2, + igraph_real_t* result) { + double h1, h2, mut_inf; + + IGRAPH_CHECK(igraph_i_entropy_and_mutual_information(v1, v2, &h1, &h2, &mut_inf)); + *result = h1 + h2 - 2 * mut_inf; + + return IGRAPH_SUCCESS; +} + +/** + * \brief Calculates the confusion matrix for two clusterings. + * + * + * This function assumes that the community membership vectors have already + * been normalized using igraph_reindex_communities(). + * + * + * Time complexity: O(n log(max(k1, k2))), where n is the number of vertices, k1 + * and k2 are the number of clusters in each of the clusterings. + */ +static int igraph_i_confusion_matrix(const igraph_vector_t *v1, const igraph_vector_t *v2, + igraph_spmatrix_t *m) { + long int k1; + long int k2; + long int i, n; + + n = igraph_vector_size(v1); + if (n == 0 ) { + IGRAPH_CHECK(igraph_spmatrix_resize(m, 0, 0)); + return IGRAPH_SUCCESS; + } + k1 = (long int)igraph_vector_max(v1) + 1; + k2 = (long int)igraph_vector_max(v2) + 1; + IGRAPH_CHECK(igraph_spmatrix_resize(m, k1, k2)); + for (i = 0; i < n; i++) { + IGRAPH_CHECK(igraph_spmatrix_add_e(m, + (int)VECTOR(*v1)[i], (int)VECTOR(*v2)[i], 1)); + } + + return IGRAPH_SUCCESS; +} + +/** + * Implementation of the split-join distance of van Dongen. + * + * + * This function assumes that the community membership vectors have already + * been normalized using igraph_reindex_communities(). + * + * + * Reference: van Dongen S: Performance criteria for graph clustering and Markov + * cluster experiments. Technical Report INS-R0012, National Research Institute + * for Mathematics and Computer Science in the Netherlands, Amsterdam, May 2000. + * + * + * Time complexity: O(n log(max(k1, k2))), where n is the number of vertices, k1 + * and k2 are the number of clusters in each of the clusterings. + */ +static int igraph_i_split_join_distance(const igraph_vector_t *v1, const igraph_vector_t *v2, + igraph_integer_t* distance12, igraph_integer_t* distance21) { + long int n = igraph_vector_size(v1); + igraph_vector_t rowmax, colmax; + igraph_spmatrix_t m; + igraph_spmatrix_iter_t mit; + + if (n == 0) { + *distance12 = 0; + *distance21 = 0; + return IGRAPH_SUCCESS; + } + /* Calculate the confusion matrix */ + IGRAPH_CHECK(igraph_spmatrix_init(&m, 1, 1)); + IGRAPH_FINALLY(igraph_spmatrix_destroy, &m); + IGRAPH_CHECK(igraph_i_confusion_matrix(v1, v2, &m)); + + /* Initialize vectors that will store the row/columnwise maxima */ + IGRAPH_VECTOR_INIT_FINALLY(&rowmax, igraph_spmatrix_nrow(&m)); + IGRAPH_VECTOR_INIT_FINALLY(&colmax, igraph_spmatrix_ncol(&m)); + + /* Find the row/columnwise maxima */ + IGRAPH_CHECK(igraph_spmatrix_iter_create(&mit, &m)); + IGRAPH_FINALLY(igraph_spmatrix_iter_destroy, &mit); + while (!igraph_spmatrix_iter_end(&mit)) { + if (mit.value > VECTOR(rowmax)[mit.ri]) { + VECTOR(rowmax)[mit.ri] = mit.value; + } + if (mit.value > VECTOR(colmax)[mit.ci]) { + VECTOR(colmax)[mit.ci] = mit.value; + } + igraph_spmatrix_iter_next(&mit); + } + igraph_spmatrix_iter_destroy(&mit); + IGRAPH_FINALLY_CLEAN(1); + + /* Calculate the distances */ + *distance12 = (igraph_integer_t) (n - igraph_vector_sum(&rowmax)); + *distance21 = (igraph_integer_t) (n - igraph_vector_sum(&colmax)); + + igraph_vector_destroy(&rowmax); + igraph_vector_destroy(&colmax); + igraph_spmatrix_destroy(&m); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * Implementation of the adjusted and unadjusted Rand indices. + * + * + * This function assumes that the community membership vectors have already + * been normalized using igraph_reindex_communities(). + * + * + * References: + * + * + * Rand WM: Objective criteria for the evaluation of clustering methods. J Am + * Stat Assoc 66(336):846-850, 1971. + * + * + * Hubert L and Arabie P: Comparing partitions. Journal of Classification + * 2:193-218, 1985. + * + * + * Time complexity: O(n log(max(k1, k2))), where n is the number of vertices, k1 + * and k2 are the number of clusters in each of the clusterings. + */ +static int igraph_i_compare_communities_rand( + const igraph_vector_t *v1, const igraph_vector_t *v2, + igraph_real_t *result, igraph_bool_t adjust) { + igraph_spmatrix_t m; + igraph_spmatrix_iter_t mit; + igraph_vector_t rowsums, colsums; + long int i, nrow, ncol; + double rand, n; + double frac_pairs_in_1, frac_pairs_in_2; + + if (igraph_vector_size(v1) <= 1) { + IGRAPH_ERRORF("Rand indices not defined for only zero or one vertices. " + "Found membership vector of size %ld", IGRAPH_EINVAL, igraph_vector_size(v1)); + } + + /* Calculate the confusion matrix */ + IGRAPH_CHECK(igraph_spmatrix_init(&m, 1, 1)); + IGRAPH_FINALLY(igraph_spmatrix_destroy, &m); + IGRAPH_CHECK(igraph_i_confusion_matrix(v1, v2, &m)); + + /* The unadjusted Rand index is defined as (a+d) / (a+b+c+d), where: + * + * - a is the number of pairs in the same cluster both in v1 and v2. This + * equals the sum of n(i,j) choose 2 for all i and j. + * + * - b is the number of pairs in the same cluster in v1 and in different + * clusters in v2. This is sum n(i,*) choose 2 for all i minus a. + * n(i,*) is the number of elements in cluster i in v1. + * + * - c is the number of pairs in the same cluster in v2 and in different + * clusters in v1. This is sum n(*,j) choose 2 for all j minus a. + * n(*,j) is the number of elements in cluster j in v2. + * + * - d is (n choose 2) - a - b - c. + * + * Therefore, a+d = (n choose 2) - b - c + * = (n choose 2) - sum (n(i,*) choose 2) + * - sum (n(*,j) choose 2) + * + 2 * sum (n(i,j) choose 2). + * + * Since a+b+c+d = (n choose 2) and this goes in the denominator, we can + * just as well start dividing each term in a+d by (n choose 2), which + * yields: + * + * 1 - sum( n(i,*)/n * (n(i,*)-1)/(n-1) ) + * - sum( n(*,i)/n * (n(*,i)-1)/(n-1) ) + * + sum( n(i,j)/n * (n(i,j)-1)/(n-1) ) * 2 + */ + + /* Calculate row and column sums */ + nrow = igraph_spmatrix_nrow(&m); + ncol = igraph_spmatrix_ncol(&m); + n = igraph_vector_size(v1) + 0.0; + IGRAPH_VECTOR_INIT_FINALLY(&rowsums, nrow); + IGRAPH_VECTOR_INIT_FINALLY(&colsums, ncol); + IGRAPH_CHECK(igraph_spmatrix_rowsums(&m, &rowsums)); + IGRAPH_CHECK(igraph_spmatrix_colsums(&m, &colsums)); + + /* Start calculating the unadjusted Rand index */ + rand = 0.0; + IGRAPH_CHECK(igraph_spmatrix_iter_create(&mit, &m)); + IGRAPH_FINALLY(igraph_spmatrix_iter_destroy, &mit); + while (!igraph_spmatrix_iter_end(&mit)) { + rand += (mit.value / n) * (mit.value - 1) / (n - 1); + igraph_spmatrix_iter_next(&mit); + } + igraph_spmatrix_iter_destroy(&mit); + IGRAPH_FINALLY_CLEAN(1); + + frac_pairs_in_1 = frac_pairs_in_2 = 0.0; + for (i = 0; i < nrow; i++) { + frac_pairs_in_1 += (VECTOR(rowsums)[i] / n) * (VECTOR(rowsums)[i] - 1) / (n - 1); + } + for (i = 0; i < ncol; i++) { + frac_pairs_in_2 += (VECTOR(colsums)[i] / n) * (VECTOR(colsums)[i] - 1) / (n - 1); + } + + rand = 1.0 + 2 * rand - frac_pairs_in_1 - frac_pairs_in_2; + + if (adjust) { + double expected = frac_pairs_in_1 * frac_pairs_in_2 + + (1 - frac_pairs_in_1) * (1 - frac_pairs_in_2); + rand = (rand - expected) / (1 - expected); + } + + igraph_vector_destroy(&rowsums); + igraph_vector_destroy(&colsums); + igraph_spmatrix_destroy(&m); + IGRAPH_FINALLY_CLEAN(3); + + *result = rand; + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/community/edge_betweenness.c b/src/rigraph/core/community/edge_betweenness.c new file mode 100644 index 0000000..b1c8739 --- /dev/null +++ b/src/rigraph/core/community/edge_betweenness.c @@ -0,0 +1,750 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_progress.h" +#include "igraph_stack.h" + +#include "../core/indheap.h" +#include "../core/interruption.h" + +#include + +static int igraph_i_rewrite_membership_vector(igraph_vector_t *membership) { + long int no = (long int) igraph_vector_max(membership) + 1; + igraph_vector_t idx; + long int realno = 0; + long int i; + long int len = igraph_vector_size(membership); + + IGRAPH_VECTOR_INIT_FINALLY(&idx, no); + for (i = 0; i < len; i++) { + long int t = (long int) VECTOR(*membership)[i]; + if (VECTOR(idx)[t]) { + VECTOR(*membership)[i] = VECTOR(idx)[t] - 1; + } else { + VECTOR(idx)[t] = ++realno; + VECTOR(*membership)[i] = VECTOR(idx)[t] - 1; + } + } + igraph_vector_destroy(&idx); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_community_eb_get_merges2(const igraph_t *graph, + const igraph_bool_t directed, + const igraph_vector_t *edges, + const igraph_vector_t *weights, + igraph_matrix_t *res, + igraph_vector_t *bridges, + igraph_vector_t *modularity, + igraph_vector_t *membership) { + + igraph_vector_t mymembership; + long int no_of_nodes = igraph_vcount(graph); + long int i; + igraph_real_t maxmod = -1; + long int midx = 0; + igraph_integer_t no_comps; + igraph_bool_t use_directed = directed && igraph_is_directed(graph); + + IGRAPH_VECTOR_INIT_FINALLY(&mymembership, no_of_nodes); + + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + } + + if (modularity || res || bridges) { + IGRAPH_CHECK(igraph_clusters(graph, 0, 0, &no_comps, + IGRAPH_WEAK)); + + if (modularity) { + IGRAPH_CHECK(igraph_vector_resize(modularity, + no_of_nodes - no_comps + 1)); + } + if (res) { + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes - no_comps, + 2)); + } + if (bridges) { + IGRAPH_CHECK(igraph_vector_resize(bridges, + no_of_nodes - no_comps)); + } + } + + for (i = 0; i < no_of_nodes; i++) { + VECTOR(mymembership)[i] = i; + } + if (membership) { + igraph_vector_update(membership, &mymembership); + } + + IGRAPH_CHECK(igraph_modularity(graph, &mymembership, weights, + /* resolution */ 1, + use_directed, &maxmod)); + if (modularity) { + VECTOR(*modularity)[0] = maxmod; + } + + for (i = igraph_vector_size(edges) - 1; i >= 0; i--) { + long int edge = (long int) VECTOR(*edges)[i]; + long int from = IGRAPH_FROM(graph, (igraph_integer_t) edge); + long int to = IGRAPH_TO(graph, (igraph_integer_t) edge); + long int c1 = (long int) VECTOR(mymembership)[from]; + long int c2 = (long int) VECTOR(mymembership)[to]; + igraph_real_t actmod; + long int j; + if (c1 != c2) { /* this is a merge */ + if (res) { + MATRIX(*res, midx, 0) = c1; + MATRIX(*res, midx, 1) = c2; + } + if (bridges) { + VECTOR(*bridges)[midx] = i + 1; + } + + /* The new cluster has id no_of_nodes+midx+1 */ + for (j = 0; j < no_of_nodes; j++) { + if (VECTOR(mymembership)[j] == c1 || + VECTOR(mymembership)[j] == c2) { + VECTOR(mymembership)[j] = no_of_nodes + midx; + } + } + + IGRAPH_CHECK(igraph_modularity(graph, &mymembership, weights, + /* resolution */ 1, + use_directed, &actmod)); + if (modularity) { + VECTOR(*modularity)[midx + 1] = actmod; + if (actmod > maxmod) { + maxmod = actmod; + if (membership) { + igraph_vector_update(membership, &mymembership); + } + } + } + + midx++; + } + } + + if (membership) { + IGRAPH_CHECK(igraph_i_rewrite_membership_vector(membership)); + } + + igraph_vector_destroy(&mymembership); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + + +/** + * \function igraph_community_eb_get_merges + * \brief Calculating the merges, i.e. the dendrogram for an edge betweenness community structure. + * + * + * This function is handy if you have a sequence of edges which are + * gradually removed from the network and you would like to know how + * the network falls apart into separate components. The edge sequence + * may come from the \ref igraph_community_edge_betweenness() + * function, but this is not necessary. Note that \ref + * igraph_community_edge_betweenness() can also calculate the + * dendrogram, via its \p merges argument. + * + * \param graph The input graph. + * \param edges Vector containing the edges to be removed from the + * network, all edges are expected to appear exactly once in the + * vector. + * \param directed Whether to use the directed or undirected version + * of modularity. Will be ignored for undirected graphs. + * \param weights An optional vector containing edge weights. If null, + * the unweighted modularity scores will be calculated. If not null, + * the weighted modularity scores will be calculated. Ignored if both + * \p modularity and \p membership are \c NULL pointers. + * \param res Pointer to an initialized matrix, if not \c NULL then the + * dendrogram will be stored here, in the same form as for the \ref + * igraph_community_walktrap() function: the matrix has two columns + * and each line is a merge given by the ids of the merged + * components. The component ids are numbered from zero and + * component ids smaller than the number of vertices in the graph + * belong to individual vertices. The non-trivial components + * containing at least two vertices are numbered from \c n, where \c n is + * the number of vertices in the graph. So if the first line + * contains \c a and \c b that means that components \c a and \c b + * are merged into component \c n, the second line creates + * component \c n+1, etc. The matrix will be resized as needed. + * \param bridges Pointer to an initialized vector or \c NULL. If not + * null then the index of the edge removals which split the network + * will be stored here. The vector will be resized as needed. + * \param modularity If not a null pointer, then the modularity values + * for the different divisions, corresponding to the merges matrix, + * will be stored here. + * \param membership If not a null pointer, then the membership vector + * for the best division (in terms of modularity) will be stored + * here. + * \return Error code. + * + * \sa \ref igraph_community_edge_betweenness(). + * + * Time complexity: O(|E|+|V|log|V|), |V| is the number of vertices, + * |E| is the number of edges. + */ +int igraph_community_eb_get_merges(const igraph_t *graph, + const igraph_bool_t directed, + const igraph_vector_t *edges, + const igraph_vector_t *weights, + igraph_matrix_t *res, + igraph_vector_t *bridges, + igraph_vector_t *modularity, + igraph_vector_t *membership) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t ptr; + long int i, midx = 0; + igraph_integer_t no_comps; + + /* catch null graph early */ + if (no_of_nodes == 0) { + if (res) { + igraph_matrix_resize(res, 0, 2); + } + if (bridges) { + igraph_vector_clear(bridges); + } + if (modularity) { + igraph_vector_clear(modularity); + } + if (membership) { + igraph_vector_clear(membership); + } + return IGRAPH_SUCCESS; + } + + if (membership || modularity) { + return igraph_i_community_eb_get_merges2(graph, + directed && igraph_is_directed(graph), + edges, weights, + res, bridges, modularity, membership); + } + + IGRAPH_CHECK(igraph_clusters(graph, 0, 0, &no_comps, IGRAPH_WEAK)); + + IGRAPH_VECTOR_INIT_FINALLY(&ptr, no_of_nodes * 2 - 1); + if (res) { + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes - no_comps, 2)); + } + if (bridges) { + IGRAPH_CHECK(igraph_vector_resize(bridges, no_of_nodes - no_comps)); + } + + for (i = igraph_vector_size(edges) - 1; i >= 0; i--) { + igraph_integer_t edge = (igraph_integer_t) VECTOR(*edges)[i]; + igraph_integer_t from, to, c1, c2, idx; + igraph_edge(graph, edge, &from, &to); + idx = from + 1; + while (VECTOR(ptr)[idx - 1] != 0) { + idx = (igraph_integer_t) VECTOR(ptr)[idx - 1]; + } + c1 = idx - 1; + idx = to + 1; + while (VECTOR(ptr)[idx - 1] != 0) { + idx = (igraph_integer_t) VECTOR(ptr)[idx - 1]; + } + c2 = idx - 1; + if (c1 != c2) { /* this is a merge */ + if (res) { + MATRIX(*res, midx, 0) = c1; + MATRIX(*res, midx, 1) = c2; + } + if (bridges) { + VECTOR(*bridges)[midx] = i + 1; + } + + VECTOR(ptr)[c1] = no_of_nodes + midx + 1; + VECTOR(ptr)[c2] = no_of_nodes + midx + 1; + VECTOR(ptr)[from] = no_of_nodes + midx + 1; + VECTOR(ptr)[to] = no_of_nodes + midx + 1; + + midx++; + } + } + + igraph_vector_destroy(&ptr); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* Find the smallest active element in the vector */ +static long int igraph_i_vector_which_max_not_null(const igraph_vector_t *v, + const char *passive) { + long int which, i = 0, size = igraph_vector_size(v); + igraph_real_t max; + while (passive[i]) { + i++; + } + which = i; + max = VECTOR(*v)[which]; + for (i++; i < size; i++) { + igraph_real_t elem = VECTOR(*v)[i]; + if (!passive[i] && elem > max) { + max = elem; + which = i; + } + } + + return which; +} + +/** + * \function igraph_community_edge_betweenness + * \brief Community finding based on edge betweenness. + * + * Community structure detection based on the betweenness of the edges + * in the network. The algorithm was invented by M. Girvan and + * M. Newman, see: M. Girvan and M. E. J. Newman: Community structure in + * social and biological networks, Proc. Nat. Acad. Sci. USA 99, 7821-7826 + * (2002). + * + * + * The idea is that the betweenness of the edges connecting two + * communities is typically high, as many of the shortest paths + * between nodes in separate communities go through them. So we + * gradually remove the edge with highest betweenness from the + * network, and recalculate edge betweenness after every removal. + * This way sooner or later the network falls off to two components, + * then after a while one of these components falls off to two smaller + * components, etc. until all edges are removed. This is a divisive + * hierarchical approach, the result is a dendrogram. + * \param graph The input graph. + * \param result Pointer to an initialized vector, the result will be + * stored here, the ids of the removed edges in the order of their + * removal. It will be resized as needed. It may be \c NULL if + * the edge IDs are not needed by the caller. + * \param edge_betweenness Pointer to an initialized vector or + * \c NULL. In the former case the edge betweenness of the removed + * edge is stored here. The vector will be resized as needed. + * \param merges Pointer to an initialized matrix or \c NULL. If not \c NULL + * then merges performed by the algorithm are stored here. Even if + * this is a divisive algorithm, we can replay it backwards and + * note which two clusters were merged. Clusters are numbered from + * zero, see the \p merges argument of \ref + * igraph_community_walktrap() for details. The matrix will be + * resized as needed. + * \param bridges Pointer to an initialized vector of \c NULL. If not + * NULL then all edge removals which separated the network into + * more components are marked here. + * \param modularity If not a null pointer, then the modularity values + * of the different divisions are stored here, in the order + * corresponding to the merge matrix. The modularity values will + * take weights into account if \p weights is not null. + * \param membership If not a null pointer, then the membership vector, + * corresponding to the highest modularity value, is stored here. + * \param directed Logical constant, whether to calculate directed + * betweenness (i.e. directed paths) for directed graphs. It is + * ignored for undirected graphs. + * \param weights An optional vector containing edge weights. If null, + * the unweighted edge betweenness scores will be calculated and + * used. If not null, the weighted edge betweenness scores will be + * calculated and used. + * \return Error code. + * + * \sa \ref igraph_community_eb_get_merges(), \ref + * igraph_community_spinglass(), \ref igraph_community_walktrap(). + * + * Time complexity: O(|V||E|^2), as the betweenness calculation requires + * O(|V||E|) and we do it |E|-1 times. + * + * \example examples/simple/igraph_community_edge_betweenness.c + */ +int igraph_community_edge_betweenness(const igraph_t *graph, + igraph_vector_t *result, + igraph_vector_t *edge_betweenness, + igraph_matrix_t *merges, + igraph_vector_t *bridges, + igraph_vector_t *modularity, + igraph_vector_t *membership, + igraph_bool_t directed, + const igraph_vector_t *weights) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + double *distance, *tmpscore; + double *nrgeo; + long int source, i, e; + + igraph_inclist_t elist_out, elist_in, fathers; + igraph_inclist_t *elist_out_p, *elist_in_p; + igraph_vector_int_t *neip; + long int neino; + igraph_vector_t eb; + long int maxedge, pos; + igraph_integer_t from, to; + igraph_bool_t result_owned = 0; + igraph_stack_t stack = IGRAPH_STACK_NULL; + igraph_real_t steps, steps_done; + + char *passive; + + /* Needed only for the unweighted case */ + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + + /* Needed only for the weighted case */ + igraph_2wheap_t heap; + + if (result == 0) { + result = IGRAPH_CALLOC(1, igraph_vector_t); + if (result == 0) { + IGRAPH_ERROR("Edge betweenness community structure failed.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, result); + IGRAPH_VECTOR_INIT_FINALLY(result, 0); + result_owned = 1; + } + + directed = directed && igraph_is_directed(graph); + if (directed) { + IGRAPH_CHECK(igraph_inclist_init(graph, &elist_out, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out); + IGRAPH_CHECK(igraph_inclist_init(graph, &elist_in, IGRAPH_IN, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &elist_in); + elist_out_p = &elist_out; + elist_in_p = &elist_in; + } else { + IGRAPH_CHECK(igraph_inclist_init(graph, &elist_out, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out); + elist_out_p = elist_in_p = &elist_out; + } + + distance = IGRAPH_CALLOC(no_of_nodes, double); + if (distance == 0) { + IGRAPH_ERROR("Edge betweenness community structure failed.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, distance); + nrgeo = IGRAPH_CALLOC(no_of_nodes, double); + if (nrgeo == 0) { + IGRAPH_ERROR("Edge betweenness community structure failed.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, nrgeo); + tmpscore = IGRAPH_CALLOC(no_of_nodes, double); + if (tmpscore == 0) { + IGRAPH_ERROR("Edge betweenness community structure failed.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, tmpscore); + + if (weights == 0) { + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + } else { + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length must agree with number of edges.", IGRAPH_EINVAL); + } + + if (no_of_edges > 0) { + /* Must not call vector_min on empty vector */ + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight <= 0) { + IGRAPH_ERROR("Weights must be strictly positive.", IGRAPH_EINVAL); + } + + if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); + } + } + + if (membership != 0) { + IGRAPH_WARNING("Membership vector will be selected based on the lowest " + "modularity score."); + } + + if (modularity != 0 || membership != 0) { + IGRAPH_WARNING("Modularity calculation with weighted edge betweenness " + "community detection might not make sense -- modularity treats edge " + "weights as similarities while edge betwenness treats them as " + "distances."); + } + + IGRAPH_CHECK(igraph_2wheap_init(&heap, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &heap); + IGRAPH_CHECK(igraph_inclist_init_empty(&fathers, + (igraph_integer_t) no_of_nodes)); + IGRAPH_FINALLY(igraph_inclist_destroy, &fathers); + } + + IGRAPH_CHECK(igraph_stack_init(&stack, no_of_nodes)); + IGRAPH_FINALLY(igraph_stack_destroy, &stack); + + IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges)); + if (edge_betweenness) { + IGRAPH_CHECK(igraph_vector_resize(edge_betweenness, no_of_edges)); + if (no_of_edges > 0) { + VECTOR(*edge_betweenness)[no_of_edges - 1] = 0; + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&eb, no_of_edges); + + passive = IGRAPH_CALLOC(no_of_edges, char); + if (!passive) { + IGRAPH_ERROR("Edge betweenness community structure failed.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, passive); + + /* Estimate the number of steps to be taken. + * It is assumed that one iteration is O(|E||V|), but |V| is constant + * anyway, so we will have approximately |E|^2 / 2 steps, and one + * iteration of the outer loop advances the step counter by the number + * of remaining edges at that iteration. + */ + steps = no_of_edges / 2.0 * (no_of_edges + 1); + steps_done = 0; + + for (e = 0; e < no_of_edges; steps_done += no_of_edges - e, e++) { + IGRAPH_PROGRESS("Edge betweenness community detection: ", + 100.0 * steps_done / steps, NULL); + + igraph_vector_null(&eb); + + if (weights == 0) { + /* Unweighted variant follows */ + + /* The following for loop is copied almost intact from + * igraph_edge_betweenness_cutoff */ + for (source = 0; source < no_of_nodes; source++) { + + IGRAPH_ALLOW_INTERRUPTION(); + + memset(distance, 0, (size_t) no_of_nodes * sizeof(double)); + memset(nrgeo, 0, (size_t) no_of_nodes * sizeof(double)); + memset(tmpscore, 0, (size_t) no_of_nodes * sizeof(double)); + igraph_stack_clear(&stack); /* it should be empty anyway... */ + + IGRAPH_CHECK(igraph_dqueue_push(&q, source)); + + nrgeo[source] = 1; + distance[source] = 0; + + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + + neip = igraph_inclist_get(elist_out_p, actnode); + neino = igraph_vector_int_size(neip); + for (i = 0; i < neino; i++) { + igraph_integer_t edge = (igraph_integer_t) VECTOR(*neip)[i]; + long int neighbor= (long int) IGRAPH_OTHER(graph, edge, actnode); + if (nrgeo[neighbor] != 0) { + /* we've already seen this node, another shortest path? */ + if (distance[neighbor] == distance[actnode] + 1) { + nrgeo[neighbor] += nrgeo[actnode]; + } + } else { + /* we haven't seen this node yet */ + nrgeo[neighbor] += nrgeo[actnode]; + distance[neighbor] = distance[actnode] + 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_stack_push(&stack, neighbor)); + } + } + } /* while !igraph_dqueue_empty */ + + /* Ok, we've the distance of each node and also the number of + shortest paths to them. Now we do an inverse search, starting + with the farthest nodes. */ + while (!igraph_stack_empty(&stack)) { + long int actnode = (long int) igraph_stack_pop(&stack); + if (distance[actnode] < 1) { + continue; /* skip source node */ + } + + /* set the temporary score of the friends */ + neip = igraph_inclist_get(elist_in_p, actnode); + neino = igraph_vector_int_size(neip); + for (i = 0; i < neino; i++) { + long int edge = (long int) VECTOR(*neip)[i]; + long int neighbor = IGRAPH_OTHER(graph, edge, actnode); + if (distance[neighbor] == distance[actnode] - 1 && + nrgeo[neighbor] != 0) { + tmpscore[neighbor] += + (tmpscore[actnode] + 1) * nrgeo[neighbor] / nrgeo[actnode]; + VECTOR(eb)[edge] += + (tmpscore[actnode] + 1) * nrgeo[neighbor] / nrgeo[actnode]; + } + } + } + /* Ok, we've the scores for this source */ + } /* for source <= no_of_nodes */ + } else { + /* Weighted variant follows */ + + /* The following for loop is copied almost intact from + * igraph_i_edge_betweenness_cutoff_weighted */ + for (source = 0; source < no_of_nodes; source++) { + /* This will contain the edge betweenness in the current step */ + IGRAPH_ALLOW_INTERRUPTION(); + + memset(distance, 0, (size_t) no_of_nodes * sizeof(double)); + memset(nrgeo, 0, (size_t) no_of_nodes * sizeof(double)); + memset(tmpscore, 0, (size_t) no_of_nodes * sizeof(double)); + + igraph_2wheap_push_with_index(&heap, source, 0); + distance[source] = 1.0; + nrgeo[source] = 1; + + while (!igraph_2wheap_empty(&heap)) { + long int minnei = igraph_2wheap_max_index(&heap); + igraph_real_t mindist = -igraph_2wheap_delete_max(&heap); + + igraph_stack_push(&stack, minnei); + + neip = igraph_inclist_get(elist_out_p, minnei); + neino = igraph_vector_int_size(neip); + + for (i = 0; i < neino; i++) { + long int edge = VECTOR(*neip)[i]; + long int to = IGRAPH_OTHER(graph, edge, minnei); + igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; + igraph_real_t curdist = distance[to]; + igraph_vector_int_t *v; + + if (curdist == 0) { + /* This is the first finite distance to 'to' */ + v = igraph_inclist_get(&fathers, to); + igraph_vector_int_resize(v, 1); + VECTOR(*v)[0] = edge; + nrgeo[to] = nrgeo[minnei]; + distance[to] = altdist + 1.0; + IGRAPH_CHECK(igraph_2wheap_push_with_index(&heap, to, -altdist)); + } else if (altdist < curdist - 1) { + /* This is a shorter path */ + v = igraph_inclist_get(&fathers, to); + igraph_vector_int_resize(v, 1); + VECTOR(*v)[0] = edge; + nrgeo[to] = nrgeo[minnei]; + distance[to] = altdist + 1.0; + IGRAPH_CHECK(igraph_2wheap_modify(&heap, to, -altdist)); + } else if (altdist == curdist - 1) { + /* Another path with the same length */ + v = igraph_inclist_get(&fathers, to); + igraph_vector_int_push_back(v, edge); + nrgeo[to] += nrgeo[minnei]; + } + } + } /* igraph_2wheap_empty(&Q) */ + + while (!igraph_stack_empty(&stack)) { + long int w = (long int) igraph_stack_pop(&stack); + igraph_vector_int_t *fatv = igraph_inclist_get(&fathers, w); + long int fatv_len = igraph_vector_int_size(fatv); + + for (i = 0; i < fatv_len; i++) { + long int fedge = (long int) VECTOR(*fatv)[i]; + long int neighbor = IGRAPH_OTHER(graph, fedge, w); + tmpscore[neighbor] += (tmpscore[w] + 1) * nrgeo[neighbor] / nrgeo[w]; + VECTOR(eb)[fedge] += (tmpscore[w] + 1) * nrgeo[neighbor] / nrgeo[w]; + } + + tmpscore[w] = 0; + distance[w] = 0; + nrgeo[w] = 0; + igraph_vector_int_clear(fatv); + } + } /* source < no_of_nodes */ + } + + /* Now look for the smallest edge betweenness */ + /* and eliminate that edge from the network */ + maxedge = igraph_i_vector_which_max_not_null(&eb, passive); + VECTOR(*result)[e] = maxedge; + if (edge_betweenness) { + VECTOR(*edge_betweenness)[e] = VECTOR(eb)[maxedge]; + if (!directed) { + VECTOR(*edge_betweenness)[e] /= 2.0; + } + } + passive[maxedge] = 1; + igraph_edge(graph, (igraph_integer_t) maxedge, &from, &to); + + neip = igraph_inclist_get(elist_in_p, to); + neino = igraph_vector_int_size(neip); + igraph_vector_int_search(neip, 0, maxedge, &pos); + VECTOR(*neip)[pos] = VECTOR(*neip)[neino - 1]; + igraph_vector_int_pop_back(neip); + + neip = igraph_inclist_get(elist_out_p, from); + neino = igraph_vector_int_size(neip); + igraph_vector_int_search(neip, 0, maxedge, &pos); + VECTOR(*neip)[pos] = VECTOR(*neip)[neino - 1]; + igraph_vector_int_pop_back(neip); + } + + IGRAPH_PROGRESS("Edge betweenness community detection: ", 100.0, NULL); + + igraph_free(passive); + igraph_vector_destroy(&eb); + igraph_stack_destroy(&stack); + IGRAPH_FINALLY_CLEAN(3); + + if (weights == 0) { + igraph_dqueue_destroy(&q); + IGRAPH_FINALLY_CLEAN(1); + } else { + igraph_2wheap_destroy(&heap); + igraph_inclist_destroy(&fathers); + IGRAPH_FINALLY_CLEAN(2); + } + igraph_free(tmpscore); + igraph_free(nrgeo); + igraph_free(distance); + IGRAPH_FINALLY_CLEAN(3); + + if (directed) { + igraph_inclist_destroy(&elist_out); + igraph_inclist_destroy(&elist_in); + IGRAPH_FINALLY_CLEAN(2); + } else { + igraph_inclist_destroy(&elist_out); + IGRAPH_FINALLY_CLEAN(1); + } + + if (merges || bridges || modularity || membership) { + IGRAPH_CHECK(igraph_community_eb_get_merges(graph, directed, result, weights, merges, + bridges, modularity, + membership)); + } + + if (result_owned) { + igraph_vector_destroy(result); + IGRAPH_FREE(result); + IGRAPH_FINALLY_CLEAN(2); + } + + return 0; +} diff --git a/src/rigraph/core/community/fast_modularity.c b/src/rigraph/core/community/fast_modularity.c new file mode 100644 index 0000000..2f0e16d --- /dev/null +++ b/src/rigraph/core/community/fast_modularity.c @@ -0,0 +1,1078 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_memory.h" +#include "igraph_iterators.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_structural.h" +#include "igraph_vector_ptr.h" + +#include "../core/interruption.h" + +/* #define IGRAPH_FASTCOMM_DEBUG */ + +#ifdef _MSC_VER +/* MSVC does not support variadic macros */ +#include +void debug(const char* fmt, ...) { + va_list args; + va_start(args, fmt); +#ifdef IGRAPH_FASTCOMM_DEBUG + vfprintf(stderr, fmt, args); +#endif + va_end(args); +} +#else +#ifdef IGRAPH_FASTCOMM_DEBUG + #define debug(...) fprintf(stderr, __VA_ARGS__) +#else + #define debug(...) +#endif +#endif + +/* + * Implementation of the community structure algorithm originally published + * by Clauset et al in: + * + * A. Clauset, M.E.J. Newman and C. Moore, "Finding community structure in + * very large networks.". Phys. Rev. E 70, 066111 (2004). + * + * The data structures being used are slightly different and they are described + * most closely in: + * + * K. Wakita, T. Tsurumi, "Finding community structure in mega-scale social + * networks.". arXiv:cs/0702048v1. + * + * We maintain a vector of communities, each of which containing a list of + * pointers to their neighboring communities along with the increase in the + * modularity score that could be achieved by joining the two communities. + * Each community has a pointer to one of its neighbors - the one which would + * result in the highest increase in modularity after a join. The local + * (community-level) maximums are also stored in an indexed max-heap. The + * max-heap itself stores its elements in an array which satisfies the heap + * property, but to allow us to access any of the elements in the array based + * on the community index (and not based on the array index - which depends on + * the element's actual position in the heap), we also maintain an index + * vector in the heap: the ith element of the index vector contains the + * position of community i in the array of the max-heap. When we perform + * sifting operations on the heap to restore the heap property, we also maintain + * the index vector. + */ + +/* Structure storing a pair of communities along with their dQ values */ +typedef struct s_igraph_i_fastgreedy_commpair { + long int first; /* first member of the community pair */ + long int second; /* second member of the community pair */ + igraph_real_t *dq; /* pointer to a member of the dq vector storing the */ + /* increase in modularity achieved when joining */ + struct s_igraph_i_fastgreedy_commpair *opposite; +} igraph_i_fastgreedy_commpair; + +/* Structure storing a community */ +typedef struct { + igraph_integer_t id; /* Identifier of the community (for merges matrix) */ + igraph_integer_t size; /* Size of the community */ + igraph_vector_ptr_t neis; /* references to neighboring communities */ + igraph_i_fastgreedy_commpair* maxdq; /* community pair with maximal dq */ +} igraph_i_fastgreedy_community; + +/* Global community list structure */ +typedef struct { + long int no_of_communities, n; /* number of communities, number of vertices */ + igraph_i_fastgreedy_community* e; /* list of communities */ + igraph_i_fastgreedy_community** heap; /* heap of communities */ + igraph_integer_t *heapindex; /* heap index to speed up lookup by community idx */ +} igraph_i_fastgreedy_community_list; + +/* Scans the community neighborhood list for the new maximal dq value. + * Returns 1 if the maximum is different from the previous one, + * 0 otherwise. */ +static int igraph_i_fastgreedy_community_rescan_max( + igraph_i_fastgreedy_community* comm) { + long int i, n; + igraph_i_fastgreedy_commpair *p, *best; + igraph_real_t bestdq, currdq; + + n = igraph_vector_ptr_size(&comm->neis); + if (n == 0) { + comm->maxdq = 0; + return 1; + } + + best = (igraph_i_fastgreedy_commpair*)VECTOR(comm->neis)[0]; + bestdq = *best->dq; + for (i = 1; i < n; i++) { + p = (igraph_i_fastgreedy_commpair*)VECTOR(comm->neis)[i]; + currdq = *p->dq; + if (currdq > bestdq) { + best = p; + bestdq = currdq; + } + } + + if (best != comm->maxdq) { + comm->maxdq = best; + return 1; + } else { + return 0; + } +} + +/* Destroys the global community list object */ +static void igraph_i_fastgreedy_community_list_destroy( + igraph_i_fastgreedy_community_list* list) { + long int i; + for (i = 0; i < list->n; i++) { + igraph_vector_ptr_destroy(&list->e[i].neis); + } + IGRAPH_FREE(list->e); + if (list->heapindex != 0) { + IGRAPH_FREE(list->heapindex); + } + if (list->heap != 0) { + IGRAPH_FREE(list->heap); + } +} + +/* Community list heap maintenance: sift down */ +static void igraph_i_fastgreedy_community_list_sift_down( + igraph_i_fastgreedy_community_list* list, long int idx) { + long int root, child, c1, c2; + igraph_i_fastgreedy_community* dummy; + igraph_integer_t dummy2; + igraph_i_fastgreedy_community** heap = list->heap; + igraph_integer_t* heapindex = list->heapindex; + + root = idx; + while (root * 2 + 1 < list->no_of_communities) { + child = root * 2 + 1; + if (child + 1 < list->no_of_communities && + *heap[child]->maxdq->dq < *heap[child + 1]->maxdq->dq) { + child++; + } + if (*heap[root]->maxdq->dq < *heap[child]->maxdq->dq) { + c1 = heap[root]->maxdq->first; + c2 = heap[child]->maxdq->first; + + dummy = heap[root]; + heap[root] = heap[child]; + heap[child] = dummy; + + dummy2 = heapindex[c1]; + heapindex[c1] = heapindex[c2]; + heapindex[c2] = dummy2; + + root = child; + } else { + break; + } + } +} + +/* Community list heap maintenance: sift up */ +static void igraph_i_fastgreedy_community_list_sift_up( + igraph_i_fastgreedy_community_list* list, long int idx) { + long int root, parent, c1, c2; + igraph_i_fastgreedy_community* dummy; + igraph_integer_t dummy2; + igraph_i_fastgreedy_community** heap = list->heap; + igraph_integer_t* heapindex = list->heapindex; + + root = idx; + while (root > 0) { + parent = (root - 1) / 2; + if (*heap[parent]->maxdq->dq < *heap[root]->maxdq->dq) { + c1 = heap[root]->maxdq->first; + c2 = heap[parent]->maxdq->first; + + dummy = heap[parent]; + heap[parent] = heap[root]; + heap[root] = dummy; + + dummy2 = heapindex[c1]; + heapindex[c1] = heapindex[c2]; + heapindex[c2] = dummy2; + + root = parent; + } else { + break; + } + } +} + +/* Builds the community heap for the first time */ +static void igraph_i_fastgreedy_community_list_build_heap( + igraph_i_fastgreedy_community_list* list) { + long int i; + for (i = list->no_of_communities / 2 - 1; i >= 0; i--) { + igraph_i_fastgreedy_community_list_sift_down(list, i); + } +} + +/* Finds the element belonging to a given community in the heap and return its + * index in the heap array */ +#define igraph_i_fastgreedy_community_list_find_in_heap(list, idx) (list)->heapindex[idx] + +/* Dumps the heap - for debugging purposes */ +/* +static void igraph_i_fastgreedy_community_list_dump_heap( + igraph_i_fastgreedy_community_list* list) { + long int i; + debug("Heap:\n"); + for (i = 0; i < list->no_of_communities; i++) { + debug("(%ld, %p, %p)", i, list->heap[i], + list->heap[i]->maxdq); + if (list->heap[i]->maxdq) { + debug(" (%ld, %ld, %.7f)", list->heap[i]->maxdq->first, + list->heap[i]->maxdq->second, *list->heap[i]->maxdq->dq); + } + debug("\n"); + } + debug("Heap index:\n"); + for (i = 0; i < list->no_of_communities; i++) { + debug("%ld ", (long)list->heapindex[i]); + } + debug("\nEND\n"); +} +*/ + +/* Checks if the community heap satisfies the heap property. + * Only useful for debugging. */ +/* +static void igraph_i_fastgreedy_community_list_check_heap( + igraph_i_fastgreedy_community_list* list) { + long int i; + for (i = 0; i < list->no_of_communities / 2; i++) { + if ((2 * i + 1 < list->no_of_communities && *list->heap[i]->maxdq->dq < *list->heap[2 * i + 1]->maxdq->dq) || + (2 * i + 2 < list->no_of_communities && *list->heap[i]->maxdq->dq < *list->heap[2 * i + 2]->maxdq->dq)) { + IGRAPH_WARNING("Heap property violated"); + debug("Position: %ld, %ld and %ld\n", i, 2 * i + 1, 2 * i + 2); + igraph_i_fastgreedy_community_list_dump_heap(list); + } + } +} +*/ + +/* Removes a given element from the heap */ +static void igraph_i_fastgreedy_community_list_remove( + igraph_i_fastgreedy_community_list* list, long int idx) { + igraph_real_t old; + long int commidx; + + /* First adjust the index */ + commidx = list->heap[list->no_of_communities - 1]->maxdq->first; + list->heapindex[commidx] = (igraph_integer_t) idx; + commidx = list->heap[idx]->maxdq->first; + list->heapindex[commidx] = -1; + + /* Now remove the element */ + old = *list->heap[idx]->maxdq->dq; + list->heap[idx] = list->heap[list->no_of_communities - 1]; + list->no_of_communities--; + + /* Recover heap property */ + if (old > *list->heap[idx]->maxdq->dq) { + igraph_i_fastgreedy_community_list_sift_down(list, idx); + } else { + igraph_i_fastgreedy_community_list_sift_up(list, idx); + } +} + +/* Removes a given element from the heap when there are no more neighbors + * for it (comm->maxdq is NULL) */ +static void igraph_i_fastgreedy_community_list_remove2( + igraph_i_fastgreedy_community_list* list, long int idx, long int comm) { + long int i; + + if (idx == list->no_of_communities - 1) { + /* We removed the rightmost element on the bottom level, no problem, + * there's nothing to be done */ + list->heapindex[comm] = -1; + list->no_of_communities--; + return; + } + + /* First adjust the index */ + i = list->heap[list->no_of_communities - 1]->maxdq->first; + list->heapindex[i] = (igraph_integer_t) idx; + list->heapindex[comm] = -1; + + /* Now remove the element */ + list->heap[idx] = list->heap[list->no_of_communities - 1]; + list->no_of_communities--; + + /* Recover heap property */ + for (i = list->no_of_communities / 2 - 1; i >= 0; i--) { + igraph_i_fastgreedy_community_list_sift_down(list, i); + } +} + +/* Removes the pair belonging to community k from the neighborhood list + * of community c (that is, clist[c]) and recalculates maxdq */ +static void igraph_i_fastgreedy_community_remove_nei( + igraph_i_fastgreedy_community_list* list, long int c, long int k) { + long int i, n; + igraph_bool_t rescan = 0; + igraph_i_fastgreedy_commpair *p; + igraph_i_fastgreedy_community *comm; + igraph_real_t olddq; + + comm = &list->e[c]; + n = igraph_vector_ptr_size(&comm->neis); + for (i = 0; i < n; i++) { + p = (igraph_i_fastgreedy_commpair*)VECTOR(comm->neis)[i]; + if (p->second == k) { + /* Check current maxdq */ + if (comm->maxdq == p) { + rescan = 1; + } + break; + } + } + if (i < n) { + olddq = *comm->maxdq->dq; + igraph_vector_ptr_remove(&comm->neis, i); + if (rescan) { + igraph_i_fastgreedy_community_rescan_max(comm); + i = igraph_i_fastgreedy_community_list_find_in_heap(list, c); + if (comm->maxdq) { + if (*comm->maxdq->dq > olddq) { + igraph_i_fastgreedy_community_list_sift_up(list, i); + } else { + igraph_i_fastgreedy_community_list_sift_down(list, i); + } + } else { + /* no more neighbors for this community. we should remove this + * community from the heap and restore the heap property */ + debug("REMOVING (NO MORE NEIS): %ld\n", i); + igraph_i_fastgreedy_community_list_remove2(list, i, c); + } + } + } +} + +/* Auxiliary function to sort a community pair list with respect to the + * `second` field */ +static int igraph_i_fastgreedy_commpair_cmp(const void* p1, const void* p2) { + igraph_i_fastgreedy_commpair *cp1, *cp2; + cp1 = *(igraph_i_fastgreedy_commpair**)p1; + cp2 = *(igraph_i_fastgreedy_commpair**)p2; + return (int) (cp1->second - cp2->second); +} + +/* Sorts the neighbor list of the community with the given index, optionally + * optimizing the process if we know that the list is nearly sorted and only + * a given pair is in the wrong place. */ +static void igraph_i_fastgreedy_community_sort_neighbors_of( + igraph_i_fastgreedy_community_list* list, long int index, + igraph_i_fastgreedy_commpair* changed_pair) { + igraph_vector_ptr_t* vec; + long int i, n; + igraph_bool_t can_skip_sort = 0; + igraph_i_fastgreedy_commpair *other_pair; + + vec = &list->e[index].neis; + if (changed_pair != 0) { + /* Optimized sorting */ + + /* First we look for changed_pair in vec */ + n = igraph_vector_ptr_size(vec); + for (i = 0; i < n; i++) { + if (VECTOR(*vec)[i] == changed_pair) { + break; + } + } + + /* Did we find it? We should have -- otherwise it's a bug */ + if (i >= n) { + IGRAPH_WARNING("changed_pair not found in neighbor vector while re-sorting " + "the neighbors of a community; this is probably a bug. Falling back to " + "full sort instead." + ); + } else { + /* Okay, the pair that changed is at index i. We need to figure out where + * its new place should be. We can simply try moving the item all the way + * to the left as long as the comparison function tells so (since the + * rest of the vector is sorted), and then move all the way to the right + * as long as the comparison function tells so, and we will be okay. */ + + /* Shifting to the left */ + while (i > 0) { + other_pair = VECTOR(*vec)[i - 1]; + if (other_pair->second > changed_pair->second) { + VECTOR(*vec)[i] = other_pair; + i--; + } else { + break; + } + } + VECTOR(*vec)[i] = changed_pair; + + /* Shifting to the right */ + while (i < n - 1) { + other_pair = VECTOR(*vec)[i + 1]; + if (other_pair->second < changed_pair->second) { + VECTOR(*vec)[i] = other_pair; + i++; + } else { + break; + } + } + VECTOR(*vec)[i] = changed_pair; + + /* Mark that we don't need a full sort */ + can_skip_sort = 1; + } + } + + if (!can_skip_sort) { + /* Fallback to full sorting */ + igraph_vector_ptr_sort(vec, igraph_i_fastgreedy_commpair_cmp); + } +} + +/* Updates the dq value of community pair p in the community with index p->first + * of the community list clist to newdq and restores the heap property + * in community c if necessary. Returns 1 if the maximum in the row had + * to be updated, zero otherwise */ +static int igraph_i_fastgreedy_community_update_dq( + igraph_i_fastgreedy_community_list* list, + igraph_i_fastgreedy_commpair* p, igraph_real_t newdq) { + long int i, j, to, from; + igraph_real_t olddq; + igraph_i_fastgreedy_community *comm_to, *comm_from; + to = p->first; from = p->second; + comm_to = &list->e[to]; + comm_from = &list->e[from]; + if (comm_to->maxdq == p && newdq >= *p->dq) { + /* If we are adjusting the current maximum and it is increased, we don't + * have to re-scan for the new maximum */ + *p->dq = newdq; + /* The maximum was increased, so perform a sift-up in the heap */ + i = igraph_i_fastgreedy_community_list_find_in_heap(list, to); + igraph_i_fastgreedy_community_list_sift_up(list, i); + /* Let's check the opposite side. If the pair was not the maximal in + * the opposite side (the other community list)... */ + if (comm_from->maxdq != p->opposite) { + if (*comm_from->maxdq->dq < newdq) { + /* ...and it will become the maximal, we need to adjust and sift up */ + comm_from->maxdq = p->opposite; + j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); + igraph_i_fastgreedy_community_list_sift_up(list, j); + } else { + /* The pair was not the maximal in the opposite side and it will + * NOT become the maximal, there's nothing to do there */ + } + } else { + /* The pair was maximal in the opposite side, so we need to sift it up + * with the new value */ + j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); + igraph_i_fastgreedy_community_list_sift_up(list, j); + } + return 1; + } else if (comm_to->maxdq != p && (newdq <= *comm_to->maxdq->dq)) { + /* If we are modifying an item which is not the current maximum, and the + * new value is less than the current maximum, we don't + * have to re-scan for the new maximum */ + olddq = *p->dq; + *p->dq = newdq; + /* However, if the item was the maximum on the opposite side, we'd better + * re-scan it */ + if (comm_from->maxdq == p->opposite) { + if (olddq > newdq) { + /* Decreased the maximum on the other side, we have to re-scan for the + * new maximum */ + igraph_i_fastgreedy_community_rescan_max(comm_from); + j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); + igraph_i_fastgreedy_community_list_sift_down(list, j); + } else { + /* Increased the maximum on the other side, we don't have to re-scan + * but we might have to sift up */ + j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); + igraph_i_fastgreedy_community_list_sift_up(list, j); + } + } + return 0; + } else { + /* We got here in two cases: + (1) the pair we are modifying right now is the maximum in the given + community and we are decreasing it + (2) the pair we are modifying right now is NOT the maximum in the + given community, but we increase it so much that it will become + the new maximum + */ + *p->dq = newdq; + if (comm_to->maxdq != p) { + /* case (2) */ + comm_to->maxdq = p; + /* The maximum was increased, so perform a sift-up in the heap */ + i = igraph_i_fastgreedy_community_list_find_in_heap(list, to); + igraph_i_fastgreedy_community_list_sift_up(list, i); + /* Opposite side. Chances are that the new value became the maximum + * in the opposite side, but check it first */ + if (comm_from->maxdq != p->opposite) { + if (*comm_from->maxdq->dq < newdq) { + /* Yes, it will become the new maximum */ + comm_from->maxdq = p->opposite; + j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); + igraph_i_fastgreedy_community_list_sift_up(list, j); + } else { + /* No, nothing to do there */ + } + } else { + /* Already increased the maximum on the opposite side, so sift it up */ + j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); + igraph_i_fastgreedy_community_list_sift_up(list, j); + } + } else { + /* case (1) */ + /* This is the worst, we have to re-scan the whole community to find + * the new maximum and update the global maximum as well if necessary */ + igraph_i_fastgreedy_community_rescan_max(comm_to); + /* The maximum was decreased, so perform a sift-down in the heap */ + i = igraph_i_fastgreedy_community_list_find_in_heap(list, to); + igraph_i_fastgreedy_community_list_sift_down(list, i); + if (comm_from->maxdq != p->opposite) { + /* The one that we decreased on the opposite side is not the + * maximal one. Nothing to do. */ + } else { + /* We decreased the maximal on the opposite side as well. Re-scan + * and sift down */ + igraph_i_fastgreedy_community_rescan_max(comm_from); + j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); + igraph_i_fastgreedy_community_list_sift_down(list, j); + } + } + } + return 1; +} + +/** + * \function igraph_community_fastgreedy + * \brief Finding community structure by greedy optimization of modularity. + * + * This function implements the fast greedy modularity optimization + * algorithm for finding community structure, see + * A Clauset, MEJ Newman, C Moore: Finding community structure in very + * large networks, http://www.arxiv.org/abs/cond-mat/0408187 for the + * details. + * + * + * Some improvements proposed in K Wakita, T Tsurumi: Finding community + * structure in mega-scale social networks, + * http://www.arxiv.org/abs/cs.CY/0702048v1 have also been implemented. + * + * \param graph The input graph. It must be a graph without multiple edges. + * This is checked and an error message is given for graphs with multiple + * edges. + * \param weights Potentially a numeric vector containing edge + * weights. Supply a null pointer here for unweighted graphs. The + * weights are expected to be non-negative. + * \param merges Pointer to an initialized matrix or \c NULL, the result of the + * computation is stored here. The matrix has two columns and each + * merge corresponds to one merge, the ids of the two merged + * components are stored. The component ids are numbered from zero and + * the first \c n components are the individual vertices, \c n is + * the number of vertices in the graph. Component \c n is created + * in the first merge, component n+1 in the second merge, etc. + * The matrix will be resized as needed. If this argument is \c NULL + * then it is ignored completely. + * \param modularity Pointer to an initialized vector or \c NULL pointer, + * in the former case the modularity scores along the stages of the + * computation are recorded here. The vector will be resized as + * needed. + * \param membership Pointer to a vector. If not a null pointer, then + * the membership vector corresponding to the best split (in terms + * of modularity) is stored here. + * \return Error code. + * + * \sa \ref igraph_community_walktrap(), \ref + * igraph_community_edge_betweenness() for other community detection + * algorithms, \ref igraph_community_to_membership() to convert the + * dendrogram to a membership vector. + * + * Time complexity: O(|E||V|log|V|) in the worst case, + * O(|E|+|V|log^2|V|) typically, |V| is the number of vertices, |E| is + * the number of edges. + * + * \example examples/simple/igraph_community_fastgreedy.c + */ +int igraph_community_fastgreedy(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_matrix_t *merges, + igraph_vector_t *modularity, + igraph_vector_t *membership) { + long int no_of_edges, no_of_nodes, no_of_joins, total_joins; + long int i, j, k, n, m, from, to, dummy, best_no_of_joins; + igraph_integer_t ffrom, fto; + igraph_eit_t edgeit; + igraph_i_fastgreedy_commpair *pairs, *p1, *p2; + igraph_i_fastgreedy_community_list communities; + igraph_vector_t a; + igraph_real_t q, *dq, bestq, weight_sum, loop_weight_sum; + igraph_bool_t has_multiple; + igraph_matrix_t merges_local; + + /*long int join_order[] = { 16,5, 5,6, 6,0, 4,0, 10,0, 26,29, 29,33, 23,33, 27,33, 25,24, 24,31, 12,3, 21,1, 30,8, 8,32, 9,2, 17,1, 11,0, 7,3, 3,2, 13,2, 1,2, 28,31, 31,33, 22,32, 18,32, 20,32, 32,33, 15,33, 14,33, 0,19, 19,2, -1,-1 };*/ + /*long int join_order[] = { 43,42, 42,41, 44,41, 41,36, 35,36, 37,36, 36,29, 38,29, 34,29, 39,29, 33,29, 40,29, 32,29, 14,29, 30,29, 31,29, 6,18, 18,4, 23,4, 21,4, 19,4, 27,4, 20,4, 22,4, 26,4, 25,4, 24,4, 17,4, 0,13, 13,2, 1,2, 11,2, 8,2, 5,2, 3,2, 10,2, 9,2, 7,2, 2,28, 28,15, 12,15, 29,16, 4,15, -1,-1 };*/ + + no_of_nodes = igraph_vcount(graph); + no_of_edges = igraph_ecount(graph); + + if (igraph_is_directed(graph)) { + IGRAPH_ERROR("Fast greedy community detection works on undirected graphs only.", IGRAPH_UNIMPLEMENTED); + } + + total_joins = no_of_nodes > 0 ? no_of_nodes - 1 : 0; + + if (weights != 0) { + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Length of weight vector must agree with number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight < 0) { + IGRAPH_ERROR("Weights must not be negative.", IGRAPH_EINVAL); + } + if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); + } + } + weight_sum = igraph_vector_sum(weights); + } else { + weight_sum = no_of_edges; + } + + IGRAPH_CHECK(igraph_has_multiple(graph, &has_multiple)); + if (has_multiple) { + IGRAPH_ERROR("Fast greedy community detection works only on graphs without multi-edges.", IGRAPH_EINVAL); + } + + if (membership != 0 && merges == 0) { + /* We need the merge matrix because the user wants the membership + * vector, so we allocate one on our own */ + IGRAPH_CHECK(igraph_matrix_init(&merges_local, total_joins, 2)); + IGRAPH_FINALLY(igraph_matrix_destroy, &merges_local); + merges = &merges_local; + } + + if (merges != 0) { + IGRAPH_CHECK(igraph_matrix_resize(merges, total_joins, 2)); + igraph_matrix_null(merges); + } + + if (modularity != 0) { + IGRAPH_CHECK(igraph_vector_resize(modularity, total_joins + 1)); + } + + /* Create degree vector */ + IGRAPH_VECTOR_INIT_FINALLY(&a, no_of_nodes); + if (weights) { + debug("Calculating weighted degrees\n"); + for (i = 0; i < no_of_edges; i++) { + VECTOR(a)[(long int)IGRAPH_FROM(graph, i)] += VECTOR(*weights)[i]; + VECTOR(a)[(long int)IGRAPH_TO(graph, i)] += VECTOR(*weights)[i]; + } + } else { + debug("Calculating degrees\n"); + IGRAPH_CHECK(igraph_degree(graph, &a, igraph_vss_all(), IGRAPH_ALL, 1)); + } + + /* Create list of communities */ + debug("Creating community list\n"); + communities.n = no_of_nodes; + communities.no_of_communities = no_of_nodes; + communities.e = (igraph_i_fastgreedy_community*)calloc((size_t) no_of_nodes, sizeof(igraph_i_fastgreedy_community)); + if (communities.e == 0) { + IGRAPH_ERROR("Insufficient memory for fast greedy community detection.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, communities.e); + communities.heap = (igraph_i_fastgreedy_community**)calloc((size_t) no_of_nodes, sizeof(igraph_i_fastgreedy_community*)); + if (communities.heap == 0) { + IGRAPH_ERROR("Insufficient memory for fast greedy community detection.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, communities.heap); + communities.heapindex = (igraph_integer_t*)calloc((size_t)no_of_nodes, sizeof(igraph_integer_t)); + if (communities.heapindex == 0) { + IGRAPH_ERROR("Insufficient memory for fast greedy community detection.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY_CLEAN(2); + IGRAPH_FINALLY(igraph_i_fastgreedy_community_list_destroy, &communities); + for (i = 0; i < no_of_nodes; i++) { + igraph_vector_ptr_init(&communities.e[i].neis, 0); + communities.e[i].id = (igraph_integer_t) i; + communities.e[i].size = 1; + } + + /* Create list of community pairs from edges */ + debug("Allocating dq vector\n"); + dq = (igraph_real_t*)calloc((size_t) no_of_edges, sizeof(igraph_real_t)); + if (dq == 0) { + IGRAPH_ERROR("Insufficient memory for fast greedy community detection.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, dq); + debug("Creating community pair list\n"); + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); + IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); + pairs = (igraph_i_fastgreedy_commpair*)calloc(2 * (size_t) no_of_edges, sizeof(igraph_i_fastgreedy_commpair)); + if (pairs == 0) { + IGRAPH_ERROR("Insufficient memory for fast greedy community detection.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, pairs); + loop_weight_sum = 0; + for (i = 0, j = 0; !IGRAPH_EIT_END(edgeit); i += 2, j++, IGRAPH_EIT_NEXT(edgeit)) { + long int eidx = IGRAPH_EIT_GET(edgeit); + igraph_edge(graph, (igraph_integer_t) eidx, &ffrom, &fto); + + /* Create the pairs themselves */ + from = (long int)ffrom; to = (long int)fto; + if (from == to) { + loop_weight_sum += weights ? 2 * VECTOR(*weights)[eidx] : 2; + continue; + } + + if (from > to) { + dummy = from; from = to; to = dummy; + } + if (weights) { + dq[j] = 2 * (VECTOR(*weights)[eidx] / (weight_sum * 2.0) - VECTOR(a)[from] * VECTOR(a)[to] / (4.0 * weight_sum * weight_sum)); + } else { + dq[j] = 2 * (1.0 / (no_of_edges * 2.0) - VECTOR(a)[from] * VECTOR(a)[to] / (4.0 * no_of_edges * no_of_edges)); + } + pairs[i].first = from; + pairs[i].second = to; + pairs[i].dq = &dq[j]; + pairs[i].opposite = &pairs[i + 1]; + pairs[i + 1].first = to; + pairs[i + 1].second = from; + pairs[i + 1].dq = pairs[i].dq; + pairs[i + 1].opposite = &pairs[i]; + /* Link the pair to the communities */ + igraph_vector_ptr_push_back(&communities.e[from].neis, &pairs[i]); + igraph_vector_ptr_push_back(&communities.e[to].neis, &pairs[i + 1]); + /* Update maximums */ + if (communities.e[from].maxdq == 0 || *communities.e[from].maxdq->dq < *pairs[i].dq) { + communities.e[from].maxdq = &pairs[i]; + } + if (communities.e[to].maxdq == 0 || *communities.e[to].maxdq->dq < *pairs[i + 1].dq) { + communities.e[to].maxdq = &pairs[i + 1]; + } + } + igraph_eit_destroy(&edgeit); + IGRAPH_FINALLY_CLEAN(1); + + /* Sorting community neighbor lists by community IDs */ + debug("Sorting community neighbor lists\n"); + for (i = 0, j = 0; i < no_of_nodes; i++) { + igraph_i_fastgreedy_community_sort_neighbors_of(&communities, i, 0); + /* Isolated vertices and vertices with loop edges only won't be stored in + * the heap (to avoid maxdq == 0) */ + if (communities.e[i].maxdq != 0) { + communities.heap[j] = &communities.e[i]; + communities.heapindex[i] = (igraph_integer_t) j; + j++; + } else { + communities.heapindex[i] = -1; + } + } + communities.no_of_communities = j; + + /* Calculate proper vector a (see paper) and initial modularity */ + q = 2.0 * (weights ? weight_sum : no_of_edges); + if (q == 0) { + /* All the weights are zero */ + } else { + igraph_vector_scale(&a, 1.0 / q); + q = loop_weight_sum / q; + for (i = 0; i < no_of_nodes; i++) { + q -= VECTOR(a)[i] * VECTOR(a)[i]; + } + } + + /* Initialize "best modularity" value and best merge counter */ + bestq = q; + best_no_of_joins = 0; + + /* Initializing community heap */ + debug("Initializing community heap\n"); + igraph_i_fastgreedy_community_list_build_heap(&communities); + + debug("Initial modularity: %.4f\n", q); + + /* Let's rock ;) */ + no_of_joins = 0; + while (no_of_joins < total_joins) { + IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_PROGRESS("Fast greedy community detection", no_of_joins * 100.0 / total_joins, 0); + + /* Store the modularity */ + if (modularity) { + VECTOR(*modularity)[no_of_joins] = q; + } + + /* Update best modularity if needed */ + if (q >= bestq) { + bestq = q; + best_no_of_joins = no_of_joins; + } + + /* Some debug info if needed */ + /* igraph_i_fastgreedy_community_list_check_heap(&communities); */ +#ifdef IGRAPH_FASTCOMM_DEBUG + debug("===========================================\n"); + for (i = 0; i < communities.n; i++) { + if (communities.e[i].maxdq == 0) { + debug("Community #%ld: PASSIVE\n", i); + continue; + } + debug("Community #%ld\n ", i); + for (j = 0; j < igraph_vector_ptr_size(&communities.e[i].neis); j++) { + p1 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[i].neis)[j]; + debug(" (%ld,%ld,%.4f)", p1->first, p1->second, *p1->dq); + } + p1 = communities.e[i].maxdq; + debug("\n Maxdq: (%ld,%ld,%.4f)\n", p1->first, p1->second, *p1->dq); + } + debug("Global maxdq is: (%ld,%ld,%.4f)\n", communities.heap[0]->maxdq->first, + communities.heap[0]->maxdq->second, *communities.heap[0]->maxdq->dq); + for (i = 0; i < communities.no_of_communities; i++) { + debug("(%ld,%ld,%.4f) ", communities.heap[i]->maxdq->first, communities.heap[i]->maxdq->second, *communities.heap[0]->maxdq->dq); + } + debug("\n"); +#endif + if (communities.heap[0] == 0) { + break; /* no more communities */ + } + if (communities.heap[0]->maxdq == 0) { + break; /* there are only isolated comms */ + } + to = communities.heap[0]->maxdq->second; + from = communities.heap[0]->maxdq->first; + + debug("Q[%ld] = %.7f\tdQ = %.7f\t |H| = %ld\n", + no_of_joins, q, *communities.heap[0]->maxdq->dq, no_of_nodes - no_of_joins - 1); + + /* IGRAPH_FASTCOMM_DEBUG */ + /* from=join_order[no_of_joins*2]; to=join_order[no_of_joins*2+1]; + if (to == -1) break; + for (i=0; isecond == from) communities.maxdq = p1; + } */ + + n = igraph_vector_ptr_size(&communities.e[to].neis); + m = igraph_vector_ptr_size(&communities.e[from].neis); + /*if (n>m) { + dummy=n; n=m; m=dummy; + dummy=to; to=from; from=dummy; + }*/ + debug(" joining: %ld <- %ld\n", to, from); + q += *communities.heap[0]->maxdq->dq; + + /* Merge the second community into the first */ + i = j = 0; + while (i < n && j < m) { + p1 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[to].neis)[i]; + p2 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[from].neis)[j]; + debug("Pairs: %ld-%ld and %ld-%ld\n", p1->first, p1->second, + p2->first, p2->second); + if (p1->second < p2->second) { + /* Considering p1 from now on */ + debug(" Considering: %ld-%ld\n", p1->first, p1->second); + if (p1->second == from) { + debug(" WILL REMOVE: %ld-%ld\n", to, from); + } else { + /* chain, case 1 */ + debug(" CHAIN(1): %ld-%ld %ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n", + to, p1->second, from, *p1->dq, -2 * VECTOR(a)[from]*VECTOR(a)[p1->second], p1->first, p1->second, *p1->dq - 2 * VECTOR(a)[from]*VECTOR(a)[p1->second]); + igraph_i_fastgreedy_community_update_dq(&communities, p1, *p1->dq - 2 * VECTOR(a)[from]*VECTOR(a)[p1->second]); + } + i++; + } else if (p1->second == p2->second) { + /* p1->first, p1->second and p2->first form a triangle */ + debug(" Considering: %ld-%ld and %ld-%ld\n", p1->first, p1->second, + p2->first, p2->second); + /* Update dq value */ + debug(" TRIANGLE: %ld-%ld-%ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n", + to, p1->second, from, *p1->dq, *p2->dq, p1->first, p1->second, *p1->dq + *p2->dq); + igraph_i_fastgreedy_community_update_dq(&communities, p1, *p1->dq + *p2->dq); + igraph_i_fastgreedy_community_remove_nei(&communities, p1->second, from); + i++; + j++; + } else { + debug(" Considering: %ld-%ld\n", p2->first, p2->second); + if (p2->second == to) { + debug(" WILL REMOVE: %ld-%ld\n", p2->second, p2->first); + } else { + /* chain, case 2 */ + debug(" CHAIN(2): %ld %ld-%ld, newdq(%ld,%ld)=%.7f\n", + to, p2->second, from, to, p2->second, *p2->dq - 2 * VECTOR(a)[to]*VECTOR(a)[p2->second]); + p2->opposite->second = to; + /* p2->opposite->second changed, so it means that + * communities.e[p2->second].neis (which contains p2->opposite) is + * not sorted any more. We have to find the index of p2->opposite in + * this vector and move it to the correct place. Moving should be an + * O(n) operation; re-sorting would be O(n*logn) or even worse, + * depending on the pivoting strategy used by qsort() since the + * vector is nearly sorted */ + igraph_i_fastgreedy_community_sort_neighbors_of( + &communities, p2->second, p2->opposite); + /* link from.neis[j] to the current place in to.neis if + * from.neis[j] != to */ + p2->first = to; + IGRAPH_CHECK(igraph_vector_ptr_insert(&communities.e[to].neis, i, p2)); + n++; i++; + if (*p2->dq > *communities.e[to].maxdq->dq) { + communities.e[to].maxdq = p2; + k = igraph_i_fastgreedy_community_list_find_in_heap(&communities, to); + igraph_i_fastgreedy_community_list_sift_up(&communities, k); + } + igraph_i_fastgreedy_community_update_dq(&communities, p2, *p2->dq - 2 * VECTOR(a)[to]*VECTOR(a)[p2->second]); + } + j++; + } + } + + p1 = 0; + while (i < n) { + p1 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[to].neis)[i]; + if (p1->second == from) { + debug(" WILL REMOVE: %ld-%ld\n", p1->first, from); + } else { + /* chain, case 1 */ + debug(" CHAIN(1): %ld-%ld %ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n", + to, p1->second, from, *p1->dq, -2 * VECTOR(a)[from]*VECTOR(a)[p1->second], p1->first, p1->second, *p1->dq - 2 * VECTOR(a)[from]*VECTOR(a)[p1->second]); + igraph_i_fastgreedy_community_update_dq(&communities, p1, *p1->dq - 2 * VECTOR(a)[from]*VECTOR(a)[p1->second]); + } + i++; + } + while (j < m) { + p2 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[from].neis)[j]; + if (to == p2->second) { + j++; + continue; + } + /* chain, case 2 */ + debug(" CHAIN(2): %ld %ld-%ld, newdq(%ld,%ld)=%.7f\n", + to, p2->second, from, p1 ? p1->first : -1, p2->second, *p2->dq - 2 * VECTOR(a)[to]*VECTOR(a)[p2->second]); + p2->opposite->second = to; + /* need to re-sort community nei list `p2->second` */ + igraph_i_fastgreedy_community_sort_neighbors_of(&communities, p2->second, p2->opposite); + /* link from.neis[j] to the current place in to.neis if + * from.neis[j] != to */ + p2->first = to; + IGRAPH_CHECK(igraph_vector_ptr_push_back(&communities.e[to].neis, p2)); + if (*p2->dq > *communities.e[to].maxdq->dq) { + communities.e[to].maxdq = p2; + k = igraph_i_fastgreedy_community_list_find_in_heap(&communities, to); + igraph_i_fastgreedy_community_list_sift_up(&communities, k); + } + igraph_i_fastgreedy_community_update_dq(&communities, p2, *p2->dq - 2 * VECTOR(a)[to]*VECTOR(a)[p2->second]); + j++; + } + + /* Now, remove community `from` from the neighbors of community `to` */ + if (communities.no_of_communities > 2) { + debug(" REMOVING: %ld-%ld\n", to, from); + igraph_i_fastgreedy_community_remove_nei(&communities, to, from); + i = igraph_i_fastgreedy_community_list_find_in_heap(&communities, from); + igraph_i_fastgreedy_community_list_remove(&communities, i); + } + communities.e[from].maxdq = 0; + + /* Update community sizes */ + communities.e[to].size += communities.e[from].size; + communities.e[from].size = 0; + + /* record what has been merged */ + /* igraph_vector_ptr_clear is not enough here as it won't free + * the memory consumed by communities.e[from].neis. Thanks + * to Tom Gregorovic for pointing that out. */ + igraph_vector_ptr_destroy(&communities.e[from].neis); + if (merges) { + MATRIX(*merges, no_of_joins, 0) = communities.e[to].id; + MATRIX(*merges, no_of_joins, 1) = communities.e[from].id; + communities.e[to].id = (igraph_integer_t) (no_of_nodes + no_of_joins); + } + + /* Update vector a */ + VECTOR(a)[to] += VECTOR(a)[from]; + VECTOR(a)[from] = 0.0; + + no_of_joins++; + } + /* TODO: continue merging when some isolated communities remained. Always + * joining the communities with the least number of nodes results in the + * smallest decrease in modularity every step. Now we're simply deleting + * the excess rows from the merge matrix */ + if (no_of_joins < total_joins) { + long int *ivec; + long int merges_nrow = igraph_matrix_nrow(merges); + ivec = IGRAPH_CALLOC(merges_nrow, long int); + if (ivec == 0) { + IGRAPH_ERROR("Insufficient memory for fast greedy community detection.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, ivec); + for (i = 0; i < no_of_joins; i++) { + ivec[i] = i + 1; + } + igraph_matrix_permdelete_rows(merges, ivec, total_joins - no_of_joins); + IGRAPH_FREE(ivec); + IGRAPH_FINALLY_CLEAN(1); + } + IGRAPH_PROGRESS("Fast greedy community detection", 100.0, 0); + + if (modularity) { + VECTOR(*modularity)[no_of_joins] = q; + igraph_vector_resize(modularity, no_of_joins + 1); + } + + debug("Freeing memory\n"); + IGRAPH_FREE(pairs); + IGRAPH_FREE(dq); + igraph_i_fastgreedy_community_list_destroy(&communities); + igraph_vector_destroy(&a); + IGRAPH_FINALLY_CLEAN(4); + + if (membership) { + IGRAPH_CHECK(igraph_community_to_membership(merges, + (igraph_integer_t) no_of_nodes, + /*steps=*/ (igraph_integer_t) best_no_of_joins, + membership, + /*csize=*/ 0)); + } + + if (merges == &merges_local) { + igraph_matrix_destroy(&merges_local); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +#ifdef IGRAPH_FASTCOMM_DEBUG + #undef IGRAPH_FASTCOMM_DEBUG +#endif diff --git a/src/rigraph/core/community/fluid.c b/src/rigraph/core/community/fluid.c new file mode 100644 index 0000000..e272a06 --- /dev/null +++ b/src/rigraph/core/community/fluid.c @@ -0,0 +1,268 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_interface.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +/** + * \ingroup communities + * \function igraph_community_fluid_communities + * \brief Community detection based on fluids interacting on the graph. + * + * The algorithm is based on the simple idea of + * several fluids interacting in a non-homogeneous environment + * (the graph topology), expanding and contracting based on their + * interaction and density. + * + * This function implements the community detection method described in: + * Parés F, Gasulla DG, et. al. (2018) Fluid Communities: A Competitive, + * Scalable and Diverse Community Detection Algorithm. In: Complex Networks + * & Their Applications VI: Proceedings of Complex Networks 2017 (The Sixth + * International Conference on Complex Networks and Their Applications), + * Springer, vol 689, p 229. + * + * \param graph The input graph. The graph must be simple and connected. + * Empty graphs are not supported as well as single vertex graphs. + * Edge directions are ignored. Weights are not considered. + * \param no_of_communities The number of communities to be found. Must be + * greater than 0 and fewer than number of vertices in the graph. + * \param membership The result vector mapping vertices to the communities + * they are assigned to. + * \param modularity If not a null pointer, then it must be a pointer + * to a real number. The modularity score of the detected community + * structure is stored here. + * \return Error code. + * + * Time complexity: O(|E|) + */ +int igraph_community_fluid_communities(const igraph_t *graph, + igraph_integer_t no_of_communities, + igraph_vector_t *membership, + igraph_real_t *modularity) { + /* Declaration of variables */ + long int no_of_nodes, i, j, k, kv1; + igraph_adjlist_t al; + double max_density; + igraph_bool_t res, running; + igraph_vector_t node_order, density, label_counters, dominant_labels, nonzero_labels; + igraph_vector_int_t com_to_numvertices; + + /* Initialization of variables needed for initial checking */ + no_of_nodes = igraph_vcount(graph); + + /* Checking input values */ + if (no_of_nodes < 2) { + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_fill(membership, 0); + } + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, 0, 1, igraph_is_directed(graph), modularity)); + } + return IGRAPH_SUCCESS; + } + if ((long int) no_of_communities < 1) { + IGRAPH_ERROR("Number of requested communities must be greater than zero.", IGRAPH_EINVAL); + } + if ((long int) no_of_communities > no_of_nodes) { + IGRAPH_ERROR("Number of requested communities must not be greater than the number of nodes.", + IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_is_simple(graph, &res)); + if (!res) { + IGRAPH_ERROR("Fluid community detection supports only simple graphs.", IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_is_connected(graph, &res, IGRAPH_WEAK)); + if (!res) { + IGRAPH_ERROR("Fluid community detection supports only connected graphs.", IGRAPH_EINVAL); + } + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("Edge directions are ignored by fluid community detection."); + } + + /* Internal variables initialization */ + max_density = 1.0; + + /* Resize membership vector (number of nodes) */ + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + + /* Initialize density and com_to_numvertices vectors */ + IGRAPH_CHECK(igraph_vector_init(&density, (long int) no_of_communities)); + IGRAPH_FINALLY(igraph_vector_destroy, &density); + IGRAPH_CHECK(igraph_vector_int_init(&com_to_numvertices, (long int) no_of_communities)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &com_to_numvertices); + + /* Initialize node ordering vector */ + IGRAPH_CHECK(igraph_vector_init_seq(&node_order, 0, no_of_nodes - 1)); + IGRAPH_FINALLY(igraph_vector_destroy, &node_order); + + /* Initialize the membership vector with 0 values */ + igraph_vector_null(membership); + /* Initialize densities to max_density */ + igraph_vector_fill(&density, max_density); + + /* Initialize com_to_numvertices and initialize communities into membership vector */ + IGRAPH_CHECK(igraph_vector_shuffle(&node_order)); + for (i = 0; i < no_of_communities; i++) { + /* Initialize membership at initial nodes for each community + * where 0 refers to have no label*/ + VECTOR(*membership)[(long int)VECTOR(node_order)[i]] = i + 1.0; + /* Initialize com_to_numvertices list: Number of vertices for each community */ + VECTOR(com_to_numvertices)[i] = 1; + } + + /* Create an adjacency list representation for efficiency. */ + IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + + /* Create storage space for counting distinct labels and dominant ones */ + IGRAPH_VECTOR_INIT_FINALLY(&dominant_labels, (long int) no_of_communities); + IGRAPH_VECTOR_INIT_FINALLY(&nonzero_labels, (long int) no_of_communities); + + IGRAPH_CHECK(igraph_vector_init(&label_counters, (long int) no_of_communities)); + IGRAPH_FINALLY(igraph_vector_destroy, &label_counters); + + /* running is the convergence boolean variable */ + running = 1; + while (running) { + /* Declarations of varibales used inside main loop */ + long int v1, size, rand_idx; + igraph_real_t max_count, label_counter_diff; + igraph_vector_int_t *neis; + igraph_bool_t same_label_in_dominant; + + running = 0; + + /* Shuffle the node ordering vector */ + IGRAPH_CHECK(igraph_vector_shuffle(&node_order)); + /* In the prescribed order, loop over the vertices and reassign labels */ + for (i = 0; i < no_of_nodes; i++) { + /* Clear dominant_labels and nonzero_labels vectors */ + igraph_vector_clear(&dominant_labels); + igraph_vector_null(&label_counters); + + /* Obtain actual node index */ + v1 = (long int) VECTOR(node_order)[i]; + /* Take into account same label in updating rule */ + kv1 = (long int) VECTOR(*membership)[v1]; + max_count = 0.0; + if (kv1 != 0) { + VECTOR(label_counters)[kv1 - 1] += VECTOR(density)[kv1 - 1]; + /* Set up max_count */ + max_count = VECTOR(density)[kv1 - 1]; + /* Initialize dominant_labels */ + IGRAPH_CHECK(igraph_vector_resize(&dominant_labels, 1)); + VECTOR(dominant_labels)[0] = kv1; + } + + /* Count the weights corresponding to different labels */ + neis = igraph_adjlist_get(&al, v1); + size = igraph_vector_int_size(neis); + for (j = 0; j < size; j++) { + k = (long int) VECTOR(*membership)[(long)VECTOR(*neis)[j]]; + /* skip if it has no label yet */ + if (k == 0) { + continue; + } + /* Update label counter and evaluate diff against max_count*/ + VECTOR(label_counters)[k - 1] += VECTOR(density)[k - 1]; + label_counter_diff = VECTOR(label_counters)[k - 1] - max_count; + /* Check if this label must be included in dominant_labels vector */ + if (label_counter_diff > 0.0001) { + max_count = VECTOR(label_counters)[k - 1]; + IGRAPH_CHECK(igraph_vector_resize(&dominant_labels, 1)); + VECTOR(dominant_labels)[0] = k; + } else if (-0.0001 < label_counter_diff && label_counter_diff < 0.0001) { + IGRAPH_CHECK(igraph_vector_push_back(&dominant_labels, k)); + } + } + + RNG_BEGIN(); + if (!igraph_vector_empty(&dominant_labels)) { + /* Maintain same label if it exists in dominant_labels */ + same_label_in_dominant = igraph_vector_contains(&dominant_labels, kv1); + + if (!same_label_in_dominant) { + /* We need at least one more iteration */ + running = 1; + + /* Select randomly from the dominant labels */ + rand_idx = RNG_INTEGER(0, igraph_vector_size(&dominant_labels) - 1); + k = (long int) VECTOR(dominant_labels)[rand_idx]; + + if (kv1 != 0) { + /* Subtract 1 vertex from corresponding community in com_to_numvertices */ + VECTOR(com_to_numvertices)[kv1 - 1] -= 1; + /* Re-calculate density for community kv1 */ + VECTOR(density)[kv1 - 1] = max_density / VECTOR(com_to_numvertices)[kv1 - 1]; + } + + /* Update vertex new label */ + VECTOR(*membership)[v1] = k; + + /* Add 1 vertex to corresponding new community in com_to_numvertices */ + VECTOR(com_to_numvertices)[k - 1] += 1; + /* Re-calculate density for new community k */ + VECTOR(density)[k - 1] = max_density / VECTOR(com_to_numvertices)[k - 1]; + } + } + RNG_END(); + } + } + + + /* Shift back the membership vector */ + /* There must be no 0 labels in membership vector at this point */ + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*membership)[i] -= 1; + /* Something went wrong: At least one vertex has no community assigned */ + if (VECTOR(*membership)[i] < 0) { + IGRAPH_ERROR("Something went wrong during execution. One or more vertices got " + "no community assigned at algorithm convergence.", IGRAPH_EINTERNAL); + } + } + + igraph_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(1); + + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, NULL, + /* resolution */ 1, + /* only undirected */ 0, modularity)); + } + + igraph_vector_destroy(&node_order); + igraph_vector_destroy(&density); + igraph_vector_int_destroy(&com_to_numvertices); + igraph_vector_destroy(&label_counters); + igraph_vector_destroy(&dominant_labels); + igraph_vector_destroy(&nonzero_labels); + IGRAPH_FINALLY_CLEAN(6); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/community/infomap/infomap.cc b/src/rigraph/core/community/infomap/infomap.cc new file mode 100644 index 0000000..35a7ed3 --- /dev/null +++ b/src/rigraph/core/community/infomap/infomap.cc @@ -0,0 +1,325 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + ---- + The original version of this file was written by Martin Rosvall + email: martin.rosvall@physics.umu.se + homePage: http://www.tp.umu.se/~rosvall/ + + It was integrated in igraph by Emmanuel Navarro + email: navarro@irit.fr + homePage: http://www.irit.fr/~Emmanuel.Navarro/ +*/ + +#include +#include "igraph_interface.h" +#include "igraph_community.h" +#include "../../core/interruption.h" + + +#include "infomap_Node.h" +#include "infomap_Greedy.h" + +/****************************************************************************/ +int infomap_partition(FlowGraph * fgraph, bool rcall) { + Greedy * greedy; + + // save the original graph + FlowGraph * cpy_fgraph = new FlowGraph(fgraph); + IGRAPH_FINALLY(delete_FlowGraph, cpy_fgraph); + + int Nnode = cpy_fgraph->Nnode; + // "real" number of vertex, ie. number of vertex of the graph + + int iteration = 0; + double outer_oldCodeLength, newCodeLength; + + int *initial_move = NULL; + bool initial_move_done = true; + + do { // Main loop + outer_oldCodeLength = fgraph->codeLength; + + if (iteration > 0) { + /**********************************************************************/ + // FIRST PART: re-split the network (if need) + // =========================================== + + // intial_move indicate current clustering + initial_move = new int[Nnode]; + // new_cluster_id --> old_cluster_id (save curent clustering state) + + IGRAPH_FINALLY(operator delete [], initial_move); + initial_move_done = false; + + int *subMoveTo = NULL; // enventual new partitionment of original graph + + if ((iteration % 2 == 0) && (fgraph->Nnode > 1)) { + // 0/ Submodule movements : partition each module of the + // current partition (rec. call) + + subMoveTo = new int[Nnode]; + // vid_cpy_fgraph --> new_cluster_id (new partition) + + IGRAPH_FINALLY(operator delete [], subMoveTo); + + int subModIndex = 0; + + for (int i = 0 ; i < fgraph->Nnode ; i++) { + // partition each non trivial module + int sub_Nnode = fgraph->node[i]->members.size(); + if (sub_Nnode > 1) { // If the module is not trivial + int *sub_members = new int[sub_Nnode]; // id_sub --> id + IGRAPH_FINALLY(operator delete [], sub_members); + + for (int j = 0 ; j < sub_Nnode ; j++) { + sub_members[j] = fgraph->node[i]->members[j]; + } + + // extraction of the subgraph + FlowGraph *sub_fgraph = new FlowGraph(cpy_fgraph, sub_Nnode, + sub_members); + IGRAPH_FINALLY(delete_FlowGraph, sub_fgraph); + sub_fgraph->initiate(); + + // recursif call of partitionment on the subgraph + infomap_partition(sub_fgraph, true); + + // Record membership changes + for (int j = 0; j < sub_fgraph->Nnode; j++) { + int Nmembers = sub_fgraph->node[j]->members.size(); + for (int k = 0; k < Nmembers; k++) { + subMoveTo[sub_members[sub_fgraph->node[j]->members[k]]] = + subModIndex; + } + initial_move[subModIndex] = i; + subModIndex++; + } + + delete sub_fgraph; + IGRAPH_FINALLY_CLEAN(1); + delete [] sub_members; + IGRAPH_FINALLY_CLEAN(1); + } else { + subMoveTo[fgraph->node[i]->members[0]] = subModIndex; + initial_move[subModIndex] = i; + subModIndex++; + } + } + } else { + // 1/ Single-node movements : allows each node to move (again) + // save current modules + for (int i = 0; i < fgraph->Nnode; i++) { // for each module + int Nmembers = fgraph->node[i]->members.size(); // Module size + for (int j = 0; j < Nmembers; j++) { // for each vertex (of the module) + initial_move[fgraph->node[i]->members[j]] = i; + } + } + } + + fgraph->back_to(cpy_fgraph); + if (subMoveTo) { + Greedy *cpy_greedy = new Greedy(fgraph); + IGRAPH_FINALLY(delete_Greedy, cpy_greedy); + + cpy_greedy->setMove(subMoveTo); + cpy_greedy->apply(false); + + delete_Greedy(cpy_greedy); + IGRAPH_FINALLY_CLEAN(1); + delete [] subMoveTo; + IGRAPH_FINALLY_CLEAN(1); + } + } + /**********************************************************************/ + // SECOND PART: greedy optimizing it self + // =========================================== + double oldCodeLength; + + do { + // greedy optimizing object creation + greedy = new Greedy(fgraph); + IGRAPH_FINALLY(delete_Greedy, greedy); + + // Initial move to apply ? + if (!initial_move_done && initial_move) { + initial_move_done = true; + greedy->setMove(initial_move); + } + + oldCodeLength = greedy->codeLength; + bool moved = true; + int Nloops = 0; + //int count = 0; + double inner_oldCodeLength = 1000; + + while (moved) { // main greedy optimizing loop + inner_oldCodeLength = greedy->codeLength; + moved = greedy->optimize(); + + Nloops++; + //count++; + + if (fabs(greedy->codeLength - inner_oldCodeLength) < 1.0e-10) + // if the move does'n reduce the codelenght -> exit ! + { + moved = false; + } + + //if (count == 10) { + // greedy->tune(); + // count = 0; + //} + } + + // transform the network to network of modules: + greedy->apply(true); + newCodeLength = greedy->codeLength; + + // destroy greedy object + delete greedy; + IGRAPH_FINALLY_CLEAN(1); + + } while (oldCodeLength - newCodeLength > 1.0e-10); + // while there is some improvement + + if (iteration > 0) { + delete [] initial_move; + IGRAPH_FINALLY_CLEAN(1); + } + + iteration++; + if (!rcall) { + IGRAPH_ALLOW_INTERRUPTION(); + } + } while (outer_oldCodeLength - newCodeLength > 1.0e-10); + + delete cpy_fgraph; + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_community_infomap + * \brief Find community structure that minimizes the expected + * description length of a random walker trajectory. + * + * Implementation of the InfoMap community detection algorithm.of + * Martin Rosvall and Carl T. Bergstrom. + * + * See : + * Visualization of the math and the map generator: www.mapequation.org + * [2] The original paper: M. Rosvall and C. T. Bergstrom, Maps of + * information flow reveal community structure in complex networks, PNAS + * 105, 1118 (2008) [http://dx.doi.org/10.1073/pnas.0706851105 , + * http://arxiv.org/abs/0707.0609 ] + * [3] A more detailed paper: M. Rosvall, D. Axelsson, and C. T. Bergstrom, + * The map equation, Eur. Phys. J. Special Topics 178, 13 (2009). + * [http://dx.doi.org/10.1140/epjst/e2010-01179-1 , + * http://arxiv.org/abs/0906.1405 ] + + * + * The original C++ implementation of Martin Rosvall is used, + * see http://www.tp.umu.se/~rosvall/downloads/infomap_undir.tgz . + * Intergation in igraph has be done by Emmanuel Navarro (who is grateful to + * Martin Rosvall and Carl T. Bergstrom for providing this source code.) + * + * + * Note that the graph must not contain isolated vertices. + * + * + * If you want to specify a random seed (as in original + * implementation) you can use \ref igraph_rng_seed(). + * + * \param graph The input graph. + * \param e_weights Numeric vector giving the weights of the edges. + * If it is a NULL pointer then all edges will have equal + * weights. The weights are expected to be positive. + * \param v_weights Numeric vector giving the weights of the vertices. + * If it is a NULL pointer then all vertices will have equal + * weights. The weights are expected to be positive. + * \param nb_trials The number of attempts to partition the network + * (can be any integer value equal or larger than 1). + * \param membership Pointer to a vector. The membership vector is + * stored here. + * \param codelength Pointer to a real. If not NULL the code length of the + * partition is stored here. + * \return Error code. + * + * \sa \ref igraph_community_spinglass(), \ref + * igraph_community_edge_betweenness(), \ref igraph_community_walktrap(). + * + * Time complexity: TODO. + */ +int igraph_community_infomap(const igraph_t * graph, + const igraph_vector_t *e_weights, + const igraph_vector_t *v_weights, + int nb_trials, + igraph_vector_t *membership, + igraph_real_t *codelength) { + + FlowGraph * fgraph = new FlowGraph(graph, e_weights, v_weights); + IGRAPH_FINALLY(delete_FlowGraph, fgraph); + + // compute stationary distribution + fgraph->initiate(); + + FlowGraph * cpy_fgraph ; + double shortestCodeLength = 1000.0; + + // create membership vector + int Nnode = fgraph->Nnode; + IGRAPH_CHECK(igraph_vector_resize(membership, Nnode)); + + for (int trial = 0; trial < nb_trials; trial++) { + cpy_fgraph = new FlowGraph(fgraph); + IGRAPH_FINALLY(delete_FlowGraph, cpy_fgraph); + + //partition the network + IGRAPH_CHECK(infomap_partition(cpy_fgraph, false)); + + // if better than the better... + if (cpy_fgraph->codeLength < shortestCodeLength) { + shortestCodeLength = cpy_fgraph->codeLength; + // ... store the partition + for (int i = 0 ; i < cpy_fgraph->Nnode ; i++) { + int Nmembers = cpy_fgraph->node[i]->members.size(); + for (int k = 0; k < Nmembers; k++) { + //cluster[ cpy_fgraph->node[i]->members[k] ] = i; + VECTOR(*membership)[cpy_fgraph->node[i]->members[k]] = i; + } + } + } + + delete_FlowGraph(cpy_fgraph); + IGRAPH_FINALLY_CLEAN(1); + } + + *codelength = (igraph_real_t) shortestCodeLength / log(2.0); + + delete fgraph; + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_reindex_membership(membership, 0, 0)); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/community/infomap/infomap_FlowGraph.cc b/src/rigraph/core/community/infomap/infomap_FlowGraph.cc new file mode 100644 index 0000000..9af7eb3 --- /dev/null +++ b/src/rigraph/core/community/infomap/infomap_FlowGraph.cc @@ -0,0 +1,420 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "infomap_FlowGraph.h" + +#define plogp( x ) ( (x) > 0.0 ? (x)*log(x) : 0.0 ) + +using namespace std; + +void FlowGraph::init(int n, const igraph_vector_t *v_weights) { + alpha = 0.15; + beta = 1.0 - alpha; + Nnode = n; + node = new Node*[Nnode]; + if (v_weights) { + for (int i = 0; i < Nnode; i++) { + node[i] = new Node(i, (double)VECTOR(*v_weights)[i]); + } + } else { + for (int i = 0; i < Nnode; i++) { + node[i] = new Node(i, 1.0); + } + } +} + +FlowGraph::FlowGraph(int n) { + init(n, NULL); +} + +FlowGraph::FlowGraph(int n, const igraph_vector_t *v_weights) { + init(n, v_weights); +} + +/* Build the graph from igraph_t object + */ +FlowGraph::FlowGraph(const igraph_t * graph, + const igraph_vector_t *e_weights, + const igraph_vector_t *v_weights) { + + int n = (int)igraph_vcount(graph); + init(n, v_weights); + + int directed = (int) igraph_is_directed(graph); + + double linkWeight = 1.0; + igraph_integer_t from, to; + + long int Nlinks = (long int) igraph_ecount(graph); + if (!directed) { + Nlinks = Nlinks * 2 ; + } + for (int i = 0; i < Nlinks; i++) { + if (!directed) { // not directed + if (i % 2 == 0) { + linkWeight = e_weights ? (double)VECTOR(*e_weights)[i / 2] : 1.0; + igraph_edge(graph, i / 2, &from, &to); + } else { + igraph_edge(graph, (i - 1) / 2, &to, &from); + } + } else { // directed + linkWeight = e_weights ? (double)VECTOR(*e_weights)[i] : 1.0; + igraph_edge(graph, i, &from, &to); + } + + // Populate node from igraph_graph + if (linkWeight > 0.0) { + if (from != to) { + node[(int) from]->outLinks.push_back(make_pair((int)to, linkWeight)); + node[(int) to]->inLinks.push_back(make_pair((int) from, linkWeight)); + } + } + } +} + +FlowGraph::FlowGraph(FlowGraph * fgraph) { + int n = fgraph->Nnode; + init(n, NULL); + for (int i = 0; i < n; i++) { + cpyNode(node[i], fgraph->node[i]); + } + + //XXX: quid de danglings et Ndanglings? + + alpha = fgraph->alpha ; + beta = fgraph->beta ; + + exit = fgraph->exit; + exitFlow = fgraph->exitFlow; + exit_log_exit = fgraph->exit_log_exit; + size_log_size = fgraph->size_log_size ; + nodeSize_log_nodeSize = fgraph->nodeSize_log_nodeSize; + + codeLength = fgraph->codeLength; +} + +/** construct a graph by extracting a subgraph from the given graph + */ +FlowGraph::FlowGraph(FlowGraph * fgraph, int sub_Nnode, int * sub_members) { + init(sub_Nnode, NULL); + + //XXX: use set of integer to ensure that elements are sorted + set sub_mem; + for (int j = 0 ; j < sub_Nnode ; j++) { + sub_mem.insert(sub_members[j]); + } + set::iterator it_mem = sub_mem.begin(); + + vector sub_renumber = vector(fgraph->Nnode); + // id --> sub_id + + for (int j = 0; j < fgraph->Nnode; j++) { + sub_renumber[j] = -1; + } + + + for (int j = 0; j < sub_Nnode; j++) { + //int orig_nr = sub_members[j]; + int orig_nr = (*it_mem); + + node[j]->teleportWeight = fgraph->node[orig_nr]->teleportWeight; + node[j]->selfLink = fgraph->node[orig_nr]->selfLink; + // Take care of self-link + + int orig_NoutLinks = fgraph->node[orig_nr]->outLinks.size(); + int orig_NinLinks = fgraph->node[orig_nr]->inLinks.size(); + + sub_renumber[orig_nr] = j; + + for (int k = 0; k < orig_NoutLinks; k++) { + int to = fgraph->node[orig_nr]->outLinks[k].first; + int to_newnr = sub_renumber[to]; + double link_weight = fgraph->node[orig_nr]->outLinks[k].second; + + if (to < orig_nr) { + // we add links if the destination (to) has already be seen + // (ie. smaller than current id) => orig + + if (sub_mem.find(to) != sub_mem.end()) { + // printf("%2d | %4d to %4d\n", j, orig_nr, to); + // printf("from %4d (%4d:%1.5f) to %4d (%4d)\n", j, orig_nr, + // node[j]->selfLink, to_newnr, to); + node[j]->outLinks.push_back(make_pair(to_newnr, link_weight)); + node[to_newnr]->inLinks.push_back(make_pair(j, link_weight)); + } + } + } + + for (int k = 0; k < orig_NinLinks; k++) { + int to = fgraph->node[orig_nr]->inLinks[k].first; + int to_newnr = sub_renumber[to]; + double link_weight = fgraph->node[orig_nr]->inLinks[k].second; + if (to < orig_nr) { + if (sub_mem.find(to) != sub_mem.end()) { + node[j]->inLinks.push_back(make_pair(to_newnr, link_weight)); + node[to_newnr]->outLinks.push_back(make_pair(j, link_weight)); + } + } + } + it_mem++; + } +} + + +FlowGraph::~FlowGraph() { + //printf("delete FlowGraph !\n"); + for (int i = 0; i < Nnode; i++) { + delete node[i]; + } + delete [] node; +} + +void delete_FlowGraph(FlowGraph *fgraph) { + delete fgraph; +} + + +/** Swap the graph with the one given + the graph is "re" calibrate + but NOT the given one. + */ +void FlowGraph::swap(FlowGraph * fgraph) { + Node ** node_tmp = fgraph->node; + int Nnode_tmp = fgraph->Nnode; + + fgraph->node = node; + fgraph->Nnode = Nnode; + + node = node_tmp; + Nnode = Nnode_tmp; + + calibrate(); +} + +/** Initialisation of the graph, compute the flow inside the graph + * - count danglings nodes + * - normalized edge weights + * - Call eigenvector() to compute steady state distribution + * - call calibrate to compute codelenght + */ +void FlowGraph::initiate() { + // Take care of dangling nodes, normalize outLinks, and calculate + // total teleport weight + Ndanglings = 0; + double totTeleportWeight = 0.0; + for (int i = 0; i < Nnode; i++) { + totTeleportWeight += node[i]->teleportWeight; + } + + for (int i = 0; i < Nnode; i++) { + node[i]->teleportWeight /= totTeleportWeight; + // normalize teleportation weight + + if (node[i]->outLinks.empty() && (node[i]->selfLink <= 0.0)) { + danglings.push_back(i); + Ndanglings++; + } else { // Normalize the weights + int NoutLinks = node[i]->outLinks.size(); + double sum = node[i]->selfLink; // Take care of self-links + for (int j = 0; j < NoutLinks; j++) { + sum += node[i]->outLinks[j].second; + } + node[i]->selfLink /= sum; + for (int j = 0; j < NoutLinks; j++) { + node[i]->outLinks[j].second /= sum; + } + } + } + + // Calculate steady state matrix + eigenvector(); + + // Update links to represent flow + for (int i = 0; i < Nnode; i++) { + node[i]->selfLink = beta * node[i]->size * node[i]->selfLink; + // (1 - \tau) * \pi_i * P_{ii} + + if (!node[i]->outLinks.empty()) { + int NoutLinks = node[i]->outLinks.size(); + for (int j = 0; j < NoutLinks; j++) { + node[i]->outLinks[j].second = beta * node[i]->size * + node[i]->outLinks[j].second; + // (1 - \tau) * \pi_i * P_{ij} + } + + // Update values for corresponding inlink + for (int j = 0; j < NoutLinks; j++) { + int NinLinks = node[node[i]->outLinks[j].first]->inLinks.size(); + for (int k = 0; k < NinLinks; k++) { + if (node[node[i]->outLinks[j].first]->inLinks[k].first == i) { + node[node[i]->outLinks[j].first]->inLinks[k].second = + node[i]->outLinks[j].second; + k = NinLinks; + } + } + } + } + } + + // To be able to handle dangling nodes efficiently + for (int i = 0; i < Nnode; i++) + if (node[i]->outLinks.empty() && (node[i]->selfLink <= 0.0)) { + node[i]->danglingSize = node[i]->size; + } else { + node[i]->danglingSize = 0.0; + } + + nodeSize_log_nodeSize = 0.0 ; + // The exit flow from each node at initiation + for (int i = 0; i < Nnode; i++) { + node[i]->exit = node[i]->size // Proba to be on i + - (alpha * node[i]->size + beta * node[i]->danglingSize) * + node[i]->teleportWeight // Proba teleport back to i + - node[i]->selfLink; // Proba stay on i + + // node[i]->exit == q_{i\exit} + nodeSize_log_nodeSize += plogp(node[i]->size); + } + + calibrate(); +} + + +/* Compute steady state distribution (ie. PageRank) over the network + * (for all i update node[i]->size) + */ +void FlowGraph::eigenvector() { + vector size_tmp = vector(Nnode, 1.0 / Nnode); + + int Niterations = 0; + double danglingSize; + + double sqdiff = 1.0; + double sqdiff_old; + double sum; + do { + // Calculate dangling size + danglingSize = 0.0; + for (int i = 0; i < Ndanglings; i++) { + danglingSize += size_tmp[danglings[i]]; + } + + // Flow from teleportation + for (int i = 0; i < Nnode; i++) { + node[i]->size = (alpha + beta * danglingSize) * node[i]->teleportWeight; + } + + // Flow from network steps + for (int i = 0; i < Nnode; i++) { + node[i]->size += beta * node[i]->selfLink * size_tmp[i]; + int Nlinks = node[i]->outLinks.size(); + for (int j = 0; j < Nlinks; j++) + node[node[i]->outLinks[j].first]->size += beta * + node[i]->outLinks[j].second * size_tmp[i]; + } + + // Normalize + sum = 0.0; + for (int i = 0; i < Nnode; i++) { + sum += node[i]->size; + } + sqdiff_old = sqdiff; + sqdiff = 0.0; + for (int i = 0; i < Nnode; i++) { + node[i]->size /= sum; + sqdiff += fabs(node[i]->size - size_tmp[i]); + size_tmp[i] = node[i]->size; + } + Niterations++; + + if (sqdiff == sqdiff_old) { + alpha += 1.0e-10; + beta = 1.0 - alpha; + } + + } while ((Niterations < 200) && (sqdiff > 1.0e-15 || Niterations < 50)); + + danglingSize = 0.0; + for (int i = 0; i < Ndanglings; i++) { + danglingSize += size_tmp[danglings[i]]; + } + // cout << "done! (the error is " << sqdiff << " after " << Niterations + // << " iterations)" << endl; +} + + +/* Compute the codeLength of the given network + * note: (in **node, one node == one module) + */ +void FlowGraph::calibrate() { + exit_log_exit = 0.0; + exitFlow = 0.0; + size_log_size = 0.0; + + for (int i = 0; i < Nnode; i++) { // For each module + // own node/module codebook + size_log_size += plogp(node[i]->exit + node[i]->size); + + // use of index codebook + exitFlow += node[i]->exit; + exit_log_exit += plogp(node[i]->exit); + } + + exit = plogp(exitFlow); + + codeLength = exit - 2.0 * exit_log_exit + size_log_size - + nodeSize_log_nodeSize; +} + + +/* Restore the data from the given FlowGraph object + */ +void FlowGraph::back_to(FlowGraph * fgraph) { + // delete current nodes + for (int i = 0 ; i < Nnode ; i++) { + delete node[i]; + } + delete [] node; + + Nnode = fgraph->Nnode; + + // copy original ones + node = new Node*[Nnode]; + for (int i = 0; i < Nnode; i++) { + node[i] = new Node(); + cpyNode(node[i], fgraph->node[i]); + } + + // restore atributs + alpha = fgraph->alpha ; + beta = fgraph->beta ; + + exit = fgraph->exit; + exitFlow = fgraph->exitFlow; + exit_log_exit = fgraph->exit_log_exit; + size_log_size = fgraph->size_log_size ; + nodeSize_log_nodeSize = fgraph->nodeSize_log_nodeSize; + + codeLength = fgraph->codeLength; +} diff --git a/src/rigraph/core/community/infomap/infomap_FlowGraph.h b/src/rigraph/core/community/infomap/infomap_FlowGraph.h new file mode 100644 index 0000000..937347c --- /dev/null +++ b/src/rigraph/core/community/infomap/infomap_FlowGraph.h @@ -0,0 +1,78 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef FLOWGRAPH_H +#define FLOWGRAPH_H + +#include +#include + +#include "igraph_interface.h" + +#include "infomap_Node.h" + +class FlowGraph { +private: + void init(int n, const igraph_vector_t *nodeWeights); + +public: + FlowGraph(int n); + FlowGraph(int n, const igraph_vector_t *nodeWeights); + FlowGraph(FlowGraph * fgraph); + FlowGraph(FlowGraph * fgraph, int sub_Nnode, int * sub_members); + + FlowGraph(const igraph_t * graph, const igraph_vector_t *e_weights, + const igraph_vector_t *v_weights); + + ~FlowGraph(); + + void swap(FlowGraph * fgraph); + + void initiate(); + void eigenvector(); + void calibrate(); + + void back_to(FlowGraph * fgraph); + + /*************************************************************************/ + Node **node; + int Nnode; + + double alpha, beta; + + int Ndanglings; + std::vector danglings; // id of dangling nodes + + double exit; // + double exitFlow; // + double exit_log_exit; // + double size_log_size; // + double nodeSize_log_nodeSize; // \sum_{v in V} p log(p) + + double codeLength; +}; + +void delete_FlowGraph(FlowGraph *fgraph); + +#endif diff --git a/src/rigraph/core/community/infomap/infomap_Greedy.cc b/src/rigraph/core/community/infomap/infomap_Greedy.cc new file mode 100644 index 0000000..62e2945 --- /dev/null +++ b/src/rigraph/core/community/infomap/infomap_Greedy.cc @@ -0,0 +1,612 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "infomap_Greedy.h" +#include +#define plogp( x ) ( (x) > 0.0 ? (x)*log(x) : 0.0 ) + +using namespace std; + +Greedy::Greedy(FlowGraph * fgraph) { + graph = fgraph; + Nnode = graph->Nnode; + + alpha = graph->alpha;// teleportation probability + beta = 1.0 - alpha; // probability to take normal step + + Nempty = 0; + vector(Nnode).swap(mod_empty); + + vector(Nnode).swap(node_index); + vector(Nnode).swap(mod_exit); + vector(Nnode).swap(mod_size); + vector(Nnode).swap(mod_danglingSize); + vector(Nnode).swap(mod_teleportWeight); + vector(Nnode).swap(mod_members); + + nodeSize_log_nodeSize = graph->nodeSize_log_nodeSize; + exit_log_exit = graph->exit_log_exit; + size_log_size = graph->size_log_size; + exitFlow = graph->exitFlow; + + Node ** node = graph->node; + for (int i = 0; i < Nnode; i++) { // For each module + node_index[i] = i; + mod_exit[i] = node[i]->exit; + mod_size[i] = node[i]->size; + + mod_danglingSize[i] = node[i]->danglingSize; + mod_teleportWeight[i] = node[i]->teleportWeight; + mod_members[i] = node[i]->members.size(); + } + + exit = plogp(exitFlow); + + codeLength = exit - 2.0 * exit_log_exit + size_log_size - + nodeSize_log_nodeSize; +} + +Greedy::~Greedy() { +} + +void delete_Greedy(Greedy *greedy) { + delete greedy; +} + + +/** Greedy optimizing (as in Blodel and Al.) : + * for each vertex (selected in a random order) compute the best possible move within neighborhood + */ +bool Greedy::optimize() { + bool moved = false; + Node ** node = graph->node; + + RNG_BEGIN(); + + // Generate random enumeration of nodes + vector randomOrder(Nnode); + for (int i = 0; i < Nnode; i++) { + randomOrder[i] = i; + } + + for (int i = 0; i < Nnode - 1; i++) { + //int randPos = i ; //XXX + int randPos = RNG_INTEGER(i, Nnode - 1); + // swap i & randPos + int tmp = randomOrder[i]; + randomOrder[i] = randomOrder[randPos]; + randomOrder[randPos] = tmp; + } + + unsigned int offset = 1; + vector redirect(Nnode, 0); + vector > > flowNtoM(Nnode); + + for (int k = 0; k < Nnode; k++) { + + // Pick nodes in random order + int flip = randomOrder[k]; + int oldM = node_index[flip]; + + // Reset offset when int overflows + if (offset > INT_MAX) { + for (int j = 0; j < Nnode; j++) { + redirect[j] = 0; + } + offset = 1; + } + // Size of vector with module links + int NmodLinks = 0; + // For all outLinks + int NoutLinks = node[flip]->outLinks.size(); + if (NoutLinks == 0) { //dangling node, add node to calculate flow below + redirect[oldM] = offset + NmodLinks; + flowNtoM[NmodLinks].first = oldM; + flowNtoM[NmodLinks].second.first = 0.0; + flowNtoM[NmodLinks].second.second = 0.0; + NmodLinks++; + } else { + for (int j = 0; j < NoutLinks; j++) { + int nb_M = node_index[node[flip]->outLinks[j].first]; + // index destination du lien + double nb_flow = node[flip]->outLinks[j].second; + // wgt du lien + if (redirect[nb_M] >= offset) { + flowNtoM[redirect[nb_M] - offset].second.first += nb_flow; + } else { + redirect[nb_M] = offset + NmodLinks; + flowNtoM[NmodLinks].first = nb_M; + flowNtoM[NmodLinks].second.first = nb_flow; + flowNtoM[NmodLinks].second.second = 0.0; + NmodLinks++; + } + } + } + // For all inLinks + int NinLinks = node[flip]->inLinks.size(); + for (int j = 0; j < NinLinks; j++) { + int nb_M = node_index[node[flip]->inLinks[j].first]; + double nb_flow = node[flip]->inLinks[j].second; + + if (redirect[nb_M] >= offset) { + flowNtoM[redirect[nb_M] - offset].second.second += nb_flow; + } else { + redirect[nb_M] = offset + NmodLinks; + flowNtoM[NmodLinks].first = nb_M; + flowNtoM[NmodLinks].second.first = 0.0; + flowNtoM[NmodLinks].second.second = nb_flow; + NmodLinks++; + } + } + + // For teleportation and dangling nodes + for (int j = 0; j < NmodLinks; j++) { + int newM = flowNtoM[j].first; + if (newM == oldM) { + flowNtoM[j].second.first += + (alpha * node[flip]->size + beta * node[flip]->danglingSize) * + (mod_teleportWeight[oldM] - node[flip]->teleportWeight); + flowNtoM[j].second.second += + (alpha * (mod_size[oldM] - node[flip]->size) + + beta * (mod_danglingSize[oldM] - node[flip]->danglingSize)) * + node[flip]->teleportWeight; + } else { + flowNtoM[j].second.first += + (alpha * node[flip]->size + beta * node[flip]->danglingSize) * + mod_teleportWeight[newM]; + flowNtoM[j].second.second += + (alpha * mod_size[newM] + beta * mod_danglingSize[newM] ) * + node[flip]->teleportWeight; + } + } + + // Calculate flow to/from own module (default value if no link to + // own module) + double outFlowOldM = + (alpha * node[flip]->size + beta * node[flip]->danglingSize) * + (mod_teleportWeight[oldM] - node[flip]->teleportWeight) ; + double inFlowOldM = + (alpha * (mod_size[oldM] - node[flip]->size) + + beta * (mod_danglingSize[oldM] - node[flip]->danglingSize)) * + node[flip]->teleportWeight; + if (redirect[oldM] >= offset) { + outFlowOldM = flowNtoM[redirect[oldM] - offset].second.first; + inFlowOldM = flowNtoM[redirect[oldM] - offset].second.second; + } + + // Option to move to empty module (if node not already alone) + if (mod_members[oldM] > static_cast(node[flip]->members.size())) { + if (Nempty > 0) { + flowNtoM[NmodLinks].first = mod_empty[Nempty - 1]; + flowNtoM[NmodLinks].second.first = 0.0; + flowNtoM[NmodLinks].second.second = 0.0; + NmodLinks++; + } + } + + // Randomize link order for optimized search + for (int j = 0; j < NmodLinks - 1; j++) { + //int randPos = j ; // XXX + int randPos = RNG_INTEGER(j, NmodLinks - 1); + int tmp_M = flowNtoM[j].first; + double tmp_outFlow = flowNtoM[j].second.first; + double tmp_inFlow = flowNtoM[j].second.second; + flowNtoM[j].first = flowNtoM[randPos].first; + flowNtoM[j].second.first = flowNtoM[randPos].second.first; + flowNtoM[j].second.second = flowNtoM[randPos].second.second; + flowNtoM[randPos].first = tmp_M; + flowNtoM[randPos].second.first = tmp_outFlow; + flowNtoM[randPos].second.second = tmp_inFlow; + } + + int bestM = oldM; + double best_outFlow = 0.0; + double best_inFlow = 0.0; + double best_delta = 0.0; + + // Find the move that minimizes the description length + for (int j = 0; j < NmodLinks; j++) { + + int newM = flowNtoM[j].first; + double outFlowNewM = flowNtoM[j].second.first; + double inFlowNewM = flowNtoM[j].second.second; + + if (newM != oldM) { + + double delta_exit = plogp(exitFlow + outFlowOldM + inFlowOldM - + outFlowNewM - inFlowNewM) - exit; + + double delta_exit_log_exit = - plogp(mod_exit[oldM]) - + plogp(mod_exit[newM]) + + plogp(mod_exit[oldM] - node[flip]->exit + outFlowOldM + inFlowOldM) + + plogp(mod_exit[newM] + node[flip]->exit - outFlowNewM - + inFlowNewM); + + double delta_size_log_size = - plogp(mod_exit[oldM] + mod_size[oldM]) + - plogp(mod_exit[newM] + mod_size[newM]) + + plogp(mod_exit[oldM] + mod_size[oldM] - node[flip]->exit - + node[flip]->size + outFlowOldM + inFlowOldM) + + plogp(mod_exit[newM] + mod_size[newM] + node[flip]->exit + + node[flip]->size - outFlowNewM - inFlowNewM); + + double deltaL = delta_exit - 2.0 * delta_exit_log_exit + + delta_size_log_size; + + if (deltaL - best_delta < -1e-10) { + bestM = newM; + best_outFlow = outFlowNewM; + best_inFlow = inFlowNewM; + best_delta = deltaL; + } + } + } + + // Make best possible move + if (bestM != oldM) { + //Update empty module vector + if (mod_members[bestM] == 0) { + Nempty--; + } + if (mod_members[oldM] == static_cast(node[flip]->members.size())) { + mod_empty[Nempty] = oldM; + Nempty++; + } + + exitFlow -= mod_exit[oldM] + mod_exit[bestM]; + + exit_log_exit -= plogp(mod_exit[oldM]) + plogp(mod_exit[bestM]); + size_log_size -= plogp(mod_exit[oldM] + mod_size[oldM]) + + plogp(mod_exit[bestM] + mod_size[bestM]); + + mod_exit[oldM] -= node[flip]->exit - outFlowOldM - + inFlowOldM; + mod_size[oldM] -= node[flip]->size; + mod_danglingSize[oldM] -= node[flip]->danglingSize; + mod_teleportWeight[oldM] -= node[flip]->teleportWeight; + mod_members[oldM] -= node[flip]->members.size(); + + mod_exit[bestM] += node[flip]->exit - best_outFlow - + best_inFlow; + mod_size[bestM] += node[flip]->size; + mod_danglingSize[bestM] += node[flip]->danglingSize; + mod_teleportWeight[bestM] += node[flip]->teleportWeight; + mod_members[bestM] += node[flip]->members.size(); + + exitFlow += mod_exit[oldM] + mod_exit[bestM]; + + // Update terms in map equation + + exit_log_exit += plogp(mod_exit[oldM]) + plogp(mod_exit[bestM]); + size_log_size += plogp(mod_exit[oldM] + mod_size[oldM]) + + plogp(mod_exit[bestM] + mod_size[bestM]); + exit = plogp(exitFlow); + + // Update code length + + codeLength = exit - 2.0 * exit_log_exit + size_log_size - + nodeSize_log_nodeSize; + + node_index[flip] = bestM; + moved = true; + } + offset += Nnode; + } + + RNG_END(); + + return moved; +} + +/** Apply the move to the given network + */ +void Greedy::apply(bool sort) { +//void Greedy::level(Node ***node_tmp, bool sort) { + + //old fct prepare(sort) + vector modSnode; // will give ids of no-empty modules (nodes) + int Nmod = 0; + if (sort) { + multimap Msize; + for (int i = 0; i < Nnode; i++) { + if (mod_members[i] > 0) { + Nmod++; + Msize.insert(pair(mod_size[i], i)); + } + } + for (multimap::reverse_iterator it = Msize.rbegin(); + it != Msize.rend(); it++) { + modSnode.push_back(it->second); + } + } else { + for (int i = 0; i < Nnode; i++) { + if (mod_members[i] > 0) { + Nmod++; + modSnode.push_back(i); + } + } + } + //modSnode[id_when_no_empty_node] = id_in_mod_tbl + + // Create the new graph + FlowGraph * tmp_fgraph = new FlowGraph(Nmod); + IGRAPH_FINALLY(delete_FlowGraph, tmp_fgraph); + Node ** node_tmp = tmp_fgraph->node ; + + Node ** node = graph->node; + + vector nodeInMod = vector(Nnode); + + // creation of new nodes + for (int i = 0; i < Nmod; i++) { + //node_tmp[i] = new Node(); + vector().swap(node_tmp[i]->members); // clear membership + node_tmp[i]->exit = mod_exit[modSnode[i]]; + node_tmp[i]->size = mod_size[modSnode[i]]; + node_tmp[i]->danglingSize = mod_danglingSize[modSnode[i]]; + node_tmp[i]->teleportWeight = mod_teleportWeight[modSnode[i]]; + + nodeInMod[modSnode[i]] = i; + } + //nodeInMode[id_in_mod_tbl] = id_when_no_empty_node + + // Calculate outflow of links to different modules + vector > outFlowNtoM(Nmod); + map::iterator it_M; + + for (int i = 0; i < Nnode; i++) { + int i_M = nodeInMod[node_index[i]]; //final id of the module of the node i + // add node members to the module + copy( node[i]->members.begin(), node[i]->members.end(), + back_inserter( node_tmp[i_M]->members ) ); + + int NoutLinks = node[i]->outLinks.size(); + for (int j = 0; j < NoutLinks; j++) { + int nb = node[i]->outLinks[j].first; + int nb_M = nodeInMod[node_index[nb]]; + double nb_flow = node[i]->outLinks[j].second; + if (nb != i) { + it_M = outFlowNtoM[i_M].find(nb_M); + if (it_M != outFlowNtoM[i_M].end()) { + it_M->second += nb_flow; + } else { + outFlowNtoM[i_M].insert(make_pair(nb_M, nb_flow)); + } + } + } + } + + // Create outLinks at new level + for (int i = 0; i < Nmod; i++) { + for (it_M = outFlowNtoM[i].begin(); it_M != outFlowNtoM[i].end(); it_M++) { + if (it_M->first != i) { + node_tmp[i]->outLinks.push_back(make_pair(it_M->first, it_M->second)); + } + } + } + + // Calculate inflow of links from different modules + vector > inFlowNtoM(Nmod); + + for (int i = 0; i < Nnode; i++) { + int i_M = nodeInMod[node_index[i]]; + int NinLinks = node[i]->inLinks.size(); + for (int j = 0; j < NinLinks; j++) { + int nb = node[i]->inLinks[j].first; + int nb_M = nodeInMod[node_index[nb]]; + double nb_flow = node[i]->inLinks[j].second; + if (nb != i) { + it_M = inFlowNtoM[i_M].find(nb_M); + if (it_M != inFlowNtoM[i_M].end()) { + it_M->second += nb_flow; + } else { + inFlowNtoM[i_M].insert(make_pair(nb_M, nb_flow)); + } + } + } + } + + // Create inLinks at new level + for (int i = 0; i < Nmod; i++) { + for (it_M = inFlowNtoM[i].begin(); it_M != inFlowNtoM[i].end(); it_M++) { + if (it_M->first != i) { + node_tmp[i]->inLinks.push_back(make_pair(it_M->first, it_M->second)); + } + } + } + + // Option to move to empty module + vector().swap(mod_empty); + Nempty = 0; + + //swap node between tmp_graph and graph, then destroy tmp_fgraph + graph->swap(tmp_fgraph); + Nnode = Nmod; + + delete tmp_fgraph; + IGRAPH_FINALLY_CLEAN(1); +} + + +/** + * RAZ et recalcul : + * - mod_exit + * - mod_size + * - mod_danglingSize + * - mod_teleportWeight + * - mod_members + * and + * - exit_log_exit + * - size_log_size + * - exitFlow + * - exit + * - codeLength + * according to **node / node[i]->index + */ +void Greedy::tune(void) { + + exit_log_exit = 0.0; + size_log_size = 0.0; + exitFlow = 0.0; + + for (int i = 0; i < Nnode; i++) { + mod_exit[i] = 0.0; + mod_size[i] = 0.0; + mod_danglingSize[i] = 0.0; + mod_teleportWeight[i] = 0.0; + mod_members[i] = 0; + } + + Node ** node = graph->node; + // Update all values except contribution from teleportation + for (int i = 0; i < Nnode; i++) { + int i_M = node_index[i]; // module id of node i + int Nlinks = node[i]->outLinks.size(); + + mod_size[i_M] += node[i]->size; + mod_danglingSize[i_M] += node[i]->danglingSize; + mod_teleportWeight[i_M] += node[i]->teleportWeight; + mod_members[i_M]++; + + for (int j = 0; j < Nlinks; j++) { + int neighbor = node[i]->outLinks[j].first; + double neighbor_w = node[i]->outLinks[j].second; + int neighbor_M = node_index[neighbor]; + if (i_M != neighbor_M) { // neighbor in an other module + mod_exit[i_M] += neighbor_w; + } + } + } + + // Update contribution from teleportation + for (int i = 0; i < Nnode; i++) { + mod_exit[i] += (alpha * mod_size[i] + beta * mod_danglingSize[i]) * + (1.0 - mod_teleportWeight[i]); + } + + for (int i = 0; i < Nnode; i++) { + exit_log_exit += plogp(mod_exit[i]); + size_log_size += plogp(mod_exit[i] + mod_size[i]); + exitFlow += mod_exit[i]; + } + exit = plogp(exitFlow); + + codeLength = exit - 2.0 * exit_log_exit + size_log_size - + nodeSize_log_nodeSize; +} + + +/* Compute the new CodeSize if modules are merged as indicated by moveTo + */ +void Greedy::setMove(int *moveTo) { + //void Greedy::determMove(int *moveTo) { + Node ** node = graph->node; + //printf("setMove nNode:%d \n", Nnode); + for (int i = 0 ; i < Nnode ; i++) { // pour chaque module + int oldM = i; + int newM = moveTo[i]; + //printf("old -> new : %d -> %d \n", oldM, newM); + if (newM != oldM) { + + // Si je comprend bien : + // outFlow... : c'est le "flow" de i-> autre sommet du meme module + // inFlow... : c'est le "flow" depuis un autre sommet du meme module --> i + double outFlowOldM = (alpha * node[i]->size + beta * node[i]->danglingSize) * + (mod_teleportWeight[oldM] - node[i]->teleportWeight); + double inFlowOldM = (alpha * (mod_size[oldM] - node[i]->size) + + beta * (mod_danglingSize[oldM] - + node[i]->danglingSize)) * + node[i]->teleportWeight; + double outFlowNewM = (alpha * node[i]->size + beta * node[i]->danglingSize) + * mod_teleportWeight[newM]; + double inFlowNewM = (alpha * mod_size[newM] + + beta * mod_danglingSize[newM]) * + node[i]->teleportWeight; + + // For all outLinks + int NoutLinks = node[i]->outLinks.size(); + for (int j = 0; j < NoutLinks; j++) { + int nb_M = node_index[node[i]->outLinks[j].first]; + double nb_flow = node[i]->outLinks[j].second; + if (nb_M == oldM) { + outFlowOldM += nb_flow; + } else if (nb_M == newM) { + outFlowNewM += nb_flow; + } + } + + // For all inLinks + int NinLinks = node[i]->inLinks.size(); + for (int j = 0; j < NinLinks; j++) { + int nb_M = node_index[node[i]->inLinks[j].first]; + double nb_flow = node[i]->inLinks[j].second; + if (nb_M == oldM) { + inFlowOldM += nb_flow; + } else if (nb_M == newM) { + inFlowNewM += nb_flow; + } + } + + // Update empty module vector + // RAZ de mod_empty et Nempty ds calibrate() + if (mod_members[newM] == 0) { + // si le nouveau etait vide, on a un vide de moins... + Nempty--; + } + if (mod_members[oldM] == static_cast(node[i]->members.size())) { + // si l'ancien avait la taille de celui qui bouge, un vide de plus + mod_empty[Nempty] = oldM; + Nempty++; + } + + exitFlow -= mod_exit[oldM] + mod_exit[newM]; + exit_log_exit -= plogp(mod_exit[oldM]) + plogp(mod_exit[newM]); + size_log_size -= plogp(mod_exit[oldM] + mod_size[oldM]) + + plogp(mod_exit[newM] + mod_size[newM]); + + mod_exit[oldM] -= node[i]->exit - outFlowOldM - inFlowOldM; + mod_size[oldM] -= node[i]->size; + mod_danglingSize[oldM] -= node[i]->danglingSize; + mod_teleportWeight[oldM] -= node[i]->teleportWeight; + mod_members[oldM] -= node[i]->members.size(); + mod_exit[newM] += node[i]->exit - outFlowNewM - inFlowNewM; + mod_size[newM] += node[i]->size; + mod_danglingSize[newM] += node[i]->danglingSize; + mod_teleportWeight[newM] += node[i]->teleportWeight; + mod_members[newM] += node[i]->members.size(); + + exitFlow += mod_exit[oldM] + mod_exit[newM]; + exit_log_exit += plogp(mod_exit[oldM]) + plogp(mod_exit[newM]); + size_log_size += plogp(mod_exit[oldM] + mod_size[oldM]) + + plogp(mod_exit[newM] + mod_size[newM]); + exit = plogp(exitFlow); + + codeLength = exit - 2.0 * exit_log_exit + size_log_size - + nodeSize_log_nodeSize; + + node_index[i] = newM; + + } + + } +} diff --git a/src/rigraph/core/community/infomap/infomap_Greedy.h b/src/rigraph/core/community/infomap/infomap_Greedy.h new file mode 100644 index 0000000..9769d1d --- /dev/null +++ b/src/rigraph/core/community/infomap/infomap_Greedy.h @@ -0,0 +1,85 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef GREEDY_H +#define GREEDY_H + +#include +#include +#include +#include + +#include "igraph_random.h" + +#include "infomap_Node.h" +#include "infomap_FlowGraph.h" + +class Greedy { +public: + Greedy(FlowGraph * fgraph); + // initialise les attributs par rapport au graph + + ~Greedy(); + + void setMove(int *moveTo); + //virtual void determMove(int *moveTo); + + bool optimize(); + //virtual void move(bool &moved); + + void apply(bool sort); + //virtual void level(Node ***, bool sort); + + void tune(void); + + /**************************************************************************/ + + FlowGraph * graph; + int Nnode; + + double exit; + double exitFlow; + double exit_log_exit; + double size_log_size; + double nodeSize_log_nodeSize; + + double codeLength; + + double alpha, beta; + // local copy of fgraph alpha, beta (=alpha - Nnode = graph->Nnode;1) + + std::vector node_index; // module number of each node + + int Nempty; + std::vector mod_empty; + + std::vector mod_exit; // version tmp de node + std::vector mod_size; + std::vector mod_danglingSize; + std::vector mod_teleportWeight; + std::vector mod_members; +}; + +void delete_Greedy(Greedy *greedy); +#endif diff --git a/src/rigraph/core/community/infomap/infomap_Node.cc b/src/rigraph/core/community/infomap/infomap_Node.cc new file mode 100644 index 0000000..21f1b31 --- /dev/null +++ b/src/rigraph/core/community/infomap/infomap_Node.cc @@ -0,0 +1,71 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "infomap_Node.h" + +using namespace std; + +Node::Node() { + exit = 0.0; + size = 0.0; + selfLink = 0.0; +} + +Node::Node(int nodenr, double tpweight) { + teleportWeight = tpweight; + exit = 0.0; + size = 0.0; + selfLink = 0.0; + members.push_back(nodenr); // members = [nodenr] +} + +void cpyNode(Node *newNode, Node *oldNode) { + newNode->exit = oldNode->exit; + newNode->size = oldNode->size; + newNode->teleportWeight = oldNode->teleportWeight; + newNode->danglingSize = oldNode->danglingSize; + + int Nmembers = oldNode->members.size(); + newNode->members = vector(Nmembers); + for (int i = 0; i < Nmembers; i++) { + newNode->members[i] = oldNode->members[i]; + } + + newNode->selfLink = oldNode->selfLink; + + int NoutLinks = oldNode->outLinks.size(); + newNode->outLinks = vector >(NoutLinks); + for (int i = 0; i < NoutLinks; i++) { + newNode->outLinks[i].first = oldNode->outLinks[i].first; + newNode->outLinks[i].second = oldNode->outLinks[i].second; + } + + int NinLinks = oldNode->inLinks.size(); + newNode->inLinks = vector >(NinLinks); + for (int i = 0; i < NinLinks; i++) { + newNode->inLinks[i].first = oldNode->inLinks[i].first; + newNode->inLinks[i].second = oldNode->inLinks[i].second; + } + +} diff --git a/src/rigraph/core/community/infomap/infomap_Node.h b/src/rigraph/core/community/infomap/infomap_Node.h new file mode 100644 index 0000000..5cd0407 --- /dev/null +++ b/src/rigraph/core/community/infomap/infomap_Node.h @@ -0,0 +1,52 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef NODE_H +#define NODE_H + +#include +#include + +#include "igraph_interface.h" + +class Node { +public: + + Node(); + Node(int modulenr, double tpweight); + + std::vector members; + std::vector< std::pair > inLinks; + std::vector< std::pair > outLinks; + double selfLink; + + double teleportWeight; + double danglingSize; + double exit; + double size; +}; + +void cpyNode(Node *newNode, Node *oldNode); + +#endif diff --git a/src/rigraph/core/community/label_propagation.c b/src/rigraph/core/community/label_propagation.c new file mode 100644 index 0000000..e4eab42 --- /dev/null +++ b/src/rigraph/core/community/label_propagation.c @@ -0,0 +1,435 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_random.h" + +/** + * \ingroup communities + * \function igraph_community_label_propagation + * \brief Community detection based on label propagation. + * + * This function implements the label propagation-based community detection + * algorithm described by Raghavan, Albert and Kumara. This version extends + * the original method by the ability to take edge weights into consideration + * and also by allowing some labels to be fixed. + * + * + * Weights are taken into account as follows: when the new label of node + * \c i is determined, the algorithm iterates over all edges incident on + * node \c i and calculate the total weight of edges leading to other + * nodes with label 0, 1, 2, ..., \c k - 1 (where \c k is the number of possible + * labels). The new label of node \c i will then be the label whose edges + * (among the ones incident on node \c i) have the highest total weight. + * + * + * Reference: + * + * + * Raghavan, U.N. and Albert, R. and Kumara, S.: + * Near linear time algorithm to detect community structures in large-scale networks. + * Phys Rev E 76, 036106. (2007). + * https://doi.org/10.1103/PhysRevE.76.036106 + * + * \param graph The input graph, should be undirected to make sense. + * \param membership The membership vector, the result is returned here. + * For each vertex it gives the ID of its community (label). + * \param weights The weight vector, it should contain a positive + * weight for all the edges. + * \param initial The initial state. If \c NULL, every vertex will have + * a different label at the beginning. Otherwise it must be a vector + * with an entry for each vertex. Non-negative values denote different + * labels, negative entries denote vertices without labels. Unlabeled + * vertices which are not reachable from any labeled ones will remain + * unlabeled at the end of the label propagation process, and will be + * labeled in an additional step to avoid returning negative values in + * \p membership. In undirected graphs, this happens when entire connected + * components are unlabeled. Then, each unlabeled component will receive + * its own separate label. In directed graphs, the outcome of the + * additional labeling should be considered undefined and may change + * in the future; please do not rely on it. + * \param fixed Boolean vector denoting which labels are fixed. Of course + * this makes sense only if you provided an initial state, otherwise + * this element will be ignored. Also note that vertices without labels + * cannot be fixed. If they are, this vector will be modified to + * make it consistent with \p initial. + * \param modularity If not a null pointer, then it must be a pointer + * to a real number. The modularity score of the detected community + * structure is stored here. + * \return Error code. + * + * Time complexity: O(m+n) + * + * \example examples/simple/igraph_community_label_propagation.c + */ +int igraph_community_label_propagation(const igraph_t *graph, + igraph_vector_t *membership, + const igraph_vector_t *weights, + const igraph_vector_t *initial, + const igraph_vector_bool_t *fixed, + igraph_real_t *modularity) { + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int no_of_not_fixed_nodes = no_of_nodes; + long int i, j, k; + igraph_adjlist_t al; + igraph_inclist_t il; + igraph_bool_t running, control_iteration; + igraph_bool_t unlabelled_left; + + igraph_vector_t label_counters, dominant_labels, nonzero_labels, node_order; + + /* We make a copy of 'fixed' as a pointer into 'fixed_copy' after casting + * away the constness, and promise ourselves that we will make a proper + * copy of 'fixed' into 'fixed_copy' as soon as we start mutating it */ + igraph_vector_bool_t* fixed_copy = (igraph_vector_bool_t*) fixed; + + /* The implementation uses a trick to avoid negative array indexing: + * elements of the membership vector are increased by 1 at the start + * of the algorithm; this to allow us to denote unlabeled vertices + * (if any) by zeroes. The membership vector is shifted back in the end + */ + + /* Do some initial checks */ + if (fixed && igraph_vector_bool_size(fixed) != no_of_nodes) { + IGRAPH_ERROR("Fixed labeling vector length must agree with number of nodes.", IGRAPH_EINVAL); + } + if (weights) { + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Length of weight vector must agree with number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight < 0) { + IGRAPH_ERROR("Weights must not be negative.", IGRAPH_EINVAL); + } + if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); + } + } + } + if (fixed && !initial) { + IGRAPH_WARNING("Ignoring fixed vertices as no initial labeling given."); + } + + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + + if (initial) { + if (igraph_vector_size(initial) != no_of_nodes) { + IGRAPH_ERROR("Initial labeling vector length must agree with number of nodes.", IGRAPH_EINVAL); + } + /* Check if the labels used are valid, initialize membership vector */ + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*initial)[i] < 0) { + VECTOR(*membership)[i] = 0; + } else { + VECTOR(*membership)[i] = floor(VECTOR(*initial)[i]) + 1; + } + } + if (fixed) { + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*fixed)[i]) { + if (VECTOR(*membership)[i] == 0) { + IGRAPH_WARNING("Fixed nodes cannot be unlabeled, ignoring them."); + + /* We cannot modify 'fixed' because it is const, so we make a copy and + * modify 'fixed_copy' instead */ + if (fixed_copy == fixed) { + fixed_copy = igraph_Calloc(1, igraph_vector_bool_t); + if (fixed_copy == 0) { + IGRAPH_ERROR("Failed to copy 'fixed' vector.", IGRAPH_ENOMEM); + } + + IGRAPH_FINALLY(igraph_free, fixed_copy); + IGRAPH_CHECK(igraph_vector_bool_copy(fixed_copy, fixed)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, fixed_copy); + } + + VECTOR(*fixed_copy)[i] = 0; + } else { + no_of_not_fixed_nodes--; + } + } + } + } + + i = (long int) igraph_vector_max(membership); + if (i > no_of_nodes) { + IGRAPH_ERROR("Elements of the initial labeling vector must be between 0 and |V|-1.", IGRAPH_EINVAL); + } + } else { + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*membership)[i] = i + 1; + } + } + + /* From this point onwards we use 'fixed_copy' instead of 'fixed' */ + + /* Create an adjacency/incidence list representation for efficiency. + * For the unweighted case, the adjacency list is enough. For the + * weighted case, we need the incidence list */ + if (weights) { + IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_IN, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + } else { + IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + } + + /* Create storage space for counting distinct labels and dominant ones */ + IGRAPH_VECTOR_INIT_FINALLY(&label_counters, no_of_nodes + 1); + IGRAPH_VECTOR_INIT_FINALLY(&dominant_labels, 0); + IGRAPH_VECTOR_INIT_FINALLY(&nonzero_labels, 0); + IGRAPH_CHECK(igraph_vector_reserve(&dominant_labels, 2)); + + /* Initialize node ordering vector with only the not fixed nodes */ + if (fixed_copy) { + IGRAPH_VECTOR_INIT_FINALLY(&node_order, no_of_not_fixed_nodes); + for (i = 0, j = 0; i < no_of_nodes; i++) { + if (!VECTOR(*fixed_copy)[i]) { + VECTOR(node_order)[j] = i; + j++; + } + } + } else { + IGRAPH_CHECK(igraph_vector_init_seq(&node_order, 0, no_of_nodes - 1)); + IGRAPH_FINALLY(igraph_vector_destroy, &node_order); + } + + /* There are two alternating types of iterations, one for changing labels and + the other one for checking the end condition - every vertex in the graph has + a label to which the maximum number of its neighbors belongs. If control_iteration + is true, we are just checking the end condition and not relabeling nodes. */ + control_iteration = 1; + running = 1; + while (running) { + long int v1, num_neis; + igraph_real_t max_count; + igraph_vector_int_t *neis; + igraph_vector_int_t *ineis; + igraph_bool_t was_zero; + + if (control_iteration) { + /* If we are in the control iteration, we expect in the begining of + the iterationthat all vertices meet the end condition, so running is false. + If some of them does not, running is set to true later in the code. */ + running = 0; + } else { + /* Shuffle the node ordering vector if we are in the label updating iteration */ + IGRAPH_CHECK(igraph_vector_shuffle(&node_order)); + } + + RNG_BEGIN(); + /* In the prescribed order, loop over the vertices and reassign labels */ + for (i = 0; i < no_of_not_fixed_nodes; i++) { + v1 = (long int) VECTOR(node_order)[i]; + + /* Count the weights corresponding to different labels */ + igraph_vector_clear(&dominant_labels); + igraph_vector_clear(&nonzero_labels); + max_count = 0.0; + if (weights) { + ineis = igraph_inclist_get(&il, v1); + num_neis = igraph_vector_int_size(ineis); + for (j = 0; j < num_neis; j++) { + k = (long int) VECTOR(*membership)[ + (long)IGRAPH_OTHER(graph, VECTOR(*ineis)[j], v1) ]; + if (k == 0) { + continue; /* skip if it has no label yet */ + } + was_zero = (VECTOR(label_counters)[k] == 0); + VECTOR(label_counters)[k] += VECTOR(*weights)[(long)VECTOR(*ineis)[j]]; + if (was_zero && VECTOR(label_counters)[k] != 0) { + /* counter just became nonzero */ + IGRAPH_CHECK(igraph_vector_push_back(&nonzero_labels, k)); + } + if (max_count < VECTOR(label_counters)[k]) { + max_count = VECTOR(label_counters)[k]; + IGRAPH_CHECK(igraph_vector_resize(&dominant_labels, 1)); + VECTOR(dominant_labels)[0] = k; + } else if (max_count == VECTOR(label_counters)[k]) { + IGRAPH_CHECK(igraph_vector_push_back(&dominant_labels, k)); + } + } + } else { + neis = igraph_adjlist_get(&al, v1); + num_neis = igraph_vector_int_size(neis); + for (j = 0; j < num_neis; j++) { + k = (long int) VECTOR(*membership)[(long)VECTOR(*neis)[j]]; + if (k == 0) { + continue; /* skip if it has no label yet */ + } + VECTOR(label_counters)[k]++; + if (VECTOR(label_counters)[k] == 1) { + /* counter just became nonzero */ + IGRAPH_CHECK(igraph_vector_push_back(&nonzero_labels, k)); + } + if (max_count < VECTOR(label_counters)[k]) { + max_count = VECTOR(label_counters)[k]; + IGRAPH_CHECK(igraph_vector_resize(&dominant_labels, 1)); + VECTOR(dominant_labels)[0] = k; + } else if (max_count == VECTOR(label_counters)[k]) { + IGRAPH_CHECK(igraph_vector_push_back(&dominant_labels, k)); + } + } + } + + if (igraph_vector_size(&dominant_labels) > 0) { + if (control_iteration) { + /* Check if the _current_ label of the node is also dominant */ + if (VECTOR(label_counters)[(long)VECTOR(*membership)[v1]] != max_count) { + /* Nope, we need at least one more iteration */ + running = 1; + } + } + else { + /* Select randomly from the dominant labels */ + k = RNG_INTEGER(0, igraph_vector_size(&dominant_labels) - 1); + VECTOR(*membership)[v1] = VECTOR(dominant_labels)[(long int)k]; + } + } + + /* Clear the nonzero elements in label_counters */ + num_neis = igraph_vector_size(&nonzero_labels); + for (j = 0; j < num_neis; j++) { + VECTOR(label_counters)[(long int)VECTOR(nonzero_labels)[j]] = 0; + } + } + RNG_END(); + + /* Alternating between control iterations and label updating iterations */ + control_iteration = !control_iteration; + } + + if (weights) { + igraph_inclist_destroy(&il); + } else { + igraph_adjlist_destroy(&al); + } + IGRAPH_FINALLY_CLEAN(1); + + /* Shift back the membership vector, permute labels in increasing order */ + /* We recycle label_counters here :) */ + igraph_vector_fill(&label_counters, -1); + j = 0; + unlabelled_left = 0; + for (i = 0; i < no_of_nodes; i++) { + k = (long)VECTOR(*membership)[i] - 1; + if (k >= 0) { + if (VECTOR(label_counters)[k] == -1) { + /* We have seen this label for the first time */ + VECTOR(label_counters)[k] = j; + k = j; + j++; + } else { + k = (long int) VECTOR(label_counters)[k]; + } + } else { + /* This is an unlabeled vertex */ + unlabelled_left = 1; + } + VECTOR(*membership)[i] = k; + } + + /* From this point on, unlabelled nodes are represented with -1 (no longer 0). */ +#define IS_UNLABELLED(x) (VECTOR(*membership)[x] < 0) + + /* If any nodes are left unlabelled, we assign the remaining labels to them, + * as well as to all unlabelled nodes reachable from them. + * + * Note that only those nodes could remain unlabelled which were unreachable + * from any labelled ones. Thus, in the undirected case, fully unlabelled + * connected components remain unlabelled. Here we label each such component + * with the same label. + */ + if (unlabelled_left) { + igraph_dqueue_t q; + igraph_vector_t neis; + + /* In the directed case, the outcome depends on the node ordering, thus we + * shuffle nodes one more time. */ + IGRAPH_CHECK(igraph_vector_shuffle(&node_order)); + + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + IGRAPH_CHECK(igraph_dqueue_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &q); + + for (i=0; i < no_of_nodes; ++i) { + long int v = VECTOR(node_order)[i]; + + /* Is this node unlabelled? */ + if (IS_UNLABELLED(v)) { + /* If yes, we label it, and do a BFS to apply the same label + * to all other unlabelled nodes reachable from it */ + igraph_dqueue_push(&q, v); + VECTOR(*membership)[v] = j; + while (!igraph_dqueue_empty(&q)) { + long int ni, num_neis; + long int actnode = igraph_dqueue_pop(&q); + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, IGRAPH_OUT)); + num_neis = igraph_vector_size(&neis); + + for (ni = 0; ni < num_neis; ++ni) { + long int neighbor = VECTOR(neis)[ni]; + if (IS_UNLABELLED(neighbor)) { + VECTOR(*membership)[neighbor] = j; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + } + } + } + j++; + } + } + + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&q); + IGRAPH_FINALLY_CLEAN(2); + } + + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, weights, + /* resolution */ 1, + /* directed */ 1, modularity)); + } + + igraph_vector_destroy(&node_order); + igraph_vector_destroy(&label_counters); + igraph_vector_destroy(&dominant_labels); + igraph_vector_destroy(&nonzero_labels); + IGRAPH_FINALLY_CLEAN(4); + + if (fixed != fixed_copy) { + igraph_vector_bool_destroy(fixed_copy); + igraph_Free(fixed_copy); + IGRAPH_FINALLY_CLEAN(2); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/community/leading_eigenvector.c b/src/rigraph/core/community/leading_eigenvector.c new file mode 100644 index 0000000..dd990af --- /dev/null +++ b/src/rigraph/core/community/leading_eigenvector.c @@ -0,0 +1,1063 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" +#include "igraph_memory.h" +#include "igraph_statusbar.h" +#include "igraph_structural.h" + +#include "../core/interruption.h" + +/** + * \section about_leading_eigenvector_methods + * + * + * The function documented in these section implements the + * leading eigenvector method developed by Mark Newman and + * published in MEJ Newman: Finding community structure using the + * eigenvectors of matrices, Phys Rev E 74:036104 (2006). + * + * + * The heart of the method is the definition of the modularity matrix, + * B, which is B=A-P, A being the adjacency matrix of the (undirected) + * network, and P contains the probability that certain edges are + * present according to the configuration model In + * other words, a Pij element of P is the probability that there is an + * edge between vertices i and j in a random network in which the + * degrees of all vertices are the same as in the input graph. + * + * + * The leading eigenvector method works by calculating the eigenvector + * of the modularity matrix for the largest positive eigenvalue and + * then separating vertices into two community based on the sign of + * the corresponding element in the eigenvector. If all elements in + * the eigenvector are of the same sign that means that the network + * has no underlying community structure. + * Check Newman's paper to understand why this is a good method for + * detecting community structure. + * + * + * The leading eigenvector community structure detection method is + * implemented in \ref igraph_community_leading_eigenvector(). After + * the initial split, the following splits are done in a way to + * optimize modularity regarding to the original network. Note that + * any further refinement, for example using Kernighan-Lin, as + * proposed in Section V.A of Newman (2006), is not implemented here. + * + * + * + * \example examples/simple/igraph_community_leading_eigenvector.c + * + */ + +typedef struct igraph_i_community_leading_eigenvector_data_t { + igraph_vector_t *idx; + igraph_vector_t *idx2; + igraph_adjlist_t *adjlist; + igraph_inclist_t *inclist; + igraph_vector_t *tmp; + long int no_of_edges; + igraph_vector_t *mymembership; + long int comm; + const igraph_vector_t *weights; + const igraph_t *graph; + igraph_vector_t *strength; + igraph_real_t sumweights; +} igraph_i_community_leading_eigenvector_data_t; + +static int igraph_i_community_leading_eigenvector(igraph_real_t *to, + const igraph_real_t *from, + int n, void *extra) { + + igraph_i_community_leading_eigenvector_data_t *data = extra; + long int j, k, nlen, size = n; + igraph_vector_t *idx = data->idx; + igraph_vector_t *idx2 = data->idx2; + igraph_vector_t *tmp = data->tmp; + igraph_adjlist_t *adjlist = data->adjlist; + igraph_real_t ktx, ktx2; + long int no_of_edges = data->no_of_edges; + igraph_vector_t *mymembership = data->mymembership; + long int comm = data->comm; + + /* Ax */ + for (j = 0; j < size; j++) { + long int oldid = (long int) VECTOR(*idx)[j]; + igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid); + nlen = igraph_vector_int_size(neis); + to[j] = 0.0; + VECTOR(*tmp)[j] = 0.0; + for (k = 0; k < nlen; k++) { + long int nei = (long int) VECTOR(*neis)[k]; + long int neimemb = (long int) VECTOR(*mymembership)[nei]; + if (neimemb == comm) { + to[j] += from[ (long int) VECTOR(*idx2)[nei] ]; + VECTOR(*tmp)[j] += 1; + } + } + } + + /* Now calculate k^Tx/2m */ + ktx = 0.0; ktx2 = 0.0; + for (j = 0; j < size; j++) { + long int oldid = (long int) VECTOR(*idx)[j]; + igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid); + long int degree = igraph_vector_int_size(neis); + ktx += from[j] * degree; + ktx2 += degree; + } + ktx = ktx / no_of_edges / 2.0; + ktx2 = ktx2 / no_of_edges / 2.0; + + /* Now calculate Bx */ + for (j = 0; j < size; j++) { + long int oldid = (long int) VECTOR(*idx)[j]; + igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid); + igraph_real_t degree = igraph_vector_int_size(neis); + to[j] = to[j] - ktx * degree; + VECTOR(*tmp)[j] = VECTOR(*tmp)[j] - ktx2 * degree; + } + + /* -d_ij summa l in G B_il */ + for (j = 0; j < size; j++) { + to[j] -= VECTOR(*tmp)[j] * from[j]; + } + + return 0; +} + +static int igraph_i_community_leading_eigenvector2(igraph_real_t *to, + const igraph_real_t *from, + int n, void *extra) { + + igraph_i_community_leading_eigenvector_data_t *data = extra; + long int j, k, nlen, size = n; + igraph_vector_t *idx = data->idx; + igraph_vector_t *idx2 = data->idx2; + igraph_vector_t *tmp = data->tmp; + igraph_adjlist_t *adjlist = data->adjlist; + igraph_real_t ktx, ktx2; + long int no_of_edges = data->no_of_edges; + igraph_vector_t *mymembership = data->mymembership; + long int comm = data->comm; + + /* Ax */ + for (j = 0; j < size; j++) { + long int oldid = (long int) VECTOR(*idx)[j]; + igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid); + nlen = igraph_vector_int_size(neis); + to[j] = 0.0; + VECTOR(*tmp)[j] = 0.0; + for (k = 0; k < nlen; k++) { + long int nei = (long int) VECTOR(*neis)[k]; + long int neimemb = (long int) VECTOR(*mymembership)[nei]; + if (neimemb == comm) { + long int fi = (long int) VECTOR(*idx2)[nei]; + if (fi < size) { + to[j] += from[fi]; + } + VECTOR(*tmp)[j] += 1; + } + } + } + + /* Now calculate k^Tx/2m */ + ktx = 0.0; ktx2 = 0.0; + for (j = 0; j < size + 1; j++) { + long int oldid = (long int) VECTOR(*idx)[j]; + igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid); + long int degree = igraph_vector_int_size(neis); + if (j < size) { + ktx += from[j] * degree; + } + ktx2 += degree; + } + ktx = ktx / no_of_edges / 2.0; + ktx2 = ktx2 / no_of_edges / 2.0; + + /* Now calculate Bx */ + for (j = 0; j < size; j++) { + long int oldid = (long int) VECTOR(*idx)[j]; + igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid); + igraph_real_t degree = igraph_vector_int_size(neis); + to[j] = to[j] - ktx * degree; + VECTOR(*tmp)[j] = VECTOR(*tmp)[j] - ktx2 * degree; + } + + /* -d_ij summa l in G B_il */ + for (j = 0; j < size; j++) { + to[j] -= VECTOR(*tmp)[j] * from[j]; + } + + return 0; +} + +static int igraph_i_community_leading_eigenvector_weighted(igraph_real_t *to, + const igraph_real_t *from, + int n, void *extra) { + + igraph_i_community_leading_eigenvector_data_t *data = extra; + long int j, k, nlen, size = n; + igraph_vector_t *idx = data->idx; + igraph_vector_t *idx2 = data->idx2; + igraph_vector_t *tmp = data->tmp; + igraph_inclist_t *inclist = data->inclist; + igraph_real_t ktx, ktx2; + igraph_vector_t *mymembership = data->mymembership; + long int comm = data->comm; + const igraph_vector_t *weights = data->weights; + const igraph_t *graph = data->graph; + igraph_vector_t *strength = data->strength; + igraph_real_t sw = data->sumweights; + + /* Ax */ + for (j = 0; j < size; j++) { + long int oldid = (long int) VECTOR(*idx)[j]; + igraph_vector_int_t *inc = igraph_inclist_get(inclist, oldid); + nlen = igraph_vector_int_size(inc); + to[j] = 0.0; + VECTOR(*tmp)[j] = 0.0; + for (k = 0; k < nlen; k++) { + long int edge = (long int) VECTOR(*inc)[k]; + igraph_real_t w = VECTOR(*weights)[edge]; + long int nei = IGRAPH_OTHER(graph, edge, oldid); + long int neimemb = (long int) VECTOR(*mymembership)[nei]; + if (neimemb == comm) { + to[j] += from[ (long int) VECTOR(*idx2)[nei] ] * w; + VECTOR(*tmp)[j] += w; + } + } + } + + /* k^Tx/2m */ + ktx = 0.0; ktx2 = 0.0; + for (j = 0; j < size; j++) { + long int oldid = (long int) VECTOR(*idx)[j]; + igraph_real_t str = VECTOR(*strength)[oldid]; + ktx += from[j] * str; + ktx2 += str; + } + ktx = ktx / sw / 2.0; + ktx2 = ktx2 / sw / 2.0; + + /* Bx */ + for (j = 0; j < size; j++) { + long int oldid = (long int) VECTOR(*idx)[j]; + igraph_real_t str = VECTOR(*strength)[oldid]; + to[j] = to[j] - ktx * str; + VECTOR(*tmp)[j] = VECTOR(*tmp)[j] - ktx2 * str; + } + + /* -d_ij summa l in G B_il */ + for (j = 0; j < size; j++) { + to[j] -= VECTOR(*tmp)[j] * from[j]; + } + + return 0; +} + +static int igraph_i_community_leading_eigenvector2_weighted(igraph_real_t *to, + const igraph_real_t *from, + int n, void *extra) { + + igraph_i_community_leading_eigenvector_data_t *data = extra; + long int j, k, nlen, size = n; + igraph_vector_t *idx = data->idx; + igraph_vector_t *idx2 = data->idx2; + igraph_vector_t *tmp = data->tmp; + igraph_inclist_t *inclist = data->inclist; + igraph_real_t ktx, ktx2; + igraph_vector_t *mymembership = data->mymembership; + long int comm = data->comm; + const igraph_vector_t *weights = data->weights; + const igraph_t *graph = data->graph; + igraph_vector_t *strength = data->strength; + igraph_real_t sw = data->sumweights; + + /* Ax */ + for (j = 0; j < size; j++) { + long int oldid = (long int) VECTOR(*idx)[j]; + igraph_vector_int_t *inc = igraph_inclist_get(inclist, oldid); + nlen = igraph_vector_int_size(inc); + to[j] = 0.0; + VECTOR(*tmp)[j] = 0.0; + for (k = 0; k < nlen; k++) { + long int edge = (long int) VECTOR(*inc)[k]; + igraph_real_t w = VECTOR(*weights)[edge]; + long int nei = IGRAPH_OTHER(graph, edge, oldid); + long int neimemb = (long int) VECTOR(*mymembership)[nei]; + if (neimemb == comm) { + long int fi = (long int) VECTOR(*idx2)[nei]; + if (fi < size) { + to[j] += from[fi] * w; + } + VECTOR(*tmp)[j] += w; + } + } + } + + /* k^Tx/2m */ + ktx = 0.0; ktx2 = 0.0; + for (j = 0; j < size + 1; j++) { + long int oldid = (long int) VECTOR(*idx)[j]; + igraph_real_t str = VECTOR(*strength)[oldid]; + if (j < size) { + ktx += from[j] * str; + } + ktx2 += str; + } + ktx = ktx / sw / 2.0; + ktx2 = ktx2 / sw / 2.0; + + /* Bx */ + for (j = 0; j < size; j++) { + long int oldid = (long int) VECTOR(*idx)[j]; + igraph_real_t str = VECTOR(*strength)[oldid]; + to[j] = to[j] - ktx * str; + VECTOR(*tmp)[j] = VECTOR(*tmp)[j] - ktx2 * str; + } + + /* -d_ij summa l in G B_il */ + for (j = 0; j < size; j++) { + to[j] -= VECTOR(*tmp)[j] * from[j]; + } + + return 0; +} + +static void igraph_i_levc_free(igraph_vector_ptr_t *ptr) { + long int i, n = igraph_vector_ptr_size(ptr); + for (i = 0; i < n; i++) { + igraph_vector_t *v = VECTOR(*ptr)[i]; + if (v) { + igraph_vector_destroy(v); + igraph_free(v); + } + } +} + +static void igraph_i_error_handler_none(const char *reason, const char *file, + int line, int igraph_errno) { + IGRAPH_UNUSED(reason); + IGRAPH_UNUSED(file); + IGRAPH_UNUSED(line); + IGRAPH_UNUSED(igraph_errno); + /* do nothing */ +} + + +/** + * \ingroup communities + * \function igraph_community_leading_eigenvector + * \brief Leading eigenvector community finding (proper version). + * + * Newman's leading eigenvector method for detecting community + * structure. This is the proper implementation of the recursive, + * divisive algorithm: each split is done by maximizing the modularity + * regarding the original network, see MEJ Newman: Finding community + * structure in networks using the eigenvectors of matrices, + * Phys Rev E 74:036104 (2006). + * + * \param graph The undirected input graph. + * \param weights The weights of the edges, or a null pointer for + * unweighted graphs. + * \param merges The result of the algorithm, a matrix containing the + * information about the splits performed. The matrix is built in + * the opposite way however, it is like the result of an + * agglomerative algorithm. If at the end of the algorithm (after + * \p steps steps was done) there are p communities, + * then these are numbered from zero to p-1. The + * first line of the matrix contains the first merge + * (which is in reality the last split) of two communities into + * community p, the merge in the second line forms + * community p+1, etc. The matrix should be + * initialized before calling and will be resized as needed. + * This argument is ignored of it is \c NULL. + * \param membership The membership of the vertices after all the + * splits were performed will be stored here. The vector must be + * initialized before calling and will be resized as needed. + * This argument is ignored if it is \c NULL. This argument can + * also be used to supply a starting configuration for the community + * finding, in the format of a membership vector. In this case the + * \p start argument must be set to 1. + * \param steps The maximum number of steps to perform. It might + * happen that some component (or the whole network) has no + * underlying community structure and no further steps can be + * done. If you want as many steps as possible then supply the + * number of vertices in the network here. + * \param options The options for ARPACK. \c n is always + * overwritten. \c ncv is set to at least 4. + * \param modularity If not a null pointer, then it must be a pointer + * to a real number and the modularity score of the final division + * is stored here. + * \param start Boolean, whether to use the community structure given + * in the \p membership argument as a starting point. + * \param eigenvalues Pointer to an initialized vector or a null + * pointer. If not a null pointer, then the eigenvalues calculated + * along the community structure detection are stored here. The + * non-positive eigenvalues, that do not result a split, are stored + * as well. + * \param eigenvectors If not a null pointer, then the eigenvectors + * that are calculated in each step of the algorithm, are stored here, + * in a pointer vector. Each eigenvector is stored in an + * \ref igraph_vector_t object. The user is responsible of + * deallocating the memory that belongs to the individual vectors, + * by calling first \ref igraph_vector_destroy(), and then + * \ref igraph_free() on them. + * \param history Pointer to an initialized vector or a null pointer. + * If not a null pointer, then a trace of the algorithm is stored + * here, encoded numerically. The various operations: + * \clist + * \cli IGRAPH_LEVC_HIST_START_FULL + * Start the algorithm from an initial state where each connected + * component is a separate community. + * \cli IGRAPH_LEVC_HIST_START_GIVEN + * Start the algorithm from a given community structure. The next + * value in the vector contains the initial number of + * communities. + * \cli IGRAPH_LEVC_HIST_SPLIT + * Split a community into two communities. The id of the splitted + * community is given in the next element of the history vector. + * The id of the first new community is the same as the id of the + * splitted community. The id of the second community equals to + * the number of communities before the split. + * \cli IGRAPH_LEVC_HIST_FAILED + * Tried to split a community, but it was not worth it, as it + * does not result in a bigger modularity value. The id of the + * community is given in the next element of the vector. + * \endclist + * \param callback A null pointer or a function of type \ref + * igraph_community_leading_eigenvector_callback_t. If given, this + * callback function is called after each eigenvector/eigenvalue + * calculation. If the callback returns a non-zero value, then the + * community finding algorithm stops. See the arguments passed to + * the callback at the documentation of \ref + * igraph_community_leading_eigenvector_callback_t. + * \param callback_extra Extra argument to pass to the callback + * function. + * \return Error code. + * + * \sa \ref igraph_community_walktrap() and \ref + * igraph_community_spinglass() for other community structure + * detection methods. + * + * Time complexity: O(|E|+|V|^2*steps), |V| is the number of vertices, + * |E| the number of edges, steps the number of splits + * performed. + */ +int igraph_community_leading_eigenvector(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_matrix_t *merges, + igraph_vector_t *membership, + igraph_integer_t steps, + igraph_arpack_options_t *options, + igraph_real_t *modularity, + igraph_bool_t start, + igraph_vector_t *eigenvalues, + igraph_vector_ptr_t *eigenvectors, + igraph_vector_t *history, + igraph_community_leading_eigenvector_callback_t *callback, + void *callback_extra) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_dqueue_t tosplit; + igraph_vector_t idx, idx2, mymerges; + igraph_vector_t strength, tmp; + long int staken = 0; + igraph_adjlist_t adjlist; + igraph_inclist_t inclist; + long int i, j, k, l; + long int communities; + igraph_vector_t vmembership, *mymembership = membership; + igraph_i_community_leading_eigenvector_data_t extra; + igraph_arpack_storage_t storage; + igraph_real_t mod = 0; + igraph_arpack_function_t *arpcb1 = + weights ? igraph_i_community_leading_eigenvector_weighted : + igraph_i_community_leading_eigenvector; + igraph_arpack_function_t *arpcb2 = + weights ? igraph_i_community_leading_eigenvector2_weighted : + igraph_i_community_leading_eigenvector2; + igraph_real_t sumweights = 0.0; + + if (weights && no_of_edges != igraph_vector_size(weights)) { + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + } + + if (start && !membership) { + IGRAPH_ERROR("Cannot start from given configuration if memberships " + "missing", IGRAPH_EINVAL); + } + + if (start && membership && + igraph_vector_size(membership) != no_of_nodes) { + IGRAPH_ERROR("Wrong length for vector of predefined memberships", + IGRAPH_EINVAL); + } + + if (start && membership && igraph_vector_max(membership) >= no_of_nodes) { + IGRAPH_WARNING("Too many communities in membership start vector"); + } + + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("This method was developed for undirected graphs"); + } + + if (steps < 0 || steps > no_of_nodes - 1) { + steps = (igraph_integer_t) no_of_nodes - 1; + } + + if (!membership) { + mymembership = &vmembership; + IGRAPH_VECTOR_INIT_FINALLY(mymembership, 0); + } + + IGRAPH_VECTOR_INIT_FINALLY(&mymerges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&mymerges, steps * 2)); + IGRAPH_VECTOR_INIT_FINALLY(&idx, 0); + if (eigenvalues) { + igraph_vector_clear(eigenvalues); + } + if (eigenvectors) { + igraph_vector_ptr_clear(eigenvectors); + IGRAPH_FINALLY(igraph_i_levc_free, eigenvectors); + } + + IGRAPH_STATUS("Starting leading eigenvector method.\n", 0); + + if (!start) { + /* Calculate the weakly connected components in the graph and use them as + * an initial split */ + IGRAPH_CHECK(igraph_clusters(graph, mymembership, &idx, 0, IGRAPH_WEAK)); + communities = igraph_vector_size(&idx); + IGRAPH_STATUSF(("Starting from %li component(s).\n", 0, communities)); + if (history) { + IGRAPH_CHECK(igraph_vector_push_back(history, + IGRAPH_LEVC_HIST_START_FULL)); + } + } else { + /* Just create the idx vector for the given membership vector */ + communities = (long int) igraph_vector_max(mymembership) + 1; + IGRAPH_STATUSF(("Starting from given membership vector with %li " + "communities.\n", 0, communities)); + if (history) { + IGRAPH_CHECK(igraph_vector_push_back(history, + IGRAPH_LEVC_HIST_START_GIVEN)); + IGRAPH_CHECK(igraph_vector_push_back(history, communities)); + } + IGRAPH_CHECK(igraph_vector_resize(&idx, communities)); + igraph_vector_null(&idx); + for (i = 0; i < no_of_nodes; i++) { + int t = (int) VECTOR(*mymembership)[i]; + VECTOR(idx)[t] += 1; + } + } + + IGRAPH_DQUEUE_INIT_FINALLY(&tosplit, 100); + for (i = 0; i < communities; i++) { + if (VECTOR(idx)[i] > 2) { + igraph_dqueue_push(&tosplit, i); + } + } + for (i = 1; i < communities; i++) { + /* Record merge */ + IGRAPH_CHECK(igraph_vector_push_back(&mymerges, i - 1)); + IGRAPH_CHECK(igraph_vector_push_back(&mymerges, i)); + if (eigenvalues) { + IGRAPH_CHECK(igraph_vector_push_back(eigenvalues, IGRAPH_NAN)); + } + if (eigenvectors) { + igraph_vector_t *v = IGRAPH_CALLOC(1, igraph_vector_t); + if (!v) { + IGRAPH_ERROR("Cannot do leading eigenvector community detection", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, v); + IGRAPH_VECTOR_INIT_FINALLY(v, 0); + IGRAPH_CHECK(igraph_vector_ptr_push_back(eigenvectors, v)); + IGRAPH_FINALLY_CLEAN(2); + } + if (history) { + IGRAPH_CHECK(igraph_vector_push_back(history, IGRAPH_LEVC_HIST_SPLIT)); + IGRAPH_CHECK(igraph_vector_push_back(history, i - 1)); + } + } + staken = communities - 1; + + IGRAPH_VECTOR_INIT_FINALLY(&tmp, no_of_nodes); + IGRAPH_CHECK(igraph_vector_resize(&idx, no_of_nodes)); + igraph_vector_null(&idx); + IGRAPH_VECTOR_INIT_FINALLY(&idx2, no_of_nodes); + if (!weights) { + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + } else { + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + IGRAPH_VECTOR_INIT_FINALLY(&strength, no_of_nodes); + IGRAPH_CHECK(igraph_strength(graph, &strength, igraph_vss_all(), + IGRAPH_ALL, IGRAPH_LOOPS, weights)); + sumweights = igraph_vector_sum(weights); + } + + options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rssolve */ + options->start = 0; + options->which[0] = 'L'; options->which[1] = 'A'; + + /* Memory for ARPACK */ + /* We are allocating memory for 20 eigenvectors since options->ncv won't be + * larger than 20 when using automatic mode in igraph_arpack_rssolve */ + IGRAPH_CHECK(igraph_arpack_storage_init(&storage, (int) no_of_nodes, 20, + (int) no_of_nodes, 1)); + IGRAPH_FINALLY(igraph_arpack_storage_destroy, &storage); + extra.idx = &idx; + extra.idx2 = &idx2; + extra.tmp = &tmp; + extra.adjlist = &adjlist; + extra.inclist = &inclist; + extra.weights = weights; + extra.sumweights = sumweights; + extra.graph = graph; + extra.strength = &strength; + extra.no_of_edges = no_of_edges; + extra.mymembership = mymembership; + + while (!igraph_dqueue_empty(&tosplit) && staken < steps) { + long int comm = (long int) igraph_dqueue_pop_back(&tosplit); + /* depth first search */ + long int size = 0; + igraph_real_t tmpev; + + IGRAPH_STATUSF(("Trying to split community %li... ", 0, comm)); + IGRAPH_ALLOW_INTERRUPTION(); + + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*mymembership)[i] == comm) { + VECTOR(idx)[size] = i; + VECTOR(idx2)[i] = size++; + } + } + + staken++; + if (size <= 2) { + continue; + } + + /* We solve two eigenproblems, one for the original modularity + matrix, and one for the modularity matrix after deleting the + last row and last column from it. This is a trick to find + multiple leading eigenvalues, because ARPACK is sometimes + unstable when the first two eigenvalues are requested, but it + does much better for the single principal eigenvalue. */ + + /* We start with the smaller eigenproblem. */ + + options->n = (int) size - 1; + options->info = 0; + options->nev = 1; + options->ldv = 0; + options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rssolve */ + options->nconv = 0; + options->lworkl = 0; /* we surely have enough space */ + extra.comm = comm; + + /* We try calling the solver twice, once from a random starting + point, once from a fixed one. This is because for some hard + cases it tends to fail. We need to suppress error handling for + the first call. */ + { + int i; + igraph_error_handler_t *errh = + igraph_set_error_handler(igraph_i_error_handler_none); + igraph_warning_handler_t *warnh = + igraph_set_warning_handler(igraph_warning_handler_ignore); + igraph_arpack_rssolve(arpcb2, &extra, options, &storage, + /*values=*/ 0, /*vectors=*/ 0); + igraph_set_error_handler(errh); + igraph_set_warning_handler(warnh); + if (options->nconv < 1) { + /* Call again from a fixed starting point. Note that we cannot use a + * fixed all-1 starting vector as sometimes ARPACK would return a + * 'starting vector is zero' error -- this is of course not true but + * it's a result of ARPACK >= 3.6.3 trying to force the starting vector + * into the range of OP (i.e. the matrix being solved). The initial + * vector we use here seems to work, but I have no theoretical argument + * for its usage; it just happens to work. */ + options->start = 1; + options->info = 0; + options->ncv = 0; + options->lworkl = 0; /* we surely have enough space */ + for (i = 0; i < options->n ; i++) { + storage.resid[i] = i % 2 ? 1 : -1; + } + IGRAPH_CHECK(igraph_arpack_rssolve(arpcb2, &extra, options, &storage, + /*values=*/ 0, /*vectors=*/ 0)); + options->start = 0; + } + } + + if (options->nconv < 1) { + IGRAPH_ERROR("ARPACK did not converge", IGRAPH_ARPACK_FAILED); + } + + tmpev = storage.d[0]; + + /* Now we do the original eigenproblem, again, twice if needed */ + + options->n = (int) size; + options->info = 0; + options->nev = 1; + options->ldv = 0; + options->nconv = 0; + options->lworkl = 0; /* we surely have enough space */ + options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rssolve */ + + { + int i; + igraph_error_handler_t *errh = + igraph_set_error_handler(igraph_i_error_handler_none); + igraph_arpack_rssolve(arpcb1, &extra, options, &storage, + /*values=*/ 0, /*vectors=*/ 0); + igraph_set_error_handler(errh); + if (options->nconv < 1) { + /* Call again from a fixed starting point. See the comment a few lines + * above about the exact choice of this starting vector */ + options->start = 1; + options->info = 0; + options->ncv = 0; + options->lworkl = 0; /* we surely have enough space */ + for (i = 0; i < options->n; i++) { + storage.resid[i] = i % 2 ? 1 : -1; + } + IGRAPH_CHECK(igraph_arpack_rssolve(arpcb1, &extra, options, &storage, + /*values=*/ 0, /*vectors=*/ 0)); + options->start = 0; + } + } + + if (options->nconv < 1) { + IGRAPH_ERROR("ARPACK did not converge", IGRAPH_ARPACK_FAILED); + } + + /* Ok, we have the leading eigenvector of the modularity matrix*/ + + /* ---------------------------------------------------------------*/ + /* To avoid numeric errors */ + if (fabs(storage.d[0]) < 1e-8) { + storage.d[0] = 0; + } + + /* We replace very small (in absolute value) elements of the + leading eigenvector with zero, to get the same result, + consistently.*/ + for (i = 0; i < size; i++) { + if (fabs(storage.v[i]) < 1e-8) { + storage.v[i] = 0; + } + } + + /* Just to have the always the same result, we multiply by -1 + if the first (nonzero) element is not positive. */ + for (i = 0; i < size; i++) { + if (storage.v[i] != 0) { + break; + } + } + if (i < size && storage.v[i] < 0) { + for (i = 0; i < size; i++) { + storage.v[i] = - storage.v[i]; + } + } + /* ---------------------------------------------------------------*/ + + if (callback) { + igraph_vector_t vv; + int ret; + igraph_vector_view(&vv, storage.v, size); + ret = callback(mymembership, comm, storage.d[0], &vv, + arpcb1, &extra, callback_extra); + if (ret) { + break; + } + } + + if (eigenvalues) { + IGRAPH_CHECK(igraph_vector_push_back(eigenvalues, storage.d[0])); + } + + if (eigenvectors) { + igraph_vector_t *v = IGRAPH_CALLOC(1, igraph_vector_t); + if (!v) { + IGRAPH_ERROR("Cannot do leading eigenvector community detection", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, v); + IGRAPH_VECTOR_INIT_FINALLY(v, size); + for (i = 0; i < size; i++) { + VECTOR(*v)[i] = storage.v[i]; + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(eigenvectors, v)); + IGRAPH_FINALLY_CLEAN(2); + } + + if (storage.d[0] <= 0) { + IGRAPH_STATUS("no split.\n", 0); + if (history) { + IGRAPH_CHECK(igraph_vector_push_back(history, + IGRAPH_LEVC_HIST_FAILED)); + IGRAPH_CHECK(igraph_vector_push_back(history, comm)); + } + continue; + } + + /* Check for multiple leading eigenvalues */ + + if (fabs(storage.d[0] - tmpev) < 1e-8) { + IGRAPH_STATUS("multiple principal eigenvalue, no split.\n", 0); + if (history) { + IGRAPH_CHECK(igraph_vector_push_back(history, + IGRAPH_LEVC_HIST_FAILED)); + IGRAPH_CHECK(igraph_vector_push_back(history, comm)); + } + continue; + } + + /* Count the number of vertices in each community after the split */ + l = 0; + for (j = 0; j < size; j++) { + if (storage.v[j] < 0) { + storage.v[j] = -1; + l++; + } else { + storage.v[j] = 1; + } + } + if (l == 0 || l == size) { + IGRAPH_STATUS("no split.\n", 0); + if (history) { + IGRAPH_CHECK(igraph_vector_push_back(history, + IGRAPH_LEVC_HIST_FAILED)); + IGRAPH_CHECK(igraph_vector_push_back(history, comm)); + } + continue; + } + + /* Check that Q increases with our choice of split */ + arpcb1(storage.v + size, storage.v, (int) size, &extra); + mod = 0; + for (i = 0; i < size; i++) { + mod += storage.v[size + i] * storage.v[i]; + } + if (mod <= 1e-8) { + IGRAPH_STATUS("no modularity increase, no split.\n", 0); + if (history) { + IGRAPH_CHECK(igraph_vector_push_back(history, + IGRAPH_LEVC_HIST_FAILED)); + IGRAPH_CHECK(igraph_vector_push_back(history, comm)); + } + continue; + } + + communities++; + IGRAPH_STATUS("split.\n", 0); + + /* Rewrite the mymembership vector */ + for (j = 0; j < size; j++) { + if (storage.v[j] < 0) { + long int oldid = (long int) VECTOR(idx)[j]; + VECTOR(*mymembership)[oldid] = communities - 1; + } + } + + /* Record merge */ + IGRAPH_CHECK(igraph_vector_push_back(&mymerges, comm)); + IGRAPH_CHECK(igraph_vector_push_back(&mymerges, communities - 1)); + if (history) { + IGRAPH_CHECK(igraph_vector_push_back(history, IGRAPH_LEVC_HIST_SPLIT)); + IGRAPH_CHECK(igraph_vector_push_back(history, comm)); + } + + /* Store the resulting communities in the queue if needed */ + if (l > 1) { + IGRAPH_CHECK(igraph_dqueue_push(&tosplit, communities - 1)); + } + if (size - l > 1) { + IGRAPH_CHECK(igraph_dqueue_push(&tosplit, comm)); + } + + } + + igraph_arpack_storage_destroy(&storage); + IGRAPH_FINALLY_CLEAN(1); + if (!weights) { + igraph_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + } else { + igraph_inclist_destroy(&inclist); + igraph_vector_destroy(&strength); + IGRAPH_FINALLY_CLEAN(2); + } + igraph_dqueue_destroy(&tosplit); + igraph_vector_destroy(&tmp); + igraph_vector_destroy(&idx2); + IGRAPH_FINALLY_CLEAN(3); + + IGRAPH_STATUS("Done.\n", 0); + + /* reform the mymerges vector */ + if (merges) { + igraph_vector_null(&idx); + l = igraph_vector_size(&mymerges); + k = communities; + j = 0; + IGRAPH_CHECK(igraph_matrix_resize(merges, l / 2, 2)); + for (i = l; i > 0; i -= 2) { + long int from = (long int) VECTOR(mymerges)[i - 1]; + long int to = (long int) VECTOR(mymerges)[i - 2]; + MATRIX(*merges, j, 0) = VECTOR(mymerges)[i - 2]; + MATRIX(*merges, j, 1) = VECTOR(mymerges)[i - 1]; + if (VECTOR(idx)[from] != 0) { + MATRIX(*merges, j, 1) = VECTOR(idx)[from] - 1; + } + if (VECTOR(idx)[to] != 0) { + MATRIX(*merges, j, 0) = VECTOR(idx)[to] - 1; + } + VECTOR(idx)[to] = ++k; + j++; + } + } + + if (eigenvectors) { + IGRAPH_FINALLY_CLEAN(1); + } + igraph_vector_destroy(&idx); + igraph_vector_destroy(&mymerges); + IGRAPH_FINALLY_CLEAN(2); + + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, mymembership, weights, + /* resolution */ 1, + /* only undirected */ 0, modularity)); + } + + if (!membership) { + igraph_vector_destroy(mymembership); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_le_community_to_membership + * Vertex membership from the leading eigenvector community structure + * + * This function creates a membership vector from the + * result of \ref igraph_community_leading_eigenvector(), + * It takes \c membership + * and performs \c steps merges, according to the supplied + * \c merges matrix. + * \param merges The two-column matrix containing the merge + * operations. See \ref igraph_community_walktrap() for the + * detailed syntax. This is usually from the output of the + * leading eigenvector community structure detection routines. + * \param steps The number of steps to make according to \c merges. + * \param membership Initially the starting membership vector, + * on output the resulting membership vector, after performing \c steps merges. + * \param csize Optionally the sizes of the communities is stored here, + * if this is not a null pointer, but an initialized vector. + * \return Error code. + * + * Time complexity: O(|V|), the number of vertices. + */ +int igraph_le_community_to_membership(const igraph_matrix_t *merges, + igraph_integer_t steps, + igraph_vector_t *membership, + igraph_vector_t *csize) { + + long int no_of_nodes = igraph_vector_size(membership); + igraph_vector_t fake_memb; + long int components, i; + + if (no_of_nodes > 0) { + components = (long int) igraph_vector_max(membership) + 1; + } else { + components = 0; + } + if (components > no_of_nodes) { + IGRAPH_ERRORF("Invalid membership vector: number of components (%ld) must " + "not be greater than the number of nodes (%ld).", IGRAPH_EINVAL, components, no_of_nodes); + } + if (steps >= components) { + IGRAPH_ERRORF("Number of steps (%" IGRAPH_PRId ") must be smaller than number of components (%ld).", + IGRAPH_EINVAL, steps, components); + } + + IGRAPH_VECTOR_INIT_FINALLY(&fake_memb, components); + + /* Check membership vector */ + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*membership)[i] < 0) { + IGRAPH_ERRORF("Invalid membership vector, negative ID found: %g.", IGRAPH_EINVAL, VECTOR(*membership)[i]); + } + VECTOR(fake_memb)[ (long int) VECTOR(*membership)[i] ] += 1; + } + for (i = 0; i < components; i++) { + if (VECTOR(fake_memb)[i] == 0) { + /* Ideally the empty cluster's index would be reported. + However, doing so would be confusing as some high-level interfaces + use 1-based indexing, some 0-based. */ + IGRAPH_ERROR("Invalid membership vector, empty cluster found.", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_community_to_membership(merges, (igraph_integer_t) + components, steps, + &fake_memb, 0)); + + /* Ok, now we have the membership of the initial components, + rewrite the original membership vector. */ + + if (csize) { + IGRAPH_CHECK(igraph_vector_resize(csize, components - steps)); + igraph_vector_null(csize); + } + + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*membership)[i] = VECTOR(fake_memb)[ (long int) VECTOR(*membership)[i] ]; + if (csize) { + VECTOR(*csize)[ (long int) VECTOR(*membership)[i] ] += 1; + } + } + + igraph_vector_destroy(&fake_memb); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/community/leiden.c b/src/rigraph/core/community/leiden.c new file mode 100644 index 0000000..ba9debc --- /dev/null +++ b/src/rigraph/core/community/leiden.c @@ -0,0 +1,1064 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_random.h" +#include "igraph_stack.h" +#include "igraph_vector.h" +#include "igraph_constructors.h" + +#include "../core/interruption.h" + +/* Move nodes in order to improve the quality of a partition. + * + * This function considers each node and greedily moves it to a neighboring + * community that maximizes the improvement in the quality of a partition. + * + * The nodes are examined in a queue, and initially all nodes are put in the + * queue in a random order. Nodes are popped from the queue when they are + * examined, and only neighbors of nodes that are moved (which are not part of + * the cluster the node was moved to) are pushed to the queue again. + * + * The \c membership vector is used as the starting point to move around nodes, + * and is updated in-place. + * + */ +static int igraph_i_community_leiden_fastmovenodes( + const igraph_t *graph, + const igraph_inclist_t *edges_per_node, + const igraph_vector_t *edge_weights, const igraph_vector_t *node_weights, + const igraph_real_t resolution_parameter, + igraph_integer_t *nb_clusters, + igraph_vector_t *membership) { + + igraph_dqueue_t unstable_nodes; + igraph_real_t max_diff = 0.0, diff = 0.0; + igraph_integer_t n = igraph_vcount(graph); + igraph_vector_bool_t neighbor_cluster_added, node_is_stable; + igraph_vector_t node_order, cluster_weights, edge_weights_per_cluster, neighbor_clusters; + igraph_vector_int_t nb_nodes_per_cluster; + igraph_stack_t empty_clusters; + long int i, j, c, nb_neigh_clusters; + + /* Initialize queue of unstable nodes and whether node is stable. Only + * unstable nodes are in the queue. */ + IGRAPH_CHECK(igraph_vector_bool_init(&node_is_stable, n)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &node_is_stable); + + IGRAPH_CHECK(igraph_dqueue_init(&unstable_nodes, n)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &unstable_nodes); + + /* Shuffle nodes */ + IGRAPH_CHECK(igraph_vector_init_seq(&node_order, 0, n - 1)); + IGRAPH_FINALLY(igraph_vector_destroy, &node_order); + IGRAPH_CHECK(igraph_vector_shuffle(&node_order)); + + /* Add to the queue */ + for (i = 0; i < n; i++) { + igraph_dqueue_push(&unstable_nodes, (long int)VECTOR(node_order)[i]); + } + + /* Initialize cluster weights and nb nodes */ + IGRAPH_CHECK(igraph_vector_init(&cluster_weights, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &cluster_weights); + IGRAPH_CHECK(igraph_vector_int_init(&nb_nodes_per_cluster, n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &nb_nodes_per_cluster); + for (i = 0; i < n; i++) { + c = (long int)VECTOR(*membership)[i]; + VECTOR(cluster_weights)[c] += VECTOR(*node_weights)[i]; + VECTOR(nb_nodes_per_cluster)[c] += 1; + } + + /* Initialize empty clusters */ + IGRAPH_CHECK(igraph_stack_init(&empty_clusters, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &empty_clusters); + for (c = 0; c < n; c++) + if (VECTOR(nb_nodes_per_cluster)[c] == 0) { + igraph_stack_push(&empty_clusters, c); + } + + /* Initialize vectors to be used in calculating differences */ + IGRAPH_CHECK(igraph_vector_init(&edge_weights_per_cluster, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &edge_weights_per_cluster); + + /* Initialize neighboring cluster */ + IGRAPH_CHECK(igraph_vector_bool_init(&neighbor_cluster_added, n)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &neighbor_cluster_added); + IGRAPH_CHECK(igraph_vector_init(&neighbor_clusters, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &neighbor_clusters); + + /* Iterate while the queue is not empty */ + j = 0; + while (!igraph_dqueue_empty(&unstable_nodes)) { + long int v = (long int) igraph_dqueue_pop(&unstable_nodes); + long int best_cluster, current_cluster = VECTOR(*membership)[v]; + long int degree, i; + igraph_vector_int_t *edges; + + /* Remove node from current cluster */ + VECTOR(cluster_weights)[current_cluster] -= VECTOR(*node_weights)[v]; + VECTOR(nb_nodes_per_cluster)[current_cluster]--; + if (VECTOR(nb_nodes_per_cluster)[current_cluster] == 0) { + IGRAPH_CHECK(igraph_stack_push(&empty_clusters, current_cluster)); + } + + /* Find out neighboring clusters */ + c = (long int) igraph_stack_top(&empty_clusters); + VECTOR(neighbor_clusters)[0] = c; + VECTOR(neighbor_cluster_added)[c] = 1; + nb_neigh_clusters = 1; + + /* Determine the edge weight to each neighboring cluster */ + edges = igraph_inclist_get(edges_per_node, v); + degree = igraph_vector_int_size(edges); + for (i = 0; i < degree; i++) { + long int e = VECTOR(*edges)[i]; + long int u = (long int)IGRAPH_OTHER(graph, e, v); + if (u != v) { + c = VECTOR(*membership)[u]; + if (!VECTOR(neighbor_cluster_added)[c]) { + VECTOR(neighbor_cluster_added)[c] = 1; + VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c; + } + VECTOR(edge_weights_per_cluster)[c] += VECTOR(*edge_weights)[e]; + } + } + + /* Calculate maximum diff */ + best_cluster = current_cluster; + max_diff = VECTOR(edge_weights_per_cluster)[current_cluster] - VECTOR(*node_weights)[v] * VECTOR(cluster_weights)[current_cluster] * resolution_parameter; + for (i = 0; i < nb_neigh_clusters; i++) { + c = VECTOR(neighbor_clusters)[i]; + diff = VECTOR(edge_weights_per_cluster)[c] - VECTOR(*node_weights)[v] * VECTOR(cluster_weights)[c] * resolution_parameter; + if (diff > max_diff) { + best_cluster = c; + max_diff = diff; + } + VECTOR(edge_weights_per_cluster)[c] = 0.0; + VECTOR(neighbor_cluster_added)[c] = 0; + } + + /* Move node to best cluster */ + VECTOR(cluster_weights)[best_cluster] += VECTOR(*node_weights)[v]; + VECTOR(nb_nodes_per_cluster)[best_cluster]++; + if (best_cluster == igraph_stack_top(&empty_clusters)) { + igraph_stack_pop(&empty_clusters); + } + + /* Mark node as stable */ + VECTOR(node_is_stable)[v] = 1; + + /* Add stable neighbours that are not part of the new cluster to the queue */ + if (best_cluster != current_cluster) { + VECTOR(*membership)[v] = best_cluster; + + for (i = 0; i < degree; i++) { + long int e = VECTOR(*edges)[i]; + long int u = (long int) IGRAPH_OTHER(graph, e, v); + if (VECTOR(node_is_stable)[u] && VECTOR(*membership)[u] != best_cluster) { + IGRAPH_CHECK(igraph_dqueue_push(&unstable_nodes, u)); + VECTOR(node_is_stable)[u] = 0; + } + } + } + + j++; + if (j > 10000) { + IGRAPH_ALLOW_INTERRUPTION(); + j = 0; + } + } + + IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, nb_clusters)); + + igraph_vector_destroy(&neighbor_clusters); + igraph_vector_bool_destroy(&neighbor_cluster_added); + igraph_vector_destroy(&edge_weights_per_cluster); + igraph_stack_destroy(&empty_clusters); + igraph_vector_int_destroy(&nb_nodes_per_cluster); + igraph_vector_destroy(&cluster_weights); + igraph_vector_destroy(&node_order); + igraph_dqueue_destroy(&unstable_nodes); + igraph_vector_bool_destroy(&node_is_stable); + + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} + +/* Clean a refined membership vector. + * + * This function examines all nodes in \c node_subset and updates \c + * refined_membership to ensure that the clusters are numbered consecutively, + * starting from \c nb_refined_clusters. The \c nb_refined_clusters is also + * updated itself. If C is the initial \c nb_refined_clusters and C' the + * resulting \c nb_refined_clusters, then nodes in \c node_subset are numbered + * C, C + 1, ..., C' - 1. + */ +static int igraph_i_community_leiden_clean_refined_membership( + const igraph_vector_t* node_subset, + igraph_vector_t *refined_membership, + igraph_integer_t* nb_refined_clusters) { + long int i, n = igraph_vector_size(node_subset); + igraph_vector_t new_cluster; + + IGRAPH_CHECK(igraph_vector_init(&new_cluster, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &new_cluster); + + /* Clean clusters. We will store the new cluster + 1 so that cluster == 0 + * indicates that no membership was assigned yet. */ + *nb_refined_clusters += 1; + for (i = 0; i < n; i++) { + long int v = (long int) VECTOR(*node_subset)[i]; + long int c = (long int) VECTOR(*refined_membership)[v]; + if (VECTOR(new_cluster)[c] == 0) { + VECTOR(new_cluster)[c] = (igraph_real_t)(*nb_refined_clusters); + *nb_refined_clusters += 1; + } + } + + /* Assign new cluster */ + for (i = 0; i < n; i++) { + long int v = (long int) VECTOR(*node_subset)[i]; + long int c = (long int) VECTOR(*refined_membership)[v]; + VECTOR(*refined_membership)[v] = VECTOR(new_cluster)[c] - 1; + } + /* We used the cluster + 1, so correct */ + *nb_refined_clusters -= 1; + + igraph_vector_destroy(&new_cluster); + + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* Merge nodes for a subset of the nodes. This is used to refine a partition. + * + * The nodes included in \c node_subset are assumed to be the nodes i for which + * membership[i] = cluster_subset. + * + * All nodes in \c node_subset are initialized to a singleton partition in \c + * refined_membership. Only singleton clusters can be merged if they are + * sufficiently well connected to the current subgraph induced by \c + * node_subset. + * + * We only examine each node once. Instead of greedily choosing the maximum + * possible cluster to merge with, the cluster is chosen randomly among all + * possibilities that do not decrease the quality of the partition. The + * probability of choosing a certain cluster is proportional to exp(diff/beta). + * For beta to 0 this converges to selecting a cluster with the maximum + * improvement. For beta to infinity this converges to a uniform distribution + * among all eligible clusters. + * + * The \c refined_membership is updated for node in \c node_subset. The number + * of refined clusters, \c nb_refined_clusters is used to set the actual refined + * cluster membership and is updated after this routine. Within each cluster + * (i.e. for a given \c node_subset), the refined membership is initially simply + * set to 0, ..., n - 1 (for n nodes in \c node_subset). However, for each \c + * node_subset the refined membership should of course be unique. Hence, after + * merging, the refined membership starts with \c nb_refined_clusters, which is + * also updated to ensure that the resulting \c nb_refined_clusters counts all + * refined clusters that have already been processed. See + * igraph_i_community_leiden_clean_refined_membership for more information about + * this aspect. + */ +static int igraph_i_community_leiden_mergenodes( + const igraph_t *graph, + const igraph_inclist_t *edges_per_node, + const igraph_vector_t *edge_weights, const igraph_vector_t *node_weights, + const igraph_vector_t *node_subset, + const igraph_vector_t *membership, + const igraph_integer_t cluster_subset, + const igraph_real_t resolution_parameter, + const igraph_real_t beta, + igraph_integer_t *nb_refined_clusters, + igraph_vector_t *refined_membership) { + igraph_vector_t node_order; + igraph_vector_bool_t non_singleton_cluster, neighbor_cluster_added; + igraph_real_t max_diff, total_cum_trans_diff, diff = 0.0, total_node_weight = 0.0; + igraph_integer_t n = igraph_vector_size(node_subset); + igraph_vector_t cluster_weights, cum_trans_diff, edge_weights_per_cluster, external_edge_weight_per_cluster_in_subset, neighbor_clusters; + igraph_vector_int_t *edges, nb_nodes_per_cluster; + long int i, j, degree, nb_neigh_clusters; + + /* Initialize cluster weights */ + IGRAPH_CHECK(igraph_vector_init(&cluster_weights, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &cluster_weights); + + /* Initialize number of nodes per cluster */ + IGRAPH_CHECK(igraph_vector_int_init(&nb_nodes_per_cluster, n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &nb_nodes_per_cluster); + + /* Initialize external edge weight per cluster in subset */ + IGRAPH_CHECK(igraph_vector_init(&external_edge_weight_per_cluster_in_subset, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &external_edge_weight_per_cluster_in_subset); + + /* Initialize administration for a singleton partition */ + for (i = 0; i < n; i++) { + long int v = (long int) VECTOR(*node_subset)[i]; + VECTOR(*refined_membership)[v] = i; + VECTOR(cluster_weights)[i] += VECTOR(*node_weights)[v]; + VECTOR(nb_nodes_per_cluster)[i] += 1; + total_node_weight += VECTOR(*node_weights)[v]; + + /* Find out neighboring clusters */ + edges = igraph_inclist_get(edges_per_node, v); + degree = igraph_vector_int_size(edges); + for (j = 0; j < degree; j++) { + long int e = VECTOR(*edges)[j]; + long int u = (long int)IGRAPH_OTHER(graph, e, v); + if (u != v && VECTOR(*membership)[u] == cluster_subset) { + VECTOR(external_edge_weight_per_cluster_in_subset)[i] += VECTOR(*edge_weights)[e]; + } + } + } + + /* Shuffle nodes */ + IGRAPH_CHECK(igraph_vector_copy(&node_order, node_subset)); + IGRAPH_FINALLY(igraph_vector_destroy, &node_order); + IGRAPH_CHECK(igraph_vector_shuffle(&node_order)); + + /* Initialize non singleton clusters */ + IGRAPH_CHECK(igraph_vector_bool_init(&non_singleton_cluster, n)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &non_singleton_cluster); + + /* Initialize vectors to be used in calculating differences */ + IGRAPH_CHECK(igraph_vector_init(&edge_weights_per_cluster, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &edge_weights_per_cluster); + + /* Initialize neighboring cluster */ + IGRAPH_CHECK(igraph_vector_bool_init(&neighbor_cluster_added, n)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &neighbor_cluster_added); + IGRAPH_CHECK(igraph_vector_init(&neighbor_clusters, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &neighbor_clusters); + + /* Initialize cumulative transformed difference */ + IGRAPH_CHECK(igraph_vector_init(&cum_trans_diff, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &cum_trans_diff); + + RNG_BEGIN(); + + for (i = 0; i < n; i++) { + long int v = (long int) VECTOR(node_order)[i]; + long int chosen_cluster, best_cluster, current_cluster = (long int) VECTOR(*refined_membership)[v]; + + if (!VECTOR(non_singleton_cluster)[current_cluster] && + (VECTOR(external_edge_weight_per_cluster_in_subset)[current_cluster] >= + VECTOR(cluster_weights)[current_cluster] * (total_node_weight - VECTOR(cluster_weights)[current_cluster]) * resolution_parameter)) { + /* Remove node from current cluster, which is then a singleton by + * definition. */ + VECTOR(cluster_weights)[current_cluster] = 0.0; + VECTOR(nb_nodes_per_cluster)[current_cluster] = 0; + + /* Find out neighboring clusters */ + edges = igraph_inclist_get(edges_per_node, v); + degree = igraph_vector_int_size(edges); + + /* Also add current cluster to ensure it can be chosen. */ + VECTOR(neighbor_clusters)[0] = current_cluster; + VECTOR(neighbor_cluster_added)[current_cluster] = 1; + nb_neigh_clusters = 1; + for (j = 0; j < degree; j++) { + long int e = (long int)VECTOR(*edges)[j]; + long int u = (long int)IGRAPH_OTHER(graph, e, v); + if (u != v && VECTOR(*membership)[u] == cluster_subset) { + long int c = VECTOR(*refined_membership)[u]; + if (!VECTOR(neighbor_cluster_added)[c]) { + VECTOR(neighbor_cluster_added)[c] = 1; + VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c; + } + VECTOR(edge_weights_per_cluster)[c] += VECTOR(*edge_weights)[e]; + } + } + + /* Calculate diffs */ + best_cluster = current_cluster; + max_diff = 0.0; + total_cum_trans_diff = 0.0; + for (j = 0; j < nb_neigh_clusters; j++) { + long int c = (long int) VECTOR(neighbor_clusters)[j]; + if (VECTOR(external_edge_weight_per_cluster_in_subset)[c] >= VECTOR(cluster_weights)[c] * (total_node_weight - VECTOR(cluster_weights)[c]) * resolution_parameter) { + diff = VECTOR(edge_weights_per_cluster)[c] - VECTOR(*node_weights)[v] * VECTOR(cluster_weights)[c] * resolution_parameter; + + if (diff > max_diff) { + best_cluster = c; + max_diff = diff; + } + + /* Calculate the transformed difference for sampling */ + if (diff >= 0) { + total_cum_trans_diff += exp(diff / beta); + } + + } + + VECTOR(cum_trans_diff)[j] = total_cum_trans_diff; + VECTOR(edge_weights_per_cluster)[c] = 0.0; + VECTOR(neighbor_cluster_added)[c] = 0; + } + + /* Determine the neighboring cluster to which the currently selected node + * will be moved. + */ + if (total_cum_trans_diff < IGRAPH_INFINITY) { + igraph_real_t r = RNG_UNIF(0, total_cum_trans_diff); + long int chosen_idx; + igraph_vector_binsearch_slice(&cum_trans_diff, r, &chosen_idx, 0, nb_neigh_clusters); + chosen_cluster = VECTOR(neighbor_clusters)[chosen_idx]; + } else { + chosen_cluster = best_cluster; + } + + /* Move node to randomly chosen cluster */ + VECTOR(cluster_weights)[chosen_cluster] += VECTOR(*node_weights)[v]; + VECTOR(nb_nodes_per_cluster)[chosen_cluster]++; + + for (j = 0; j < degree; j++) { + long int e = (long int) VECTOR(*edges)[j]; + long int u = (long int) IGRAPH_OTHER(graph, e, v); + if (VECTOR(*membership)[u] == cluster_subset) { + if (VECTOR(*refined_membership)[u] == chosen_cluster) { + VECTOR(external_edge_weight_per_cluster_in_subset)[chosen_cluster] -= VECTOR(*edge_weights)[e]; + } else { + VECTOR(external_edge_weight_per_cluster_in_subset)[chosen_cluster] += VECTOR(*edge_weights)[e]; + } + } + } + + /* Set cluster */ + if (chosen_cluster != current_cluster) { + VECTOR(*refined_membership)[v] = chosen_cluster; + + VECTOR(non_singleton_cluster)[chosen_cluster] = 1; + } + } /* end if singleton and may be merged */ + } + + RNG_END(); + + IGRAPH_CHECK(igraph_i_community_leiden_clean_refined_membership(node_subset, refined_membership, nb_refined_clusters)); + + igraph_vector_destroy(&cum_trans_diff); + igraph_vector_destroy(&neighbor_clusters); + igraph_vector_bool_destroy(&neighbor_cluster_added); + igraph_vector_destroy(&edge_weights_per_cluster); + igraph_vector_bool_destroy(&non_singleton_cluster); + igraph_vector_destroy(&node_order); + igraph_vector_destroy(&external_edge_weight_per_cluster_in_subset); + igraph_vector_int_destroy(&nb_nodes_per_cluster); + igraph_vector_destroy(&cluster_weights); + + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} + +/* Create clusters out of a membership vector. + * + * The cluster pointer vector should be initialized for all entries of the + * membership vector, no range checking is performed. If a vector for a cluster + * does not yet exist it will be created and initialized. If a vector for a + * cluster already does exist it will not be emptied on first use. Hence, it + * should be ensured that all clusters are always properly empty (or + * non-existing) before calling this function. + */ +static int igraph_i_community_get_clusters(const igraph_vector_t *membership, igraph_vector_ptr_t *clusters) { + long int i, c, n = igraph_vector_size(membership); + igraph_vector_t *cluster; + for (i = 0; i < n; i++) { + /* Get cluster for node i */ + c = VECTOR(*membership)[i]; + cluster = (igraph_vector_t*)VECTOR(*clusters)[c]; + + /* No cluster vector exists yet, so we create a new one */ + if (!cluster) { + cluster = IGRAPH_CALLOC(1, igraph_vector_t); + if (cluster == 0) { + IGRAPH_ERROR("Cannot allocate memory for assigning cluster", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(cluster, 0)); + VECTOR(*clusters)[c] = cluster; + } + + /* Add node i to cluster vector */ + IGRAPH_CHECK(igraph_vector_push_back(cluster, i)); + } + + return IGRAPH_SUCCESS; +} + +/* Aggregate the graph based on the \c refined membership while setting the + * membership of each aggregated node according to the \c membership. + * + * Technically speaking we have that + * aggregated_membership[refined_membership[v]] = membership[v] for each node v. + * + * The new aggregated graph is returned in \c aggregated_graph. This graph + * object should not yet be initialized, `igraph_create` is called on it, and + * responsibility for destroying the object lies with the calling method + * + * The remaining results, aggregated_edge_weights, aggregate_node_weights and + * aggregated_membership are all expected to be initialized. + * + */ +static int igraph_i_community_leiden_aggregate( + const igraph_t *graph, const igraph_inclist_t *edges_per_node, const igraph_vector_t *edge_weights, const igraph_vector_t *node_weights, + const igraph_vector_t *membership, const igraph_vector_t *refined_membership, const igraph_integer_t nb_refined_clusters, + igraph_t *aggregated_graph, igraph_vector_t *aggregated_edge_weights, igraph_vector_t *aggregated_node_weights, igraph_vector_t *aggregated_membership) { + igraph_vector_t aggregated_edges, edge_weight_to_cluster; + igraph_vector_ptr_t refined_clusters; + igraph_vector_int_t *incident_edges; + igraph_vector_t neighbor_clusters; + igraph_vector_bool_t neighbor_cluster_added; + long int i, j, c, degree, nb_neigh_clusters; + + /* Get refined clusters */ + IGRAPH_CHECK(igraph_vector_ptr_init(&refined_clusters, nb_refined_clusters)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&refined_clusters, igraph_vector_destroy); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &refined_clusters); + IGRAPH_CHECK(igraph_i_community_get_clusters(refined_membership, &refined_clusters)); + + /* Initialize new edges */ + IGRAPH_CHECK(igraph_vector_init(&aggregated_edges, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_edges); + + /* We clear the aggregated edge weights, we will push each new edge weight */ + igraph_vector_clear(aggregated_edge_weights); + /* Simply resize the aggregated node weights and membership, they can be set + * directly */ + IGRAPH_CHECK(igraph_vector_resize(aggregated_node_weights, nb_refined_clusters)); + IGRAPH_CHECK(igraph_vector_resize(aggregated_membership, nb_refined_clusters)); + + IGRAPH_CHECK(igraph_vector_init(&edge_weight_to_cluster, nb_refined_clusters)); + IGRAPH_FINALLY(igraph_vector_destroy, &edge_weight_to_cluster); + + /* Initialize neighboring cluster */ + IGRAPH_CHECK(igraph_vector_bool_init(&neighbor_cluster_added, nb_refined_clusters)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &neighbor_cluster_added); + IGRAPH_CHECK(igraph_vector_init(&neighbor_clusters, nb_refined_clusters)); + IGRAPH_FINALLY(igraph_vector_destroy, &neighbor_clusters); + + /* Check per cluster */ + for (c = 0; c < nb_refined_clusters; c++) { + igraph_vector_t* refined_cluster = (igraph_vector_t*)VECTOR(refined_clusters)[c]; + long int n_c = igraph_vector_size(refined_cluster); + long int v = -1; + + /* Calculate the total edge weight to other clusters */ + VECTOR(*aggregated_node_weights)[c] = 0.0; + nb_neigh_clusters = 0; + for (i = 0; i < n_c; i++) { + v = (long int) VECTOR(*refined_cluster)[i]; + incident_edges = igraph_inclist_get(edges_per_node, v); + degree = igraph_vector_int_size(incident_edges); + + for (j = 0; j < degree; j++) { + long int e = VECTOR(*incident_edges)[j]; + long int u = (long int) IGRAPH_OTHER(graph, e, v); + long int c2 = VECTOR(*refined_membership)[u]; + + if (c2 > c) { + if (!VECTOR(neighbor_cluster_added)[c2]) { + VECTOR(neighbor_cluster_added)[c2] = 1; + VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c2; + } + VECTOR(edge_weight_to_cluster)[c2] += VECTOR(*edge_weights)[e]; + } + } + + VECTOR(*aggregated_node_weights)[c] += VECTOR(*node_weights)[v]; + } + + /* Add actual edges from this cluster to the other clusters */ + for (i = 0; i < nb_neigh_clusters; i++) { + long int c2 = VECTOR(neighbor_clusters)[i]; + + /* Add edge */ + IGRAPH_CHECK(igraph_vector_push_back(&aggregated_edges, c)); + IGRAPH_CHECK(igraph_vector_push_back(&aggregated_edges, c2)); + + /* Add edge weight */ + IGRAPH_CHECK(igraph_vector_push_back(aggregated_edge_weights, VECTOR(edge_weight_to_cluster)[c2])); + + VECTOR(edge_weight_to_cluster)[c2] = 0.0; + VECTOR(neighbor_cluster_added)[c2] = 0; + } + + VECTOR(*aggregated_membership)[c] = VECTOR(*membership)[v]; + + } + + igraph_vector_destroy(&neighbor_clusters); + igraph_vector_bool_destroy(&neighbor_cluster_added); + igraph_vector_destroy(&edge_weight_to_cluster); + igraph_vector_ptr_destroy_all(&refined_clusters); + + IGRAPH_FINALLY_CLEAN(4); + + igraph_destroy(aggregated_graph); + IGRAPH_CHECK(igraph_create(aggregated_graph, &aggregated_edges, nb_refined_clusters, + IGRAPH_UNDIRECTED)); + + igraph_vector_destroy(&aggregated_edges); + + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* Calculate the quality of the partition. + * + * The quality is defined as + * + * 1 / 2m sum_ij (A_ij - gamma n_i n_j)d(s_i, s_j) + * + * where m is the total edge weight, A_ij is the weight of edge (i, j), gamma is + * the so-called resolution parameter, n_i is the node weight of node i, s_i is + * the cluster of node i and d(x, y) = 1 if and only if x = y and 0 otherwise. + * + * Note that by setting n_i = k_i the degree of node i and dividing gamma by 2m, + * we effectively optimize modularity. By setting n_i = 1 we optimize the + * Constant Potts Model. + * + * This can be represented as a sum over clusters as + * + * 1 / 2m sum_c (e_c - gamma N_c^2) + * + * where e_c = sum_ij A_ij d(s_i, c)d(s_j, c) is (twice) the internal edge + * weight in cluster c and N_c = sum_i n_i d(s_i, c) is the sum of the node + * weights inside cluster c. This is how the quality is calculated in practice. + * + */ +static int igraph_i_community_leiden_quality( + const igraph_t *graph, const igraph_vector_t *edge_weights, const igraph_vector_t *node_weights, + const igraph_vector_t *membership, const igraph_integer_t nb_comms, const igraph_real_t resolution_parameter, + igraph_real_t *quality) { + igraph_vector_t cluster_weights; + igraph_real_t total_edge_weight = 0.0; + igraph_eit_t eit; + long int i, c, n = igraph_vcount(graph);; + + *quality = 0.0; + + /* Create the edgelist */ + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + while (!IGRAPH_EIT_END(eit)) { + igraph_integer_t e = IGRAPH_EIT_GET(eit), from, to; + IGRAPH_CHECK(igraph_edge(graph, e, &from, &to)); + total_edge_weight += VECTOR(*edge_weights)[e]; + /* We add the internal edge weights */ + if (VECTOR(*membership)[(long int) from] == VECTOR(*membership)[(long int) to]) { + *quality += 2 * VECTOR(*edge_weights)[e]; + } + IGRAPH_EIT_NEXT(eit); + } + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); + + /* Initialize cluster weights and nb nodes */ + IGRAPH_CHECK(igraph_vector_init(&cluster_weights, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &cluster_weights); + for (i = 0; i < n; i++) { + c = VECTOR(*membership)[i]; + VECTOR(cluster_weights)[c] += VECTOR(*node_weights)[i]; + } + + /* We subtract gamma * N_c^2 */ + for (c = 0; c < nb_comms; c++) { + *quality -= resolution_parameter * VECTOR(cluster_weights)[c] * VECTOR(cluster_weights)[c]; + } + + igraph_vector_destroy(&cluster_weights); + IGRAPH_FINALLY_CLEAN(1); + + /* We normalise by 2m */ + *quality /= (2.0 * total_edge_weight); + + return IGRAPH_SUCCESS; +} + +/* This is the core of the Leiden algorithm and relies on subroutines to + * perform the three different phases: (1) local moving of nodes, (2) + * refinement of the partition and (3) aggregation of the network based on the + * refined partition, using the non-refined partition to create an initial + * partition for the aggregate network. + */ +static int igraph_i_community_leiden( + const igraph_t *graph, + igraph_vector_t *edge_weights, igraph_vector_t *node_weights, + const igraph_real_t resolution_parameter, const igraph_real_t beta, + igraph_vector_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality) { + igraph_integer_t nb_refined_clusters; + long int i, c, n = igraph_vcount(graph); + igraph_t aggregated_graph, *i_graph; + igraph_vector_t aggregated_edge_weights, aggregated_node_weights, aggregated_membership; + igraph_vector_t *i_edge_weights, *i_node_weights, *i_membership; + igraph_vector_t tmp_edge_weights, tmp_node_weights, tmp_membership; + igraph_vector_t refined_membership; + igraph_vector_int_t aggregate_node; + igraph_vector_ptr_t clusters; + igraph_inclist_t edges_per_node; + igraph_bool_t continue_clustering; + igraph_integer_t level = 0; + + /* Initialize temporary weights and membership to be used in aggregation */ + IGRAPH_CHECK(igraph_vector_init(&tmp_edge_weights, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &tmp_edge_weights); + IGRAPH_CHECK(igraph_vector_init(&tmp_node_weights, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &tmp_node_weights); + IGRAPH_CHECK(igraph_vector_init(&tmp_membership, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &tmp_membership); + + /* Initialize clusters */ + IGRAPH_CHECK(igraph_vector_ptr_init(&clusters, n)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&clusters, igraph_vector_destroy); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &clusters); + + /* Initialize aggregate nodes, which initially is identical to simply the + * nodes in the graph. */ + IGRAPH_CHECK(igraph_vector_int_init(&aggregate_node, n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &aggregate_node); + for (i = 0; i < n; i++) { + VECTOR(aggregate_node)[i] = i; + } + + /* Initialize refined membership */ + IGRAPH_CHECK(igraph_vector_init(&refined_membership, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &refined_membership); + + /* Initialize aggregated graph */ + IGRAPH_CHECK(igraph_empty(&aggregated_graph, 0, IGRAPH_UNDIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &aggregated_graph); + + /* Initialize aggregated edge weights */ + IGRAPH_CHECK(igraph_vector_init(&aggregated_edge_weights, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_edge_weights); + + /* Initialize aggregated node weights */ + IGRAPH_CHECK(igraph_vector_init(&aggregated_node_weights, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_node_weights); + + /* Initialize aggregated membership */ + IGRAPH_CHECK(igraph_vector_init(&aggregated_membership, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_membership); + + /* Set actual graph, weights and membership to be used. */ + i_graph = (igraph_t*)graph; + i_edge_weights = edge_weights; + i_node_weights = node_weights; + i_membership = membership; + + /* Clean membership and count number of *clusters */ + + IGRAPH_CHECK(igraph_reindex_membership(i_membership, NULL, nb_clusters)); + + if (*nb_clusters > n) { + IGRAPH_ERROR("Too many communities in membership vector", IGRAPH_EINVAL); + } + + do { + + /* Get incidence list for fast iteration */ + IGRAPH_CHECK(igraph_inclist_init( i_graph, &edges_per_node, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &edges_per_node); + + /* Move around the nodes in order to increase the quality */ + IGRAPH_CHECK(igraph_i_community_leiden_fastmovenodes(i_graph, + &edges_per_node, + i_edge_weights, i_node_weights, + resolution_parameter, + nb_clusters, + i_membership)); + + /* We only continue clustering if not all clusters are represented by a + * single node yet + */ + continue_clustering = (*nb_clusters < igraph_vcount(i_graph)); + + if (continue_clustering) { + /* Set original membership */ + if (level > 0) { + for (i = 0; i < n; i++) { + long int v_aggregate = VECTOR(aggregate_node)[i]; + VECTOR(*membership)[i] = VECTOR(*i_membership)[v_aggregate]; + } + } + + /* Get node sets for each cluster. */ + IGRAPH_CHECK(igraph_i_community_get_clusters(i_membership, &clusters)); + + /* Ensure refined membership is correct size */ + IGRAPH_CHECK(igraph_vector_resize(&refined_membership, igraph_vcount(i_graph))); + + /* Refine each cluster */ + nb_refined_clusters = 0; + for (c = 0; c < *nb_clusters; c++) { + igraph_vector_t* cluster = (igraph_vector_t*)VECTOR(clusters)[c]; + IGRAPH_CHECK(igraph_i_community_leiden_mergenodes(i_graph, + &edges_per_node, + i_edge_weights, i_node_weights, + cluster, i_membership, c, + resolution_parameter, beta, + &nb_refined_clusters, &refined_membership)); + /* Empty cluster */ + igraph_vector_clear(cluster); + } + + /* If refinement didn't aggregate anything, we aggregate on the basis of + * the actual clustering */ + if (nb_refined_clusters >= igraph_vcount(i_graph)) { + igraph_vector_update(&refined_membership, i_membership); + nb_refined_clusters = *nb_clusters; + } + + /* Keep track of aggregate node. */ + for (i = 0; i < n; i++) { + /* Current aggregate node */ + igraph_integer_t v_aggregate = VECTOR(aggregate_node)[i]; + /* New aggregate node */ + VECTOR(aggregate_node)[i] = (igraph_integer_t)VECTOR(refined_membership)[v_aggregate]; + } + + IGRAPH_CHECK(igraph_i_community_leiden_aggregate( + i_graph, &edges_per_node, i_edge_weights, i_node_weights, + i_membership, &refined_membership, nb_refined_clusters, + &aggregated_graph, &tmp_edge_weights, &tmp_node_weights, &tmp_membership)); + + /* On the lowest level, the actual graph and node and edge weights and + * membership are used. On higher levels, we will use the aggregated graph + * and associated vectors. + */ + if (level == 0) { + /* Set actual graph, weights and membership to be used. */ + i_graph = &aggregated_graph; + i_edge_weights = &aggregated_edge_weights; + i_node_weights = &aggregated_node_weights; + i_membership = &aggregated_membership; + } + + /* Update the aggregated administration. */ + IGRAPH_CHECK(igraph_vector_update(i_edge_weights, &tmp_edge_weights)); + IGRAPH_CHECK(igraph_vector_update(i_node_weights, &tmp_node_weights)); + IGRAPH_CHECK(igraph_vector_update(i_membership, &tmp_membership)); + + level += 1; + } + + /* We are done iterating, so we destroy the incidence list */ + igraph_inclist_destroy(&edges_per_node); + IGRAPH_FINALLY_CLEAN(1); + } while (continue_clustering); + + /* Free aggregated graph and associated vectors */ + igraph_vector_destroy(&aggregated_membership); + igraph_vector_destroy(&aggregated_node_weights); + igraph_vector_destroy(&aggregated_edge_weights); + igraph_destroy(&aggregated_graph); + IGRAPH_FINALLY_CLEAN(4); + + /* Free remaining memory */ + igraph_vector_destroy(&refined_membership); + igraph_vector_int_destroy(&aggregate_node); + igraph_vector_ptr_destroy_all(&clusters); + igraph_vector_destroy(&tmp_membership); + igraph_vector_destroy(&tmp_node_weights); + igraph_vector_destroy(&tmp_edge_weights); + IGRAPH_FINALLY_CLEAN(6); + + /* Calculate quality */ + if (quality) { + IGRAPH_CHECK(igraph_i_community_leiden_quality(graph, edge_weights, node_weights, membership, *nb_clusters, resolution_parameter, quality)); + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup communities + * \function igraph_community_leiden + * \brief Finding community structure using the Leiden algorithm. + * + * This function implements the Leiden algorithm for finding community + * structure, see Traag, V. A., Waltman, L., & van Eck, N. J. (2019). From + * Louvain to Leiden: guaranteeing well-connected communities. Scientific + * reports, 9(1), 5233. http://dx.doi.org/10.1038/s41598-019-41695-z + * + * + * It is similar to the multilevel algorithm, often called the Louvain + * algorithm, but it is faster and yields higher quality solutions. It can + * optimize both modularity and the Constant Potts Model, which does not suffer + * from the resolution-limit (see preprint http://arxiv.org/abs/1104.3083). + * + * + * The Leiden algorithm consists of three phases: (1) local moving of nodes, + * (2) refinement of the partition and (3) aggregation of the network based on + * the refined partition, using the non-refined partition to create an initial + * partition for the aggregate network. In the local move procedure in the + * Leiden algorithm, only nodes whose neighborhood has changed are visited. The + * refinement is done by restarting from a singleton partition within each + * cluster and gradually merging the subclusters. When aggregating, a single + * cluster may then be represented by several nodes (which are the subclusters + * identified in the refinement). + * + * + * The Leiden algorithm provides several guarantees. The Leiden algorithm is + * typically iterated: the output of one iteration is used as the input for the + * next iteration. At each iteration all clusters are guaranteed to be + * connected and well-separated. After an iteration in which nothing has + * changed, all nodes and some parts are guaranteed to be locally optimally + * assigned. Finally, asymptotically, all subsets of all clusters are + * guaranteed to be locally optimally assigned. For more details, please see + * Traag, Waltman & van Eck (2019). + * + * + * The objective function being optimized is + * + * + * 1 / 2m sum_ij (A_ij - gamma n_i n_j)d(s_i, s_j) + * + * + * where m is the total edge weight, A_ij is the weight of edge (i, j), gamma is + * the so-called resolution parameter, n_i is the node weight of node i, s_i is + * the cluster of node i and d(x, y) = 1 if and only if x = y and 0 otherwise. + * By setting n_i = k_i, the degree of node i, and dividing gamma by 2m, you + * effectively obtain an expression for modularity. Hence, the standard + * modularity will be optimized when you supply the degrees as \c node_weights + * and by supplying as a resolution parameter 1.0/(2*m), with m the number of + * edges. + * + * \param graph The input graph. It must be an undirected graph. + * \param edge_weights Numeric vector containing edge weights. If \c NULL, every edge + * has equal weight of 1. The weights need not be non-negative. + * \param node_weights Numeric vector containing node weights. + * \param resolution_parameter The resolution parameter used, which is + * represented by gamma in the objective function mentioned in the + * documentation. + * \param beta The randomness used in the refinement step when merging. A small + * amount of randomness (\c beta = 0.01) typically works well. + * \param start Start from membership vector. If this is true, the optimization + * will start from the provided membership vector. If this is false, the + * optimization will start from a singleton partition. + * \param membership The membership vector. This is both used as the initial + * membership from which optimisation starts and is updated in place. It + * must hence be properly initialized. When finding clusters from scratch it + * is typically started using a singleton clustering. This can be achieved + * using \c igraph_vector_init_seq. + * \param nb_clusters The number of clusters contained in \c membership. Must + * not be a \c NULL pointer. + * \param quality The quality of the partition, in terms of the objective + * function as included in the documentation. If \c NULL the quality will + * not be calculated. + * \return Error code. + * + * Time complexity: near linear on sparse graphs. + * + * \example examples/simple/igraph_community_leiden.c + */ +int igraph_community_leiden(const igraph_t *graph, + const igraph_vector_t *edge_weights, const igraph_vector_t *node_weights, + const igraph_real_t resolution_parameter, const igraph_real_t beta, const igraph_bool_t start, + igraph_vector_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality) { + igraph_vector_t *i_edge_weights, *i_node_weights; + igraph_integer_t n = igraph_vcount(graph); + + if (start) { + if (!membership) { + IGRAPH_ERROR("Cannot start optimization if membership is missing", IGRAPH_EINVAL); + } + + if (igraph_vector_size(membership) != n) { + IGRAPH_ERROR("Initial membership length does not equal the number of vertices", IGRAPH_EINVAL); + } + } else { + int i; + if (!membership) + IGRAPH_ERROR("Membership vector should be supplied and initialized, " + "even when not starting optimization from it", IGRAPH_EINVAL); + + igraph_vector_resize(membership, n); + for (i = 0; i < n; i++) { + VECTOR(*membership)[i] = i; + } + } + + + if (igraph_is_directed(graph)) { + IGRAPH_ERROR("Leiden algorithm is only implemented for undirected graphs", IGRAPH_EINVAL); + } + + /* Check edge weights to possibly use default */ + if (!edge_weights) { + i_edge_weights = IGRAPH_CALLOC(1, igraph_vector_t); + if (i_edge_weights == 0) { + IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for edge weights", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, i_edge_weights); + IGRAPH_CHECK(igraph_vector_init(i_edge_weights, igraph_ecount(graph))); + IGRAPH_FINALLY(igraph_vector_destroy, i_edge_weights); + igraph_vector_fill(i_edge_weights, 1); + } else { + i_edge_weights = (igraph_vector_t*)edge_weights; + } + + /* Check edge weights to possibly use default */ + if (!node_weights) { + i_node_weights = IGRAPH_CALLOC(1, igraph_vector_t); + if (i_node_weights == 0) { + IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for node weights", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, i_node_weights); + IGRAPH_CHECK(igraph_vector_init(i_node_weights, n)); + IGRAPH_FINALLY(igraph_vector_destroy, i_node_weights); + igraph_vector_fill(i_node_weights, 1); + } else { + i_node_weights = (igraph_vector_t*)node_weights; + } + + /* Perform actual Leiden algorithm */ + IGRAPH_CHECK(igraph_i_community_leiden(graph, i_edge_weights, i_node_weights, + resolution_parameter, beta, + membership, nb_clusters, quality)); + + if (!edge_weights) { + igraph_vector_destroy(i_edge_weights); + IGRAPH_FREE(i_edge_weights); + IGRAPH_FINALLY_CLEAN(2); + } + + if (!node_weights) { + igraph_vector_destroy(i_node_weights); + IGRAPH_FREE(i_node_weights); + IGRAPH_FINALLY_CLEAN(2); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/community/louvain.c b/src/rigraph/core/community/louvain.c new file mode 100644 index 0000000..e2feab8 --- /dev/null +++ b/src/rigraph/core/community/louvain.c @@ -0,0 +1,723 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_qsort.h" +#include "igraph_random.h" + +#include "../core/interruption.h" + +/* Structure storing a community */ +typedef struct { + igraph_integer_t size; /* Size of the community */ + igraph_real_t weight_inside; /* Sum of edge weights inside community */ + igraph_real_t weight_all; /* Sum of edge weights starting/ending + in the community */ +} igraph_i_multilevel_community; + +/* Global community list structure */ +typedef struct { + long int communities_no, vertices_no; /* Number of communities, number of vertices */ + igraph_real_t weight_sum; /* Sum of edges weight in the whole graph */ + igraph_i_multilevel_community *item; /* List of communities */ + igraph_vector_t *membership; /* Community IDs */ + igraph_vector_t *weights; /* Graph edge weights */ +} igraph_i_multilevel_community_list; + +/* Computes the modularity of a community partitioning */ +static igraph_real_t igraph_i_multilevel_community_modularity( + const igraph_i_multilevel_community_list *communities, + const igraph_real_t resolution) { + igraph_real_t result = 0; + long int i; + igraph_real_t m = communities->weight_sum; + + for (i = 0; i < communities->vertices_no; i++) { + if (communities->item[i].size > 0) { + result += (communities->item[i].weight_inside - resolution * communities->item[i].weight_all * communities->item[i].weight_all / m) / m; + } + } + + return result; +} + +typedef struct { + long int from; + long int to; + long int id; +} igraph_i_multilevel_link; + +static int igraph_i_multilevel_link_cmp(const void *a, const void *b) { + long int r = (((igraph_i_multilevel_link*)a)->from - + ((igraph_i_multilevel_link*)b)->from); + if (r != 0) { + return (int) r; + } + + return (int) (((igraph_i_multilevel_link*)a)->to - + ((igraph_i_multilevel_link*)b)->to); +} + +/* removes multiple edges and returns new edge id's for each edge in |E|log|E| */ +static int igraph_i_multilevel_simplify_multiple(igraph_t *graph, igraph_vector_t *eids) { + long int ecount = igraph_ecount(graph); + long int i, l = -1, last_from = -1, last_to = -1; + igraph_bool_t directed = igraph_is_directed(graph); + igraph_vector_t edges; + igraph_i_multilevel_link *links; + + /* Make sure there's enough space in eids to store the new edge IDs */ + IGRAPH_CHECK(igraph_vector_resize(eids, ecount)); + + links = IGRAPH_CALLOC(ecount, igraph_i_multilevel_link); + if (links == 0) { + IGRAPH_ERROR("multi-level community structure detection failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, links); + + for (i = 0; i < ecount; i++) { + igraph_integer_t from, to; + igraph_edge(graph, (igraph_integer_t) i, &from, &to); + links[i].from = from; + links[i].to = to; + links[i].id = i; + } + + igraph_qsort((void*)links, (size_t) ecount, sizeof(igraph_i_multilevel_link), + igraph_i_multilevel_link_cmp); + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + for (i = 0; i < ecount; i++) { + if (links[i].from == last_from && links[i].to == last_to) { + VECTOR(*eids)[links[i].id] = l; + continue; + } + + last_from = links[i].from; + last_to = links[i].to; + + igraph_vector_push_back(&edges, last_from); + igraph_vector_push_back(&edges, last_to); + + l++; + + VECTOR(*eids)[links[i].id] = l; + } + + IGRAPH_FREE(links); + IGRAPH_FINALLY_CLEAN(1); + + igraph_destroy(graph); + IGRAPH_CHECK(igraph_create(graph, &edges, igraph_vcount(graph), directed)); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +typedef struct { + long int community; + igraph_real_t weight; +} igraph_i_multilevel_community_link; + +static int igraph_i_multilevel_community_link_cmp(const void *a, const void *b) { + return (int) (((igraph_i_multilevel_community_link*)a)->community - + ((igraph_i_multilevel_community_link*)b)->community); +} + +/** + * Given a graph, a community structure and a vertex ID, this method + * calculates: + * + * - edges: the list of edge IDs that are incident on the vertex + * - weight_all: the total weight of these edges + * - weight_inside: the total weight of edges that stay within the same + * community where the given vertex is right now, excluding loop edges + * - weight_loop: the total weight of loop edges + * - links_community and links_weight: together these two vectors list the + * communities incident on this vertex and the total weight of edges + * pointing to these communities + */ +static int igraph_i_multilevel_community_links( + const igraph_t *graph, + const igraph_i_multilevel_community_list *communities, + igraph_integer_t vertex, igraph_vector_t *edges, + igraph_real_t *weight_all, igraph_real_t *weight_inside, igraph_real_t *weight_loop, + igraph_vector_t *links_community, igraph_vector_t *links_weight) { + + long int i, n, last = -1, c = -1; + igraph_real_t weight = 1; + long int to, to_community; + long int community = (long int) VECTOR(*(communities->membership))[(long int)vertex]; + igraph_i_multilevel_community_link *links; + + *weight_all = *weight_inside = *weight_loop = 0; + + igraph_vector_clear(links_community); + igraph_vector_clear(links_weight); + + /* Get the list of incident edges */ + igraph_incident(graph, edges, vertex, IGRAPH_ALL); + + n = igraph_vector_size(edges); + links = IGRAPH_CALLOC(n, igraph_i_multilevel_community_link); + if (links == 0) { + IGRAPH_ERROR("multi-level community structure detection failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, links); + + for (i = 0; i < n; i++) { + long int eidx = (long int) VECTOR(*edges)[i]; + weight = VECTOR(*communities->weights)[eidx]; + + to = IGRAPH_OTHER(graph, eidx, vertex); + + *weight_all += weight; + if (to == vertex) { + *weight_loop += weight; + + links[i].community = community; + links[i].weight = 0; + continue; + } + + to_community = (long int)VECTOR(*(communities->membership))[to]; + if (community == to_community) { + *weight_inside += weight; + } + + /* debug("Link %ld (C: %ld) <-> %ld (C: %ld)\n", vertex, community, to, to_community); */ + + links[i].community = to_community; + links[i].weight = weight; + } + + /* Sort links by community ID and merge the same */ + igraph_qsort((void*)links, (size_t) n, sizeof(igraph_i_multilevel_community_link), + igraph_i_multilevel_community_link_cmp); + for (i = 0; i < n; i++) { + to_community = links[i].community; + if (to_community != last) { + igraph_vector_push_back(links_community, to_community); + igraph_vector_push_back(links_weight, links[i].weight); + last = to_community; + c++; + } else { + VECTOR(*links_weight)[c] += links[i].weight; + } + } + + igraph_free(links); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static igraph_real_t igraph_i_multilevel_community_modularity_gain( + const igraph_i_multilevel_community_list *communities, + igraph_integer_t community, igraph_integer_t vertex, + igraph_real_t weight_all, igraph_real_t weight_inside, + const igraph_real_t resolution) { + IGRAPH_UNUSED(vertex); + return weight_inside - + resolution * communities->item[(long int)community].weight_all * weight_all / communities->weight_sum; +} + +/* Shrinks communities into single vertices, keeping all the edges. + * This method is internal because it destroys the graph in-place and + * creates a new one -- this is fine for the multilevel community + * detection where a copy of the original graph is used anyway. + * The membership vector will also be rewritten by the underlying + * igraph_membership_reindex call */ +static int igraph_i_multilevel_shrink(igraph_t *graph, igraph_vector_t *membership) { + igraph_vector_t edges; + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + + long int i; + igraph_eit_t eit; + + if (no_of_nodes == 0) { + return 0; + } + + if (igraph_vector_size(membership) < no_of_nodes) { + IGRAPH_ERROR("cannot shrink graph, membership vector too short", + IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + + IGRAPH_CHECK(igraph_reindex_membership(membership, 0, NULL)); + + /* Create the new edgelist */ + igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &eit); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + i = 0; + while (!IGRAPH_EIT_END(eit)) { + igraph_integer_t from, to; + IGRAPH_CHECK(igraph_edge(graph, IGRAPH_EIT_GET(eit), &from, &to)); + VECTOR(edges)[i++] = VECTOR(*membership)[(long int) from]; + VECTOR(edges)[i++] = VECTOR(*membership)[(long int) to]; + IGRAPH_EIT_NEXT(eit); + } + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); + + /* Create the new graph */ + igraph_destroy(graph); + no_of_nodes = (long int) igraph_vector_max(membership) + 1; + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + directed)); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \ingroup communities + * \function igraph_i_community_multilevel_step + * \brief Performs a single step of the multi-level modularity optimization method + * + * This function implements a single step of the multi-level modularity optimization + * algorithm for finding community structure, see VD Blondel, J-L Guillaume, + * R Lambiotte and E Lefebvre: Fast unfolding of community hierarchies in large + * networks, http://arxiv.org/abs/0803.0476 for the details. + * + * This function was contributed by Tom Gregorovic. + * + * \param graph The input graph. It must be an undirected graph. + * \param weights Numeric vector containing edge weights. If \c NULL, + * every edge has equal weight. The weights are expected + * to be non-negative. + * \param membership The membership vector, the result is returned here. + * For each vertex it gives the ID of its community. + * \param modularity The modularity of the partition is returned here. + * \c NULL means that the modularity is not needed. + * \param resolution Resolution parameter. Must be greater than or equal to 0. + * Default is 1. Lower values favor fewer, larger communities; + * higher values favor more, smaller communities. + * \return Error code. + * + * Time complexity: in average near linear on sparse graphs. + */ +static int igraph_i_community_multilevel_step( + igraph_t *graph, + igraph_vector_t *weights, + igraph_vector_t *membership, + igraph_real_t *modularity, + const igraph_real_t resolution) { + + long int i, j; + long int vcount = igraph_vcount(graph); + long int ecount = igraph_ecount(graph); + igraph_real_t q, pass_q; + int pass; + igraph_bool_t changed = 0; + igraph_vector_t links_community; + igraph_vector_t links_weight; + igraph_vector_t edges; + igraph_vector_t temp_membership; + igraph_i_multilevel_community_list communities; + igraph_vector_t node_order; + + /* Initial sanity checks on the input parameters */ + if (igraph_is_directed(graph)) { + IGRAPH_ERROR("multi-level community detection works for undirected graphs only", + IGRAPH_UNIMPLEMENTED); + } + if (igraph_vector_size(weights) < igraph_ecount(graph)) { + IGRAPH_ERROR("multi-level community detection: weight vector too short", IGRAPH_EINVAL); + } + if (igraph_vector_any_smaller(weights, 0)) { + IGRAPH_ERROR("weights must be positive", IGRAPH_EINVAL); + } + if (resolution < 0.0) { + IGRAPH_ERROR("The resolution parameter must be non-negative", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_init_seq(&node_order, 0, vcount - 1)); + IGRAPH_FINALLY(igraph_vector_destroy, &node_order); + IGRAPH_CHECK(igraph_vector_shuffle(&node_order)); + + /* Initialize data structures */ + IGRAPH_VECTOR_INIT_FINALLY(&links_community, 0); + IGRAPH_VECTOR_INIT_FINALLY(&links_weight, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&temp_membership, vcount); + IGRAPH_CHECK(igraph_vector_resize(membership, vcount)); + + /* Initialize list of communities from graph vertices */ + communities.vertices_no = vcount; + communities.communities_no = vcount; + communities.weights = weights; + communities.weight_sum = 2 * igraph_vector_sum(weights); + communities.membership = membership; + communities.item = IGRAPH_CALLOC(vcount, igraph_i_multilevel_community); + if (communities.item == 0) { + IGRAPH_ERROR("multi-level community structure detection failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, communities.item); + + /* Still initializing the communities data structure */ + for (i = 0; i < vcount; i++) { + VECTOR(*communities.membership)[i] = i; + communities.item[i].size = 1; + communities.item[i].weight_inside = 0; + communities.item[i].weight_all = 0; + } + + /* Some more initialization :) */ + for (i = 0; i < ecount; i++) { + igraph_integer_t ffrom, fto; + igraph_real_t weight = 1; + igraph_edge(graph, (igraph_integer_t) i, &ffrom, &fto); + + weight = VECTOR(*weights)[i]; + communities.item[(long int) ffrom].weight_all += weight; + communities.item[(long int) fto].weight_all += weight; + if (ffrom == fto) { + communities.item[(long int) ffrom].weight_inside += 2 * weight; + } + } + + q = igraph_i_multilevel_community_modularity(&communities, resolution); + pass = 1; + + do { /* Pass begin */ + long int temp_communities_no = communities.communities_no; + + pass_q = q; + changed = 0; + + /* Save the current membership, it will be restored in case of worse result */ + IGRAPH_CHECK(igraph_vector_update(&temp_membership, communities.membership)); + + for (i = 0; i < vcount; i++) { + /* Exclude vertex from its current community */ + igraph_real_t weight_all = 0; + igraph_real_t weight_inside = 0; + igraph_real_t weight_loop = 0; + igraph_real_t max_q_gain = 0; + igraph_real_t max_weight; + long int old_id, new_id, n, ni; + + ni = VECTOR(node_order)[i]; + + igraph_i_multilevel_community_links(graph, &communities, + (igraph_integer_t) ni, &edges, + &weight_all, &weight_inside, + &weight_loop, &links_community, + &links_weight); + + old_id = (long int)VECTOR(*(communities.membership))[ni]; + new_id = old_id; + + /* Update old community */ + igraph_vector_set(communities.membership, ni, -1); + communities.item[old_id].size--; + if (communities.item[old_id].size == 0) { + communities.communities_no--; + } + communities.item[old_id].weight_all -= weight_all; + communities.item[old_id].weight_inside -= 2 * weight_inside + weight_loop; + + /* debug("Remove %ld all: %lf Inside: %lf\n", ni, -weight_all, -2*weight_inside + weight_loop); */ + + /* Find new community to join with the best modification gain */ + max_q_gain = 0; + max_weight = weight_inside; + n = igraph_vector_size(&links_community); + + for (j = 0; j < n; j++) { + long int c = (long int) VECTOR(links_community)[j]; + igraph_real_t w = VECTOR(links_weight)[j]; + + igraph_real_t q_gain = + igraph_i_multilevel_community_modularity_gain(&communities, + (igraph_integer_t) c, + (igraph_integer_t) ni, + weight_all, w, resolution); + /* debug("Link %ld -> %ld weight: %lf gain: %lf\n", ni, c, (double) w, (double) q_gain); */ + if (q_gain > max_q_gain) { + new_id = c; + max_q_gain = q_gain; + max_weight = w; + } + } + + /* debug("Added vertex %ld to community %ld (gain %lf).\n", ni, new_id, (double) max_q_gain); */ + + /* Add vertex to "new" community and update it */ + igraph_vector_set(communities.membership, ni, new_id); + if (communities.item[new_id].size == 0) { + communities.communities_no++; + } + communities.item[new_id].size++; + communities.item[new_id].weight_all += weight_all; + communities.item[new_id].weight_inside += 2 * max_weight + weight_loop; + + if (new_id != old_id) { + changed++; + } + } + + q = igraph_i_multilevel_community_modularity(&communities, resolution); + + if (changed && (q > pass_q)) { + /* debug("Pass %d (changed: %d) Communities: %ld Modularity from %lf to %lf\n", + pass, changed, communities.communities_no, (double) pass_q, (double) q); */ + pass++; + } else { + /* No changes or the modularity became worse, restore last membership */ + IGRAPH_CHECK(igraph_vector_update(communities.membership, &temp_membership)); + communities.communities_no = temp_communities_no; + break; + } + + IGRAPH_ALLOW_INTERRUPTION(); + } while (changed && (q > pass_q)); /* Pass end */ + + if (modularity) { + *modularity = q; + } + + /* debug("Result Communities: %ld Modularity: %lf\n", + communities.communities_no, (double) q); */ + + IGRAPH_CHECK(igraph_reindex_membership(membership, 0, NULL)); + + /* Shrink the nodes of the graph according to the present community structure + * and simplify the resulting graph */ + + /* TODO: check if we really need to copy temp_membership */ + IGRAPH_CHECK(igraph_vector_update(&temp_membership, membership)); + IGRAPH_CHECK(igraph_i_multilevel_shrink(graph, &temp_membership)); + igraph_vector_destroy(&temp_membership); + IGRAPH_FINALLY_CLEAN(1); + + /* Update edge weights after shrinking and simplification */ + /* Here we reuse the edges vector as we don't need the previous contents anymore */ + /* TODO: can we use igraph_simplify here? */ + IGRAPH_CHECK(igraph_i_multilevel_simplify_multiple(graph, &edges)); + + /* We reuse the links_weight vector to store the old edge weights */ + IGRAPH_CHECK(igraph_vector_update(&links_weight, weights)); + igraph_vector_fill(weights, 0); + + for (i = 0; i < ecount; i++) { + VECTOR(*weights)[(long int)VECTOR(edges)[i]] += VECTOR(links_weight)[i]; + } + + igraph_free(communities.item); + igraph_vector_destroy(&links_community); + igraph_vector_destroy(&links_weight); + igraph_vector_destroy(&edges); + igraph_vector_destroy(&node_order); + IGRAPH_FINALLY_CLEAN(5); + + return 0; +} + +/** + * \ingroup communities + * \function igraph_community_multilevel + * \brief Finding community structure by multi-level optimization of modularity. + * + * This function implements the multi-level modularity optimization + * algorithm for finding community structure, see + * Blondel, V. D., Guillaume, J.-L., Lambiotte, R., & Lefebvre, E. (2008). Fast + * unfolding of communities in large networks. Journal of Statistical Mechanics: + * Theory and Experiment, 10008(10), 6. + * https://doi.org/10.1088/1742-5468/2008/10/P10008 for the details (preprint: + * http://arxiv.org/abs/0803.0476). The algorithm is sometimes known as the + * "Louvain" algorithm. + * + * + * The algorithm is based on the modularity measure and a hierarchical approach. + * Initially, each vertex is assigned to a community on its own. In every step, + * vertices are re-assigned to communities in a local, greedy way: in a random + * order, each vertex is moved to the community with which it achieves the highest + * contribution to modularity. When no vertices can be reassigned, each community + * is considered a vertex on its own, and the process starts again with the merged + * communities. The process stops when there is only a single vertex left or when + * the modularity cannot be increased any more in a step. + * + * + * The resolution parameter \c gamma allows finding communities at different + * resolutions. Higher values of the resolution parameter typically result in + * more, smaller communities. Lower values typically result in fewer, larger + * communities. The original definition of modularity is retrieved when setting + * gamma=1. Note that the returned modularity value is calculated using + * the indicated resolution parameter. See \ref igraph_modularity() for more details. + * + * This function was contributed by Tom Gregorovic. + * + * \param graph The input graph. It must be an undirected graph. + * \param weights Numeric vector containing edge weights. If \c NULL, every edge + * has equal weight. The weights are expected to be non-negative. + * \param resolution Resolution parameter. Must be greater than or equal to 0. + * Lower values favor fewer, larger communities; + * higher values favor more, smaller communities. + * Set it to 1 to use the classical definition of modularity. + * \param membership The membership vector, the result is returned here. + * For each vertex it gives the ID of its community. The vector + * must be initialized and it will be resized accordingly. + * \param memberships Numeric matrix that will contain the membership vector after + * each level, if not \c NULL. It must be initialized and + * it will be resized accordingly. + * \param modularity Numeric vector that will contain the modularity score + * after each level, if not \c NULL. It must be initialized + * and it will be resized accordingly. + * \return Error code. + * + * Time complexity: in average near linear on sparse graphs. + * + * \example examples/simple/igraph_community_multilevel.c + */ + +int igraph_community_multilevel(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_real_t resolution, + igraph_vector_t *membership, + igraph_matrix_t *memberships, igraph_vector_t *modularity) { + + igraph_t g; + igraph_vector_t w, m, level_membership; + igraph_real_t prev_q = -1, q = -1; + int i, level = 1; + long int vcount = igraph_vcount(graph); + + /* Make a copy of the original graph, we will do the merges on the copy */ + IGRAPH_CHECK(igraph_copy(&g, graph)); + IGRAPH_FINALLY(igraph_destroy, &g); + + if (weights) { + IGRAPH_CHECK(igraph_vector_copy(&w, weights)); + IGRAPH_FINALLY(igraph_vector_destroy, &w); + } else { + IGRAPH_VECTOR_INIT_FINALLY(&w, igraph_ecount(&g)); + igraph_vector_fill(&w, 1); + } + + IGRAPH_VECTOR_INIT_FINALLY(&m, vcount); + IGRAPH_VECTOR_INIT_FINALLY(&level_membership, vcount); + + if (memberships || membership) { + /* Put each vertex in its own community */ + for (i = 0; i < vcount; i++) { + VECTOR(level_membership)[i] = i; + } + } + if (memberships) { + /* Resize the membership matrix to have vcount columns and no rows */ + IGRAPH_CHECK(igraph_matrix_resize(memberships, 0, vcount)); + } + if (modularity) { + /* Clear the modularity vector */ + igraph_vector_clear(modularity); + } + + while (1) { + /* Remember the previous modularity and vertex count, do a single step */ + igraph_integer_t step_vcount = igraph_vcount(&g); + + prev_q = q; + IGRAPH_CHECK(igraph_i_community_multilevel_step(&g, &w, &m, &q, resolution)); + + /* Were there any merges? If not, we have to stop the process */ + if (igraph_vcount(&g) == step_vcount || q < prev_q) { + break; + } + + if (memberships || membership) { + for (i = 0; i < vcount; i++) { + /* Readjust the membership vector */ + VECTOR(level_membership)[i] = VECTOR(m)[(long int) VECTOR(level_membership)[i]]; + } + } + + if (modularity) { + /* If we have to return the modularity scores, add it to the modularity vector */ + IGRAPH_CHECK(igraph_vector_push_back(modularity, q)); + } + + if (memberships) { + /* If we have to return the membership vectors at each level, store the new + * membership vector */ + IGRAPH_CHECK(igraph_matrix_add_rows(memberships, 1)); + IGRAPH_CHECK(igraph_matrix_set_row(memberships, &level_membership, level - 1)); + } + + /* debug("Level: %d Communities: %ld Modularity: %f\n", level, (long int) igraph_vcount(&g), + (double) q); */ + + /* Increase the level counter */ + level++; + } + + /* It might happen that there are no merges, so every vertex is in its + own community. We still might want the modularity score for that. */ + if (modularity && igraph_vector_size(modularity) == 0) { + igraph_vector_t tmp; + igraph_real_t mod; + int i; + IGRAPH_VECTOR_INIT_FINALLY(&tmp, vcount); + for (i = 0; i < vcount; i++) { + VECTOR(tmp)[i] = i; + } + IGRAPH_CHECK(igraph_modularity(graph, &tmp, weights, resolution, + /* only undirected */ 0, &mod)); + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(igraph_vector_resize(modularity, 1)); + VECTOR(*modularity)[0] = mod; + } + + /* If we need the final membership vector, copy it to the output */ + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, vcount)); + for (i = 0; i < vcount; i++) { + VECTOR(*membership)[i] = VECTOR(level_membership)[i]; + } + } + + /* Destroy the copy of the graph */ + igraph_destroy(&g); + + /* Destroy the temporary vectors */ + igraph_vector_destroy(&m); + igraph_vector_destroy(&w); + igraph_vector_destroy(&level_membership); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/community/modularity.c b/src/rigraph/core/community/modularity.c new file mode 100644 index 0000000..e1ec616 --- /dev/null +++ b/src/rigraph/core/community/modularity.c @@ -0,0 +1,376 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_conversion.h" +#include "igraph_interface.h" +#include "igraph_structural.h" + +/** + * \function igraph_modularity + * \brief Calculate the modularity of a graph with respect to some clusters or vertex types. + * + * The modularity of a graph with respect to some clustering of the vertices + * (or assignment of vertex types) + * measures how strongly separated the different clusters are from each + * other compared to a random null model. It is defined as + * + * + * Q = 1/(2m) sum_ij (A_ij - gamma * k_i * k_j / (2m)) * d(c_i,c_j), + * + * + * where \c m is the number of edges, A_ij is the adjacency matrix, + * \c k_i is the degree of vertex \c i, \c c_i is the cluster that vertex \c i belongs to + * (or its vertex type), d(i,j)=1 if i=j and 0 otherwise, + * and the sum goes over all i, j pairs of vertices. + * + * + * The resolution parameter \c gamma allows weighting the random null model, which + * might be useful when finding partitions with a high modularity. Maximizing modularity + * with higher values of the resolution parameter typically results in more, smaller clusters + * when finding partitions with a high modularity. Lower values typically results in + * fewer, larger clusters. The original definition of modularity is retrieved + * when setting gamma=1. + * + * + * Modularity can also be calculated on directed graphs. This only requires a relatively + * modest change + * + * + * Q = 1/(m) sum_ij (A_ij - gamma * k^out_i * k^in_j / m) * d(c_i,c_j), + * + * + * where \c k^out_i is the out-degree of node \c i and \c k^in_j is the in-degree of node \c j. + * + * + * Modularity on weighted graphs is also meaningful. When taking + * edge weights into account, \c A_ij equals the weight of the corresponding edge + * (or 0 if there is no edge), \c k_i is the strength (i.e. the weighted degree) of + * vertex \c i, with similar counterparts for a directed graph, and \c m is the total + * weight of all edges. + * + * + * Note that the modularity is not well-defined for graphs with no edges. + * igraph returns \c NaN for graphs with no edges; see + * https://github.com/igraph/igraph/issues/1539 for + * a detailed discussion. + * + * + * For the original definition of modularity, see Newman, M. E. J., and Girvan, M. + * (2004). Finding and evaluating community structure in networks. + * Physical Review E 69, 026113. https://doi.org/10.1103/PhysRevE.69.026113 + * + * + * For the directed definition of modularity, see Leicht, E. A., and Newman, M. E. + * J. (2008). Community Structure in Directed Networks. Physical Review Letters 100, + * 118703. https://doi.org/10.1103/PhysRevLett.100.118703 + * + * + * For the introduction of the resolution parameter, see Reichardt, J., and + * Bornholdt, S. (2006). Statistical mechanics of community detection. Physical + * Review E 74, 016110. https://doi.org/10.1103/PhysRevE.74.016110 + * + * \param graph The input graph. + * \param membership Numeric vector of integer values which gives the type of each + * vertex, i.e. the cluster to which it belongs. + * It does not have to be consecutive, i.e. empty communities + * are allowed. + * \param weights Weight vector or \c NULL if no weights are specified. + * \param resolution Resolution parameter. Must be greater than or equal to 0. + * Set it to 1 to use the classical definition of modularity. + * \param directed Whether to use the directed or undirected version of modularity. + * Ignored for undirected graphs. + * \param modularity Pointer to a real number, the result will be + * stored here. + * \return Error code. + * + * \sa \ref igraph_modularity_matrix() + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number + * of edges. + */ +int igraph_modularity(const igraph_t *graph, + const igraph_vector_t *membership, + const igraph_vector_t *weights, + const igraph_real_t resolution, + const igraph_bool_t directed, + igraph_real_t *modularity) { + + igraph_vector_t e, k_out, k_in; + long int types; + long int no_of_edges = igraph_ecount(graph); + long int i; + igraph_real_t m; + long int c1, c2; + /* Only consider the graph as directed if it actually is directed */ + igraph_bool_t use_directed = directed && igraph_is_directed(graph); + igraph_real_t directed_multiplier = (use_directed ? 1 : 2); + + if (igraph_vector_size(membership) != igraph_vcount(graph)) { + IGRAPH_ERROR("Membership vector size differs from number of vertices.", + IGRAPH_EINVAL); + } + if (resolution < 0.0) { + IGRAPH_ERROR("The resolution parameter must be non-negative.", IGRAPH_EINVAL); + } + + if (no_of_edges == 0) { + /* Special case: the modularity of graphs with no edges is not + * well-defined */ + if (modularity) { + *modularity = IGRAPH_NAN; + } + return IGRAPH_SUCCESS; + } + + /* At this point, the 'membership' vector does not have length zero, + thus it is safe to call igraph_vector_max() and min(). */ + + types = (long int) igraph_vector_max(membership) + 1; + + if (igraph_vector_min(membership) < 0) { + IGRAPH_ERROR("Invalid membership vector: negative entry.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&e, types); + IGRAPH_VECTOR_INIT_FINALLY(&k_out, types); + IGRAPH_VECTOR_INIT_FINALLY(&k_in, types); + + if (weights) { + if (igraph_vector_size(weights) != no_of_edges) + IGRAPH_ERROR("Vector size differs from number of edges.", + IGRAPH_EINVAL); + m = 0.0; + for (i = 0; i < no_of_edges; i++) { + igraph_real_t w = VECTOR(*weights)[i]; + if (w < 0) { + IGRAPH_ERROR("Negative weight in weight vector.", IGRAPH_EINVAL); + } + c1 = (long int) VECTOR(*membership)[ IGRAPH_FROM(graph, i) ]; + c2 = (long int) VECTOR(*membership)[ IGRAPH_TO(graph, i) ]; + if (c1 == c2) { + VECTOR(e)[c1] += directed_multiplier * w; + } + VECTOR(k_out)[c1] += w; + VECTOR(k_in)[c2] += w; + m += w; + } + } else { + m = no_of_edges; + for (i = 0; i < no_of_edges; i++) { + c1 = (long int) VECTOR(*membership)[ IGRAPH_FROM(graph, i) ]; + c2 = (long int) VECTOR(*membership)[ IGRAPH_TO(graph, i) ]; + if (c1 == c2) { + VECTOR(e)[c1] += directed_multiplier; + } + VECTOR(k_out)[c1] += 1; + VECTOR(k_in)[c2] += 1; + } + } + + if (!use_directed) { + /* Graph is undirected, simply add vectors */ + igraph_vector_add(&k_out, &k_in); + igraph_vector_update(&k_in, &k_out); + } + + /* Divide all vectors by total weight. */ + igraph_vector_scale(&k_out, 1.0/( directed_multiplier * m ) ); + igraph_vector_scale(&k_in, 1.0/( directed_multiplier * m ) ); + igraph_vector_scale(&e, 1.0/( directed_multiplier * m ) ); + + *modularity = 0.0; + if (m > 0) { + for (i = 0; i < types; i++) { + *modularity += VECTOR(e)[i]; + *modularity -= resolution * VECTOR(k_out)[i] * VECTOR(k_in)[i]; + } + } + + igraph_vector_destroy(&e); + igraph_vector_destroy(&k_out); + igraph_vector_destroy(&k_in); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_modularity_matrix_get_adjacency( + const igraph_t *graph, igraph_matrix_t *res, + const igraph_vector_t *weights, igraph_bool_t directed) { + /* Specifically used to handle weights and/or ignore direction */ + igraph_eit_t edgeit; + long int no_of_nodes = igraph_vcount(graph); + igraph_integer_t from, to; + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); + igraph_matrix_null(res); + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &edgeit)); + IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); + + if (weights) { + for (; !IGRAPH_EIT_END(edgeit); IGRAPH_EIT_NEXT(edgeit)) { + igraph_integer_t edge = IGRAPH_EIT_GET(edgeit); + from = IGRAPH_FROM(graph, edge); + to = IGRAPH_TO(graph, edge); + MATRIX(*res, from, to) += VECTOR(*weights)[edge]; + if (!directed) { + MATRIX(*res, to, from) += VECTOR(*weights)[edge]; + } + } + } else { + for (; !IGRAPH_EIT_END(edgeit); IGRAPH_EIT_NEXT(edgeit)) { + igraph_integer_t edge = IGRAPH_EIT_GET(edgeit); + igraph_edge(graph, edge, &from, &to); + MATRIX(*res, from, to) += 1; + if (!directed) { + MATRIX(*res, to, from) += 1; + } + } + } + + igraph_eit_destroy(&edgeit); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_modularity_matrix + * \brief Calculate the modularity matrix + * + * This function returns the modularity matrix defined as + * + * + * B_ij = A_ij - gamma * k_i * k_j / (2m) + * + * + * for undirected graphs, where \c A_ij is the adjacency matrix, \c gamma is the + * resolution parameter, \c k_i is the degree of vertex \c i, and \c m is the + * number of edges in the graph. When there are no edges, or the weights add up + * to zero, the result is undefined. + * + * + * For directed graphs the modularity matrix is changed to + * + * + * B_ij = A_ij - gamma * k^out_i * k^in_j / m + * where k^out_i is the out-degree of node \c i and k^in_j is the + * in-degree of node \c j. + * + * + * Note that self-loops in undirected graphs are multiplied by 2 in this + * implementation. If weights are specified, the weighted counterparts are used. + * + * \param graph The input graph. + * \param weights Edge weights, pointer to a vector. If this is a null pointer + * then every edge is assumed to have a weight of 1. + * \param resolution Resolution parameter. Must be greater than or equal to 0. + * Default is 1. Lower values favor fewer, larger communities; + * higher values favor more, smaller communities. + * \param modmat Pointer to an initialized matrix in which the modularity + * matrix is stored. + * \param directed For directed graphs: if the edges should be treated as + * undirected. + * For undirected graphs this is ignored. + * + * \sa \ref igraph_modularity() + */ +int igraph_modularity_matrix(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_real_t resolution, + igraph_matrix_t *modmat, + igraph_bool_t directed) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_real_t sw = weights ? igraph_vector_sum(weights) : no_of_edges; + igraph_vector_t deg, deg_unscaled, in_deg, out_deg; + long int i, j; + igraph_real_t scaling_factor; + if (weights && igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + + if (resolution < 0.0) { + IGRAPH_ERROR("The resolution parameter must be non-negative.", IGRAPH_EINVAL); + } + + if (!igraph_is_directed(graph)) { + directed = 0; + } + IGRAPH_CHECK(igraph_i_modularity_matrix_get_adjacency(graph, modmat, weights, directed)); + + if (directed) { + IGRAPH_VECTOR_INIT_FINALLY(&in_deg, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&out_deg, no_of_nodes); + if (!weights) { + IGRAPH_CHECK(igraph_degree(graph, &in_deg, igraph_vss_all(), IGRAPH_IN, + IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree(graph, &out_deg, igraph_vss_all(), IGRAPH_OUT, + IGRAPH_LOOPS)); + } else { + IGRAPH_CHECK(igraph_strength(graph, &in_deg, igraph_vss_all(), IGRAPH_IN, + IGRAPH_LOOPS, weights)); + IGRAPH_CHECK(igraph_strength(graph, &out_deg, igraph_vss_all(), IGRAPH_OUT, + IGRAPH_LOOPS, weights)); + } + /* Scaling one degree factor so every element gets scaled. */ + scaling_factor = resolution / sw; + igraph_vector_scale(&out_deg, scaling_factor); + + for (j = 0; j < no_of_nodes; j++) { + for (i = 0; i < no_of_nodes; i++) { + MATRIX(*modmat, i, j) -= VECTOR(out_deg)[i] * VECTOR(in_deg)[j]; + } + } + igraph_vector_destroy(&in_deg); + igraph_vector_destroy(&out_deg); + IGRAPH_FINALLY_CLEAN(2); + } else { + IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes); + if (!weights) { + IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS)); + } else { + IGRAPH_CHECK(igraph_strength(graph, °, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS, weights)); + } + + /* Scaling one degree factor so every element gets scaled. */ + igraph_vector_copy(°_unscaled, °); + IGRAPH_FINALLY(igraph_vector_destroy, °_unscaled); + scaling_factor = resolution / 2.0 / sw; + igraph_vector_scale(°, scaling_factor); + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < no_of_nodes; j++) { + MATRIX(*modmat, i, j) -= VECTOR(deg)[i] * VECTOR(deg_unscaled)[j]; + } + } + igraph_vector_destroy(°); + igraph_vector_destroy(°_unscaled); + IGRAPH_FINALLY_CLEAN(2); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/community/optimal_modularity.c b/src/rigraph/core/community/optimal_modularity.c new file mode 100644 index 0000000..ad25199 --- /dev/null +++ b/src/rigraph/core/community/optimal_modularity.c @@ -0,0 +1,281 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_community.h" + +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_structural.h" + +#include "../core/interruption.h" +#include "../internal/glpk_support.h" + +#include "config.h" + +#ifdef HAVE_GLPK + #include +#endif + +/** + * \function igraph_community_optimal_modularity + * Calculate the community structure with the highest modularity value + * + * This function calculates the optimal community structure for a + * graph, in terms of maximal modularity score. + * + * + * The calculation is done by transforming the modularity maximization + * into an integer programming problem, and then calling the GLPK + * library to solve that. Please see Ulrik Brandes et al.: On + * Modularity Clustering, IEEE Transactions on Knowledge and Data + * Engineering 20(2):172-188, 2008. + * + * + * Note that modularity optimization is an NP-complete problem, and + * all known algorithms for it have exponential time complexity. This + * means that you probably don't want to run this function on larger + * graphs. Graphs with up to fifty vertices should be fine, graphs + * with a couple of hundred vertices might be possible. + * + * \param graph The input graph. It is always treated as undirected. + * \param modularity Pointer to a real number, or a null pointer. + * If it is not a null pointer, then a optimal modularity value + * is returned here. + * \param membership Pointer to a vector, or a null pointer. If not a + * null pointer, then the membership vector of the optimal + * community structure is stored here. + * \param weights Vector giving the weights of the edges. If it is + * \c NULL then each edge is supposed to have the same weight. + * \return Error code. + * + * \sa \ref igraph_modularity(), \ref igraph_community_fastgreedy() + * for an algorithm that finds a local optimum in a greedy way. + * + * Time complexity: exponential in the number of vertices. + * + * \example examples/simple/igraph_community_optimal_modularity.c + */ + +int igraph_community_optimal_modularity(const igraph_t *graph, + igraph_real_t *modularity, + igraph_vector_t *membership, + const igraph_vector_t *weights) { + +#ifndef HAVE_GLPK + IGRAPH_ERROR("GLPK is not available", + IGRAPH_UNIMPLEMENTED); +#else + + igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph); + igraph_integer_t no_of_edges = (igraph_integer_t) igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + int no_of_variables = no_of_nodes * (no_of_nodes + 1) / 2; + int i, j, k, l, st; + int idx[] = { 0, 0, 0, 0 }; + double coef[] = { 0.0, 1.0, 1.0, -2.0 }; + igraph_real_t total_weight; + igraph_vector_t indegree; + igraph_vector_t outdegree; + + glp_prob *ip; + glp_iocp parm; + + if (weights) { + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length must agree with number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + /* Must not call vector_min on empty vector */ + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight < 0) { + IGRAPH_ERROR("Negative weights are not allowed in weight vector.", IGRAPH_EINVAL); + } + if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); + } + } + } + + /* Avoid problems with the null graph */ + if (no_of_nodes < 2) { + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_fill(membership, 0); + } + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, 0, 1, igraph_is_directed(graph), modularity)); + } + return IGRAPH_SUCCESS; + } + + if (weights) { + total_weight = igraph_vector_sum(weights); + } else { + total_weight = no_of_edges; + } + if (!directed) { + total_weight *= 2; + } + + /* Special case */ + if (no_of_edges == 0 || total_weight == 0) { + if (modularity) { + *modularity = IGRAPH_NAN; + } + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_null(membership); + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&indegree, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&outdegree, no_of_nodes); + IGRAPH_CHECK(igraph_strength(graph, &indegree, igraph_vss_all(), + IGRAPH_IN, IGRAPH_LOOPS, weights)); + IGRAPH_CHECK(igraph_strength(graph, &outdegree, igraph_vss_all(), + IGRAPH_OUT, IGRAPH_LOOPS, weights)); + + IGRAPH_GLPK_SETUP(); + + ip = glp_create_prob(); + IGRAPH_FINALLY(igraph_i_glp_delete_prob, ip); + + glp_set_obj_dir(ip, GLP_MAX); + st = glp_add_cols(ip, no_of_variables); + + /* variables are binary */ + for (i = 0; i < no_of_variables; i++) { + glp_set_col_kind(ip, (st + i), GLP_BV); + } + +#define IDX(a,b) ((b)*((b)+1)/2+(a)) + + /* reflexivity */ + for (i = 0; i < no_of_nodes; i++) { + glp_set_col_bnds(ip, (st + IDX(i, i)), GLP_FX, 1.0, 1.0); + } + + /* transitivity */ + for (i = 0; i < no_of_nodes; i++) { + for (j = i + 1; j < no_of_nodes; j++) { + + IGRAPH_ALLOW_INTERRUPTION(); + + for (k = j + 1; k < no_of_nodes; k++) { + int newrow = glp_add_rows(ip, 3); + + glp_set_row_bnds(ip, newrow, GLP_UP, 0.0, 1.0); + idx[1] = (st + IDX(i, j)); idx[2] = (st + IDX(j, k)); + idx[3] = (st + IDX(i, k)); + glp_set_mat_row(ip, newrow, 3, idx, coef); + + glp_set_row_bnds(ip, newrow + 1, GLP_UP, 0.0, 1.0); + idx[1] = st + IDX(i, j); idx[2] = st + IDX(i, k); idx[3] = st + IDX(j, k); + glp_set_mat_row(ip, newrow + 1, 3, idx, coef); + + glp_set_row_bnds(ip, newrow + 2, GLP_UP, 0.0, 1.0); + idx[1] = st + IDX(i, k); idx[2] = st + IDX(j, k); idx[3] = st + IDX(i, j); + glp_set_mat_row(ip, newrow + 2, 3, idx, coef); + + } + } + } + + /* objective function */ + { + igraph_real_t c; + + /* first part: -strength(i)*strength(j)/total_weight for every node pair */ + for (i = 0; i < no_of_nodes; i++) { + for (j = i + 1; j < no_of_nodes; j++) { + c = -VECTOR(indegree)[i] * VECTOR(outdegree)[j] / total_weight \ + -VECTOR(outdegree)[i] * VECTOR(indegree)[j] / total_weight; + glp_set_obj_coef(ip, st + IDX(i, j), c); + } + /* special case for (i,i) */ + c = -VECTOR(indegree)[i] * VECTOR(outdegree)[i] / total_weight; + glp_set_obj_coef(ip, st + IDX(i, i), c); + } + + /* second part: add the weighted adjacency matrix to the coefficient matrix */ + for (k = 0; k < no_of_edges; k++) { + i = IGRAPH_FROM(graph, k); + j = IGRAPH_TO(graph, k); + if (i > j) { + l = i; i = j; j = l; + } + c = weights ? VECTOR(*weights)[k] : 1.0; + if (!directed || i == j) { + c *= 2.0; + } + glp_set_obj_coef(ip, st + IDX(i, j), c + glp_get_obj_coef(ip, st + IDX(i, j))); + } + } + + /* solve it */ + glp_init_iocp(&parm); + parm.br_tech = GLP_BR_DTH; + parm.bt_tech = GLP_BT_BLB; + parm.presolve = GLP_ON; + parm.binarize = GLP_ON; + parm.cb_func = igraph_i_glpk_interruption_hook; + IGRAPH_GLPK_CHECK(glp_intopt(ip, &parm), "Modularity optimization failed"); + + /* store the results */ + if (modularity) { + *modularity = glp_mip_obj_val(ip) / total_weight; + } + + if (membership) { + long int comm = 0; /* id of the last community that was found */ + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + + IGRAPH_ALLOW_INTERRUPTION(); + + for (j = 0; j < i; j++) { + int val = (int) glp_mip_col_val(ip, st + IDX(j, i)); + if (val == 1) { + VECTOR(*membership)[i] = VECTOR(*membership)[j]; + break; + } + } + if (j == i) { /* new community */ + VECTOR(*membership)[i] = comm++; + } + } + } + +#undef IDX + + igraph_vector_destroy(&indegree); + igraph_vector_destroy(&outdegree); + glp_delete_prob(ip); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; + +#endif + +} diff --git a/src/rigraph/core/community/spinglass/NetDataTypes.cpp b/src/rigraph/core/community/spinglass/NetDataTypes.cpp new file mode 100644 index 0000000..3156d35 --- /dev/null +++ b/src/rigraph/core/community/spinglass/NetDataTypes.cpp @@ -0,0 +1,218 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Jörg Reichardt + The original copyright notice follows here */ + +/*************************************************************************** + NetDataTypes.cpp - description + ------------------- + begin : Mon Oct 6 2003 + copyright : (C) 2003 by Joerg Reichardt + email : reichardt@mitte + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "NetDataTypes.h" +#include + +//################################################################################# +//############################################################################### +//Constructor +NNode::NNode(unsigned long ind, unsigned long c_ind, DLList *ll, const char *n, int states) { + index = ind; + cluster_index = c_ind; + neighbours = new DLList(); + n_links = new DLList(); + global_link_list = ll; + strcpy(name, n); + color.red = 0; + color.green = 0; + color.blue = 0; + strcpy(color.pajek_c, "Green"); + clustering = 0.0; + marker = 0; + affiliations = 0; + weight = 0.0; + affinity = 0.0; + distance = 0; + max_states = states; + state_history = new unsigned long[states + 1]; +} + +//Destructor +NNode::~NNode() { + Disconnect_From_All(); + delete neighbours; + delete n_links; + delete [] state_history; + neighbours = NULL; + n_links = NULL; + state_history = NULL; +} + +void NNode::Add_StateHistory(unsigned int state) { + if (max_states >= state) { + state_history[state]++; + } +} + +void NNode::Set_Color(RGBcolor c) { + color.red = c.red; color.blue = c.blue; color.green = c.green; + strcpy(color.pajek_c, c.pajek_c); +} + +int NNode::Connect_To(NNode* neighbour, double weight_) { + NLink *link; + //sollen doppelte Links erlaubt sein?? NEIN + if (!neighbour) { + return 0; + } + if (!(neighbours->Is_In_List(neighbour)) && (neighbour != this)) { + neighbours->Push(neighbour); // nachbar hier eintragen + neighbour->neighbours->Push(this); // diesen knoten beim nachbarn eintragen + + link = new NLink(this, neighbour, weight_); //link erzeugen + global_link_list->Push(link); // in globaler liste eintragen + n_links->Push(link); // bei diesem Knoten eintragen + neighbour->n_links->Push(link); // beim nachbarn eintragen + + return (1); + } + return (0); +} + +NLink *NNode::Get_LinkToNeighbour(NNode* neighbour) { + DLList_Iter iter; + NLink *l_cur, *link = NULL; + bool found = false; + // finde einen bestimmten Link aus der Liste der links eines Knotens + l_cur = iter.First(n_links); + while (!iter.End() && !found) { + if (((l_cur->Get_Start() == this) && (l_cur->Get_End() == neighbour)) || ((l_cur->Get_End() == this) && (l_cur->Get_Start() == neighbour))) { + found = true; + link = l_cur; + } + l_cur = iter.Next(); + } + if (found) { + return link; + } else { + return NULL; + } +} + +int NNode::Disconnect_From(NNode* neighbour) { + //sollen doppelte Links erlaubt sein?? s.o. + if (!neighbours) { + return 0; + } + neighbours->fDelete(neighbour); + n_links->fDelete(Get_LinkToNeighbour(neighbour)); + neighbour->n_links->fDelete(neighbour->Get_LinkToNeighbour(this)); + neighbour->neighbours->fDelete(this); + return 1; +} + +int NNode::Disconnect_From_All() { + int number_of_neighbours = 0; + while (neighbours->Size()) { + Disconnect_From(neighbours->Pop()); + number_of_neighbours++; + } + return (number_of_neighbours) ; +} + +/* +int NNode::Disconnect_From_All_Grandchildren() +{ + int n_l=links->Size(); + unsigned long pos=0; + while ((n_l--)>1) { //alle bis auf das erste loeschen + pos=(links->Get(n_l+1))->links->Is_In_List(this); + // printf("%d %d\n",n_l,pos); + (links->Get(n_l+1))->links->Delete(pos); + } + return(pos) ; +} +*/ + +double NNode::Get_Links_Among_Neigbours() { +// long neighbours1, neighbours2; + double lam = 0; + DLList_Iter iter1, iter2; +// neighbours1=neighbours->Size(); //so viele Nachbarn hat die Betrachtete Node + NNode *step1, *step2; + step1 = iter1.First(neighbours); + while (!iter1.End()) { // for (int n1=1;n1<=neighbours1; n1++) + //step1=neighbours->Get(n1); + //neighbours2=step1->neighbours->Size(); //so viele Nachbarn hat der n1-ste Nachbar + step2 = iter2.First(step1->Get_Neighbours()); + while (!iter2.End()) { //for (int n2=1;n2<=neighbours2; n2++) + //step2=step1->neighbours->Get(n2); + if (step2->Get_Neighbours()->Is_In_List(this)) { + lam++; + } + step2 = iter2.Next(); + } + step1 = iter1.Next(); + } + return (lam / 2.0); +} + + +double NNode::Get_Clustering() { + double c; + unsigned long k; + k = neighbours->Size(); + if (k <= 1) { + return (0); + } + c = 2.0 * Get_Links_Among_Neigbours() / double(k * k - k); + return (c); +} +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//Constructor +NLink::NLink(NNode *s, NNode *e, double w) { + start = s; + end = e; + weight = w; + old_weight = 0; + marker = 0; +} + +//Destructor +NLink::~NLink() { + if (start && end) { + start->Disconnect_From(end); + } +} diff --git a/src/rigraph/core/community/spinglass/NetDataTypes.h b/src/rigraph/core/community/spinglass/NetDataTypes.h new file mode 100644 index 0000000..a36cafd --- /dev/null +++ b/src/rigraph/core/community/spinglass/NetDataTypes.h @@ -0,0 +1,950 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Jörg Reichardt + The original copyright notice follows here */ + +/*************************************************************************** + NetDataTypes.h - description + ------------------- + begin : Mon Oct 6 2003 + copyright : (C) 2003 by Joerg Reichardt + email : reichardt@mitte + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef NETDATATYPES_H +#define NETDATATYPES_H + +#include + +//########################################################################################### + +struct HUGE_INDEX { + unsigned int field_index; + unsigned long in_field_index; +}; + +template class HugeArray { +private: + unsigned long int size; + unsigned int highest_field_index; + unsigned long max_bit_left; + unsigned long max_index; + DATA *data; + DATA *fields[32]; +public: + HUGE_INDEX get_huge_index(unsigned long); + DATA &Set(unsigned long); + DATA Get(unsigned long); + HugeArray(); + ~HugeArray(); + DATA &operator[](unsigned long); + unsigned long Size() { + return max_index; + } +} ; +//############################################################################################### +template class DLList; +template class DL_Indexed_List; +template class ClusterList; +template class DLList_Iter; + +template +class DLItem { + friend class DLList ; + friend class DL_Indexed_List; + friend class DLList_Iter; +private: + L_DATA item; + unsigned long index; + DLItem *previous; + DLItem *next; + DLItem(L_DATA i, unsigned long ind); + DLItem(L_DATA i, unsigned long ind, DLItem *p, DLItem *n); + ~DLItem(); +public: + void del() { + delete item; + } +}; + +template +class DLList { + friend class DLList_Iter; +protected: + DLItem *head; + DLItem *tail; + unsigned long number_of_items; + DLItem *pInsert(L_DATA, DLItem*); + L_DATA pDelete(DLItem*); +public: + DLList(); + ~DLList(); + unsigned long Size() { + return number_of_items; + } + int Insert(L_DATA, unsigned long); + int Delete(unsigned long); + int fDelete(L_DATA); + L_DATA Push(L_DATA); + L_DATA Pop(); + L_DATA Get(unsigned long); + int Enqueue(L_DATA); + L_DATA Dequeue(); + unsigned long Is_In_List(L_DATA); + void delete_items(); +}; + +template +class DL_Indexed_List : virtual public DLList { + friend class DLList_Iter; +private: + DLItem *pInsert(L_DATA, DLItem*); + L_DATA pDelete(DLItem*); + HugeArray*> array; + unsigned long last_index; +public: + DL_Indexed_List(); + ~DL_Indexed_List(); + L_DATA Push(L_DATA); + L_DATA Pop(); + L_DATA Get(unsigned long); +}; + +//##################################################################################################### + +template class DLList_Iter { +private: + DLList *list; + DLItem *current; + bool end_reached; +public: + DLList_Iter(); + ~DLList_Iter() { + end_reached = true; + }; + L_DATA Next(); + L_DATA Previous(); + L_DATA First(DLList *l); + L_DATA Last(DLList *l); + bool End() { + return end_reached; + } + DLItem *Get_Current() { + return current; + } + L_DATA Get_Current_Item() { + return current->item; + } + void Set_Current(DLItem *c) { + current = c; + } + void Set_Status(bool s) { + end_reached = s; + } + bool Swap(DLList_Iter); //swapt die beiden Elemente, wenn sie in der gleichen Liste stehen!! + +}; + +//##################################################################################################### +struct RGBcolor { + unsigned int red; + unsigned int green; + unsigned int blue; + char pajek_c[20]; +}; +//------------------------------------------------------------------------------- + +class NLink; + +class NNode { + friend class NLink; +private : + unsigned long index; + unsigned long cluster_index; + unsigned long marker, affiliations; + unsigned long *state_history; + unsigned int max_states; + long distance; + double clustering; + double weight; + double affinity; +// double old_weight; + + DLList *neighbours; //list with pointers to neighbours + DLList *n_links; + DLList *global_link_list; + char name[255]; + RGBcolor color; +public : + NNode(unsigned long, unsigned long, DLList*, const char*, int); + ~NNode(); + unsigned long Get_Index() { + return (index); + } + unsigned long Get_ClusterIndex() { + return (cluster_index); + } + unsigned long Get_Marker() { + return marker; + } + void Set_Marker(unsigned long m) { + marker = m; + } + unsigned long Get_Affiliations() { + return affiliations; + } + void Set_Affiliations(unsigned long m) { + affiliations = m; + } + void Set_ClusterIndex(unsigned long ci) { + cluster_index = ci; + } + void Set_Index(unsigned long i) { + index = i; + } + unsigned long Get_Degree() { + return (neighbours->Size()); + } + char *Get_Name() { + return name; + } + void Set_Name(char* n) { + strcpy(name, n); + } + double Get_Links_Among_Neigbours(); + double Get_Clustering(); + double Get_Weight() { + return weight; + } + double Get_Affinity() { + return affinity; + } + unsigned long *Get_StateHistory() { + return state_history; + } + void Add_StateHistory(unsigned int q); + // double Get_OldWeight() {return old_weight;} + void Set_Weight(double w) { + weight = w; + } + void Set_Affinity(double w) { + affinity = w; + } + + // void Set_OldWeight(double w) {old_weight=w;} + long Get_Distance() { + return distance; + } + void Set_Distance(long d) { + distance = d; + } + int Connect_To(NNode*, double); + DLList *Get_Neighbours() { + return neighbours; + } + DLList *Get_Links() { + return n_links; + } + int Disconnect_From(NNode*); + int Disconnect_From_All(); + bool Is_Linked_To(NNode*); + RGBcolor Get_Color() { + return color; + } + void Set_Color(RGBcolor c); + NLink *Get_LinkToNeighbour(NNode *neighbour); +}; + +//##################################################################################################### + +class NLink { + friend class NNode; +private : + NNode *start; + NNode *end; + double weight; + double old_weight; + unsigned long index; + unsigned long marker; +public : + NLink( NNode*, NNode*, double); + ~NLink(); + unsigned long Get_Start_Index() { + return (start->Get_Index()); + } + unsigned long Get_End_Index() { + return (end->Get_Index()); + } + NNode *Get_Start() { + return (start); + } + NNode *Get_End() { + return (end); + } + double Get_Weight() { + return weight; + } + void Set_Weight(double w) { + weight = w; + } + double Get_OldWeight() { + return old_weight; + } + void Set_OldWeight(double w) { + old_weight = w; + } + unsigned long Get_Marker() { + return marker; + } + void Set_Marker(unsigned long m) { + marker = m; + } + unsigned long Get_Index() { + return index; + } + void Set_Index(unsigned long i) { + index = i; + } +}; + +//##################################################################################################### + +template class ClusterList : public DLList { + friend class DLList_Iter; +private: + long links_out_of_cluster; + unsigned long links_inside_cluster; + unsigned long frequency; + double cluster_energy; + DLList *candidates; + long marker; +public: + ClusterList(); + ~ClusterList(); + long Get_Links_OOC() { + return (links_out_of_cluster); + } + void Set_Links_OOC(long looc) { + links_out_of_cluster = looc; + } + unsigned long Get_Links_IC() { + return (links_inside_cluster); + } + unsigned long Get_Frequency() { + return (frequency); + } + void IncreaseFrequency() { + frequency++; + } + void Set_Links_IC(unsigned long lic) { + links_inside_cluster = lic; + } + double Get_Energy() { + return (cluster_energy); + } + void Set_Energy(double e) { + cluster_energy = e; + } + DLList *Get_Candidates() { + return candidates; + } + bool operator<(ClusterList &b); + bool operator==(ClusterList &b); + long Get_Marker() { + return marker; + } + void Set_Marker(long m) { + marker = m; + } +}; +//##################################################################################################### +template +class DL_Node_List : virtual public DL_Indexed_List { + friend class DLList_Iter; +private: + DLItem *pInsert(NNode*, DLItem*); + NNode* pDelete(DLItem*); + HugeArray*> array; + unsigned long last_index; +public: + DL_Node_List(); + ~DL_Node_List(); + NNode* Push(NNode*); + NNode* Pop(); + NNode* Get(unsigned long); + int Delete(unsigned long); + +}; +//##################################################################################################### + + + +struct cluster_join_move { + ClusterList *c1; + ClusterList *c2; + double joint_energy; + long joint_looc; + unsigned long joint_lic; +} ; + +struct network { + DL_Indexed_List *node_list; + DL_Indexed_List *link_list; + DL_Indexed_List*> *cluster_list; + // DL_Indexed_List *moveset; + unsigned long max_k; + unsigned long min_k; + unsigned long diameter; + double av_weight; + double max_weight; + double min_weight; + double sum_weights; + double av_k; + double av_bids; + unsigned long max_bids; + unsigned long min_bids; + unsigned long sum_bids; + + network() { + node_list = new DL_Indexed_List(); + link_list = new DL_Indexed_List(); + cluster_list = new DL_Indexed_List*>(); + } + + ~network() { + ClusterList *cl_cur; + + while (link_list->Size()) { + delete link_list->Pop(); + } + while (node_list->Size()) { + delete node_list->Pop(); + } + while (cluster_list->Size()) { + cl_cur = cluster_list->Pop(); + while (cl_cur->Size()) { + cl_cur->Pop(); + } + delete cl_cur; + } + delete link_list; + delete node_list; + delete cluster_list; + } +}; + +/* +struct network +{ + DLList *node_list; + DLList *link_list; + DLList*> *cluster_list; + DLList *moveset; +} ; +*/ + +template +HugeArray::HugeArray() { + max_bit_left = 1UL << 31; //wir setzen das 31. Bit auf 1 + size = 2; + max_index = 0; + highest_field_index = 0; + data = new DATA[2]; //ein extra Platz fuer das Nullelement + data[0] = 0; + data[1] = 0; + for (int i = 0; i < 32; i++) { + fields[i] = NULL; + } + fields[highest_field_index] = data; +} + +template HugeArray::~HugeArray() { + for (unsigned int i = 0; i <= highest_field_index; i++) { + data = fields[i]; + delete [] data; + } +} + +template +HUGE_INDEX HugeArray::get_huge_index(unsigned long index) { + HUGE_INDEX h_index; + unsigned int shift_index = 0; + unsigned long help_index; + help_index = index; + if (index < 2) { + h_index.field_index = 0; + h_index.in_field_index = index; + return h_index; + } + // wie oft muessen wir help_index nach links shiften, damit das 31. Bit gesetzt ist?? + while (!(max_bit_left & help_index)) { + help_index <<= 1; + shift_index++; + } + h_index.field_index = 31 - shift_index; // das hoechste besetzte Bit im Index + help_index = 1UL << h_index.field_index; // in help_index wird das hoechste besetzte Bit von Index gesetzt + h_index.in_field_index = (index ^ help_index); // index XOR help_index, womit alle bits unter dem hoechsten erhalten bleiben + return h_index; +} + +template +DATA &HugeArray::Set(unsigned long int index) { + HUGE_INDEX h_index; + unsigned long data_size; + while (size < index + 1) { + highest_field_index++; + data_size = 1UL << highest_field_index; + data = new DATA[data_size]; + for (unsigned long i = 0; i < data_size; i++) { + data[i] = 0; + } + size = size + data_size; //overflow noch abfangen + //printf("Vergroesserung auf: %u bei index %u\n",size,index); + fields[highest_field_index] = data; + } + h_index = get_huge_index(index); +//printf("index %lu = %lu . %lu\n",index,h_index.field_index,h_index.in_field_index); + data = fields[h_index.field_index]; + if (max_index < index) { + max_index = index; + } + return (data[h_index.in_field_index]); +} + +template +DATA HugeArray::Get(unsigned long index) { + return (Set(index)); +} + + +template +DATA &HugeArray::operator[](unsigned long index) { + return (Set(index)); +} + + +//############################################################################### +template +DLItem::DLItem(L_DATA i, unsigned long ind) : item(i), index(ind), previous(0), next(0) { +} + +template +DLItem::DLItem(L_DATA i, unsigned long ind, DLItem *p, DLItem *n) : item(i), index(ind), previous(p), next(n) { +} + +template +DLItem::~DLItem() { +//delete item; //eigentlich muessten wir pruefen, ob item ueberhaupt ein Pointer ist... +//previous=NULL; +//next=NULL; +} + + +//###################################################################################################################### +template +DLList::DLList() { + head = tail = NULL; + number_of_items = 0; + head = new DLItem(NULL, 0); //fuer head und Tail gibt es das gleiche Array-Element!! Vorsicht!! + tail = new DLItem(NULL, 0); + if ( !head || !tail ) { + if (head) { + delete (head); + } + if (tail) { + delete (tail); + } + return; + } else { + head->next = tail; + tail->previous = head; + } +} + +template +DLList::~DLList() { + DLItem *cur = head, *next; + while (cur) { + next = cur->next; + delete (cur); + cur = next; + } + number_of_items = 0; + // printf("Liste Zerstoert!\n"); +} + +template +void DLList::delete_items() { + DLItem *cur, *next; + cur = this->head; + while (cur) { + next = cur->next; + cur->del(); + cur = next; + } + this->number_of_items = 0; +} + +//privates Insert +template +DLItem *DLList::pInsert(L_DATA data, DLItem *pos) { + DLItem *i = new DLItem(data, number_of_items + 1, pos->previous, pos); + if (i) { + pos->previous->next = i; + pos->previous = i; + number_of_items++; + return (i); + } else { + return (0); + } +} +//privates delete +template +L_DATA DLList::pDelete(DLItem *i) { + L_DATA data = i->item; + i->previous->next = i->next; + i->next->previous = i->previous; +// array[i->index]=0; + delete (i); + number_of_items--; + return (data); +} +//oeffentliches Insert +template +int DLList::Insert(L_DATA data, unsigned long pos) { + if ((pos < 0) || (pos > (number_of_items))) { + return (0); + } + DLItem *cur = head; + while (pos--) { + cur = cur->next; + } + return (pInsert(data, cur) != 0); +} +//oeffentliche Delete +template +int DLList::Delete(unsigned long pos) { + if ((pos < 0) || (pos > (number_of_items))) { + return (0); + } + DLItem *cur = head; + while (pos--) { + cur = cur->next; + } + return (pDelete(cur) != 0); +} + +//oeffentliche Delete +template +int DLList::fDelete(L_DATA data) { + if ((number_of_items == 0) || (!data)) { + return (0); + } + DLItem *cur; + cur = head->next; + while ((cur != tail) && (cur->item != data)) { + cur = cur->next; + } + if (cur != tail) { + return (pDelete(cur) != 0); + } + return (0); +} + +template +L_DATA DLList::Push(L_DATA data) { + DLItem *tmp; + tmp = pInsert(data, tail); + if (tmp) { + return (tmp->item); + } + return (0); +} + +template +L_DATA DLList::Pop() { + return (pDelete(tail->previous)); +} + + +template +L_DATA DLList::Get(unsigned long pos) { + if ((pos < 1) || (pos > (number_of_items + 1))) { + return (0); + } +// return(array[pos]->item); + DLItem *cur = head; + while (pos--) { + cur = cur->next; + } + return (cur->item); +} + + +template +int DLList::Enqueue(L_DATA data) { + return (pInsert(data, tail) != 0); +} + +template +L_DATA DLList::Dequeue() { + return (pDelete(head->next)); +} + +//gibt Index des gesuchte Listenelement zurueck, besser waere eigentlich zeiger +template +unsigned long DLList::Is_In_List(L_DATA data) { + DLItem *cur = head, *next; + unsigned long pos = 0; + while (cur) { + next = cur->next; + if (cur->item == data) { + return (pos) ; + } + cur = next; + pos++; + } + return (0); +} + +//###################################################################################################################### +template +DL_Indexed_List::DL_Indexed_List() : DLList() { + last_index = 0; +} + +template +DL_Indexed_List::~DL_Indexed_List() { + /* This is already done by the DLList destructor */ + /* DLItem *cur, *next; */ + /* cur=this->head; */ + /* while (cur) */ + /* { */ + /* next=cur->next; */ + /* delete(cur); */ + /* cur=next; */ + /* } */ + /* this->number_of_items=0; */ + // printf("Liste Zerstoert!\n"); +} + +//privates Insert +template +DLItem *DL_Indexed_List::pInsert(L_DATA data, DLItem *pos) { + DLItem *i = new DLItem(data, last_index, pos->previous, pos); + if (i) { + pos->previous->next = i; + pos->previous = i; + this->number_of_items++; + array[last_index] = i; + last_index++; + return (i); + } else { + return (0); + } +} +//privates delete +template +L_DATA DL_Indexed_List::pDelete(DLItem *i) { + L_DATA data = i->item; + i->previous->next = i->next; + i->next->previous = i->previous; + array[i->index] = 0; + last_index = i->index; + delete (i); + this->number_of_items--; + return (data); +} +template +L_DATA DL_Indexed_List::Push(L_DATA data) { + DLItem *tmp; + tmp = pInsert(data, this->tail); + if (tmp) { + return (tmp->item); + } + return (0); +} + +template +L_DATA DL_Indexed_List::Pop() { + return (pDelete(this->tail->previous)); +} + +template +L_DATA DL_Indexed_List::Get(unsigned long pos) { + if (pos > this->number_of_items - 1) { + return (0); + } + return (array[pos]->item); +} + +//####################################################################################### + +//************************************************************************************************************ +template +ClusterList::ClusterList() : DLList() { + links_out_of_cluster = 0; + links_inside_cluster = 0; + frequency = 1; + cluster_energy = 1e30; + candidates = new DLList(); + marker = 0; +} + +template +ClusterList::~ClusterList() { + while (candidates->Size()) { + candidates->Pop(); + } + delete candidates; +} + + +template +bool ClusterList::operator==(ClusterList &b) { + bool found = false; + L_DATA n_cur, n_cur_b; + DLList_Iter a_iter, b_iter; + + if (this->Size() != b.Size()) { + return false; + } + + n_cur = a_iter.First(this); + while (!(a_iter.End())) { + found = false; + n_cur_b = b_iter.First(&b); + while (!(b_iter.End()) && !found) { + if (n_cur == n_cur_b) { + found = true; + } + n_cur_b = b_iter.Next(); + } + if (!found) { + return false; + } + n_cur = a_iter.Next(); + } + return (found); +} +//A +bool ClusterList::operator<(ClusterList &b) { + bool found = false; + L_DATA n_cur, n_cur_b; + DLList_Iter a_iter, b_iter; + + if (this->Size() >= b.Size()) { + return false; + } + n_cur = a_iter.First(this); + while (!(a_iter.End())) { + found = false; + n_cur_b = b_iter.First(&b); + while (!(b_iter.End()) && !found) { + if (n_cur == n_cur_b) { + found = true; + } + n_cur_b = b_iter.Next(); + } + if (!found) { + return false; + } + n_cur = a_iter.Next(); + } + return (found); +} + +//##################################################################################### +template +DLList_Iter::DLList_Iter() { + list = NULL; + current = NULL; + end_reached = true; +} + +template +L_DATA DLList_Iter::Next() { + current = current->next; + if (current == (list->tail)) { + end_reached = true; + } + return (current->item); +} + +template +L_DATA DLList_Iter::Previous() { + current = current->previous; + if (current == (list->head)) { + end_reached = true; + } + return (current->item); +} + +template +L_DATA DLList_Iter::First(DLList *l) { + list = l; + current = list->head->next; + if (current == (list->tail)) { + end_reached = true; + } else { + end_reached = false; + } + return (current->item); +} + +template +L_DATA DLList_Iter::Last(DLList *l) { + list = l; + current = list->tail->previous; + if (current == (list->head)) { + end_reached = true; // falls die List leer ist + } else { + end_reached = false; + } + return (current->item); +} + +template +bool DLList_Iter::Swap(DLList_Iter b) { + L_DATA h; + if (list != b.list) { + return false; //elemeten muessen aus der gleichen List stammen + } + if (end_reached || b.end_reached) { + return false; + } + h = current->item; current->item = b.current->item; b.current->item = h; + return true; +} + +#endif diff --git a/src/rigraph/core/community/spinglass/NetRoutines.cpp b/src/rigraph/core/community/spinglass/NetRoutines.cpp new file mode 100644 index 0000000..5978ef6 --- /dev/null +++ b/src/rigraph/core/community/spinglass/NetRoutines.cpp @@ -0,0 +1,271 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Jörg Reichardt + The original copyright notice follows here */ + +/*************************************************************************** + NetRoutines.cpp - description + ------------------- + begin : Tue Oct 28 2003 + copyright : (C) 2003 by Joerg Reichardt + email : reichardt@mitte + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "NetRoutines.h" +#include "NetDataTypes.h" + +#include "igraph_types.h" +#include "igraph_interface.h" +#include "igraph_conversion.h" + +int igraph_i_read_network(const igraph_t *graph, + const igraph_vector_t *weights, + network *net, igraph_bool_t use_weights, + unsigned int states) { + + double av_k = 0.0, sum_weight = 0.0, min_weight = 1e60, max_weight = -1e60; + unsigned long min_k = 999999999, max_k = 0; + char name[255]; + NNode *node1, *node2; + DLList_Iter iter; + igraph_vector_t edgelist; + long int no_of_nodes = (long int) igraph_vcount(graph); + long int no_of_edges = (long int) igraph_ecount(graph); + long int ii; + const char *empty = ""; + + IGRAPH_VECTOR_INIT_FINALLY(&edgelist, no_of_edges * 2); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, 0 /* rowwise */)); + + for (ii = 0; ii < no_of_nodes; ii++) { + net->node_list->Push(new NNode(ii, 0, net->link_list, empty, states)); + } + + for (ii = 0; ii < no_of_edges; ii++) { + long int i1 = (long int)VECTOR(edgelist)[2 * ii]; + long int i2 = (long int)VECTOR(edgelist)[2 * ii + 1]; + igraph_real_t Links; + if (use_weights) { + Links = VECTOR(*weights)[ii]; + } else { + Links = 1.0; + } + + node1 = net->node_list->Get(i1); + sprintf(name, "%li", i1+1); + node1->Set_Name(name); + + node2 = net->node_list->Get(i2); + sprintf(name, "%li", i2+1); + node2->Set_Name(name); + + node1->Connect_To(node2, Links); + + if (Links < min_weight) { + min_weight = Links; + } + if (Links > max_weight) { + max_weight = Links; + } + sum_weight += Links; + } + + IGRAPH_FINALLY_CLEAN(1); + igraph_vector_destroy(&edgelist); + + node1 = iter.First(net->node_list); + while (!iter.End()) { + if (node1->Get_Degree() > max_k) { + max_k = node1->Get_Degree(); + } + if (node1->Get_Degree() < min_k) { + min_k = node1->Get_Degree(); + } + av_k += node1->Get_Degree(); + node1 = iter.Next(); + } + net->av_k = av_k / double(net->node_list->Size()); + net->sum_weights = sum_weight; + net->av_weight = sum_weight / double(net->link_list->Size()); + net->min_k = min_k; + net->max_k = max_k; + net->min_weight = min_weight; + net->max_weight = max_weight; + net->sum_bids = 0; + net->min_bids = 0; + net->max_bids = 0; + + return IGRAPH_SUCCESS; +} + +//############################################################################################################### +void reduce_cliques(DLList*> *global_cluster_list, FILE *file) { + unsigned long size; + ClusterList *c_cur, *largest_c = NULL; + DLList*> *subsets; + DLList_Iter*> c_iter, sub_iter; + DLList_Iter iter; + NNode *n_cur; + + if (!(global_cluster_list->Size())) { + return; + } + //wir suchen den groessten Cluster + + c_cur = c_iter.First(global_cluster_list); + size = 0; + while (!(c_iter.End())) { + if (c_cur->Size() > size) { + size = c_cur->Size(); + largest_c = c_cur; + } + c_cur = c_iter.Next(); + } +// printf("Groesster Cluster hat %u Elemente.\n",largest_c->Size()); + + //Schauen, ob es Teilmengen gibt, die ebenfalls gefunden wurden + subsets = new DLList*>(); + c_cur = c_iter.First(global_cluster_list); + while (!(c_iter.End())) { + if ((*c_cur < *largest_c || *c_cur == *largest_c) && c_cur != largest_c) { //alle echten Teilcluster von largest_c und die doppelten + subsets->Push(c_cur); + } + c_cur = c_iter.Next(); + } + // die gefundenen Subsets werden aus der cluster_liste geloescht + while (subsets->Size()) { + global_cluster_list->fDelete(subsets->Pop()); + } + delete subsets; + // Dann schreiben wir den groessten Cluster in das File + fprintf(file, "Energie: %1.12f Nodes:%3lu - ", largest_c->Get_Energy(), largest_c->Size()); + + n_cur = iter.First(largest_c); + while (!(iter.End())) { + fprintf(file, "%s", n_cur->Get_Name()); + n_cur = iter.Next(); + if (n_cur) { + fprintf(file, ", "); + } + } + fprintf(file, "\n"); + + + //Schliesslich schmeissen wir noch den eben gefundenen groessten Cluster raus + global_cluster_list->fDelete(largest_c); + //und dann geht es von vorn mit der Reduzierten ClusterListe los + reduce_cliques(global_cluster_list, file); + +} +//################################################################################## +void reduce_cliques2(network *net, bool only_double, long marker) { + unsigned long size; + ClusterList *c_cur, *largest_c = NULL; + DLList_Iter*> c_iter; + do { + //wir suchen den groessten, nicht markierten Cluster + size = 0; + c_cur = c_iter.First(net->cluster_list); + while (!(c_iter.End())) { + if ((c_cur->Size() > size) && (c_cur->Get_Marker() != marker)) { + size = c_cur->Size(); + largest_c = c_cur; + } + c_cur = c_iter.Next(); + } + // printf("Groesster Cluster hat %u Elemente.\n",largest_c->Size()); + //Schauen, ob es Teilmengen gibt, die ebenfalls gefunden wurden + c_cur = c_iter.First(net->cluster_list); + while (!(c_iter.End())) { + if (((!only_double && (*c_cur < *largest_c)) || (*c_cur == *largest_c)) && (c_cur != largest_c)) { //alle echten Teilcluster von largest_c und die doppelten + net->cluster_list->fDelete(c_cur); + while (c_cur->Get_Candidates()->Size()) { + c_cur->Get_Candidates()->Pop(); + } + while (c_cur->Size()) { + c_cur->Pop(); // die knoten aber nicht loeschen!! + } + delete c_cur; // nicht vergessen, die global geloeschte Clusterliste zu loeschen + } + c_cur = c_iter.Next(); + } + //Schliesslich markieren wir noch den eben gefundenen groessten Cluster + largest_c->Set_Marker(marker); + } while (size); +} + +//################################################################################################## +unsigned long iterate_nsf_hierarchy(NNode *parent, unsigned long depth, FILE *file) { + NNode* next_node; + unsigned long newdepth, maxdepth; + bool first = true; + DLList_Iter *iter; + maxdepth = newdepth = depth; + iter = new DLList_Iter; + next_node = iter->First(parent->Get_Neighbours()); + while (!(iter->End())) { + if (next_node->Get_Marker() > parent->Get_Marker()) { // wir gehen nach unten + if (first) { + fprintf(file, ",("); // eine Neue Klammer auf + } + if (first) { + fprintf(file, "%s", next_node->Get_Name()); // nur vor dem ersten kein Komma + } else { + fprintf(file, ",%s", next_node->Get_Name()); // sonst immer mit Komma + } + first = false; + newdepth = iterate_nsf_hierarchy(next_node, depth + 1, file); + if (maxdepth < newdepth) { + maxdepth = newdepth; + } + } + next_node = iter->Next(); + } + if (!first) { + fprintf(file, ")"); //hat es ueberhaupt einen gegeben? + } + //dann klamer zu! + delete iter; + return maxdepth; +} + +//################################################################ +void clear_all_markers(network *net) { + DLList_Iter iter; + NNode *n_cur; + n_cur = iter.First(net->node_list); + while (!iter.End()) { + n_cur->Set_Marker(0); + n_cur = iter.Next(); + } +} diff --git a/src/rigraph/core/community/spinglass/NetRoutines.h b/src/rigraph/core/community/spinglass/NetRoutines.h new file mode 100644 index 0000000..02030fb --- /dev/null +++ b/src/rigraph/core/community/spinglass/NetRoutines.h @@ -0,0 +1,60 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Jörg Reichardt + The original copyright notice follows here */ + +/*************************************************************************** + NetRoutines.h - description + ------------------- + begin : Tue Oct 28 2003 + copyright : (C) 2003 by Joerg Reichardt + email : reichardt@mitte + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef NETROUTINES_H +#define NETROUTINES_H + +#include "NetDataTypes.h" +#include "igraph_types.h" +#include "igraph_datatype.h" + +int igraph_i_read_network(const igraph_t *graph, + const igraph_vector_t *weights, + network *net, igraph_bool_t use_weights, + unsigned int states); + +void reduce_cliques(DLList*>*, FILE *file); +void reduce_cliques2(network*, bool, long ); +void clear_all_markers(network *net); + +#endif diff --git a/src/rigraph/core/community/spinglass/clustertool.cpp b/src/rigraph/core/community/spinglass/clustertool.cpp new file mode 100644 index 0000000..08b0cc3 --- /dev/null +++ b/src/rigraph/core/community/spinglass/clustertool.cpp @@ -0,0 +1,664 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Joerg Reichardt + The original copyright notice follows here */ + +/*************************************************************************** + main.cpp - description + ------------------- + begin : Tue Jul 13 11:26:47 CEST 2004 + copyright : (C) 2004 by + email : + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "NetDataTypes.h" +#include "NetRoutines.h" +#include "pottsmodel_2.h" + +#include "igraph_community.h" +#include "igraph_error.h" +#include "igraph_random.h" +#include "../../core/math.h" +#include "igraph_interface.h" +#include "igraph_components.h" +#include "../../core/interruption.h" +#include "../../core/exceptions.h" + +static int igraph_i_community_spinglass_orig( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_t *membership, + igraph_vector_t *csize, + igraph_integer_t spins, + igraph_bool_t parupdate, + igraph_real_t starttemp, + igraph_real_t stoptemp, + igraph_real_t coolfact, + igraph_spincomm_update_t update_rule, + igraph_real_t gamma); + +static int igraph_i_community_spinglass_negative( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_t *membership, + igraph_vector_t *csize, + igraph_integer_t spins, + igraph_bool_t parupdate, + igraph_real_t starttemp, + igraph_real_t stoptemp, + igraph_real_t coolfact, + igraph_spincomm_update_t update_rule, + igraph_real_t gamma, + /* igraph_matrix_t *adhesion, */ + /* igraph_matrix_t *normalised_adhesion, */ + /* igraph_real_t *polarization, */ + igraph_real_t gamma_minus); + +/** + * \function igraph_community_spinglass + * \brief Community detection based on statistical mechanics + * + * This function implements the community structure detection + * algorithm proposed by Joerg Reichardt and Stefan Bornholdt. + * The algorithm is described in their paper: Statistical Mechanics of + * Community Detection, http://arxiv.org/abs/cond-mat/0603718 . + * + * + * From version 0.6, igraph also supports an extension to + * the algorithm that allows negative edge weights. This is described + * in V. A. Traag and Jeroen Bruggeman: Community detection in networks + * with positive and negative links, http://arxiv.org/abs/0811.2329 . + * + * \param graph The input graph, it may be directed but the direction + * of the edges is not used in the algorithm. + * \param weights The vector giving the edge weights, it may be \c NULL, + * in which case all edges are weighted equally. The edge weights + * must be positive unless using the \c IGRAPH_SPINCOMM_IMP_NEG + * implementation. This condition is not verified by the function. + * \param modularity Pointer to a real number, if not \c NULL then the + * modularity score of the solution will be stored here. This is the + * gereralized modularity that simplifies to the one defined in + * M. E. J. Newman and M. Girvan, Phys. Rev. E 69, 026113 (2004), + * if the gamma parameter is one. + * \param temperature Pointer to a real number, if not \c NULL then + * the temperature at the end of the algorithm will be stored + * here. + * \param membership Pointer to an initialized vector or \c NULL. If + * not \c NULL then the result of the clustering will be stored + * here. For each vertex, the number of its cluster is given, with the + * first cluster numbered zero. The vector will be resized as + * needed. + * \param csize Pointer to an initialized vector or \c NULL. If not \c + * NULL then the sizes of the clusters will stored here in cluster + * number order. The vector will be resized as needed. + * \param spins Integer giving the number of spins, i.e. the maximum + * number of clusters. Usually it is not a program to give a high + * number here, the default was 25 in the original code. Even if + * the number of spins is high the number of clusters in the + * result might be small. + * \param parupdate A logical constant, whether to update all spins in + * parallel. The default for this argument was \c FALSE (i.e. 0) in + * the original code. It is not implemented in the \c + * IGRAPH_SPINCOMM_INP_NEG implementation. + * \param starttemp Real number, the temperature at the start. The + * value of this argument was 1.0 in the original code. + * \param stoptemp Real number, the algorithm stops at this + * temperature. The default was 0.01 in the original code. + * \param coolfact Real number, the cooling factor for the simulated + * annealing. The default was 0.99 in the original code. + * \param update_rule The type of the update rule. Possible values: \c + * IGRAPH_SPINCOMM_UPDATE_SIMPLE and \c + * IGRAPH_SPINCOMM_UPDATE_CONFIG. Basically this parameter defines + * the null model based on which the actual clustering is done. If + * this is \c IGRAPH_SPINCOMM_UPDATE_SIMPLE then the random graph + * (i.e. G(n,p)), if it is \c IGRAPH_SPINCOMM_UPDATE then the + * configuration model is used. The configuration means that the + * baseline for the clustering is a random graph with the same + * degree distribution as the input graph. + * \param gamma Real number. The gamma parameter of the + * algorithm. This defines the weight of the missing and existing + * links in the quality function for the clustering. The default + * value in the original code was 1.0, which is equal weight to + * missing and existing edges. Smaller values make the existing + * links contibute more to the energy function which is minimized + * in the algorithm. Bigger values make the missing links more + * important. (If my understanding is correct.) + * \param implementation Constant, chooses between the two + * implementations of the spin-glass algorithm that are included + * in igraph. \c IGRAPH_SPINCOMM_IMP_ORIG selects the original + * implementation, this is faster, \c IGRAPH_SPINCOMM_INP_NEG selects + * a new implementation by Vincent Traag that allows negative edge + * weights. + * \param gamma_minus Real number. Parameter for the \c + * IGRAPH_SPINCOMM_IMP_NEG implementation. This + * specifies the balance between the importance of present and + * non-present negative weighted edges in a community. Smaller values of + * \p gamma_minus lead to communities with lesser + * negative intra-connectivity. + * If this argument is set to zero, the algorithm reduces to a graph + * coloring algorithm, using the number of spins as the number of + * colors. + * \return Error code. + * + * \sa igraph_community_spinglass_single() for calculating the community + * of a single vertex. + * + * Time complexity: TODO. + * + */ + +int igraph_community_spinglass(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_t *membership, + igraph_vector_t *csize, + igraph_integer_t spins, + igraph_bool_t parupdate, + igraph_real_t starttemp, + igraph_real_t stoptemp, + igraph_real_t coolfact, + igraph_spincomm_update_t update_rule, + igraph_real_t gamma, + /* the rest is for the NegSpin implementation */ + igraph_spinglass_implementation_t implementation, + /* igraph_matrix_t *adhesion, */ + /* igraph_matrix_t *normalised_adhesion, */ + /* igraph_real_t *polarization, */ + igraph_real_t gamma_minus) { + + IGRAPH_HANDLE_EXCEPTIONS( + switch (implementation) { + case IGRAPH_SPINCOMM_IMP_ORIG: + return igraph_i_community_spinglass_orig(graph, weights, modularity, + temperature, membership, csize, + spins, parupdate, starttemp, + stoptemp, coolfact, update_rule, + gamma); + break; + case IGRAPH_SPINCOMM_IMP_NEG: + return igraph_i_community_spinglass_negative(graph, weights, modularity, + temperature, membership, csize, + spins, parupdate, starttemp, + stoptemp, coolfact, + update_rule, gamma, + /* adhesion, normalised_adhesion, */ + /* polarization, */ + gamma_minus); + break; + default: + IGRAPH_ERROR("Unknown `implementation' in spinglass community finding", + IGRAPH_EINVAL); + } + ); +} + +static int igraph_i_community_spinglass_orig( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_t *membership, + igraph_vector_t *csize, + igraph_integer_t spins, + igraph_bool_t parupdate, + igraph_real_t starttemp, + igraph_real_t stoptemp, + igraph_real_t coolfact, + igraph_spincomm_update_t update_rule, + igraph_real_t gamma) { + + long int no_of_nodes = igraph_vcount(graph); + unsigned long changes, runs; + igraph_bool_t use_weights = 0; + bool zeroT; + double kT, acc, prob; + + /* Check arguments */ + + if (spins < 2) { + IGRAPH_ERROR("Number of spins must be at least 2", IGRAPH_EINVAL); + } + if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE && + update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) { + IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL); + } + if (weights) { + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + } + use_weights = 1; + } + if (coolfact < 0 || coolfact >= 1.0) { + IGRAPH_ERROR("Invalid cooling factor", IGRAPH_EINVAL); + } + if (gamma < 0.0) { + IGRAPH_ERROR("Invalid gamma value", IGRAPH_EINVAL); + } + if (starttemp / stoptemp < 1.0) { + IGRAPH_ERROR("starttemp should be larger in absolute value than stoptemp", + IGRAPH_EINVAL); + } + + /* The spinglass algorithm does not handle the trivial cases of the + null and singleton graphs, so we catch them here. */ + if (no_of_nodes < 2) { + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_fill(membership, 0); + } + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, 0, 1, igraph_is_directed(graph), modularity)); + } + if (temperature) { + *temperature = stoptemp; + } + if (csize) { + /* 0 clusters for 0 nodes, 1 cluster for 1 node */ + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_fill(membership, 1); + } + return IGRAPH_SUCCESS; + } + + /* Check whether we have a single component */ + igraph_bool_t conn; + IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); + if (!conn) { + IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL); + } + + network net; + + /* Transform the igraph_t */ + IGRAPH_CHECK(igraph_i_read_network(graph, weights, + &net, use_weights, 0)); + + prob = 2.0 * net.sum_weights / double(net.node_list->Size()) + / double(net.node_list->Size() - 1); + + PottsModel pm(&net, (unsigned int)spins, update_rule); + + /* initialize the random number generator */ + RNG_BEGIN(); + + if ((stoptemp == 0.0) && (starttemp == 0.0)) { + zeroT = true; + } else { + zeroT = false; + } + if (!zeroT) { + kT = pm.FindStartTemp(gamma, prob, starttemp); + } else { + kT = stoptemp; + } + /* assign random initial configuration */ + pm.assign_initial_conf(-1); + runs = 0; + changes = 1; + + while (changes > 0 && (kT / stoptemp > 1.0 || (zeroT && runs < 150))) { + + IGRAPH_ALLOW_INTERRUPTION(); + + runs++; + if (!zeroT) { + kT *= coolfact; + if (parupdate) { + changes = pm.HeatBathParallelLookup(gamma, prob, kT, 50); + } else { + acc = pm.HeatBathLookup(gamma, prob, kT, 50); + if (acc < (1.0 - 1.0 / double(spins)) * 0.01) { + changes = 0; + } else { + changes = 1; + } + } + } else { + if (parupdate) { + changes = pm.HeatBathParallelLookupZeroTemp(gamma, prob, 50); + } else { + acc = pm.HeatBathLookupZeroTemp(gamma, prob, 50); + /* less than 1 percent acceptance ratio */ + if (acc < (1.0 - 1.0 / double(spins)) * 0.01) { + changes = 0; + } else { + changes = 1; + } + } + } + } /* while loop */ + + pm.WriteClusters(modularity, temperature, csize, membership, kT, gamma); + + RNG_END(); + + return 0; +} + +/** + * \function igraph_community_spinglass_single + * \brief Community of a single node based on statistical mechanics + * + * This function implements the community structure detection + * algorithm proposed by Joerg Reichardt and Stefan Bornholdt. It is + * described in their paper: Statistical Mechanics of + * Community Detection, http://arxiv.org/abs/cond-mat/0603718 . + * + * + * This function calculates the community of a single vertex without + * calculating all the communities in the graph. + * + * \param graph The input graph, it may be directed but the direction + * of the edges is not used in the algorithm. + * \param weights Pointer to a vector with the weights of the edges. + * Alternatively \c NULL can be supplied to have the same weight + * for every edge. + * \param vertex The vertex id of the vertex of which ths community is + * calculated. + * \param community Pointer to an initialized vector, the result, the + * ids of the vertices in the community of the input vertex will be + * stored here. The vector will be resized as needed. + * \param cohesion Pointer to a real variable, if not \c NULL the + * cohesion index of the community will be stored here. + * \param adhesion Pointer to a real variable, if not \c NULL the + * adhesion index of the community will be stored here. + * \param inner_links Pointer to an integer, if not \c NULL the + * number of edges within the community is stored here. + * \param outer_links Pointer to an integer, if not \c NULL the + * number of edges between the community and the rest of the graph + * will be stored here. + * \param spins The number of spins to use, this can be higher than + * the actual number of clusters in the network, in which case some + * clusters will contain zero vertices. + * \param update_rule The type of the update rule. Possible values: \c + * IGRAPH_SPINCOMM_UPDATE_SIMPLE and \c + * IGRAPH_SPINCOMM_UPDATE_CONFIG. Basically this parameter defined + * the null model based on which the actual clustering is done. If + * this is \c IGRAPH_SPINCOMM_UPDATE_SIMPLE then the random graph + * (ie. G(n,p)), if it is \c IGRAPH_SPINCOMM_UPDATE then the + * configuration model is used. The configuration means that the + * baseline for the clustering is a random graph with the same + * degree distribution as the input graph. + * \param gamma Real number. The gamma parameter of the + * algorithm. This defined the weight of the missing and existing + * links in the quality function for the clustering. The default + * value in the original code was 1.0, which is equal weight to + * missing and existing edges. Smaller values make the existing + * links contibute more to the energy function which is minimized + * in the algorithm. Bigger values make the missing links more + * important. (If my understanding is correct.) + * \return Error code. + * + * \sa igraph_community_spinglass() for the traditional version of the + * algorithm. + * + * Time complexity: TODO. + */ + +int igraph_community_spinglass_single(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_integer_t vertex, + igraph_vector_t *community, + igraph_real_t *cohesion, + igraph_real_t *adhesion, + igraph_integer_t *inner_links, + igraph_integer_t *outer_links, + igraph_integer_t spins, + igraph_spincomm_update_t update_rule, + igraph_real_t gamma) { + IGRAPH_HANDLE_EXCEPTIONS( + igraph_bool_t use_weights = 0; + double prob; + char startnode[255]; + + /* Check arguments */ + + if (spins < 2) { + IGRAPH_ERROR("Number of spins must be at least 2", IGRAPH_EINVAL); + } + if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE && + update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) { + IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL); + } + if (weights) { + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + } + use_weights = 1; + } + if (gamma < 0.0) { + IGRAPH_ERROR("Invalid gamme value", IGRAPH_EINVAL); + } + if (vertex < 0 || vertex > igraph_vcount(graph)) { + IGRAPH_ERROR("Invalid vertex id", IGRAPH_EINVAL); + } + + /* Check whether we have a single component */ + igraph_bool_t conn; + IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); + if (!conn) { + IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL); + } + + network net; + + /* Transform the igraph_t */ + IGRAPH_CHECK(igraph_i_read_network(graph, weights, + &net, use_weights, 0)); + + prob = 2.0 * net.sum_weights / double(net.node_list->Size()) + / double(net.node_list->Size() - 1); + + PottsModel pm(&net, (unsigned int)spins, update_rule); + + /* initialize the random number generator */ + RNG_BEGIN(); + + /* to be exected, if we want to find the community around a particular node*/ + /* the initial conf is needed, because otherwise, + the degree of the nodes is not in the weight property, stupid!!! */ + pm.assign_initial_conf(-1); + snprintf(startnode, 255, "%li", (long int)vertex + 1); + pm.FindCommunityFromStart(gamma, prob, startnode, community, + cohesion, adhesion, inner_links, outer_links); + + RNG_END(); + ); + + return 0; +} + +static int igraph_i_community_spinglass_negative( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_t *membership, + igraph_vector_t *csize, + igraph_integer_t spins, + igraph_bool_t parupdate, + igraph_real_t starttemp, + igraph_real_t stoptemp, + igraph_real_t coolfact, + igraph_spincomm_update_t update_rule, + igraph_real_t gamma, + /* igraph_matrix_t *adhesion, */ + /* igraph_matrix_t *normalised_adhesion, */ + /* igraph_real_t *polarization, */ + igraph_real_t gamma_minus) { + + long int no_of_nodes = igraph_vcount(graph); + unsigned long changes, runs; + igraph_bool_t use_weights = 0; + bool zeroT; + double kT, acc; + igraph_real_t d_n; + igraph_real_t d_p; + + /* Check arguments */ + + if (parupdate) { + IGRAPH_ERROR("Parallel spin update not implemented with " + "negative gamma", IGRAPH_UNIMPLEMENTED); + } + + if (spins < 2) { + IGRAPH_ERROR("Number of spins must be at least 2", IGRAPH_EINVAL); + } + if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE && + update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) { + IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL); + } + if (weights) { + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + } + use_weights = 1; + } + if (coolfact < 0 || coolfact >= 1.0) { + IGRAPH_ERROR("Invalid cooling factor", IGRAPH_EINVAL); + } + if (gamma < 0.0) { + IGRAPH_ERROR("Invalid gamma value", IGRAPH_EINVAL); + } + if (starttemp / stoptemp < 1.0) { + IGRAPH_ERROR("starttemp should be larger in absolute value than stoptemp", + IGRAPH_EINVAL); + } + + /* The spinglass algorithm does not handle the trivial cases of the + null and singleton graphs, so we catch them here. */ + if (no_of_nodes < 2) { + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_fill(membership, 0); + } + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, 0, 1, igraph_is_directed(graph), modularity)); + } + if (temperature) { + *temperature = stoptemp; + } + if (csize) { + /* 0 clusters for 0 nodes, 1 cluster for 1 node */ + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_fill(membership, 1); + } + return IGRAPH_SUCCESS; + } + + /* Check whether we have a single component */ + igraph_bool_t conn; + IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); + if (!conn) { + IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL); + } + + if (weights) { + igraph_vector_minmax(weights, &d_n, &d_p); + } else { + d_n = d_p = 1; + } + + if (d_n > 0) { + d_n = 0; + } + if (d_p < 0) { + d_p = 0; + } + d_n = -d_n; + + network net; + + /* Transform the igraph_t */ + IGRAPH_CHECK(igraph_i_read_network(graph, weights, + &net, use_weights, 0)); + + bool directed = igraph_is_directed(graph); + + PottsModelN pm(&net, (unsigned int)spins, directed); + + /* initialize the random number generator */ + RNG_BEGIN(); + + if ((stoptemp == 0.0) && (starttemp == 0.0)) { + zeroT = true; + } else { + zeroT = false; + } + + //Begin at a high enough temperature + kT = pm.FindStartTemp(gamma, gamma_minus, starttemp); + + /* assign random initial configuration */ + pm.assign_initial_conf(true); + + runs = 0; + changes = 1; + acc = 0; + while (changes > 0 && (kT / stoptemp > 1.0 || (zeroT && runs < 150))) { + + IGRAPH_ALLOW_INTERRUPTION(); + + runs++; + kT = kT * coolfact; + acc = pm.HeatBathLookup(gamma, gamma_minus, kT, 50); + if (acc < (1.0 - 1.0 / double(spins)) * 0.001) { + changes = 0; + } else { + changes = 1; + } + + } /* while loop */ + + /* These are needed, otherwise 'modularity' is not calculated */ + igraph_matrix_t adhesion, normalized_adhesion; + igraph_real_t polarization; + IGRAPH_MATRIX_INIT_FINALLY(&adhesion, 0, 0); + IGRAPH_MATRIX_INIT_FINALLY(&normalized_adhesion, 0, 0); + pm.WriteClusters(modularity, temperature, csize, membership, + &adhesion, &normalized_adhesion, &polarization, + kT, d_p, d_n, gamma, gamma_minus); + igraph_matrix_destroy(&normalized_adhesion); + igraph_matrix_destroy(&adhesion); + IGRAPH_FINALLY_CLEAN(2); + + RNG_END(); + + return 0; +} diff --git a/src/rigraph/core/community/spinglass/pottsmodel_2.cpp b/src/rigraph/core/community/spinglass/pottsmodel_2.cpp new file mode 100644 index 0000000..d26e0e9 --- /dev/null +++ b/src/rigraph/core/community/spinglass/pottsmodel_2.cpp @@ -0,0 +1,2236 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Jörg Reichardt + This file was modified by Vincent Traag + The original copyright notice follows here */ + +/*************************************************************************** + pottsmodel.cpp - description + ------------------- + begin : Fri May 28 2004 + copyright : (C) 2004 by + email : + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "pottsmodel_2.h" +#include "NetRoutines.h" + +#include "igraph_random.h" +#include "../../core/interruption.h" + +#include +#include + +using namespace std; + +//################################################################################################# +PottsModel::PottsModel(network *n, unsigned int qvalue, int m) : Qmatrix(qvalue+1), acceptance(0) +{ + DLList_Iter iter; + NNode *n_cur; + unsigned int *i_ptr; + net = n; + q = qvalue; + operation_mode = m; + k_max = 0; + //needed in calculating modularity + Qa = new double[q + 1]; + //weights for each spin state needed in Monte Carlo process + weights = new double[q + 1]; + //bookkeeping of occupation numbers of spin states or the number of links in community + color_field = new double[q + 1]; + neighbours = new double[q + 1]; + + num_of_nodes = net->node_list->Size(); + num_of_links = net->link_list->Size(); + + n_cur = iter.First(net->node_list); + //these lists are needed to keep track of spin states for parallel update mode + new_spins = new DL_Indexed_List(); + previous_spins = new DL_Indexed_List(); + while (!iter.End()) { + if (k_max < n_cur->Get_Degree()) { + k_max = n_cur->Get_Degree(); + } + i_ptr = new unsigned int; + *i_ptr = 0; + new_spins->Push(i_ptr); + i_ptr = new unsigned int; + *i_ptr = 0; + previous_spins->Push(i_ptr); + n_cur = iter.Next(); + } +} +//####################################################### +//Destructor of PottsModel +//######################################################## +PottsModel::~PottsModel() { + /* The DLItem destructor does not delete its item currently, + because of some bad design. As a workaround, we delete them here + by hand */ + new_spins->delete_items(); + previous_spins->delete_items(); + delete new_spins; + delete previous_spins; + delete [] Qa; + delete [] weights; + delete [] color_field; + delete [] neighbours; +} +//##################################################### +//Assing an initial random configuration of spins to nodes +//if called with negative argument or the spin used as argument +//when called with positve one. +//This may be handy, if you want to warm up the network. +//#################################################### +unsigned long PottsModel::assign_initial_conf(int spin) { + int s; + DLList_Iter iter; + DLList_Iter l_iter; + NNode *n_cur; + NLink *l_cur; + double sum_weight; + double av_k_squared = 0.0; + double av_k = 0.0; + IGRAPH_UNUSED(av_k_squared); /* We mark it as unused to prevent warnings about unused-but-set-variables. */ + IGRAPH_UNUSED(av_k); /* We mark it as unused to prevent warnings about unused-but-set-variables. */ + +// printf("Assigning initial configuration...\n"); + // initialize colorfield + for (unsigned int i = 0; i <= q; i++) { + color_field[i] = 0.0; + } + // + total_degree_sum = 0.0; + n_cur = iter.First(net->node_list); + while (!iter.End()) { + if (spin < 0) { + s = RNG_INTEGER(1, q); + } else { + s = spin; + } + n_cur->Set_ClusterIndex(s); + l_cur = l_iter.First(n_cur->Get_Links()); + sum_weight = 0; + while (!l_iter.End()) { + sum_weight += l_cur->Get_Weight(); //weight should be one, in case we are not using it. + l_cur = l_iter.Next(); + } + // we set the sum of the weights or the degree as the weight of the node, this way + // we do not have to calculate it again. + n_cur->Set_Weight(sum_weight); + av_k_squared += sum_weight * sum_weight; + av_k += sum_weight; + + // in case we want all links to be contribute equally - parameter gamm=fixed + if (operation_mode == 0) { + color_field[s]++; + } else { + color_field[s] += sum_weight; + } + // or in case we want to use a weight of each link that is proportional to k_i\times k_j + total_degree_sum += sum_weight; + n_cur = iter.Next(); + } + av_k_squared /= double(net->node_list->Size()); + av_k /= double(net->node_list->Size()); + // total_degree_sum-=av_k_squared/av_k; +// printf("Total Degree Sum=2M=%f\n",total_degree_sum); + return net->node_list->Size(); +} +//##################################################################### +//If I ever manage to write a decent LookUp function, it will be here +//##################################################################### +unsigned long PottsModel::initialize_lookup(double kT, double gamma) { + IGRAPH_UNUSED(kT); + IGRAPH_UNUSED(gamma); + /* + double beta; + // the look-up table contains all entries of exp(-beta(-neighbours+gamma*h)) + // as needed in the HeatBath algorithm + beta=1.0/kT; + for (long w=0; w<=k_max+num_of_nodes; w++) + { + neg_lookup[w]=exp(-beta*-w + } + delta_ij[0]=1.0; + for (long w=-num_of_nodes-k_max; w<=k_max+num_of_nodes; w++) + { + + } + + // wenn wir spaeter exp(-1/kT*gamma*(nk+1-nj) fuer eine spin-flip von j nach k benoetigen schauen wir nur noch hier nach + for (unsigned long n=1; n<=num_of_nodes; n++) + { + gamma_term[n]=exp(-double(n)/kT*gamma); + } + gamma_term[0]=1.0; + */ + return 1; +} +//##################################################################### +// Q denotes the modularity of the network +// This function calculates it initially +// In the event of a spin changing its state, it only needs updating +// Note that Qmatrix and Qa are only counting! The normalization +// by num_of_links is done later +//#################################################################### +double PottsModel::initialize_Qmatrix() { + DLList_Iter l_iter; + NLink *l_cur; + unsigned int i, j; + //initialize with zeros + num_of_links = net->link_list->Size(); + for (i = 0; i <= q; i++) { + Qa[i] = 0.0; + for (j = i; j <= q; j++) { + Qmatrix[i][j] = 0.0; + Qmatrix[j][i] = 0.0; + } + } + //go over all links and make corresponding entries in Q matrix + //An edge connecting state i wiht state j will get an entry in Qij and Qji + l_cur = l_iter.First(net->link_list); + while (!l_iter.End()) { + i = l_cur->Get_Start()->Get_ClusterIndex(); + j = l_cur->Get_End()->Get_ClusterIndex(); + //printf("%d %d\n",i,j); + Qmatrix[i][j] += l_cur->Get_Weight(); + Qmatrix[j][i] += l_cur->Get_Weight(); + + l_cur = l_iter.Next(); + } + //Finally, calculate sum over rows and keep in Qa + for (i = 0; i <= q; i++) { + for (j = 0; j <= q; j++) { + Qa[i] += Qmatrix[i][j]; + } + } + return calculate_Q(); +} +//#################################################################### +// This function does the actual calculation of Q from the matrix +// The normalization by num_of_links is done here +//#################################################################### +double PottsModel::calculate_Q() { + double Q = 0.0; + for (unsigned int i = 0; i <= q; i++) { + Q += Qmatrix[i][i] - Qa[i] * Qa[i] / double(2.0 * net->sum_weights); + if ((Qa[i] < 0.0) || Qmatrix[i][i] < 0.0) { +// printf("Negatives Qa oder Qii\n\n\n"); + //printf("Press any key to continue\n\n"); + //cin >> Q; + } + } + Q /= double(2.0 * net->sum_weights); + return Q; +} +double PottsModel::calculate_genQ(double gamma) { + double Q = 0.0; + for (unsigned int i = 0; i <= q; i++) { + Q += Qmatrix[i][i] - gamma * Qa[i] * Qa[i] / double(2.0 * net->sum_weights); + if ((Qa[i] < 0.0) || Qmatrix[i][i] < 0.0) { +// printf("Negatives Qa oder Qii\n\n\n"); + //printf("Press any key to continue\n\n"); + //cin >> Q; + } + } + Q /= double(2.0 * net->sum_weights); + return Q; +} +//####################################################################### +// This function calculates the Energy for the standard Hamiltonian +// given a particular value of gamma and the current spin states +// ##################################################################### +double PottsModel::calculate_energy(double gamma) { + double e = 0.0; + DLList_Iter l_iter; + NLink *l_cur; + l_cur = l_iter.First(net->link_list); + //every in-cluster edge contributes -1 + while (!l_iter.End()) { + if (l_cur->Get_Start()->Get_ClusterIndex() == l_cur->Get_End()->Get_ClusterIndex()) { + e--; + } + l_cur = l_iter.Next(); + } + //and the penalty term contributes according to cluster sizes + for (unsigned int i = 1; i <= q; i++) { + e += gamma * 0.5 * double(color_field[i]) * double((color_field[i] - 1)); + } + energy = e; + return e; +} +//########################################################################## +// We would like to start from a temperature with at least 95 of all proposed +// spin changes accepted in 50 sweeps over the network +// The function returns the Temperature found +//######################################################################### +double PottsModel::FindStartTemp(double gamma, double prob, double ts) { + double kT; + kT = ts; + //assing random initial condition + assign_initial_conf(-1); + //initialize Modularity matrix, from now on, it will be updated at every spin change + initialize_Qmatrix(); + // the factor 1-1/q is important, since even, at infinite temperature, + // only 1-1/q of all spins do change their state, since a randomly chooses new + // state is with prob. 1/q the old state. + while (acceptance < (1.0 - 1.0 / double(q)) * 0.95) { //want 95% acceptance + kT = kT * 1.1; + // if I ever have a lookup table, it will need initialization for every kT + //initialize_lookup(kT,k_max,net->node_list->Size()); + HeatBathParallelLookup(gamma, prob, kT, 50); +// printf("kT=%f acceptance=%f\n", kT, acceptance); + } + kT *= 1.1; // just to be sure... +// printf("Starting with acceptance ratio: %1.6f bei kT=%2.4f\n",acceptance,kT); + return kT; +} + +//############################################################## +//This function does a parallel update at zero T +//Hence, it is really fast on easy problems +//max sweeps is the maximum number of sweeps it should perform, +//if it does not converge earlier +//############################################################## +long PottsModel::HeatBathParallelLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps) { + DLList_Iter iter, net_iter; + DLList_Iter l_iter; + DLList_Iter i_iter, i_iter2; + NNode *node, *n_cur; + NLink *l_cur; + unsigned int *SPIN, *P_SPIN, new_spin, spin_opt, old_spin, spin, sweep; + // long h; // degree; + unsigned long changes; + double h, delta = 0, deltaE, deltaEmin, w, degree; + //HugeArray neighbours; + bool cyclic = false; + + sweep = 0; + changes = 1; + while (sweep < max_sweeps && changes) { + cyclic = true; + sweep++; + changes = 0; + //Loop over all nodes + node = net_iter.First(net->node_list); + SPIN = i_iter.First(new_spins); + while (!net_iter.End()) { + // How many neigbors of each type? + // set them all zero + for (unsigned int i = 0; i <= q; i++) { + neighbours[i] = 0; + } + degree = node->Get_Weight(); + //Loop over all links (=neighbours) + l_cur = l_iter.First(node->Get_Links()); + while (!l_iter.End()) { + //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name()); + w = l_cur->Get_Weight(); + if (node == l_cur->Get_Start()) { + n_cur = l_cur->Get_End(); + } else { + n_cur = l_cur->Get_Start(); + } + neighbours[n_cur->Get_ClusterIndex()] += w; + l_cur = l_iter.Next(); + } + //Search optimal Spin + old_spin = node->Get_ClusterIndex(); + //degree=node->Get_Degree(); + switch (operation_mode) { + case 0: { + delta = 1.0; + break; + } + case 1: { //newman modularity + prob = degree / total_degree_sum; + delta = degree; + break; + } + } + + + spin_opt = old_spin; + deltaEmin = 0.0; + for (spin = 1; spin <= q; spin++) { // all possible spin states + if (spin != old_spin) { + h = color_field[spin] + delta - color_field[old_spin]; + deltaE = double(neighbours[old_spin] - neighbours[spin]) + gamma * prob * double(h); + if (deltaE < deltaEmin) { + spin_opt = spin; + deltaEmin = deltaE; + } + } + } // for spin + + //Put optimal spin on list for later update + *SPIN = spin_opt; + node = net_iter.Next(); + SPIN = i_iter.Next(); + } // while !net_iter.End() + + //------------------------------- + //Now set all spins to new values + node = net_iter.First(net->node_list); + SPIN = i_iter.First(new_spins); + P_SPIN = i_iter2.First(previous_spins); + while (!net_iter.End()) { + old_spin = node->Get_ClusterIndex(); + new_spin = *SPIN; + if (new_spin != old_spin) { // Do we really have a change?? + changes++; + node->Set_ClusterIndex(new_spin); + //this is important!! + //In Parallel update, there occur cyclic attractors of size two + //which then make the program run for ever + if (new_spin != *P_SPIN) { + cyclic = false; + } + *P_SPIN = old_spin; + color_field[old_spin]--; + color_field[new_spin]++; + + //Qmatrix update + //iteration over all neighbours + l_cur = l_iter.First(node->Get_Links()); + while (!l_iter.End()) { + w = l_cur->Get_Weight(); + if (node == l_cur->Get_Start()) { + n_cur = l_cur->Get_End(); + } else { + n_cur = l_cur->Get_Start(); + } + Qmatrix[old_spin][n_cur->Get_ClusterIndex()] -= w; + Qmatrix[new_spin][n_cur->Get_ClusterIndex()] += w; + Qmatrix[n_cur->Get_ClusterIndex()][old_spin] -= w; + Qmatrix[n_cur->Get_ClusterIndex()][new_spin] += w; + Qa[old_spin] -= w; + Qa[new_spin] += w; + l_cur = l_iter.Next(); + } // while l_iter + } + node = net_iter.Next(); + SPIN = i_iter.Next(); + P_SPIN = i_iter2.Next(); + } // while (!net_iter.End()) + } // while markov + + // In case of a cyclic attractor, we want to interrupt + if (cyclic) { +// printf("Cyclic attractor!\n"); + acceptance = 0.0; + return 0; + } else { + acceptance = double(changes) / double(num_of_nodes); + return changes; + } +} +//################################################################################### +//The same function as before, but rather than parallel update, it pics the nodes to update +//randomly +//################################################################################### +double PottsModel::HeatBathLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps) { + DLList_Iter iter; + DLList_Iter l_iter; + DLList_Iter i_iter, i_iter2; + NNode *node, *n_cur; + NLink *l_cur; + unsigned int new_spin, spin_opt, old_spin, spin, sweep; + long r;// degree; + unsigned long changes; + double delta = 0, h, deltaE, deltaEmin, w, degree; + //HugeArray neighbours; + + sweep = 0; + changes = 0; + while (sweep < max_sweeps) { + sweep++; + //ueber alle Knoten im Netz + for (unsigned long n = 0; n < num_of_nodes; n++) { + r = -1; + while ((r < 0) || (r > (long)num_of_nodes - 1)) { + r = RNG_INTEGER(0, num_of_nodes - 1); + } + /* r=long(double(num_of_nodes*double(rand())/double(RAND_MAX+1.0)));*/ + node = net->node_list->Get(r); + // Wir zaehlen, wieviele Nachbarn von jedem spin vorhanden sind + // erst mal alles Null setzen + for (unsigned int i = 0; i <= q; i++) { + neighbours[i] = 0; + } + degree = node->Get_Weight(); + //Loop over all links (=neighbours) + l_cur = l_iter.First(node->Get_Links()); + while (!l_iter.End()) { + //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name()); + w = l_cur->Get_Weight(); + if (node == l_cur->Get_Start()) { + n_cur = l_cur->Get_End(); + } else { + n_cur = l_cur->Get_Start(); + } + neighbours[n_cur->Get_ClusterIndex()] += w; + l_cur = l_iter.Next(); + } + //Search optimal Spin + old_spin = node->Get_ClusterIndex(); + //degree=node->Get_Degree(); + switch (operation_mode) { + case 0: { + delta = 1.0; + break; + } + case 1: { //newman modularity + prob = degree / total_degree_sum; + delta = degree; + break; + } + } + + + spin_opt = old_spin; + deltaEmin = 0.0; + for (spin = 1; spin <= q; spin++) { // alle moeglichen Spins + if (spin != old_spin) { + h = color_field[spin] + delta - color_field[old_spin]; + deltaE = double(neighbours[old_spin] - neighbours[spin]) + gamma * prob * double(h); + if (deltaE < deltaEmin) { + spin_opt = spin; + deltaEmin = deltaE; + } + } + } // for spin + + //------------------------------- + //Now update the spins + new_spin = spin_opt; + if (new_spin != old_spin) { // Did we really change something?? + changes++; + node->Set_ClusterIndex(new_spin); + color_field[old_spin] -= delta; + color_field[new_spin] += delta; + + //Qmatrix update + //iteration over all neighbours + l_cur = l_iter.First(node->Get_Links()); + while (!l_iter.End()) { + w = l_cur->Get_Weight(); + if (node == l_cur->Get_Start()) { + n_cur = l_cur->Get_End(); + } else { + n_cur = l_cur->Get_Start(); + } + Qmatrix[old_spin][n_cur->Get_ClusterIndex()] -= w; + Qmatrix[new_spin][n_cur->Get_ClusterIndex()] += w; + Qmatrix[n_cur->Get_ClusterIndex()][old_spin] -= w; + Qmatrix[n_cur->Get_ClusterIndex()][new_spin] += w; + Qa[old_spin] -= w; + Qa[new_spin] += w; + l_cur = l_iter.Next(); + } // while l_iter + } + } // for n + } // while markov + + acceptance = double(changes) / double(num_of_nodes) / double(sweep); + return acceptance; +} +//##################################################################################### +//This function performs a parallel update at Terperature T +//##################################################################################### +long PottsModel::HeatBathParallelLookup(double gamma, double prob, double kT, unsigned int max_sweeps) { + DLList_Iter iter, net_iter; + DLList_Iter l_iter; + DLList_Iter i_iter, i_iter2; + NNode *node, *n_cur; + NLink *l_cur; + unsigned int new_spin, spin_opt, old_spin; + unsigned int *SPIN, *P_SPIN; + unsigned int sweep; + long max_q; + unsigned long changes, /*degree,*/ problemcount; + //HugeArray neighbours; + double h, delta = 0, norm, r, beta, minweight, prefac = 0, w, degree; + bool cyclic = false, found; + unsigned long number_of_nodes; + + sweep = 0; + changes = 1; + number_of_nodes = net->node_list->Size(); + while (sweep < max_sweeps && changes) { + cyclic = true; + sweep++; + changes = 0; + //Loop over all nodes + node = net_iter.First(net->node_list); + SPIN = i_iter.First(new_spins); + while (!net_iter.End()) { + // Initialize neighbours and weights + problemcount = 0; + for (unsigned int i = 0; i <= q; i++) { + neighbours[i] = 0; + weights[i] = 0; + } + norm = 0.0; + degree = node->Get_Weight(); + //Loop over all links (=neighbours) + l_cur = l_iter.First(node->Get_Links()); + while (!l_iter.End()) { + //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name()); + w = l_cur->Get_Weight(); + if (node == l_cur->Get_Start()) { + n_cur = l_cur->Get_End(); + } else { + n_cur = l_cur->Get_Start(); + } + neighbours[n_cur->Get_ClusterIndex()] += w; + l_cur = l_iter.Next(); + } + //Search optimal Spin + old_spin = node->Get_ClusterIndex(); + //degree=node->Get_Degree(); + switch (operation_mode) { + case 0: { + prefac = 1.0; + delta = 1.0; + break; + } + case 1: { //newman modularity + prefac = 1.0; + prob = degree / total_degree_sum; + delta = degree; + break; + } + } + spin_opt = old_spin; + beta = 1.0 / kT * prefac; + minweight = 0.0; + weights[old_spin] = 0.0; + for (unsigned spin = 1; spin <= q; spin++) { // loop over all possible new spins + if (spin != old_spin) { // only if we have a different than old spin! + h = color_field[spin] + delta - color_field[old_spin]; + weights[spin] = double(neighbours[old_spin] - neighbours[spin]) + gamma * prob * double(h); + if (weights[spin] < minweight) { + minweight = weights[spin]; + } + } + } // for spin + for (unsigned spin = 1; spin <= q; spin++) { // loop over all possibe spins + weights[spin] -= minweight; // subtract minweight + // to avoid numerical problems with large exponents + weights[spin] = exp(-beta * weights[spin]); + norm += weights[spin]; + } // for spin + + //now choose a new spin + r = RNG_UNIF(0, norm); + /* norm*double(rand())/double(RAND_MAX + 1.0); */ + new_spin = 1; + found = false; + while (!found && new_spin <= q) { + if (r <= weights[new_spin]) { + spin_opt = new_spin; + found = true; + break; + } else { + r -= weights[new_spin]; + } + new_spin++; + } + if (!found) { +// printf("."); + problemcount++; + } + //Put new spin on list + *SPIN = spin_opt; + + node = net_iter.Next(); + SPIN = i_iter.Next(); + } // while !net_iter.End() + + //------------------------------- + //now update all spins + node = net_iter.First(net->node_list); + SPIN = i_iter.First(new_spins); + P_SPIN = i_iter2.First(previous_spins); + while (!net_iter.End()) { + old_spin = node->Get_ClusterIndex(); + new_spin = *SPIN; + if (new_spin != old_spin) { // Did we really change something?? + changes++; + node->Set_ClusterIndex(new_spin); + if (new_spin != *P_SPIN) { + cyclic = false; + } + *P_SPIN = old_spin; + color_field[old_spin] -= delta; + color_field[new_spin] += delta; + + //Qmatrix update + //iteration over all neighbours + l_cur = l_iter.First(node->Get_Links()); + while (!l_iter.End()) { + w = l_cur->Get_Weight(); + if (node == l_cur->Get_Start()) { + n_cur = l_cur->Get_End(); + } else { + n_cur = l_cur->Get_Start(); + } + Qmatrix[old_spin][n_cur->Get_ClusterIndex()] -= w; + Qmatrix[new_spin][n_cur->Get_ClusterIndex()] += w; + Qmatrix[n_cur->Get_ClusterIndex()][old_spin] -= w; + Qmatrix[n_cur->Get_ClusterIndex()][new_spin] += w; + Qa[old_spin] -= w; + Qa[new_spin] += w; + l_cur = l_iter.Next(); + } // while l_iter + } + node = net_iter.Next(); + SPIN = i_iter.Next(); + P_SPIN = i_iter2.Next(); + } // while (!net_iter.End()) + + } // while markov + max_q = 0; + for (unsigned int i = 1; i <= q; i++) if (color_field[i] > max_q) { + max_q = long(color_field[i]); + } + + //again, we would not like to end up in cyclic attractors + if (cyclic && changes) { +// printf("Cyclic attractor!\n"); + acceptance = double(changes) / double(number_of_nodes); + return 0; + } else { + acceptance = double(changes) / double(number_of_nodes); + return changes; + } +} +//############################################################## +// This is the function generally used for optimisation, +// as the parallel update has its flaws, due to the cyclic attractors +//############################################################## +double PottsModel::HeatBathLookup(double gamma, double prob, double kT, unsigned int max_sweeps) { + DLList_Iter iter; + DLList_Iter l_iter; + DLList_Iter i_iter, i_iter2; + NNode *node, *n_cur; + NLink *l_cur; + unsigned int new_spin, spin_opt, old_spin; + unsigned int sweep; + long max_q, rn; + unsigned long changes, /*degree,*/ problemcount; + double degree, w, delta = 0, h; + //HugeArray neighbours; + double norm, r, beta, minweight, prefac = 0; + bool found; + long int number_of_nodes; + sweep = 0; + changes = 0; + number_of_nodes = net->node_list->Size(); + while (sweep < max_sweeps) { + sweep++; + //loop over all nodes in network + for (int n = 0; n < number_of_nodes; n++) { + rn = -1; + while ((rn < 0) || (rn > number_of_nodes - 1)) { + rn = RNG_INTEGER(0, number_of_nodes - 1); + } + /* rn=long(double(number_of_nodes*double(rand())/double(RAND_MAX+1.0))); */ + + node = net->node_list->Get(rn); + // initialize the neighbours and the weights + problemcount = 0; + for (unsigned int i = 0; i <= q; i++) { + neighbours[i] = 0.0; + weights[i] = 0.0; + } + norm = 0.0; + degree = node->Get_Weight(); + //Loop over all links (=neighbours) + l_cur = l_iter.First(node->Get_Links()); + while (!l_iter.End()) { + //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name()); + w = l_cur->Get_Weight(); + if (node == l_cur->Get_Start()) { + n_cur = l_cur->Get_End(); + } else { + n_cur = l_cur->Get_Start(); + } + neighbours[n_cur->Get_ClusterIndex()] += w; + l_cur = l_iter.Next(); + } + + //Look for optimal spin + + old_spin = node->Get_ClusterIndex(); + //degree=node->Get_Degree(); + switch (operation_mode) { + case 0: { + prefac = 1.0; + delta = 1.0; + break; + } + case 1: {//newman modularity + prefac = 1.0; + prob = degree / total_degree_sum; + delta = degree; + break; + } + } + spin_opt = old_spin; + beta = 1.0 / kT * prefac; + minweight = 0.0; + weights[old_spin] = 0.0; + for (unsigned spin = 1; spin <= q; spin++) { // all possible new spins + if (spin != old_spin) { // except the old one! + h = color_field[spin] - (color_field[old_spin] - delta); + weights[spin] = neighbours[old_spin] - neighbours[spin] + gamma * prob * h; + if (weights[spin] < minweight) { + minweight = weights[spin]; + } + } + } // for spin + for (unsigned spin = 1; spin <= q; spin++) { // all possible new spins + weights[spin] -= minweight; // subtract minweigt + // for numerical stability + weights[spin] = exp(-beta * weights[spin]); + norm += weights[spin]; + } // for spin + + + //choose a new spin + /* r = norm*double(rand())/double(RAND_MAX + 1.0); */ + r = RNG_UNIF(0, norm); + new_spin = 1; + found = false; + while (!found && new_spin <= q) { + if (r <= weights[new_spin]) { + spin_opt = new_spin; + found = true; + break; + } else { + r -= weights[new_spin]; + } + new_spin++; + } + if (!found) { +// printf("."); + problemcount++; + } + //------------------------------- + //now set the new spin + new_spin = spin_opt; + if (new_spin != old_spin) { // Did we really change something?? + changes++; + node->Set_ClusterIndex(new_spin); + color_field[old_spin] -= delta; + color_field[new_spin] += delta; + + //Qmatrix update + //iteration over all neighbours + l_cur = l_iter.First(node->Get_Links()); + while (!l_iter.End()) { + w = l_cur->Get_Weight(); + if (node == l_cur->Get_Start()) { + n_cur = l_cur->Get_End(); + } else { + n_cur = l_cur->Get_Start(); + } + Qmatrix[old_spin][n_cur->Get_ClusterIndex()] -= w; + Qmatrix[new_spin][n_cur->Get_ClusterIndex()] += w; + Qmatrix[n_cur->Get_ClusterIndex()][old_spin] -= w; + Qmatrix[n_cur->Get_ClusterIndex()][new_spin] += w; + Qa[old_spin] -= w; + Qa[new_spin] += w; + l_cur = l_iter.Next(); + } // while l_iter + } + } // for n + } // while markov + max_q = 0; + + for (unsigned int i = 1; i <= q; i++) if (color_field[i] > max_q) { + max_q = long(color_field[i] + 0.5); + } + + acceptance = double(changes) / double(number_of_nodes) / double(sweep); + return acceptance; +} + +//############################################################################################### +//# Here we try to minimize the affinity to the rest of the network +//############################################################################################### +double PottsModel::FindCommunityFromStart(double gamma, double prob, + char *nodename, + igraph_vector_t *result, + igraph_real_t *cohesion, + igraph_real_t *adhesion, + igraph_integer_t *my_inner_links, + igraph_integer_t *my_outer_links) { + DLList_Iter iter, iter2; + DLList_Iter l_iter; + DLList* to_do; + DLList* community; + NNode *start_node = NULL, *n_cur, *neighbor, *max_aff_node, *node; + NLink *l_cur; + bool found = false, add = false, remove = false; + double degree, delta_aff_add, delta_aff_rem, max_delta_aff, Ks = 0.0, Kr = 0, kis, kir, w; + long community_marker = 5; + long to_do_marker = 10; + double inner_links = 0, outer_links = 0, aff_r, aff_s; + + IGRAPH_UNUSED(prob); + + to_do = new DLList; + community = new DLList; + + // find the node in the network + n_cur = iter.First(net->node_list); + while (!found && !iter.End()) { + if (0 == strcmp(n_cur->Get_Name(), nodename)) { + start_node = n_cur; + found = true; + start_node->Set_Affinity(0.0); + community->Push(start_node); + start_node->Set_Marker(community_marker); + Ks = start_node->Get_Weight(); + Kr = total_degree_sum - start_node->Get_Weight(); + } + n_cur = iter.Next(); + } + if (!found) { +// printf("%s not found found. Aborting.\n",nodename); +// fprintf(file,"%s not found found. Aborting.\n",nodename); + delete to_do; + delete community; + return -1; + } + //############################# + // initialize the to_do list and community with the neighbours of start node + //############################# + neighbor = iter.First(start_node->Get_Neighbours()); + while (!iter.End()) { +// printf("Adding node %s to comunity.\n",neighbor->Get_Name()); + community->Push(neighbor); + neighbor->Set_Marker(community_marker); + Ks += neighbor->Get_Weight(); + Kr -= neighbor->Get_Weight(); + neighbor = iter.Next(); + } + node = iter.First(community); + while (!iter.End()) { + //now add at the second neighbors to the to_do list + neighbor = iter2.First(node->Get_Neighbours()); + while (!iter2.End()) { + if ((long)neighbor->Get_Marker() != community_marker && (long)neighbor->Get_Marker() != to_do_marker) { + to_do->Push(neighbor); + neighbor->Set_Marker(to_do_marker); +// printf("Adding node %s to to_do list.\n",neighbor->Get_Name()); + } + neighbor = iter2.Next(); + } + node = iter.Next(); + } + + //############# + //repeat, as long as we are still adding nodes to the communtiy + //############# + add = true; + remove = true; + while (add || remove) { + //############################# + //calculate the affinity changes of all nodes for adding every node in the to_do list to the community + //############################## + + IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */ + + max_delta_aff = 0.0; + max_aff_node = NULL; + add = false; + node = iter.First(to_do); + while (!iter.End()) { + //printf("Checking Links of %s\n",node->Get_Name()); + degree = node->Get_Weight(); + kis = 0.0; + kir = 0.0; + // For every of the neighbors, check, count the links to the community + l_cur = l_iter.First(node->Get_Links()); + while (!l_iter.End()) { + w = l_cur->Get_Weight(); + if (node == l_cur->Get_Start()) { + n_cur = l_cur->Get_End(); + } else { + n_cur = l_cur->Get_Start(); + } + if ((long)n_cur->Get_Marker() == community_marker) { + kis += w; //the weight/number of links to the community + } else { + kir += w; //the weight/number of links to the rest of the network + } + l_cur = l_iter.Next(); + } + aff_r = kir - gamma / total_degree_sum * (Kr - degree) * degree; + aff_s = kis - gamma / total_degree_sum * Ks * degree; + delta_aff_add = aff_r - aff_s; + // if (aff_s>=aff_r && delta_aff_add<=max_delta_aff) { + if (delta_aff_add <= max_delta_aff) { + node->Set_Affinity(aff_s); + max_delta_aff = delta_aff_add; + max_aff_node = node; + add = true; + } + //printf("%s in to_do list with affinity %f\n",node->Get_Name(),node->Get_Affinity()); + node = iter.Next(); + } + //################ + //calculate the affinity changes for removing every single node from the community + //################ + inner_links = 0; + outer_links = 0; + remove = false; + node = iter.First(community); + while (!iter.End()) { + //printf("Checking Links of %s\n",node->Get_Name()); + degree = node->Get_Weight(); + kis = 0.0; + kir = 0.0; + // For every of the neighbors, check, count the links to the community + l_cur = l_iter.First(node->Get_Links()); + while (!l_iter.End()) { + w = l_cur->Get_Weight(); + if (node == l_cur->Get_Start()) { + n_cur = l_cur->Get_End(); + } else { + n_cur = l_cur->Get_Start(); + } + if ((long)n_cur->Get_Marker() == community_marker) { + kis += w; + inner_links += w; //summing all w gives twice the number of inner links(weights) + } else { + kir += w; + outer_links += w; + } + l_cur = l_iter.Next(); + } +// if (kir+kis!=degree) { printf("error kir=%f\tkis=%f\tk=%f\n",kir,kis,degree); } + aff_r = kir - gamma / total_degree_sum * Kr * degree; + aff_s = kis - gamma / total_degree_sum * (Ks - degree) * degree; + delta_aff_rem = aff_s - aff_r; + node->Set_Affinity(aff_s); + // we should not remove the nodes, we have just added + if (delta_aff_rem < max_delta_aff) { + max_delta_aff = delta_aff_rem ; + max_aff_node = node; + remove = true; + add = false; + } + //printf("%s in to_do list with affinity %f\n",node->Get_Name(),node->Get_Affinity()); + node = iter.Next(); + } + inner_links = inner_links * 0.5; + //################ + // Now check, whether we want to remove or add a node + //################ + if (add) { + //################ + //add the node of maximum affinity to the community + //############### + community->Push(max_aff_node); + max_aff_node->Set_Marker(community_marker); + //delete node from to_do + to_do->fDelete(max_aff_node); + //update the sum of degrees in the community + Ks += max_aff_node->Get_Weight(); + Kr -= max_aff_node->Get_Weight(); +// printf("Adding node %s to community with affinity of %f delta_aff: %f.\n",max_aff_node->Get_Name(), max_aff_node->Get_Affinity(),max_delta_aff); + //now add all neighbors of this node, that are not already + //in the to_do list or in the community + neighbor = iter.First(max_aff_node->Get_Neighbours()); + while (!iter.End()) { + if ((long)neighbor->Get_Marker() != community_marker && (long)neighbor->Get_Marker() != to_do_marker) { + to_do->Push(neighbor); + neighbor->Set_Marker(to_do_marker); + //printf("Adding node %s to to_do list.\n",neighbor->Get_Name()); + } + neighbor = iter.Next(); + } + } + if (remove) { + //################ + //remove those with negative affinities + //################ + community->fDelete(max_aff_node); + max_aff_node->Set_Marker(to_do_marker); + //update the sum of degrees in the community + Ks -= max_aff_node->Get_Weight(); + Kr += max_aff_node->Get_Weight(); + //add the node to to_do again + to_do->Push(max_aff_node); +// printf("Removing node %s from community with affinity of %f delta_aff: %f.\n",max_aff_node->Get_Name(), max_aff_node->Get_Affinity(),max_delta_aff); + } + IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */ + } + //################### + //write the node in the community to a file + //################### + // TODO return this instead of writing it +// fprintf(file,"Number_of_nodes:\t%d\n",community->Size()); +// fprintf(file,"Inner_Links:\t%f\n",inner_links); +// fprintf(file,"Outer_Links:\t%f\n",Ks-2*inner_links); +// fprintf(file,"Cohesion:\t%f\n",inner_links-gamma/total_degree_sum*Ks*Ks*0.5); +// fprintf(file,"Adhesion:\t%f\n",outer_links-gamma/total_degree_sum*Ks*Kr); +// fprintf(file,"\n"); + if (cohesion) { + *cohesion = inner_links - gamma / total_degree_sum * Ks * Ks * 0.5; + } + if (adhesion) { + *adhesion = outer_links - gamma / total_degree_sum * Ks * Kr; + } + if (my_inner_links) { + *my_inner_links = inner_links; + } + if (my_outer_links) { + *my_outer_links = outer_links; + } + if (result) { + node = iter.First(community); + igraph_vector_resize(result, 0); + while (!iter.End()) { + // printf("%s in community.\n",node->Get_Name()); + // fprintf(file,"%s\t%f\n",node->Get_Name(),node->Get_Affinity()); + IGRAPH_CHECK(igraph_vector_push_back(result, node->Get_Index())); + node = iter.Next(); + } + } +// printf("%d nodes in community around %s\n",community->Size(),start_node->Get_Name()); +// fclose(file); + unsigned int size = community->Size(); + delete to_do; + delete community; + return size; +} + +//################################################################################################ +// this Function writes the clusters to disk +//################################################################################################ +long PottsModel::WriteClusters(igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_t *csize, + igraph_vector_t *membership, + double kT, double gamma) { + NNode *n_cur, *n_cur2; + /* + double a1,a2,a3,p,p1,p2; + long n,N,lin,lout; + */ + DLList_Iter iter, iter2; + HugeArray inner_links; + HugeArray outer_links; + HugeArray nodes; + + //den Header schreiben +// p=2.0*double(num_of_links)/double(num_of_nodes)/double(num_of_nodes-1); +// fprintf(file," Nodes=\t%lu\n",num_of_nodes); +// fprintf(file," Links=\t%lu\n",num_of_links); +// fprintf(file," q=\t%d\n",q); +// fprintf(file," p=\t%f\n",p); +// fprintf(file," Modularity=\t%f\n",calculate_Q()); +// fprintf(file,"Temperature=\t%f\n", kT); +// fprintf(file,"Cluster\tNodes\tInnerLinks\tOuterLinks\tp_in\tp_out\t\n"); + + if (temperature) { + *temperature = kT; + } + + if (csize || membership || modularity) { + // TODO: count the number of clusters + for (unsigned int spin = 1; spin <= q; spin++) { + inner_links[spin] = 0; + outer_links[spin] = 0; + nodes[spin] = 0; + n_cur = iter.First(net->node_list); + while (!iter.End()) { + if (n_cur->Get_ClusterIndex() == spin) { + nodes[spin]++; + n_cur2 = iter2.First(n_cur->Get_Neighbours()); + while (!iter2.End()) { + if (n_cur2->Get_ClusterIndex() == spin) { + inner_links[spin]++; + } else { + outer_links[spin]++; + } + n_cur2 = iter2.Next(); + } + } + n_cur = iter.Next(); + } + } + } + if (modularity) { + *modularity = 0.0; + for (unsigned int spin = 1; spin <= q; spin++) { + if (nodes[spin] > 0) { + double t1 = inner_links[spin] / net->sum_weights / 2.0; + double t2 = (inner_links[spin] + outer_links[spin]) / + net->sum_weights / 2.0; + *modularity += t1; + *modularity -= gamma * t2 * t2; + } + } + } + if (csize) { + igraph_vector_resize(csize, 0); + for (unsigned int spin = 1; spin <= q; spin++) { + if (nodes[spin] > 0) { + inner_links[spin] /= 2; + // fprintf(file,"Cluster\tNodes\tInnerLinks\tOuterLinks\tp_in\tp_out\n"); + /* + N=num_of_nodes; + n=nodes[spin]; + lin=inner_links[spin]; + lout=outer_links[spin]; + a1=N*log((double)N)-n*log((double)n)*(N-n)*log((double)N-n); + if ((lin==long(n*(n-1)*0.5+0.5)) || (n==1)) a2=0.0; + else a2=(n*(n-1)*0.5 )*log((double)n*(n-1)*0.5 )-(n*(n-1)*0.5 )- + (n*(n-1)*0.5-lin)*log((double)n*(n-1)*0.5-lin)+(n*(n-1)*0.5-lin)- + lin*log((double)lin )+lin; + */ + + /* + if ((lout==n*(N-n)) || n==N) a3=0.0; + else a3=(n*(N-n) )*log((double)n*(N-n) )-(n*(N-n))- + (n*(N-n)-lout)*log((double)n*(N-n)-lout)+(n*(N-n)-lout)- + lout*log((double)lout )+lout; + */ + + /* + p1=(lin+lout)*log((double)p); + p2=(0.5*n*(n-1)-lin + n*(N-n)-lout)*log((double)1.0-p); + */ + // fprintf(file,"%d\t%d\t%d\t%d\t%f\t%f\t%f\n",spin,nodes[spin], inner_links[spin], outer_links[spin], p_in, p_out,log_num_exp); + IGRAPH_CHECK(igraph_vector_push_back(csize, nodes[spin])); + } + } + // fprintf(file,"\n"); + } + + //die Elemente der Cluster + if (membership) { + long int no = -1; + IGRAPH_CHECK(igraph_vector_resize(membership, num_of_nodes)); + for (unsigned int spin = 1; spin <= q; spin++) { + if (nodes[spin] > 0) { + no++; + } + n_cur = iter.First(net->node_list); + while (!iter.End()) { + if (n_cur->Get_ClusterIndex() == spin) { + // fprintf(file,"%d\t%s\n",spin,n_cur->Get_Name()); + VECTOR(*membership)[ n_cur->Get_Index() ] = no; + } + n_cur = iter.Next(); + } + } + } + + return num_of_nodes; +} +//################################################################################################ +//This function writes the soft clusters after a gamma sweep +//that is, it groups every node together that was found in +// more than threshold percent together with the other node +// in the same cluster +//################################################################################################ +// Does not work at the moment !!! +//################################################################################################ +// long PottsModel::WriteSoftClusters(char *filename, double threshold) +// { +// FILE *file; +// NNode *n_cur, *n_cur2; +// DLList_Iter iter, iter2; +// DL_Indexed_List*> *cl_list, *old_clusterlist; +// ClusterList *cl_cur; + +// double max; + +// file=fopen(filename,"w"); +// if (!file) { +// printf("Could not open %s for writing.\n",filename); +// return -1; +// } + +// max=correlation[0]->Get(0); +// //printf("max=%f\n",max); +// cl_list=new DL_Indexed_List*>(); + +// n_cur=iter.First(net->node_list); +// while (!iter.End()) +// { +// cl_cur=new ClusterList(); +// cl_list->Push(cl_cur); +// n_cur2=iter2.First(net->node_list); +// while (!iter2.End()) +// { +// if (double(correlation[n_cur->Get_Index()]->Get(n_cur2->Get_Index()))/max>threshold) +// cl_cur->Push(n_cur2); +// n_cur2=iter2.Next(); +// } +// n_cur=iter.Next(); +// } +// old_clusterlist=net->cluster_list; +// net->cluster_list=cl_list; +// clear_all_markers(net); +// //printf("Es gibt %d Cluster\n",cl_list->Size()); +// reduce_cliques2(net, false, 15); +// //printf("Davon bleiben %d Cluster uebrig\n",cl_list->Size()); +// clear_all_markers(net); +// while (net->cluster_list->Size()){ +// cl_cur=net->cluster_list->Pop(); +// while (cl_cur->Size()) +// { +// n_cur=cl_cur->Pop(); +// fprintf(file,"%s\n",n_cur->Get_Name()); +// //printf("%s\n",n_cur->Get_Name()); +// } +// fprintf(file,"\n"); +// } +// net->cluster_list=old_clusterlist; +// fclose(file); + +// return 1; +// } +//############################################################################# +// Performs a gamma sweep +//############################################################################# +double PottsModel::GammaSweep(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel, int repetitions) { + double stepsize; + double kT = 0.5, kT_start; + long changes; + double gamma, acc; + NNode *n_cur, *n_cur2; + DLList_Iter iter, iter2; + + stepsize = (gamma_stop - gamma_start) / double(steps); + + n_cur = iter.First(net->node_list); + while (!iter.End()) { + correlation[n_cur->Get_Index()] = new HugeArray(); + n_cur2 = iter2.First(net->node_list); + while (!iter2.End()) { + correlation[n_cur->Get_Index()]->Set(n_cur->Get_Index()) = 0.0; + n_cur2 = iter2.Next(); + } + n_cur = iter.Next(); + } + + for (unsigned int n = 0; n <= steps; n++) { + assign_initial_conf(-1); + initialize_Qmatrix(); + gamma = gamma_start + stepsize * n; + kT = 0.5; + acceptance = 0.5; + while (acceptance < (1.0 - 1.0 / double(q)) * 0.95) { //wollen 95% Acceptance + kT *= 1.1; + //initialize_lookup(kT,kmax,net->node_list->Size()); + if (!non_parallel) { + HeatBathParallelLookup(gamma, prob, kT, 25); + } else { + HeatBathLookup(gamma, prob, kT, 25); + } + // printf("kT=%f acceptance=%f\n", kT, acceptance); + } + // printf("Starting with gamma=%f\n", gamma); + kT_start = kT; + + for (int i = 0; i < repetitions; i++) { + changes = 1; + kT = kT_start; + assign_initial_conf(-1); + initialize_Qmatrix(); + while ((changes > 0) && (kT > 0.01)) { + kT = kT * 0.99; + //initialize_lookup(kT,kmax,net->node_list->Size()); + if (!non_parallel) { + changes = HeatBathParallelLookup(gamma, prob, kT, 50); + // printf("kT: %f \t Changes %li\n",kT, changes); + } else { + acc = HeatBathLookup(gamma, prob, kT, 50); + if (acc > (1.0 - 1.0 / double(q)) * 0.01) { + changes = 1; + } else { + changes = 0; + } + // printf("kT: %f Acceptance: %f\n",kT, acc); + } + } + // printf("Finisched with acceptance: %1.6f bei kT=%2.4f und gamma=%2.4f\n",acceptance,kT, gamma); +// fprintf(file,"%f\t%f\n",gamma_,acceptance); +// fprintf(file2,"%f\t%f\n",gamma_,kT); + // fprintf(file3,"%f\t%d\n",gamma_,count_clusters(5)); + + //Die Correlation berechnen + n_cur = iter.First(net->node_list); + while (!iter.End()) { + n_cur2 = iter2.First(net->node_list); + while (!iter2.End()) { + if (n_cur->Get_ClusterIndex() == n_cur2->Get_ClusterIndex()) { + correlation[n_cur->Get_Index()]->Set(n_cur2->Get_Index()) += 0.5; + } + n_cur2 = iter2.Next(); + } + n_cur = iter.Next(); + } + } // for i + } //for n + return kT; +} +//############################################################################# +//Performs a Gamma sweep at zero T +//############################################################################# +double PottsModel::GammaSweepZeroTemp(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel, int repetitions) { + double stepsize; + long changes; + double gamma = gamma_start, acc; + long runs; + NNode *n_cur, *n_cur2; + DLList_Iter iter, iter2; + + stepsize = (gamma_stop - gamma_start) / double(steps); + + n_cur = iter.First(net->node_list); + while (!iter.End()) { + correlation[n_cur->Get_Index()] = new HugeArray(); + n_cur2 = iter2.First(net->node_list); + while (!iter2.End()) { + correlation[n_cur->Get_Index()]->Set(n_cur->Get_Index()) = 0.0; + n_cur2 = iter2.Next(); + } + n_cur = iter.Next(); + } + + for (unsigned int n = 0; n <= steps; n++) { + assign_initial_conf(-1); + initialize_Qmatrix(); + gamma = gamma_start + stepsize * n; + // printf("Starting with gamma=%f\n", gamma); + for (int i = 0; i < repetitions; i++) { + changes = 1; + assign_initial_conf(-1); + initialize_Qmatrix(); + runs = 0; + while (changes > 0 && runs < 250) { + //initialize_lookup(kT,kmax,net->node_list->Size()); + if (!non_parallel) { + changes = HeatBathParallelLookupZeroTemp(gamma, prob, 1); + // printf("Changes %li\n", changes); + } else { + acc = HeatBathLookupZeroTemp(gamma, prob, 1); + if (acc > (1.0 - 1.0 / double(q)) * 0.01) { + changes = 1; + } else { + changes = 0; + } + // printf("Acceptance: %f\n", acc); + } + runs++; + } + // printf("Finisched with Modularity: %1.6f bei Gamma=%1.6f\n",calculate_Q(), gamma); +// fprintf(file,"%f\t%f\n",gamma_,acceptance); +// fprintf(file2,"%f\t%f\n",gamma_,kT); + // fprintf(file3,"%f\t%d\n",gamma_,count_clusters(5)); + + //Die Correlation berechnen + n_cur = iter.First(net->node_list); + while (!iter.End()) { + n_cur2 = iter2.First(net->node_list); + while (!iter2.End()) { + if (n_cur->Get_ClusterIndex() == n_cur2->Get_ClusterIndex()) { + correlation[n_cur->Get_Index()]->Set(n_cur2->Get_Index()) += 0.5; + correlation[n_cur2->Get_Index()]->Set(n_cur->Get_Index()) += 0.5; + } + n_cur2 = iter2.Next(); + } + n_cur = iter.Next(); + } + } // for i + } //for n + return gamma; +} +//####################################################################### +//----------------------------------------------------------------------- +//####################################################################### +// This function writes the Correlation Matrix that results from a +// Gamma-Sweep, this matrix is used to make ps files of it. +// ###################################################################### +// long PottsModel::WriteCorrelationMatrix(char *filename) +// { +// FILE *file, *file2; +// char filename2[255]; +// NNode *n_cur, *n_cur2; +// DLList_Iter iter, iter2; + +// sprintf(filename2,"%s.mat",filename); +// file=fopen(filename,"w"); +// if (!file) { +// printf("Could not open %s for writing.\n",filename); +// return -1; +// } +// file2=fopen(filename2,"w"); +// if (!file2) { +// printf("Could not open %s for writing.\n",filename2); +// return -1; +// } +// //write the header in one line +// n_cur=iter.First(net->node_list); +// while (!iter.End()) +// { +// fprintf(file, "\t%s",n_cur->Get_Name()); +// n_cur=iter.Next(); +// } +// fprintf(file, "\n"); + +// //fprintf(file, "%d\t%d\n",net->node_list->Size(),net->node_list->Size()); + +// long r=0,c=0; +// n_cur=iter.First(net->node_list); +// while (!iter.End()) +// { +// fprintf(file, "%s",n_cur->Get_Name()); +// r++; +// n_cur2=iter2.First(net->node_list); +// while (!iter2.End()) +// { +// c++; +// fprintf(file,"\t%f",correlation[n_cur->Get_Index()]->Get(n_cur2->Get_Index())); +// fprintf(file2,"%li\t%li\t%f\n",r,c,correlation[n_cur->Get_Index()]->Get(n_cur2->Get_Index())); +// n_cur2=iter2.Next(); +// } +// fprintf(file,"\n"); +// n_cur=iter.Next(); +// } +// fclose(file); +// fclose(file2); +// return 1; +// } +//############################################################################## + +//################################################################################################# +PottsModelN::PottsModelN(network *n, unsigned int num_communities, bool directed) : + degree_pos_in(NULL), degree_neg_in(NULL), + degree_pos_out(NULL), degree_neg_out(NULL), + degree_community_pos_in(NULL), degree_community_neg_in(NULL), + degree_community_pos_out(NULL), degree_community_neg_out(NULL), + csize(NULL), spin(NULL), neighbours(NULL), weights(NULL) +{ + //Set internal variable + net = n; + q = num_communities; + + is_directed = directed; + + is_init = false; + + num_nodes = net->node_list->Size(); +} +//####################################################### +//Destructor of PottsModel +//######################################################## +PottsModelN::~PottsModelN() { + delete [] degree_pos_in; + delete [] degree_neg_in; + delete [] degree_pos_out; + delete [] degree_neg_out; + + delete [] degree_community_pos_in; + delete [] degree_community_neg_in; + delete [] degree_community_pos_out; + delete [] degree_community_neg_out; + + delete [] weights; + delete [] neighbours; + delete [] csize; + + delete [] spin; +} + +void PottsModelN::assign_initial_conf(bool init_spins) { +#ifdef SPINGLASS_DEBUG + printf("Start assigning.\n"); +#endif + unsigned int s; + DLList_Iter iter; + DLList_Iter l_iter; + NNode *n_cur; + NLink *l_cur; + + + if (init_spins) { +#ifdef SPINGLASS_DEBUG + printf("Initializing spin.\n"); +#endif + // Free the arrays before (re-)allocating them + // These arrays are initialized to NULL, so it is safe to delete even before allocation + delete [] degree_pos_in; + delete [] degree_neg_in; + delete [] degree_pos_out; + delete [] degree_neg_out; + + delete [] spin; + + //Bookkeeping of the various degrees (positive/negative) and (in/out) + degree_pos_in = new double[num_nodes]; //Postive indegree of the nodes (or sum of weights) + degree_neg_in = new double[num_nodes]; //Negative indegree of the nodes (or sum of weights) + degree_pos_out = new double[num_nodes]; //Postive outdegree of the nodes (or sum of weights) + degree_neg_out = new double[num_nodes]; //Negative outdegree of the nodes (or sum of weights) + + spin = new unsigned int[num_nodes]; //The spin state of each node + } + + if (is_init) { + delete [] degree_community_pos_in; + delete [] degree_community_neg_in; + delete [] degree_community_pos_out; + delete [] degree_community_neg_out; + + delete [] weights; + delete [] neighbours; + delete [] csize; + } + + is_init = true; + + //Bookkeep of occupation numbers of spin states or the number of links in community... + degree_community_pos_in = new double[q + 1]; //Positive sum of indegree for communities + degree_community_neg_in = new double[q + 1]; //Negative sum of indegree for communities + degree_community_pos_out = new double[q + 1]; //Positive sum of outegree for communities + degree_community_neg_out = new double[q + 1]; //Negative sum of outdegree for communities + + //...and of weights and neighbours for in the HeathBathLookup + weights = new double[q + 1]; //The weights for changing to another spin state + neighbours = new double[q + 1]; //The number of neighbours (or weights) in different spin states + csize = new unsigned int[q + 1]; //The number of nodes in each community + + + //Initialize communities + for (unsigned int i = 0; i <= q; i++) { + degree_community_pos_in[i] = 0.0; + degree_community_neg_in[i] = 0.0; + degree_community_pos_out[i] = 0.0; + degree_community_neg_out[i] = 0.0; + + csize[i] = 0; + } + + //Initialize vectors + if (init_spins) { + for (unsigned int i = 0; i < num_nodes; i++) { + degree_pos_in[i] = 0.0; + degree_neg_in[i] = 0.0; + degree_pos_out[i] = 0.0; + degree_neg_out[i] = 0.0; + +#ifdef SPINGLASS_DEBUG + printf("Initializing spin %d", i); +#endif + spin[i] = 0; + } + } + m_p = 0.0; + m_n = 0.0; + //Set community for each node, and + //correctly store it in the bookkeeping + + double sum_weight_pos_in, sum_weight_pos_out, sum_weight_neg_in, sum_weight_neg_out; + //double av_w = 0.0, av_k=0.0; + //int l = 0; +#ifdef SPINGLASS_DEBUG + printf("Visiting each node.\n"); +#endif + for (unsigned int v = 0; v < num_nodes; v++) { + if (init_spins) { + s = RNG_INTEGER(1, q); //The new spin s + spin[v] = (unsigned int)s; + } else { + s = spin[v]; + } + +#ifdef SPINGLASS_DEBUG + printf("Spin %d assigned to node %d.\n", s, v); +#endif + + n_cur = net->node_list->Get(v); + + l_cur = l_iter.First(n_cur->Get_Links()); + + sum_weight_pos_in = 0.0; + sum_weight_pos_out = 0.0; + sum_weight_neg_in = 0.0; + sum_weight_neg_out = 0.0; + + while (!l_iter.End()) { + double w = l_cur->Get_Weight(); + //av_w = (av_w*l + w)/(l+1); //Average weight + //l++; + if (l_cur->Get_Start() == n_cur) //From this to other, so outgoing link + if (w > 0) { + sum_weight_pos_out += w; //Increase positive outgoing weight + } else { + sum_weight_neg_out -= w; //Increase negative outgoing weight + } else if (w > 0) { + sum_weight_pos_in += w; //Increase positive incoming weight + } else { + sum_weight_neg_in -= w; //Increase negative incoming weight + } + + l_cur = l_iter.Next(); + } + + if (!is_directed) { + double sum_weight_pos = sum_weight_pos_out + sum_weight_pos_in; + sum_weight_pos_out = sum_weight_pos; + sum_weight_pos_in = sum_weight_pos; + double sum_weight_neg = sum_weight_neg_out + sum_weight_neg_in; + sum_weight_neg_out = sum_weight_neg; + sum_weight_neg_in = sum_weight_neg; + } + + //av_k = (av_k*l + sum_weight_pos_in)/(l+1); //Average k + + if (init_spins) { + //Set the degrees correctly + degree_pos_in[v] = sum_weight_pos_in; + degree_neg_in[v] = sum_weight_neg_in; + degree_pos_out[v] = sum_weight_pos_out; + degree_neg_out[v] = sum_weight_neg_out; + } + + //Correct the community bookkeeping + degree_community_pos_in[s] += sum_weight_pos_in; + degree_community_neg_in[s] += sum_weight_neg_in; + degree_community_pos_out[s] += sum_weight_pos_out; + degree_community_neg_out[s] += sum_weight_neg_out; + + //Community just increased + csize[s]++; + + //Sum the weights (notice that sum of indegrees equals sum of outdegrees) + m_p += sum_weight_pos_in; + m_n += sum_weight_neg_in; + } + +#ifdef SPINGLASS_DEBUG + printf("Done assigning.\n"); +#endif +} +//############################################################## +// This is the function generally used for optimisation, +// as the parallel update has its flaws, due to the cyclic attractors +//############################################################## +double PottsModelN::HeatBathLookup(double gamma, double lambda, double t, unsigned int max_sweeps) { +#ifdef SPINGLASS_DEBUG + printf("Starting sweep at temperature %f.\n", t); +#endif + DLList_Iter iter; + DLList_Iter l_iter; + DLList_Iter i_iter, i_iter2; + NNode *node, *n_cur; + NLink *l_cur; + /* The new_spin contains the spin to which we will update, + * the spin_opt is the optional spin we will consider and + * the old_spin is the spin of the node we are currently + * changing. + */ + unsigned int new_spin, spin_opt, old_spin; + unsigned int sweep; //current sweep + unsigned long changes, problemcount; //Number of changes and number of problems encountered + + double exp_old_spin; //The expectation value for the old spin + double exp_spin; //The expectation value for the other spin(s) + int v; //The node we will be investigating + + //The variables required for the calculations + double delta_pos_out, delta_pos_in, delta_neg_out, delta_neg_in; + double k_v_pos_out, k_v_pos_in, k_v_neg_out, k_v_neg_in; + + //weight of edge + double w; + + double beta = 1 / t; //Weight for probabilities + double r = 0.0; //random number used for assigning new spin + + double maxweight = 0.0; + double sum_weights = 0.0; //sum_weights for normalizing the probabilities + + sweep = 0; + changes = 0; + double m_pt = m_p; + double m_nt = m_n; + + if (m_pt < 0.001) { + m_pt = 1; + } + + if (m_nt < 0.001) { + m_nt = 1; + } + + while (sweep < max_sweeps) { + sweep++; + //loop over all nodes in network + for (unsigned int n = 0; n < num_nodes; n++) { + //Look for a random node + v = RNG_INTEGER(0, num_nodes - 1); + //We will be investigating node v + + node = net->node_list->Get(v); + + /*******************************************/ + // initialize the neighbours and the weights + problemcount = 0; + for (unsigned int i = 0; i <= q; i++) { + neighbours[i] = 0.0; + weights[i] = 0.0; + } + + //Loop over all links (=neighbours) + l_cur = l_iter.First(node->Get_Links()); + while (!l_iter.End()) { + w = l_cur->Get_Weight(); + if (node == l_cur->Get_Start()) { + n_cur = l_cur->Get_End(); + } else { + n_cur = l_cur->Get_Start(); + } + //Add the link to the correct cluster + neighbours[spin[n_cur->Get_Index()]] += w; + l_cur = l_iter.Next(); + } + //We now have the weight of the (in and out) neighbours + //in each cluster available to us. + /*******************************************/ + old_spin = spin[v]; + + //Look for optimal spin + + //Set the appropriate variable + delta_pos_out = degree_pos_out[v]; + delta_pos_in = degree_pos_in[v]; + delta_neg_out = degree_neg_out[v]; + delta_neg_in = degree_neg_in[v]; + + k_v_pos_out = gamma * delta_pos_out / m_pt; + k_v_pos_in = gamma * delta_pos_in / m_pt; + k_v_neg_out = lambda * delta_neg_out / m_nt; + k_v_neg_in = lambda * delta_neg_in / m_nt; + + //The expectation value for the old spin + if (is_directed) + exp_old_spin = (k_v_pos_out * (degree_community_pos_in[old_spin] - delta_pos_in) - + k_v_neg_out * (degree_community_neg_in[old_spin] - delta_neg_in)) + + (k_v_pos_in * (degree_community_pos_out[old_spin] - delta_pos_out) - + k_v_neg_in * (degree_community_neg_out[old_spin] - delta_neg_out)); + else + exp_old_spin = (k_v_pos_out * (degree_community_pos_in[old_spin] - delta_pos_in) - + k_v_neg_out * (degree_community_neg_in[old_spin] - delta_neg_in)); + + /*******************************************/ + //Calculating probabilities for each transition to another + //community. + + maxweight = 0.0; + weights[old_spin] = 0.0; + + for (spin_opt = 1; spin_opt <= q; spin_opt++) { // all possible new spins + if (spin_opt != old_spin) { // except the old one! + if (is_directed) + exp_spin = (k_v_pos_out * degree_community_pos_in[spin_opt] - k_v_neg_out * degree_community_neg_in[spin_opt]) + + (k_v_pos_in * degree_community_pos_out[spin_opt] - k_v_neg_in * degree_community_neg_out[spin_opt]); + else { + exp_spin = (k_v_pos_out * degree_community_pos_in[spin_opt] - k_v_neg_out * degree_community_neg_in[spin_opt]); + } + + weights[spin_opt] = (neighbours[spin_opt] - exp_spin) - (neighbours[old_spin] - exp_old_spin); + + if (weights[spin_opt] > maxweight) { + maxweight = weights[spin_opt]; + } + } + } // for spin + + //Calculate exp. prob. an + sum_weights = 0.0; + for (spin_opt = 1; spin_opt <= q; spin_opt++) { // all possible new spins + weights[spin_opt] -= maxweight; //subtract maxweight for numerical stability (otherwise overflow). + weights[spin_opt] = exp((double)(beta * weights[spin_opt])); + sum_weights += weights[spin_opt]; + } // for spin + /*******************************************/ + + + /*******************************************/ + //Choose a new spin dependent on the calculated probabilities + r = RNG_UNIF(0, sum_weights); + new_spin = 1; + + bool found = false; + while (!found && new_spin <= q) { + if (r <= weights[new_spin]) { + spin_opt = new_spin; //We have found are new spin + found = true; + break; + } else { + r -= weights[new_spin]; //Perhaps the next spin is the one we want + } + + new_spin++; + } + + //Some weird thing happened. We haven't found a new spin + //while that shouldn't be the case. Numerical problems? + if (!found) { + problemcount++; + } + + new_spin = spin_opt; + //If there wasn't a problem we should have found + //our new spin. + /*******************************************/ + + + /*******************************************/ + //The new spin is available to us, so change + //all the appropriate counters. + if (new_spin != old_spin) { // Did we really change something?? + changes++; + spin[v] = new_spin; + + //The new spin increase by one, and the old spin decreases by one + csize[new_spin]++; csize[old_spin]--; + + //Change the sums of degree for the old spin... + degree_community_pos_in[old_spin] -= delta_pos_in; + degree_community_neg_in[old_spin] -= delta_neg_in; + degree_community_pos_out[old_spin] -= delta_pos_out; + degree_community_neg_out[old_spin] -= delta_neg_out; + + //...and for the new spin + degree_community_pos_in[new_spin] += delta_pos_in; + degree_community_neg_in[new_spin] += delta_neg_in; + degree_community_pos_out[new_spin] += delta_pos_out; + degree_community_neg_out[new_spin] += delta_neg_out; + } + + //We have no change a node from old_spin to new_spin + /*******************************************/ + + } // for n + } // while sweep +#ifdef SPINGLASS_DEBUG + printf("Done %d sweeps.\n", max_sweeps); + printf("%ld changes made for %d nodes.\n", changes, num_nodes); + printf("Last node is %d and last random number is %f with sum of weights %f with spin %d.\n", v, r, sum_weights, old_spin); +#endif + + return (double(changes) / double(num_nodes) / double(sweep)); +} + +//We need to begin at a suitable temperature. That is, a temperature at which +//enough nodes may change their initially assigned communties +double PottsModelN::FindStartTemp(double gamma, double lambda, double ts) { + double kT; + kT = ts; + //assing random initial condition + assign_initial_conf(true); + // the factor 1-1/q is important, since even, at infinite temperature, + // only 1-1/q of all spins do change their state, since a randomly chooses new + // state is with prob. 1/q the old state. + double acceptance = 0.0; + while (acceptance < (1.0 - 1.0 / double(q)) * 0.95) { //want 95% acceptance + kT = kT * 1.1; + acceptance = HeatBathLookup(gamma, lambda, kT, 50); + } + kT *= 1.1; // just to be sure... + return kT; +} + +long PottsModelN::WriteClusters(igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_t *community_size, + igraph_vector_t *membership, + igraph_matrix_t *adhesion, + igraph_matrix_t *normalised_adhesion, + igraph_real_t *polarization, + double t, + double d_p, + double d_n, + double gamma, + double lambda) { + IGRAPH_UNUSED(gamma); + IGRAPH_UNUSED(lambda); +#ifdef SPINGLASS_DEBUG + printf("Start writing clusters.\n"); +#endif + //Reassign each community so that we retrieve a community assignment 1 through num_communities + unsigned int *cluster_assign = new unsigned int[q + 1]; + for (unsigned int i = 0; i <= q; i++) { + cluster_assign[i] = 0; + } + + int num_clusters = 0; + + //Find out what the new communities will be + for (unsigned int i = 0; i < num_nodes; i++) { + unsigned int s = spin[i]; + if (cluster_assign[s] == 0) { + num_clusters++; + cluster_assign[s] = num_clusters; +#ifdef SPINGLASS_DEBUG + printf("Setting cluster %d to %d.\n", s, num_clusters); +#endif + } + } + + + /* + DLList_Iter iter; + NNode *n_cur=iter.First(net->node_list); + n_cur = iter.First(net->node_list); + */ + + //And now assign each node to its new community + q = num_clusters; + for (unsigned int i = 0; i < num_nodes; i++) { +#ifdef SPINGLASS_DEBUG + printf("Setting node %d to %d.\n", i, cluster_assign[spin[i]]); +#endif + unsigned int s = cluster_assign[spin[i]]; + spin[i] = s; +#ifdef SPINGLASS_DEBUG + printf("Have set node %d to %d.\n", i, s); +#endif + } + assign_initial_conf(false); + + delete[] cluster_assign; + + if (temperature) { + *temperature = t; + } + + if (community_size) { + //Initialize the vector + IGRAPH_CHECK(igraph_vector_resize(community_size, q)); + for (unsigned int spin_opt = 1; spin_opt <= q; spin_opt++) { + //Set the community size + VECTOR(*community_size)[spin_opt - 1] = csize[spin_opt]; + } + } + + //Set the membership + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, num_nodes)); + for (unsigned int i = 0; i < num_nodes; i++) { + VECTOR(*membership)[ i ] = spin[i] - 1; + } + } + + double Q = 0.0; //Modularity + if (adhesion) { + IGRAPH_CHECK(igraph_matrix_resize(adhesion, q, q)); + IGRAPH_CHECK(igraph_matrix_resize(normalised_adhesion, q, q)); + + double **num_links_pos = NULL; + double **num_links_neg = NULL; + //memory allocated for elements of rows. + num_links_pos = new double *[q + 1] ; + num_links_neg = new double *[q + 1] ; + + //memory allocated for elements of each column. + for ( unsigned int i = 0 ; i < q + 1 ; i++) { + num_links_pos[i] = new double[q + 1]; + num_links_neg[i] = new double[q + 1]; + } + + + + //Init num_links + for (unsigned int i = 0; i <= q; i++) { + for (unsigned int j = 0; j <= q; j++) { + num_links_pos[i][j] = 0.0; + num_links_neg[i][j] = 0.0; + } + } + + DLList_Iter iter_l; + NLink *l_cur = iter_l.First(net->link_list); + + double w = 0.0; + + while (!iter_l.End()) { + w = l_cur->Get_Weight(); + unsigned int a = spin[l_cur->Get_Start()->Get_Index()]; + unsigned int b = spin[l_cur->Get_End()->Get_Index()]; + if (w > 0) { + num_links_pos[a][b] += w; + if (!is_directed && a != b) { //Only one edge is defined in case it is undirected + num_links_pos[b][a] += w; + } + } else { + num_links_neg[a][b] -= w; + if (!is_directed && a != b) { //Only one edge is defined in case it is undirected + num_links_neg[b][a] -= w; + } + } + + l_cur = iter_l.Next(); + } //while links + +#ifdef SPINGLASS_DEBUG + printf("d_p: %f\n", d_p); + printf("d_n: %f\n", d_n); +#endif + + double expected = 0.0; + double a = 0.0; + double normal_a = 0.0; + + double delta, u_p, u_n; + double max_expected, max_a; + + //We don't take into account the lambda or gamma for + //computing the modularity and adhesion, since they + //are then incomparable to other definitions. + for (unsigned int i = 1; i <= q; i++) { + for (unsigned int j = 1; j <= q; j++) { + if (!is_directed && i == j) + expected = degree_community_pos_out[i] * degree_community_pos_in[j] / (m_p == 0 ? 1 : 2 * m_p) + - degree_community_neg_out[i] * degree_community_neg_in[j] / (m_n == 0 ? 1 : 2 * m_n); + else + expected = degree_community_pos_out[i] * degree_community_pos_in[j] / (m_p == 0 ? 1 : m_p) + - degree_community_neg_out[i] * degree_community_neg_in[j] / (m_n == 0 ? 1 : m_n); + + a = (num_links_pos[i][j] - num_links_neg[i][j]) - expected; + + if (i == j) { //cohesion + if (is_directed) { + delta = d_p * csize[i] * (csize[i] - 1); //Maximum amount + } else { + delta = d_p * csize[i] * (csize[i] - 1) / 2; //Maximum amount + } + + u_p = delta - num_links_pos[i][i]; //Add as many positive links we can + u_n = -num_links_neg[i][i]; //Delete as many negative links we can + Q += a; + } else { //adhesion + if (is_directed) { + delta = d_n * csize[i] * csize[j] * 2; //Maximum amount + } else { + delta = d_n * csize[i] * csize[j]; //Maximum amount + } + + u_p = -num_links_pos[i][j]; //Delete as many positive links we can + u_n = delta - num_links_neg[i][j]; //Add as many negative links we can + } + + if (!is_directed && i == j) + max_expected = (degree_community_pos_out[i] + u_p) * (degree_community_pos_in[j] + u_p) / ((m_p + u_p) == 0 ? 1 : 2 * (m_p + u_p)) + - (degree_community_neg_out[i] - u_n) * (degree_community_neg_in[j] + u_n) / ((m_n + u_n) == 0 ? 1 : 2 * (m_n + u_n)); + else + max_expected = (degree_community_pos_out[i] + u_p) * (degree_community_pos_in[j] + u_p) / ((m_p + u_p) == 0 ? 1 : m_p + u_p) + - (degree_community_neg_out[i] - u_n) * (degree_community_neg_in[j] + u_n) / ((m_n + u_n) == 0 ? 1 : m_n + u_n); + //printf("%f/%f %d/%d\t", num_links_pos[i][j], num_links_neg[i][j], csize[i], csize[j]); + //printf("%f/%f - %f(%f)\t", u_p, u_n, expected, max_expected); + max_a = ((num_links_pos[i][j] + u_p) - (num_links_neg[i][j] + u_n)) - max_expected; + + + //In cases where we haven't actually found a ground state + //the adhesion/cohesion *might* not be negative/positive, + //hence the maximum adhesion and cohesion might behave quite + //strangely. In order to prevent that, we limit them to 1 in + //absolute value, and prevent from dividing by zero (even if + //chuck norris would). + if (i == j) { + normal_a = a / (max_a == 0 ? a : max_a); + } else { + normal_a = -a / (max_a == 0 ? a : max_a); + } + + if (normal_a > 1) { + normal_a = 1; + } else if (normal_a < -1) { + normal_a = -1; + } + + MATRIX(*adhesion, i - 1, j - 1) = a; + MATRIX(*normalised_adhesion, i - 1, j - 1) = normal_a; + } //for j + //printf("\n"); + } //for i + + //free the allocated memory + for ( unsigned int i = 0 ; i < q + 1 ; i++ ) { + delete [] num_links_pos[i] ; + delete [] num_links_neg[i]; + } + delete [] num_links_pos ; + delete [] num_links_neg ; + + } //adhesion + + if (modularity) { + if (is_directed) { + *modularity = Q / (m_p + m_n); + } else { + *modularity = 2 * Q / (m_p + m_n); //Correction for the way m_p and m_n are counted. Modularity is 1/m, not 1/2m + } + } + + if (polarization) { + double sum_ad = 0.0; + for (unsigned int i = 0; i < q; i++) { + for (unsigned int j = 0; j < q; j++) { + if (i != j) { + sum_ad -= MATRIX(*normalised_adhesion, i, j); + } + } + } + *polarization = sum_ad / (q * q - q); + } +#ifdef SPINGLASS_DEBUG + printf("Finished writing cluster.\n"); +#endif + return num_nodes; +} diff --git a/src/rigraph/core/community/spinglass/pottsmodel_2.h b/src/rigraph/core/community/spinglass/pottsmodel_2.h new file mode 100644 index 0000000..7d0ee28 --- /dev/null +++ b/src/rigraph/core/community/spinglass/pottsmodel_2.h @@ -0,0 +1,179 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Jörg Reichardt + This file was modified by Vincent Traag + The original copyright notice follows here */ + +/*************************************************************************** + pottsmodel.h - description + ------------------- + begin : Fri May 28 2004 + copyright : (C) 2004 by + email : + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef POTTSMODEL_H +#define POTTSMODEL_H + +#include "NetDataTypes.h" + +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_matrix.h" + +// Simple matrix class with heap allocation, allowing mat[i][j] indexing. +class SimpleMatrix { + double *data; + size_t n; + +public: + explicit SimpleMatrix(size_t n_) : n(n_) { data = new double[n*n]; } + ~SimpleMatrix() { delete [] data; } + + // Return a pointer to the i'th column, which can be indexed into using a second [] operator. + // We assume column-major storage. + double *operator [] (size_t i) { return &(data[n*i]); } +}; + +class PottsModel { +private: + // HugeArray neg_gammalookup; + // HugeArray pos_gammalookup; + DL_Indexed_List *new_spins; + DL_Indexed_List *previous_spins; + HugeArray*> correlation; + network *net; + unsigned int q; + unsigned int operation_mode; + // FILE *Qfile, *Magfile; + SimpleMatrix Qmatrix; + double* Qa; + double* weights; + double total_degree_sum; + unsigned long num_of_nodes; + unsigned long num_of_links; + unsigned long k_max; + double energy; + double acceptance; + double *neighbours; +public: + PottsModel(network *net, unsigned int q, int norm_by_degree); + ~PottsModel(); + double* color_field; + unsigned long assign_initial_conf(int spin); + unsigned long initialize_lookup(double kT, double gamma); + double initialize_Qmatrix(); + double calculate_Q(); + double calculate_genQ(double gamma); + double FindStartTemp(double gamma, double prob, double ts); + long HeatBathParallelLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps); + double HeatBathLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps); + long HeatBathParallelLookup(double gamma, double prob, double kT, unsigned int max_sweeps); + double HeatBathLookup(double gamma, double prob, double kT, unsigned int max_sweeps); + double GammaSweep(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel = true, int repetitions = 1); + double GammaSweepZeroTemp(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel = true, int repetitions = 1); + // long WriteCorrelationMatrix(char *filename); + double calculate_energy(double gamma); + long WriteClusters(igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_t *csize, igraph_vector_t *membership, + double kT, double gamma); + // long WriteSoftClusters(char *filename, double threshold); + double Get_Energy() const { + return energy; + } + double FindCommunityFromStart(double gamma, double prob, char *nodename, + igraph_vector_t *result, + igraph_real_t *cohesion, + igraph_real_t *adhesion, + igraph_integer_t *inner_links, + igraph_integer_t *outer_links); +}; + + +class PottsModelN { +private: + // HugeArray neg_gammalookup; + // HugeArray pos_gammalookup; + // DL_Indexed_List *new_spins; + // DL_Indexed_List *previous_spins; + HugeArray*> correlation; + network *net; + + unsigned int q; //number of communities + double m_p; //number of positive ties (or sum of degrees), this equals the number of edges only if it is undirected and each edge has a weight of 1 + double m_n; //number of negative ties (or sum of degrees) + unsigned int num_nodes; //number of nodes + bool is_directed; + + bool is_init; + + double *degree_pos_in; //Postive indegree of the nodes (or sum of weights) + double *degree_neg_in; //Negative indegree of the nodes (or sum of weights) + double *degree_pos_out; //Postive outdegree of the nodes (or sum of weights) + double *degree_neg_out; //Negative outdegree of the nodes (or sum of weights) + + double *degree_community_pos_in; //Positive sum of indegree for communities + double *degree_community_neg_in; //Negative sum of indegree for communities + double *degree_community_pos_out; //Positive sum of outegree for communities + double *degree_community_neg_out; //Negative sum of outdegree for communities + + unsigned int *csize; //The number of nodes in each community + unsigned int *spin; //The membership of each node + + double *neighbours; //Array of neighbours of a vertex in each community + double *weights; //Weights of all possible transitions to another community + +public: + PottsModelN(network *n, unsigned int num_communities, bool directed); + ~PottsModelN(); + void assign_initial_conf(bool init_spins); + double FindStartTemp(double gamma, double lambda, double ts); + double HeatBathLookup(double gamma, double lambda, double t, unsigned int max_sweeps); + // double HeatBathJoin(double gamma, double lambda); + // double HeatBathLookupZeroTemp(double gamma, double lambda, unsigned int max_sweeps); + long WriteClusters(igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_t *community_size, + igraph_vector_t *membership, + igraph_matrix_t *adhesion, + igraph_matrix_t *normalised_adhesion, + igraph_real_t *polarization, + double t, + double d_p, + double d_n, + double gamma, + double lambda); +}; + +#endif diff --git a/src/rigraph/core/community/walktrap/walktrap.cpp b/src/rigraph/core/community/walktrap/walktrap.cpp new file mode 100644 index 0000000..a046266 --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap.cpp @@ -0,0 +1,177 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Pascal Pons + The original copyright notice follows here. The FSF address was + fixed by Tamas Nepusz */ + +// File: walktrap.cpp +//----------------------------------------------------------------------------- +// Walktrap v0.2 -- Finds community structure of networks using random walks +// Copyright (C) 2004-2005 Pascal Pons +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301 USA +//----------------------------------------------------------------------------- +// Author : Pascal Pons +// Email : pascal.pons@gmail.com +// Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html +// Location : Paris, France +// Time : June 2005 +//----------------------------------------------------------------------------- +// see readme.txt for more details + +#include "walktrap_graph.h" +#include "walktrap_communities.h" + +#include "igraph_community.h" +#include "igraph_components.h" +#include "igraph_interface.h" + +#include "../../core/exceptions.h" +#include "../../core/interruption.h" + +using namespace igraph::walktrap; + +/** + * \function igraph_community_walktrap + * + * This function is the implementation of the Walktrap community + * finding algorithm, see Pascal Pons, Matthieu Latapy: Computing + * communities in large networks using random walks, + * https://arxiv.org/abs/physics/0512106 + * + * + * Currently the original C++ implementation is used in igraph, + * see https://www-complexnetworks.lip6.fr/~latapy/PP/walktrap.html + * We are grateful to Matthieu Latapy and Pascal Pons for providing this + * source code. + * + * + * In contrast to the original implementation, isolated vertices are allowed + * in the graph and they are assumed to have a single incident loop edge with + * weight 1. + * + * \param graph The input graph, edge directions are ignored. + * \param weights Numeric vector giving the weights of the edges. + * If it is a NULL pointer then all edges will have equal + * weights. The weights are expected to be positive. + * \param steps Integer constant, the length of the random walks. + * Typically, good results are obtained with values between + * 3-8 with 4-5 being a reasonable default. + * \param merges Pointer to a matrix, the merges performed by the + * algorithm will be stored here (if not NULL). Each merge is a + * row in a two-column matrix and contains the ids of the merged + * clusters. Clusters are numbered from zero and cluster numbers + * smaller than the number of nodes in the network belong to the + * individual vertices as singleton clusters. In each step a new + * cluster is created from two other clusters and its id will be + * one larger than the largest cluster id so far. This means that + * before the first merge we have \c n clusters (the number of + * vertices in the graph) numbered from zero to \c n-1. The first + * merge creates cluster \c n, the second cluster \c n+1, etc. + * \param modularity Pointer to a vector. If not NULL then the + * modularity score of the current clustering is stored here after + * each merge operation. + * \param membership Pointer to a vector. If not a NULL pointer, then + * the membership vector corresponding to the maximal modularity + * score is stored here. If it is not a NULL pointer, then neither + * \p modularity nor \p merges may be NULL. + * \return Error code. + * + * \sa \ref igraph_community_spinglass(), \ref + * igraph_community_edge_betweenness(). + * + * Time complexity: O(|E||V|^2) in the worst case, O(|V|^2 log|V|) typically, + * |V| is the number of vertices, |E| is the number of edges. + * + * \example examples/simple/walktrap.c + */ + +int igraph_community_walktrap(const igraph_t *graph, + const igraph_vector_t *weights, + int steps, + igraph_matrix_t *merges, + igraph_vector_t *modularity, + igraph_vector_t *membership) { + + IGRAPH_HANDLE_EXCEPTIONS( + long int no_of_nodes = (long int)igraph_vcount(graph); + int length = steps; + long max_memory = -1; + + if (steps <= 0) { + IGRAPH_ERROR("Length of random walks must be positive for walktrap community detection.", IGRAPH_EINVAL); + } + + if (membership && !(modularity && merges)) { + IGRAPH_ERROR("Cannot calculate membership without modularity or merges", + IGRAPH_EINVAL); + } + + Graph G; + if (G.convert_from_igraph(graph, weights)) { + IGRAPH_ERROR("Cannot convert igraph graph into walktrap format", IGRAPH_EINVAL); + } + + if (merges) { + igraph_integer_t no; + IGRAPH_CHECK(igraph_clusters(graph, /*membership=*/ 0, /*csize=*/ 0, + &no, IGRAPH_WEAK)); + IGRAPH_CHECK(igraph_matrix_resize(merges, no_of_nodes - no, 2)); + } + if (modularity) { + IGRAPH_CHECK(igraph_vector_resize(modularity, no_of_nodes)); + igraph_vector_null(modularity); + } + Communities C(&G, length, max_memory, merges, modularity); + + while (!C.H->is_empty()) { + IGRAPH_ALLOW_INTERRUPTION(); + C.merge_nearest_communities(); + } + + if (membership) { + long int m; + m = no_of_nodes > 0 ? igraph_vector_which_max(modularity) : 0; + IGRAPH_CHECK(igraph_community_to_membership(merges, no_of_nodes, + /*steps=*/ m, + membership, + /*csize=*/ NULL)); + } + + return IGRAPH_SUCCESS; + ) +} diff --git a/src/rigraph/core/community/walktrap/walktrap_communities.cpp b/src/rigraph/core/community/walktrap/walktrap_communities.cpp new file mode 100644 index 0000000..1f108f1 --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap_communities.cpp @@ -0,0 +1,946 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Pascal Pons + The original copyright notice follows here. The FSF address was + fixed by Tamas Nepusz */ + +// File: communities.cpp +//----------------------------------------------------------------------------- +// Walktrap v0.2 -- Finds community structure of networks using random walks +// Copyright (C) 2004-2005 Pascal Pons +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301 USA +//----------------------------------------------------------------------------- +// Author : Pascal Pons +// Email : pascal.pons@gmail.com +// Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html +// Location : Paris, France +// Time : June 2005 +//----------------------------------------------------------------------------- +// see readme.txt for more details + +#include "walktrap_communities.h" +#include "config.h" +#include +#include + +using namespace std; + +namespace igraph { + +namespace walktrap { + +int Probabilities::length = 0; +Communities* Probabilities::C = 0; +float* Probabilities::tmp_vector1 = 0; +float* Probabilities::tmp_vector2 = 0; +int* Probabilities::id = 0; +int* Probabilities::vertices1 = 0; +int* Probabilities::vertices2 = 0; +int Probabilities::current_id = 0; + + +Neighbor::Neighbor() { + next_community1 = 0; + previous_community1 = 0; + next_community2 = 0; + previous_community2 = 0; + heap_index = -1; +} + +Probabilities::~Probabilities() { + C->memory_used -= memory(); + if (P) { + delete[] P; + } + if (vertices) { + delete[] vertices; + } +} + +Probabilities::Probabilities(int community) { + Graph* G = C->G; + int nb_vertices1 = 0; + int nb_vertices2 = 0; + + float initial_proba = 1. / float(C->communities[community].size); + int last = C->members[C->communities[community].last_member]; + for (int m = C->communities[community].first_member; m != last; m = C->members[m]) { + tmp_vector1[m] = initial_proba; + vertices1[nb_vertices1++] = m; + } + + for (int t = 0; t < length; t++) { + current_id++; + if (nb_vertices1 > (G->nb_vertices / 2)) { + nb_vertices2 = G->nb_vertices; + for (int i = 0; i < G->nb_vertices; i++) { + tmp_vector2[i] = 0.; + } + if (nb_vertices1 == G->nb_vertices) { + for (int i = 0; i < G->nb_vertices; i++) { + float proba = tmp_vector1[i] / G->vertices[i].total_weight; + for (int j = 0; j < G->vertices[i].degree; j++) { + tmp_vector2[G->vertices[i].edges[j].neighbor] += proba * G->vertices[i].edges[j].weight; + } + } + } else { + for (int i = 0; i < nb_vertices1; i++) { + int v1 = vertices1[i]; + float proba = tmp_vector1[v1] / G->vertices[v1].total_weight; + for (int j = 0; j < G->vertices[v1].degree; j++) { + tmp_vector2[G->vertices[v1].edges[j].neighbor] += proba * G->vertices[v1].edges[j].weight; + } + } + } + } else { + nb_vertices2 = 0; + for (int i = 0; i < nb_vertices1; i++) { + int v1 = vertices1[i]; + float proba = tmp_vector1[v1] / G->vertices[v1].total_weight; + for (int j = 0; j < G->vertices[v1].degree; j++) { + int v2 = G->vertices[v1].edges[j].neighbor; + if (id[v2] == current_id) { + tmp_vector2[v2] += proba * G->vertices[v1].edges[j].weight; + } else { + tmp_vector2[v2] = proba * G->vertices[v1].edges[j].weight; + id[v2] = current_id; + vertices2[nb_vertices2++] = v2; + } + } + } + } + float* tmp = tmp_vector2; + tmp_vector2 = tmp_vector1; + tmp_vector1 = tmp; + + int* tmp2 = vertices2; + vertices2 = vertices1; + vertices1 = tmp2; + + nb_vertices1 = nb_vertices2; + } + + if (nb_vertices1 > (G->nb_vertices / 2)) { + P = new float[G->nb_vertices]; + size = G->nb_vertices; + vertices = 0; + if (nb_vertices1 == G->nb_vertices) { + for (int i = 0; i < G->nb_vertices; i++) { + P[i] = tmp_vector1[i] / sqrt(G->vertices[i].total_weight); + } + } else { + for (int i = 0; i < G->nb_vertices; i++) { + P[i] = 0.; + } + for (int i = 0; i < nb_vertices1; i++) { + P[vertices1[i]] = tmp_vector1[vertices1[i]] / sqrt(G->vertices[vertices1[i]].total_weight); + } + } + } else { + P = new float[nb_vertices1]; + size = nb_vertices1; + vertices = new int[nb_vertices1]; + int j = 0; + for (int i = 0; i < G->nb_vertices; i++) { + if (id[i] == current_id) { + P[j] = tmp_vector1[i] / sqrt(G->vertices[i].total_weight); + vertices[j] = i; + j++; + } + } + } + C->memory_used += memory(); +} + +Probabilities::Probabilities(int community1, int community2) { + // The two following probability vectors must exist. + // Do not call this function if it is not the case. + Probabilities* P1 = C->communities[community1].P; + Probabilities* P2 = C->communities[community2].P; + + float w1 = float(C->communities[community1].size) / float(C->communities[community1].size + C->communities[community2].size); + float w2 = float(C->communities[community2].size) / float(C->communities[community1].size + C->communities[community2].size); + + + if (P1->size == C->G->nb_vertices) { + P = new float[C->G->nb_vertices]; + size = C->G->nb_vertices; + vertices = 0; + + if (P2->size == C->G->nb_vertices) { // two full vectors + for (int i = 0; i < C->G->nb_vertices; i++) { + P[i] = P1->P[i] * w1 + P2->P[i] * w2; + } + } else { // P1 full vector, P2 partial vector + int j = 0; + for (int i = 0; i < P2->size; i++) { + for (; j < P2->vertices[i]; j++) { + P[j] = P1->P[j] * w1; + } + P[j] = P1->P[j] * w1 + P2->P[i] * w2; + j++; + } + for (; j < C->G->nb_vertices; j++) { + P[j] = P1->P[j] * w1; + } + } + } else { + if (P2->size == C->G->nb_vertices) { // P1 partial vector, P2 full vector + P = new float[C->G->nb_vertices]; + size = C->G->nb_vertices; + vertices = 0; + + int j = 0; + for (int i = 0; i < P1->size; i++) { + for (; j < P1->vertices[i]; j++) { + P[j] = P2->P[j] * w2; + } + P[j] = P1->P[i] * w1 + P2->P[j] * w2; + j++; + } + for (; j < C->G->nb_vertices; j++) { + P[j] = P2->P[j] * w2; + } + } else { // two partial vectors + int i = 0; + int j = 0; + int nb_vertices1 = 0; + while ((i < P1->size) && (j < P2->size)) { + if (P1->vertices[i] < P2->vertices[j]) { + tmp_vector1[P1->vertices[i]] = P1->P[i] * w1; + vertices1[nb_vertices1++] = P1->vertices[i]; + i++; + continue; + } + if (P1->vertices[i] > P2->vertices[j]) { + tmp_vector1[P2->vertices[j]] = P2->P[j] * w2; + vertices1[nb_vertices1++] = P2->vertices[j]; + j++; + continue; + } + tmp_vector1[P1->vertices[i]] = P1->P[i] * w1 + P2->P[j] * w2; + vertices1[nb_vertices1++] = P1->vertices[i]; + i++; + j++; + } + if (i == P1->size) { + for (; j < P2->size; j++) { + tmp_vector1[P2->vertices[j]] = P2->P[j] * w2; + vertices1[nb_vertices1++] = P2->vertices[j]; + } + } else { + for (; i < P1->size; i++) { + tmp_vector1[P1->vertices[i]] = P1->P[i] * w1; + vertices1[nb_vertices1++] = P1->vertices[i]; + } + } + + if (nb_vertices1 > (C->G->nb_vertices / 2)) { + P = new float[C->G->nb_vertices]; + size = C->G->nb_vertices; + vertices = 0; + for (int i = 0; i < C->G->nb_vertices; i++) { + P[i] = 0.; + } + for (int i = 0; i < nb_vertices1; i++) { + P[vertices1[i]] = tmp_vector1[vertices1[i]]; + } + } else { + P = new float[nb_vertices1]; + size = nb_vertices1; + vertices = new int[nb_vertices1]; + for (int i = 0; i < nb_vertices1; i++) { + vertices[i] = vertices1[i]; + P[i] = tmp_vector1[vertices1[i]]; + } + } + } + } + + C->memory_used += memory(); +} + +double Probabilities::compute_distance(const Probabilities* P2) const { + double r = 0.; + if (vertices) { + if (P2->vertices) { // two partial vectors + int i = 0; + int j = 0; + while ((i < size) && (j < P2->size)) { + if (vertices[i] < P2->vertices[j]) { + r += P[i] * P[i]; + i++; + continue; + } + if (vertices[i] > P2->vertices[j]) { + r += P2->P[j] * P2->P[j]; + j++; + continue; + } + r += (P[i] - P2->P[j]) * (P[i] - P2->P[j]); + i++; + j++; + } + if (i == size) { + for (; j < P2->size; j++) { + r += P2->P[j] * P2->P[j]; + } + } else { + for (; i < size; i++) { + r += P[i] * P[i]; + } + } + } else { // P1 partial vector, P2 full vector + + int i = 0; + for (int j = 0; j < size; j++) { + for (; i < vertices[j]; i++) { + r += P2->P[i] * P2->P[i]; + } + r += (P[j] - P2->P[i]) * (P[j] - P2->P[i]); + i++; + } + for (; i < P2->size; i++) { + r += P2->P[i] * P2->P[i]; + } + } + } else { + if (P2->vertices) { // P1 full vector, P2 partial vector + int i = 0; + for (int j = 0; j < P2->size; j++) { + for (; i < P2->vertices[j]; i++) { + r += P[i] * P[i]; + } + r += (P[i] - P2->P[j]) * (P[i] - P2->P[j]); + i++; + } + for (; i < size; i++) { + r += P[i] * P[i]; + } + } else { // two full vectors + for (int i = 0; i < size; i++) { + r += (P[i] - P2->P[i]) * (P[i] - P2->P[i]); + } + } + } + return r; +} + +long Probabilities::memory() { + if (vertices) { + return (sizeof(Probabilities) + long(size) * (sizeof(float) + sizeof(int))); + } else { + return (sizeof(Probabilities) + long(size) * sizeof(float)); + } +} + +Community::Community() { + P = 0; + first_neighbor = 0; + last_neighbor = 0; + sub_community_of = -1; + sub_communities[0] = -1; + sub_communities[1] = -1; + sigma = 0.; + internal_weight = 0.; + total_weight = 0.; +} + +Community::~Community() { + if (P) { + delete P; + } +} + + +Communities::Communities(Graph* graph, int random_walks_length, + long m, igraph_matrix_t *pmerges, + igraph_vector_t *pmodularity) { + max_memory = m; + memory_used = 0; + G = graph; + merges = pmerges; + mergeidx = 0; + modularity = pmodularity; + + Probabilities::C = this; + Probabilities::length = random_walks_length; + Probabilities::tmp_vector1 = new float[G->nb_vertices]; + Probabilities::tmp_vector2 = new float[G->nb_vertices]; + Probabilities::id = new int[G->nb_vertices]; + for (int i = 0; i < G->nb_vertices; i++) { + Probabilities::id[i] = 0; + } + Probabilities::vertices1 = new int[G->nb_vertices]; + Probabilities::vertices2 = new int[G->nb_vertices]; + Probabilities::current_id = 0; + + + members = new int[G->nb_vertices]; + for (int i = 0; i < G->nb_vertices; i++) { + members[i] = -1; + } + + H = new Neighbor_heap(G->nb_edges); + communities = new Community[2 * G->nb_vertices]; + +// init the n single vertex communities + + if (max_memory != -1) { + min_delta_sigma = new Min_delta_sigma_heap(G->nb_vertices * 2); + } else { + min_delta_sigma = 0; + } + + for (int i = 0; i < G->nb_vertices; i++) { + communities[i].this_community = i; + communities[i].first_member = i; + communities[i].last_member = i; + communities[i].size = 1; + communities[i].sub_community_of = 0; + } + + nb_communities = G->nb_vertices; + nb_active_communities = G->nb_vertices; + + for (int i = 0; i < G->nb_vertices; i++) + for (int j = 0; j < G->vertices[i].degree; j++) + if (i < G->vertices[i].edges[j].neighbor) { + communities[i].total_weight += G->vertices[i].edges[j].weight / 2.; + communities[G->vertices[i].edges[j].neighbor].total_weight += G->vertices[i].edges[j].weight / 2.; + Neighbor* N = new Neighbor; + N->community1 = i; + N->community2 = G->vertices[i].edges[j].neighbor; + N->delta_sigma = -1. / double(min(G->vertices[i].degree, G->vertices[G->vertices[i].edges[j].neighbor].degree)); + N->weight = G->vertices[i].edges[j].weight; + N->exact = false; + add_neighbor(N); + } + + if (max_memory != -1) { + memory_used += min_delta_sigma->memory(); + memory_used += 2 * long(G->nb_vertices) * sizeof(Community); + memory_used += long(G->nb_vertices) * (2 * sizeof(float) + 3 * sizeof(int)); // the static data of Probabilities class + memory_used += H->memory() + long(G->nb_edges) * sizeof(Neighbor); + memory_used += G->memory(); + } + + /* int c = 0; */ + Neighbor* N = H->get_first(); + if (N == 0) { + return; /* this can happen if there are no edges */ + } + while (!N->exact) { + update_neighbor(N, compute_delta_sigma(N->community1, N->community2)); + N->exact = true; + N = H->get_first(); + if (max_memory != -1) { + manage_memory(); + } + /* TODO: this could use igraph_progress */ + /* if(!silent) { */ + /* c++; */ + /* for(int k = (500*(c-1))/G->nb_edges + 1; k <= (500*c)/G->nb_edges; k++) { */ + /* if(k % 50 == 1) {cerr.width(2); cerr << endl << k/ 5 << "% ";} */ + /* cerr << "."; */ + /* } */ + /* } */ + } + + double Q = 0.; + for (int i = 0; i < nb_communities; i++) { + if (communities[i].sub_community_of == 0) { + Q += (communities[i].internal_weight - communities[i].total_weight * communities[i].total_weight / G->total_weight) / G->total_weight; + } + } + + if (modularity) { + VECTOR(*modularity)[mergeidx] = Q; + } +} + +Communities::~Communities() { + delete[] members; + delete[] communities; + delete H; + if (min_delta_sigma) { + delete min_delta_sigma; + } + + delete[] Probabilities::tmp_vector1; + delete[] Probabilities::tmp_vector2; + delete[] Probabilities::id; + delete[] Probabilities::vertices1; + delete[] Probabilities::vertices2; +} + +float Community::min_delta_sigma() { + float r = 1.; + for (Neighbor* N = first_neighbor; N != 0;) { + if (N->delta_sigma < r) { + r = N->delta_sigma; + } + if (N->community1 == this_community) { + N = N->next_community1; + } else { + N = N->next_community2; + } + } + return r; +} + + +void Community::add_neighbor(Neighbor* N) { // add a new neighbor at the end of the list + if (last_neighbor) { + if (last_neighbor->community1 == this_community) { + last_neighbor->next_community1 = N; + } else { + last_neighbor->next_community2 = N; + } + + if (N->community1 == this_community) { + N->previous_community1 = last_neighbor; + } else { + N->previous_community2 = last_neighbor; + } + } else { + first_neighbor = N; + if (N->community1 == this_community) { + N->previous_community1 = 0; + } else { + N->previous_community2 = 0; + } + } + last_neighbor = N; +} + +void Community::remove_neighbor(Neighbor* N) { // remove a neighbor from the list + if (N->community1 == this_community) { + if (N->next_community1) { +// if (N->next_community1->community1 == this_community) + N->next_community1->previous_community1 = N->previous_community1; +// else +// N->next_community1->previous_community2 = N->previous_community1; + } else { + last_neighbor = N->previous_community1; + } + if (N->previous_community1) { + if (N->previous_community1->community1 == this_community) { + N->previous_community1->next_community1 = N->next_community1; + } else { + N->previous_community1->next_community2 = N->next_community1; + } + } else { + first_neighbor = N->next_community1; + } + } else { + if (N->next_community2) { + if (N->next_community2->community1 == this_community) { + N->next_community2->previous_community1 = N->previous_community2; + } else { + N->next_community2->previous_community2 = N->previous_community2; + } + } else { + last_neighbor = N->previous_community2; + } + if (N->previous_community2) { +// if (N->previous_community2->community1 == this_community) +// N->previous_community2->next_community1 = N->next_community2; +// else + N->previous_community2->next_community2 = N->next_community2; + } else { + first_neighbor = N->next_community2; + } + } +} + +void Communities::remove_neighbor(Neighbor* N) { + communities[N->community1].remove_neighbor(N); + communities[N->community2].remove_neighbor(N); + H->remove(N); + + if (max_memory != -1) { + if (N->delta_sigma == min_delta_sigma->delta_sigma[N->community1]) { + min_delta_sigma->delta_sigma[N->community1] = communities[N->community1].min_delta_sigma(); + if (communities[N->community1].P) { + min_delta_sigma->update(N->community1); + } + } + + if (N->delta_sigma == min_delta_sigma->delta_sigma[N->community2]) { + min_delta_sigma->delta_sigma[N->community2] = communities[N->community2].min_delta_sigma(); + if (communities[N->community2].P) { + min_delta_sigma->update(N->community2); + } + } + } +} + +void Communities::add_neighbor(Neighbor* N) { + communities[N->community1].add_neighbor(N); + communities[N->community2].add_neighbor(N); + H->add(N); + + if (max_memory != -1) { + if (N->delta_sigma < min_delta_sigma->delta_sigma[N->community1]) { + min_delta_sigma->delta_sigma[N->community1] = N->delta_sigma; + if (communities[N->community1].P) { + min_delta_sigma->update(N->community1); + } + } + + if (N->delta_sigma < min_delta_sigma->delta_sigma[N->community2]) { + min_delta_sigma->delta_sigma[N->community2] = N->delta_sigma; + if (communities[N->community2].P) { + min_delta_sigma->update(N->community2); + } + } + } +} + +void Communities::update_neighbor(Neighbor* N, float new_delta_sigma) { + if (max_memory != -1) { + if (new_delta_sigma < min_delta_sigma->delta_sigma[N->community1]) { + min_delta_sigma->delta_sigma[N->community1] = new_delta_sigma; + if (communities[N->community1].P) { + min_delta_sigma->update(N->community1); + } + } + + if (new_delta_sigma < min_delta_sigma->delta_sigma[N->community2]) { + min_delta_sigma->delta_sigma[N->community2] = new_delta_sigma; + if (communities[N->community2].P) { + min_delta_sigma->update(N->community2); + } + } + + float old_delta_sigma = N->delta_sigma; + N->delta_sigma = new_delta_sigma; + H->update(N); + + if (old_delta_sigma == min_delta_sigma->delta_sigma[N->community1]) { + min_delta_sigma->delta_sigma[N->community1] = communities[N->community1].min_delta_sigma(); + if (communities[N->community1].P) { + min_delta_sigma->update(N->community1); + } + } + + if (old_delta_sigma == min_delta_sigma->delta_sigma[N->community2]) { + min_delta_sigma->delta_sigma[N->community2] = communities[N->community2].min_delta_sigma(); + if (communities[N->community2].P) { + min_delta_sigma->update(N->community2); + } + } + } else { + N->delta_sigma = new_delta_sigma; + H->update(N); + } +} + +void Communities::manage_memory() { + while ((memory_used > max_memory) && !min_delta_sigma->is_empty()) { + int c = min_delta_sigma->get_max_community(); + delete communities[c].P; + communities[c].P = 0; + min_delta_sigma->remove_community(c); + } +} + + + +void Communities::merge_communities(Neighbor* merge_N) { + int c1 = merge_N->community1; + int c2 = merge_N->community2; + + communities[nb_communities].first_member = communities[c1].first_member; // merge the + communities[nb_communities].last_member = communities[c2].last_member; // two lists + members[communities[c1].last_member] = communities[c2].first_member; // of members + + communities[nb_communities].size = communities[c1].size + communities[c2].size; + communities[nb_communities].this_community = nb_communities; + communities[nb_communities].sub_community_of = 0; + communities[nb_communities].sub_communities[0] = c1; + communities[nb_communities].sub_communities[1] = c2; + communities[nb_communities].total_weight = communities[c1].total_weight + communities[c2].total_weight; + communities[nb_communities].internal_weight = communities[c1].internal_weight + communities[c2].internal_weight + merge_N->weight; + communities[nb_communities].sigma = communities[c1].sigma + communities[c2].sigma + merge_N->delta_sigma; + + communities[c1].sub_community_of = nb_communities; + communities[c2].sub_community_of = nb_communities; + +// update the new probability vector... + + if (communities[c1].P && communities[c2].P) { + communities[nb_communities].P = new Probabilities(c1, c2); + } + + if (communities[c1].P) { + delete communities[c1].P; + communities[c1].P = 0; + if (max_memory != -1) { + min_delta_sigma->remove_community(c1); + } + } + if (communities[c2].P) { + delete communities[c2].P; + communities[c2].P = 0; + if (max_memory != -1) { + min_delta_sigma->remove_community(c2); + } + } + + if (max_memory != -1) { + min_delta_sigma->delta_sigma[c1] = -1.; // to avoid to update the min_delta_sigma for these communities + min_delta_sigma->delta_sigma[c2] = -1.; // + min_delta_sigma->delta_sigma[nb_communities] = -1.; + } + +// update the new neighbors +// by enumerating all the neighbors of c1 and c2 + + Neighbor* N1 = communities[c1].first_neighbor; + Neighbor* N2 = communities[c2].first_neighbor; + + while (N1 && N2) { + int neighbor_community1; + int neighbor_community2; + + if (N1->community1 == c1) { + neighbor_community1 = N1->community2; + } else { + neighbor_community1 = N1->community1; + } + if (N2->community1 == c2) { + neighbor_community2 = N2->community2; + } else { + neighbor_community2 = N2->community1; + } + + if (neighbor_community1 < neighbor_community2) { + Neighbor* tmp = N1; + if (N1->community1 == c1) { + N1 = N1->next_community1; + } else { + N1 = N1->next_community2; + } + remove_neighbor(tmp); + Neighbor* N = new Neighbor; + N->weight = tmp->weight; + N->community1 = neighbor_community1; + N->community2 = nb_communities; + N->delta_sigma = (double(communities[c1].size + communities[neighbor_community1].size) * tmp->delta_sigma + double(communities[c2].size) * merge_N->delta_sigma) / (double(communities[c1].size + communities[c2].size + communities[neighbor_community1].size)); //compute_delta_sigma(neighbor_community1, nb_communities); + N->exact = false; + delete tmp; + add_neighbor(N); + } + + if (neighbor_community2 < neighbor_community1) { + Neighbor* tmp = N2; + if (N2->community1 == c2) { + N2 = N2->next_community1; + } else { + N2 = N2->next_community2; + } + remove_neighbor(tmp); + Neighbor* N = new Neighbor; + N->weight = tmp->weight; + N->community1 = neighbor_community2; + N->community2 = nb_communities; + N->delta_sigma = (double(communities[c1].size) * merge_N->delta_sigma + double(communities[c2].size + communities[neighbor_community2].size) * tmp->delta_sigma) / (double(communities[c1].size + communities[c2].size + communities[neighbor_community2].size)); //compute_delta_sigma(neighbor_community2, nb_communities); + N->exact = false; + delete tmp; + add_neighbor(N); + } + + if (neighbor_community1 == neighbor_community2) { + Neighbor* tmp1 = N1; + Neighbor* tmp2 = N2; + bool exact = N1->exact && N2->exact; + if (N1->community1 == c1) { + N1 = N1->next_community1; + } else { + N1 = N1->next_community2; + } + if (N2->community1 == c2) { + N2 = N2->next_community1; + } else { + N2 = N2->next_community2; + } + remove_neighbor(tmp1); + remove_neighbor(tmp2); + Neighbor* N = new Neighbor; + N->weight = tmp1->weight + tmp2->weight; + N->community1 = neighbor_community1; + N->community2 = nb_communities; + N->delta_sigma = (double(communities[c1].size + communities[neighbor_community1].size) * tmp1->delta_sigma + double(communities[c2].size + communities[neighbor_community1].size) * tmp2->delta_sigma - double(communities[neighbor_community1].size) * merge_N->delta_sigma) / (double(communities[c1].size + communities[c2].size + communities[neighbor_community1].size)); + N->exact = exact; + delete tmp1; + delete tmp2; + add_neighbor(N); + } + } + + + if (!N1) { + while (N2) { +// double delta_sigma2 = N2->delta_sigma; + int neighbor_community; + if (N2->community1 == c2) { + neighbor_community = N2->community2; + } else { + neighbor_community = N2->community1; + } + Neighbor* tmp = N2; + if (N2->community1 == c2) { + N2 = N2->next_community1; + } else { + N2 = N2->next_community2; + } + remove_neighbor(tmp); + Neighbor* N = new Neighbor; + N->weight = tmp->weight; + N->community1 = neighbor_community; + N->community2 = nb_communities; + N->delta_sigma = (double(communities[c1].size) * merge_N->delta_sigma + double(communities[c2].size + communities[neighbor_community].size) * tmp->delta_sigma) / (double(communities[c1].size + communities[c2].size + communities[neighbor_community].size)); //compute_delta_sigma(neighbor_community, nb_communities); + N->exact = false; + delete tmp; + add_neighbor(N); + } + } + if (!N2) { + while (N1) { +// double delta_sigma1 = N1->delta_sigma; + int neighbor_community; + if (N1->community1 == c1) { + neighbor_community = N1->community2; + } else { + neighbor_community = N1->community1; + } + Neighbor* tmp = N1; + if (N1->community1 == c1) { + N1 = N1->next_community1; + } else { + N1 = N1->next_community2; + } + remove_neighbor(tmp); + Neighbor* N = new Neighbor; + N->weight = tmp->weight; + N->community1 = neighbor_community; + N->community2 = nb_communities; + N->delta_sigma = (double(communities[c1].size + communities[neighbor_community].size) * tmp->delta_sigma + double(communities[c2].size) * merge_N->delta_sigma) / (double(communities[c1].size + communities[c2].size + communities[neighbor_community].size)); //compute_delta_sigma(neighbor_community, nb_communities); + N->exact = false; + delete tmp; + add_neighbor(N); + } + } + + if (max_memory != -1) { + min_delta_sigma->delta_sigma[nb_communities] = communities[nb_communities].min_delta_sigma(); + min_delta_sigma->update(nb_communities); + } + + nb_communities++; + nb_active_communities--; +} + +double Communities::merge_nearest_communities() { + Neighbor* N = H->get_first(); + while (!N->exact) { + update_neighbor(N, compute_delta_sigma(N->community1, N->community2)); + N->exact = true; + N = H->get_first(); + if (max_memory != -1) { + manage_memory(); + } + } + + double d = N->delta_sigma; + remove_neighbor(N); + + merge_communities(N); + if (max_memory != -1) { + manage_memory(); + } + + if (merges) { + MATRIX(*merges, mergeidx, 0) = N->community1; + MATRIX(*merges, mergeidx, 1) = N->community2; + mergeidx++; + } + + if (modularity) { + float Q = 0.; + for (int i = 0; i < nb_communities; i++) { + if (communities[i].sub_community_of == 0) { + Q += (communities[i].internal_weight - communities[i].total_weight * communities[i].total_weight / G->total_weight) / G->total_weight; + } + } + VECTOR(*modularity)[mergeidx] = Q; + } + + delete N; + + /* This could use igraph_progress */ + /* if(!silent) { */ + /* for(int k = (500*(G->nb_vertices - nb_active_communities - 1))/(G->nb_vertices-1) + 1; k <= (500*(G->nb_vertices - nb_active_communities))/(G->nb_vertices-1); k++) { */ + /* if(k % 50 == 1) {cerr.width(2); cerr << endl << k/ 5 << "% ";} */ + /* cerr << "."; */ + /* } */ + /* } */ + return d; +} + +double Communities::compute_delta_sigma(int community1, int community2) { + if (!communities[community1].P) { + communities[community1].P = new Probabilities(community1); + if (max_memory != -1) { + min_delta_sigma->update(community1); + } + } + if (!communities[community2].P) { + communities[community2].P = new Probabilities(community2); + if (max_memory != -1) { + min_delta_sigma->update(community2); + } + } + + return communities[community1].P->compute_distance(communities[community2].P) * double(communities[community1].size) * double(communities[community2].size) / double(communities[community1].size + communities[community2].size); +} + +} +} /* end of namespaces */ diff --git a/src/rigraph/core/community/walktrap/walktrap_communities.h b/src/rigraph/core/community/walktrap/walktrap_communities.h new file mode 100644 index 0000000..9b028ac --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap_communities.h @@ -0,0 +1,175 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Pascal Pons + The original copyright notice follows here. The FSF address was + fixed by Tamas Nepusz */ + +// File: communities.h +//----------------------------------------------------------------------------- +// Walktrap v0.2 -- Finds community structure of networks using random walks +// Copyright (C) 2004-2005 Pascal Pons +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301 USA +//----------------------------------------------------------------------------- +// Author : Pascal Pons +// Email : pascal.pons@gmail.com +// Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html +// Location : Paris, France +// Time : June 2005 +//----------------------------------------------------------------------------- +// see readme.txt for more details + + +#ifndef WALKTRAP_COMMUNITIES_H +#define WALKTRAP_COMMUNITIES_H + +#include "walktrap_graph.h" +#include "walktrap_heap.h" +#include "igraph_community.h" +#include "config.h" + +namespace igraph { + +namespace walktrap { + +class Communities; +class Probabilities { +public: + static float* tmp_vector1; // + static float* tmp_vector2; // + static int* id; // + static int* vertices1; // + static int* vertices2; // + static int current_id; // + + static Communities* C; // pointer to all the communities + static int length; // length of the random walks + + + int size; // number of probabilities stored + int* vertices; // the vertices corresponding to the stored probabilities, 0 if all the probabilities are stored + float* P; // the probabilities + + long memory(); // the memory (in Bytes) used by the object + double compute_distance(const Probabilities* P2) const; // compute the squared distance r^2 between this probability vector and P2 + Probabilities(int community); // compute the probability vector of a community + Probabilities(int community1, int community2); // merge the probability vectors of two communities in a new one + // the two communities must have their probability vectors stored + + ~Probabilities(); // destructor +}; + +class Community { +public: + + Neighbor* first_neighbor; // first item of the list of adjacent communities + Neighbor* last_neighbor; // last item of the list of adjacent communities + + int this_community; // number of this community + int first_member; // number of the first vertex of the community + int last_member; // number of the last vertex of the community + int size; // number of members of the community + + Probabilities* P; // the probability vector, 0 if not stored. + + + float sigma; // sigma(C) of the community + float internal_weight; // sum of the weight of the internal edges + float total_weight; // sum of the weight of all the edges of the community (an edge between two communities is a half-edge for each community) + + int sub_communities[2]; // the two sub sommunities, -1 if no sub communities; + int sub_community_of; // number of the community in which this community has been merged + // 0 if the community is active + // -1 if the community is not used + + void merge(Community &C1, Community &C2); // create a new community by merging C1 an C2 + void add_neighbor(Neighbor* N); + void remove_neighbor(Neighbor* N); + float min_delta_sigma(); // compute the minimal delta sigma among all the neighbors of this community + + Community(); // create an empty community + ~Community(); // destructor +}; + +class Communities { +private: + long max_memory; // size in Byte of maximal memory usage, -1 for no limit + igraph_matrix_t *merges; + long int mergeidx; + igraph_vector_t *modularity; + +public: + + long memory_used; // in bytes + Min_delta_sigma_heap* min_delta_sigma; // the min delta_sigma of the community with a saved probability vector (for memory management) + + Graph* G; // the graph + int* members; // the members of each community represented as a chained list. + // a community points to the first_member the array which contains + // the next member (-1 = end of the community) + Neighbor_heap* H; // the distances between adjacent communities. + + + Community* communities; // array of the communities + + int nb_communities; // number of valid communities + int nb_active_communities; // number of active communities + + Communities(Graph* G, int random_walks_length = 3, + long max_memory = -1, igraph_matrix_t *merges = 0, + igraph_vector_t *modularity = 0); // Constructor + ~Communities(); // Destructor + + + void merge_communities(Neighbor* N); // create a community by merging two existing communities + double merge_nearest_communities(); + + + double compute_delta_sigma(int c1, int c2); // compute delta_sigma(c1,c2) + + void remove_neighbor(Neighbor* N); + void add_neighbor(Neighbor* N); + void update_neighbor(Neighbor* N, float new_delta_sigma); + + void manage_memory(); + +}; + +} +} /* end of namespaces */ + +#endif // WALKTRAP_COMMUNITIES_H diff --git a/src/rigraph/core/community/walktrap/walktrap_graph.cpp b/src/rigraph/core/community/walktrap/walktrap_graph.cpp new file mode 100644 index 0000000..3f728f8 --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap_graph.cpp @@ -0,0 +1,240 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Pascal Pons + The original copyright notice follows here. The FSF address was + fixed by Tamas Nepusz */ + +// File: graph.cpp +//----------------------------------------------------------------------------- +// Walktrap v0.2 -- Finds community structure of networks using random walks +// Copyright (C) 2004-2005 Pascal Pons +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301 USA +//----------------------------------------------------------------------------- +// Author : Pascal Pons +// Email : pascal.pons@gmail.com +// Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html +// Location : Paris, France +// Time : June 2005 +//----------------------------------------------------------------------------- +// see readme.txt for more details + +#include "walktrap_graph.h" +#include "igraph_interface.h" +#include +#include // strlen + +using namespace std; + +namespace igraph { + +namespace walktrap { + +bool operator<(const Edge& E1, const Edge& E2) { + return (E1.neighbor < E2.neighbor); +} + + +Vertex::Vertex() { + degree = 0; + edges = 0; + total_weight = 0.; +} + +Vertex::~Vertex() { + if (edges) { + delete[] edges; + } +} + +Graph::Graph() { + nb_vertices = 0; + nb_edges = 0; + vertices = 0; + index = 0; + total_weight = 0.; +} + +Graph::~Graph () { + if (vertices) { + delete[] vertices; + } +} + +class Edge_list { +public: + int* V1; + int* V2; + float* W; + + int size; + int size_max; + + void add(int v1, int v2, float w); + Edge_list() { + size = 0; + size_max = 1024; + V1 = new int[1024]; + V2 = new int[1024]; + W = new float[1024]; + } + ~Edge_list() { + if (V1) { + delete[] V1; + } + if (V2) { + delete[] V2; + } + if (W) { + delete[] W; + } + } +}; + +void Edge_list::add(int v1, int v2, float w) { + if (size == size_max) { + int* tmp1 = new int[2 * size_max]; + int* tmp2 = new int[2 * size_max]; + float* tmp3 = new float[2 * size_max]; + for (int i = 0; i < size_max; i++) { + tmp1[i] = V1[i]; + tmp2[i] = V2[i]; + tmp3[i] = W[i]; + } + delete[] V1; + delete[] V2; + delete[] W; + V1 = tmp1; + V2 = tmp2; + W = tmp3; + size_max *= 2; + } + V1[size] = v1; + V2[size] = v2; + W[size] = w; + size++; +} + +int Graph::convert_from_igraph(const igraph_t *graph, + const igraph_vector_t *weights) { + Graph &G = *this; + + int max_vertex = (int)igraph_vcount(graph) - 1; + long int no_of_edges = (long int)igraph_ecount(graph); + long int i; + long int deg; + double w; + + Edge_list EL; + + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t from, to; + int v1, v2; + w = weights ? VECTOR(*weights)[i] : 1.0; + igraph_edge(graph, i, &from, &to); + v1 = (int)from; v2 = (int)to; + EL.add(v1, v2, w); + } + + G.nb_vertices = max_vertex + 1; + G.vertices = new Vertex[G.nb_vertices]; + G.nb_edges = 0; + G.total_weight = 0.0; + + for (int i = 0; i < EL.size; i++) { + G.vertices[EL.V1[i]].degree++; + G.vertices[EL.V2[i]].degree++; + G.vertices[EL.V1[i]].total_weight += EL.W[i]; + G.vertices[EL.V2[i]].total_weight += EL.W[i]; + G.nb_edges++; + G.total_weight += EL.W[i]; + } + + for (int i = 0; i < G.nb_vertices; i++) { + deg = G.vertices[i].degree; + w = (deg == 0) ? 1.0 : (G.vertices[i].total_weight / double(deg)); + G.vertices[i].edges = new Edge[deg + 1]; + G.vertices[i].edges[0].neighbor = i; + G.vertices[i].edges[0].weight = w; + G.vertices[i].total_weight += w; + G.vertices[i].degree = 1; + } + + for (int i = 0; i < EL.size; i++) { + G.vertices[EL.V1[i]].edges[G.vertices[EL.V1[i]].degree].neighbor = EL.V2[i]; + G.vertices[EL.V1[i]].edges[G.vertices[EL.V1[i]].degree].weight = EL.W[i]; + G.vertices[EL.V1[i]].degree++; + G.vertices[EL.V2[i]].edges[G.vertices[EL.V2[i]].degree].neighbor = EL.V1[i]; + G.vertices[EL.V2[i]].edges[G.vertices[EL.V2[i]].degree].weight = EL.W[i]; + G.vertices[EL.V2[i]].degree++; + } + + for (int i = 0; i < G.nb_vertices; i++) { + sort(G.vertices[i].edges, G.vertices[i].edges + G.vertices[i].degree); + } + + for (int i = 0; i < G.nb_vertices; i++) { // merge multi edges + int a = 0; + for (int b = 1; b < G.vertices[i].degree; b++) { + if (G.vertices[i].edges[b].neighbor == G.vertices[i].edges[a].neighbor) { + G.vertices[i].edges[a].weight += G.vertices[i].edges[b].weight; + } else { + G.vertices[i].edges[++a] = G.vertices[i].edges[b]; + } + } + G.vertices[i].degree = a + 1; + } + + return 0; +} + +long Graph::memory() { + size_t m = 0; + m += size_t(nb_vertices) * sizeof(Vertex); + m += 2 * size_t(nb_edges) * sizeof(Edge); + m += sizeof(Graph); + if (index != 0) { + m += size_t(nb_vertices) * sizeof(char*); + for (int i = 0; i < nb_vertices; i++) { + m += strlen(index[i]) + 1; + } + } + return m; +} + +} +} diff --git a/src/rigraph/core/community/walktrap/walktrap_graph.h b/src/rigraph/core/community/walktrap/walktrap_graph.h new file mode 100644 index 0000000..7fe9574 --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap_graph.h @@ -0,0 +1,104 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Pascal Pons + The original copyright notice follows here */ + +// File: graph.h +//----------------------------------------------------------------------------- +// Walktrap v0.2 -- Finds community structure of networks using random walks +// Copyright (C) 2004-2005 Pascal Pons +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301 USA +//----------------------------------------------------------------------------- +// Author : Pascal Pons +// Email : pascal.pons@gmail.com +// Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html +// Location : Paris, France +// Time : June 2005 +//----------------------------------------------------------------------------- +// see readme.txt for more details + +/* FSF address above was fixed by Tamas Nepusz */ + + +#ifndef WALKTRAP_GRAPH_H +#define WALKTRAP_GRAPH_H + +#include "igraph_community.h" + +namespace igraph { + +namespace walktrap { + +class Edge { // code an edge of a given vertex +public: + int neighbor; // the number of the neighbor vertex + float weight; // the weight of the edge +}; +bool operator<(const Edge& E1, const Edge& E2); + + +class Vertex { +public: + Edge* edges; // the edges of the vertex + int degree; // number of neighbors + float total_weight; // the total weight of the vertex + + Vertex(); // creates empty vertex + ~Vertex(); // destructor +}; + +class Graph { +public: + int nb_vertices; // number of vertices + int nb_edges; // number of edges + float total_weight; // total weight of the edges + Vertex* vertices; // array of the vertices + + long memory(); // the total memory used in Bytes + Graph(); // create an empty graph + ~Graph(); // destructor + char** index; // to keep the real name of the vertices + + int convert_from_igraph(const igraph_t * igraph, + const igraph_vector_t *weights); +}; + +} +} /* end of namespaces */ + +#endif // WALKTRAP_GRAPH_H diff --git a/src/rigraph/core/community/walktrap/walktrap_heap.cpp b/src/rigraph/core/community/walktrap/walktrap_heap.cpp new file mode 100644 index 0000000..983cb7b --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap_heap.cpp @@ -0,0 +1,241 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Pascal Pons + The original copyright notice follows here. The FSF address was + fixed by Tamas Nepusz */ + +// File: heap.cpp +//----------------------------------------------------------------------------- +// Walktrap v0.2 -- Finds community structure of networks using random walks +// Copyright (C) 2004-2005 Pascal Pons +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301 USA +//----------------------------------------------------------------------------- +// Author : Pascal Pons +// Email : pascal.pons@gmail.com +// Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html +// Location : Paris, France +// Time : June 2005 +//----------------------------------------------------------------------------- +// see readme.txt for more details + +#include "walktrap_heap.h" + +using namespace igraph::walktrap; + +void Neighbor_heap::move_up(int index) { + while (H[index / 2]->delta_sigma > H[index]->delta_sigma) { + Neighbor* tmp = H[index / 2]; + H[index]->heap_index = index / 2; + H[index / 2] = H[index]; + tmp->heap_index = index; + H[index] = tmp; + index = index / 2; + } +} + +void Neighbor_heap::move_down(int index) { + while (true) { + int min = index; + if ((2 * index < size) && (H[2 * index]->delta_sigma < H[min]->delta_sigma)) { + min = 2 * index; + } + if (2 * index + 1 < size && H[2 * index + 1]->delta_sigma < H[min]->delta_sigma) { + min = 2 * index + 1; + } + if (min != index) { + Neighbor* tmp = H[min]; + H[index]->heap_index = min; + H[min] = H[index]; + tmp->heap_index = index; + H[index] = tmp; + index = min; + } else { + break; + } + } +} + +Neighbor* Neighbor_heap::get_first() { + if (size == 0) { + return 0; + } else { + return H[0]; + } +} + +void Neighbor_heap::remove(Neighbor* N) { + if (N->heap_index == -1 || size == 0) { + return; + } + Neighbor* last_N = H[--size]; + H[N->heap_index] = last_N; + last_N->heap_index = N->heap_index; + move_up(last_N->heap_index); + move_down(last_N->heap_index); + N->heap_index = -1; +} + +void Neighbor_heap::add(Neighbor* N) { + if (size >= max_size) { + return; + } + N->heap_index = size++; + H[N->heap_index] = N; + move_up(N->heap_index); +} + +void Neighbor_heap::update(Neighbor* N) { + if (N->heap_index == -1) { + return; + } + move_up(N->heap_index); + move_down(N->heap_index); +} + +long Neighbor_heap::memory() { + return (sizeof(Neighbor_heap) + long(max_size) * sizeof(Neighbor*)); +} + +Neighbor_heap::Neighbor_heap(int max_s) { + max_size = max_s; + size = 0; + H = new Neighbor*[max_s]; +} + +Neighbor_heap::~Neighbor_heap() { + delete[] H; +} + +bool Neighbor_heap::is_empty() { + return (size == 0); +} + + + +//################################################################# + +void Min_delta_sigma_heap::move_up(int index) { + while (delta_sigma[H[index / 2]] < delta_sigma[H[index]]) { + int tmp = H[index / 2]; + I[H[index]] = index / 2; + H[index / 2] = H[index]; + I[tmp] = index; + H[index] = tmp; + index = index / 2; + } +} + +void Min_delta_sigma_heap::move_down(int index) { + while (true) { + int max = index; + if (2 * index < size && delta_sigma[H[2 * index]] > delta_sigma[H[max]]) { + max = 2 * index; + } + if (2 * index + 1 < size && delta_sigma[H[2 * index + 1]] > delta_sigma[H[max]]) { + max = 2 * index + 1; + } + if (max != index) { + int tmp = H[max]; + I[H[index]] = max; + H[max] = H[index]; + I[tmp] = index; + H[index] = tmp; + index = max; + } else { + break; + } + } +} + +int Min_delta_sigma_heap::get_max_community() { + if (size == 0) { + return -1; + } else { + return H[0]; + } +} + +void Min_delta_sigma_heap::remove_community(int community) { + if (I[community] == -1 || size == 0) { + return; + } + int last_community = H[--size]; + H[I[community]] = last_community; + I[last_community] = I[community]; + move_up(I[last_community]); + move_down(I[last_community]); + I[community] = -1; +} + +void Min_delta_sigma_heap::update(int community) { + if (community < 0 || community >= max_size) { + return; + } + if (I[community] == -1) { + I[community] = size++; + H[I[community]] = community; + } + move_up(I[community]); + move_down(I[community]); +} + +long Min_delta_sigma_heap::memory() { + return (sizeof(Min_delta_sigma_heap) + long(max_size) * (2 * sizeof(int) + sizeof(float))); +} + +Min_delta_sigma_heap::Min_delta_sigma_heap(int max_s) { + max_size = max_s; + size = 0; + H = new int[max_s]; + I = new int[max_s]; + delta_sigma = new float[max_s]; + for (int i = 0; i < max_size; i++) { + I[i] = -1; + delta_sigma[i] = 1.; + } +} + +Min_delta_sigma_heap::~Min_delta_sigma_heap() { + delete[] H; + delete[] I; + delete[] delta_sigma; +} + +bool Min_delta_sigma_heap::is_empty() { + return (size == 0); +} diff --git a/src/rigraph/core/community/walktrap/walktrap_heap.h b/src/rigraph/core/community/walktrap/walktrap_heap.h new file mode 100644 index 0000000..b0702bd --- /dev/null +++ b/src/rigraph/core/community/walktrap/walktrap_heap.h @@ -0,0 +1,133 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* The original version of this file was written by Pascal Pons + The original copyright notice follows here. The FSF address was + fixed by Tamas Nepusz */ + +// File: heap.h +//----------------------------------------------------------------------------- +// Walktrap v0.2 -- Finds community structure of networks using random walks +// Copyright (C) 2004-2005 Pascal Pons +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301 USA +//----------------------------------------------------------------------------- +// Author : Pascal Pons +// Email : pons@liafa.jussieu.fr +// Web page : http://www.liafa.jussieu.fr/~pons/ +// Location : Paris, France +// Time : June 2005 +//----------------------------------------------------------------------------- +// see readme.txt for more details + +#ifndef WALKTRAP_HEAP_H +#define WALKTRAP_HEAP_H + +namespace igraph { + +namespace walktrap { + +class Neighbor { +public: + int community1; // the two adjacent communities + int community2; // community1 < community2 + + float delta_sigma; // the delta sigma between the two communities + float weight; // the total weight of the edges between the two communities + bool exact; // true if delta_sigma is exact, false if it is only a lower bound + + Neighbor* next_community1; // pointers of two double + Neighbor* previous_community1; // chained lists containing + Neighbor* next_community2; // all the neighbors of + Neighbor* previous_community2; // each communities. + + int heap_index; // + + Neighbor(); +}; + + +class Neighbor_heap { +private: + int size; + int max_size; + + Neighbor** H; // the heap that contains a pointer to each Neighbor object stored + + void move_up(int index); + void move_down(int index); + +public: + void add(Neighbor* N); // add a new distance + void update(Neighbor* N); // update a distance + void remove(Neighbor* N); // remove a distance + Neighbor* get_first(); // get the first item + long memory(); + bool is_empty(); + + Neighbor_heap(int max_size); + ~Neighbor_heap(); +}; + + +class Min_delta_sigma_heap { +private: + int size; + int max_size; + + int* H; // the heap that contains the number of each community + int* I; // the index of each community in the heap (-1 = not stored) + + void move_up(int index); + void move_down(int index); + +public: + int get_max_community(); // return the community with the maximal delta_sigma + void remove_community(int community); // remove a community; + void update(int community); // update (or insert if necessary) the community + long memory(); // the memory used in Bytes. + bool is_empty(); + + float* delta_sigma; // the delta_sigma of the stored communities + + Min_delta_sigma_heap(int max_size); + ~Min_delta_sigma_heap(); +}; + +} +} /* end of namespaces */ + +#endif // WALKTRAP_HEAP_H diff --git a/src/rigraph/core/connectivity/cohesive_blocks.c b/src/rigraph/core/connectivity/cohesive_blocks.c new file mode 100644 index 0000000..d1f13f3 --- /dev/null +++ b/src/rigraph/core/connectivity/cohesive_blocks.c @@ -0,0 +1,602 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_cohesive_blocks.h" + +#include "igraph_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_flow.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_separators.h" +#include "igraph_statusbar.h" +#include "igraph_structural.h" + +#include "../core/interruption.h" + +static void igraph_i_cohesive_blocks_free_graphs(igraph_vector_ptr_t *ptr) { + long int i, n = igraph_vector_ptr_size(ptr); + + for (i = 0; i < n; i++) { + igraph_t *g = VECTOR(*ptr)[i]; + if (g) { + igraph_destroy(g); + igraph_free(g); + } + } +} + +static void igraph_i_cohesive_blocks_free_vectors(igraph_vector_ptr_t *ptr) { + long int i, n = igraph_vector_ptr_size(ptr); + + for (i = 0; i < n; i++) { + igraph_vector_t *v = VECTOR(*ptr)[i]; + if (v) { + igraph_vector_destroy(v); + igraph_free(v); + } + } +} + +/* This is kind of a BFS to find the components of the graph, after + * deleting the vertices marked in 'excluded'. + * These vertices are not put in the BFS queue, but they are added to + * all neighboring components. + */ + +static int igraph_i_cb_components(igraph_t *graph, + const igraph_vector_bool_t *excluded, + igraph_vector_long_t *components, + long int *no, + /* working area follows */ + igraph_vector_long_t *compid, + igraph_dqueue_t *Q, + igraph_vector_t *neis) { + + long int no_of_nodes = igraph_vcount(graph); + long int i; + long int cno = 0; + + igraph_vector_long_clear(components); + igraph_dqueue_clear(Q); + IGRAPH_CHECK(igraph_vector_long_resize(compid, no_of_nodes)); + igraph_vector_long_null(compid); + + for (i = 0; i < no_of_nodes; i++) { + + if (VECTOR(*compid)[i]) { + continue; + } + if (VECTOR(*excluded)[i]) { + continue; + } + + IGRAPH_CHECK(igraph_dqueue_push(Q, i)); + IGRAPH_CHECK(igraph_vector_long_push_back(components, i)); + VECTOR(*compid)[i] = ++cno; + + while (!igraph_dqueue_empty(Q)) { + igraph_integer_t node = (igraph_integer_t) igraph_dqueue_pop(Q); + long int j, n; + IGRAPH_CHECK(igraph_neighbors(graph, neis, node, IGRAPH_ALL)); + n = igraph_vector_size(neis); + for (j = 0; j < n; j++) { + long int v = (long int) VECTOR(*neis)[j]; + if (VECTOR(*excluded)[v]) { + if (VECTOR(*compid)[v] != cno) { + VECTOR(*compid)[v] = cno; + IGRAPH_CHECK(igraph_vector_long_push_back(components, v)); + } + } else { + if (!VECTOR(*compid)[v]) { + VECTOR(*compid)[v] = cno; /* could be anything positive */ + IGRAPH_CHECK(igraph_vector_long_push_back(components, v)); + IGRAPH_CHECK(igraph_dqueue_push(Q, v)); + } + } + } + } /* while !igraph_dqueue_empty */ + + IGRAPH_CHECK(igraph_vector_long_push_back(components, -1)); + + } /* for ik. Thus a hiearchy of vertex subsets + * is found, whith the entire graph G at its root. See the following + * reference for details: J. Moody and D. R. White. Structural + * cohesion and embeddedness: A hierarchical concept of social + * groups. American Sociological Review, 68(1):103--127, Feb 2003. + * + * This function implements cohesive blocking and + * calculates the complete cohesive block hierarchy of a graph. + * + * \param graph The input graph. It must be undirected and simple. See + * \ref igraph_is_simple(). + * \param blocks If not a null pointer, then it must be an initialized + * vector of pointers and the cohesive blocks are stored here. + * Each block is encoded with a numeric vector, that contains the + * vertex ids of the block. + * \param cohesion If not a null pointer, then it must be an initialized + * vector and the cohesion of the blocks is stored here, in the same + * order as the blocks in the \p blocks pointer vector. + * \param parent If not a null pointer, then it must be an initialized + * vector and the block hierarchy is stored here. For each block, the + * id (i.e. the position in the \p blocks pointer vector) of its + * parent block is stored. For the top block in the hierarchy, + * -1 is stored. + * \param block_tree If not a null pointer, then it must be a pointer + * to an uninitialized graph, and the block hierarchy is stored + * here as an igraph graph. The vertex ids correspond to the order + * of the blocks in the \p blocks vector. + * \return Error code. + * + * Time complexity: TODO. + * + * \example examples/simple/cohesive_blocks.c + */ + +int igraph_cohesive_blocks(const igraph_t *graph, + igraph_vector_ptr_t *blocks, + igraph_vector_t *cohesion, + igraph_vector_t *parent, + igraph_t *block_tree) { + + /* Some implementation comments. Everything is relatively + straightforward, except, that we need to follow the vertex ids + of the various subgraphs, without having to store two-way + mappings at each level. The subgraphs can overlap, this + complicates things a bit. + + The 'Q' vector is used as a double ended queue and it contains + the subgraphs to work on in the future. Some other vectors are + associated with it. 'Qparent' gives the parent graph of a graph + in Q. Qmapping gives the mapping of the vertices from the graph + to the parent graph. Qcohesion is the vertex connectivity of the + graph. + + Qptr is an integer and points to the next graph to work on. + */ + + igraph_vector_ptr_t Q; + igraph_vector_ptr_t Qmapping; + igraph_vector_long_t Qparent; + igraph_vector_long_t Qcohesion; + igraph_vector_bool_t Qcheck; + long int Qptr = 0; + igraph_integer_t conn; + igraph_bool_t is_simple; + + igraph_t *graph_copy; + + igraph_vector_ptr_t separators; + igraph_vector_t compvertices; + igraph_vector_long_t components; + igraph_vector_bool_t marked; + + igraph_vector_long_t compid; + igraph_dqueue_t bfsQ; + igraph_vector_t neis; + + if (igraph_is_directed(graph)) { + IGRAPH_ERROR("Cohesive blocking only works on undirected graphs", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_is_simple(graph, &is_simple)); + if (!is_simple) { + IGRAPH_ERROR("Cohesive blocking only works on simple graphs", + IGRAPH_EINVAL); + } + + IGRAPH_STATUS("Starting cohesive block calculation.\n", 0); + + if (blocks) { + igraph_vector_ptr_clear(blocks); + } + if (cohesion) { + igraph_vector_clear(cohesion); + } + if (parent) { + igraph_vector_clear(parent); + } + + IGRAPH_CHECK(igraph_vector_ptr_init(&Q, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &Q); + IGRAPH_FINALLY(igraph_i_cohesive_blocks_free_graphs, &Q); + + IGRAPH_CHECK(igraph_vector_ptr_init(&Qmapping, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &Qmapping); + IGRAPH_FINALLY(igraph_i_cohesive_blocks_free_vectors, &Qmapping); + + IGRAPH_CHECK(igraph_vector_long_init(&Qparent, 1)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &Qparent); + + IGRAPH_CHECK(igraph_vector_long_init(&Qcohesion, 1)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &Qcohesion); + + IGRAPH_CHECK(igraph_vector_bool_init(&Qcheck, 1)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &Qcheck); + + IGRAPH_CHECK(igraph_vector_ptr_init(&separators, 0)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &separators); + + IGRAPH_VECTOR_INIT_FINALLY(&compvertices, 0); + IGRAPH_CHECK(igraph_vector_bool_init(&marked, 0)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &marked); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_init(&bfsQ, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &bfsQ); + IGRAPH_CHECK(igraph_vector_long_init(&compid, 0)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &compid); + IGRAPH_CHECK(igraph_vector_long_init(&components, 0)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &components); + + /* Put the input graph in the queue */ + graph_copy = IGRAPH_CALLOC(1, igraph_t); + if (!graph_copy) { + IGRAPH_ERROR("Cannot do cohesive blocking", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_copy(graph_copy, graph)); + VECTOR(Q)[0] = graph_copy; + VECTOR(Qmapping)[0] = NULL; /* Identity mapping */ + VECTOR(Qparent)[0] = -1; /* Has no parent */ + IGRAPH_CHECK(igraph_vertex_connectivity(graph, &conn, /*checks=*/ 1)); + VECTOR(Qcohesion)[0] = conn; + VECTOR(Qcheck)[0] = 0; + + /* Then work until the queue is empty */ + while (Qptr < igraph_vector_ptr_size(&Q)) { + igraph_t *mygraph = VECTOR(Q)[Qptr]; + igraph_bool_t mycheck = VECTOR(Qcheck)[Qptr]; + long int mynodes = igraph_vcount(mygraph); + long int i, nsep; + long int no, kept = 0; + long int cptr = 0; + long int nsepv = 0; + igraph_bool_t addedsep = 0; + + IGRAPH_STATUSF(("Candidate %li: %li vertices,", + 0, Qptr, mynodes)); + IGRAPH_ALLOW_INTERRUPTION(); + + /* Get the separators */ + IGRAPH_CHECK(igraph_minimum_size_separators(mygraph, &separators)); + IGRAPH_FINALLY(igraph_i_cohesive_blocks_free_vectors, &separators); + nsep = igraph_vector_ptr_size(&separators); + + IGRAPH_STATUSF((" %li separators,", 0, nsep)); + + /* Remove them from the graph, also mark them */ + IGRAPH_CHECK(igraph_vector_bool_resize(&marked, mynodes)); + igraph_vector_bool_null(&marked); + for (i = 0; i < nsep; i++) { + igraph_vector_t *v = VECTOR(separators)[i]; + long int j, n = igraph_vector_size(v); + for (j = 0; j < n; j++) { + long int vv = (long int) VECTOR(*v)[j]; + if (!VECTOR(marked)[vv]) { + nsepv++; + VECTOR(marked)[vv] = 1; + } + } + } + + /* Find the connected components, omitting the separator vertices, + but including the neighboring separator vertices + */ + IGRAPH_CHECK(igraph_i_cb_components(mygraph, &marked, + &components, &no, + &compid, &bfsQ, &neis)); + + /* Add the separator vertices themselves, as another component, + but only if there is at least one vertex not included in any + separator. */ + if (nsepv != mynodes) { + addedsep = 1; + for (i = 0; i < mynodes; i++) { + if (VECTOR(marked)[i]) { + IGRAPH_CHECK(igraph_vector_long_push_back(&components, i)); + } + } + IGRAPH_CHECK(igraph_vector_long_push_back(&components, -1)); + no++; + } + + IGRAPH_STATUSF((" %li new candidates,", 0, no)); + + for (i = 0; i < no; i++) { + igraph_vector_t *newmapping; + igraph_t *newgraph; + igraph_integer_t maxdeg; + + igraph_vector_clear(&compvertices); + + while (1) { + long int v = VECTOR(components)[cptr++]; + if (v < 0) { + break; + } + IGRAPH_CHECK(igraph_vector_push_back(&compvertices, v)); + } + + newmapping = IGRAPH_CALLOC(1, igraph_vector_t); + if (!newmapping) { + IGRAPH_ERROR("Cannot do cohesive blocking", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newmapping); + IGRAPH_VECTOR_INIT_FINALLY(newmapping, 0); + newgraph = IGRAPH_CALLOC(1, igraph_t); + if (!newgraph) { + IGRAPH_ERROR("Cannot do cohesive blocking", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newgraph); + IGRAPH_CHECK(igraph_induced_subgraph_map(mygraph, newgraph, + igraph_vss_vector(&compvertices), + IGRAPH_SUBGRAPH_AUTO, + /*map=*/ 0, + /*invmap=*/ newmapping)); + IGRAPH_FINALLY(igraph_destroy, newgraph); + + IGRAPH_CHECK(igraph_maxdegree(newgraph, &maxdeg, igraph_vss_all(), + IGRAPH_ALL, IGRAPH_LOOPS)); + if (maxdeg > VECTOR(Qcohesion)[Qptr]) { + igraph_integer_t newconn; + kept++; + IGRAPH_CHECK(igraph_vector_ptr_push_back(&Q, newgraph)); + IGRAPH_FINALLY_CLEAN(2); + IGRAPH_CHECK(igraph_vector_ptr_push_back(&Qmapping, newmapping)); + IGRAPH_FINALLY_CLEAN(2); + IGRAPH_CHECK(igraph_vertex_connectivity(newgraph, &newconn, + /*checks=*/ 1)); + IGRAPH_CHECK(igraph_vector_long_push_back(&Qcohesion, newconn)); + IGRAPH_CHECK(igraph_vector_long_push_back(&Qparent, Qptr)); + IGRAPH_CHECK(igraph_vector_bool_push_back(&Qcheck, + mycheck || addedsep)); + } else { + igraph_destroy(newgraph); + igraph_free(newgraph); + igraph_vector_destroy(newmapping); + igraph_free(newmapping); + IGRAPH_FINALLY_CLEAN(4); + } + } + + IGRAPH_STATUSF((" keeping %li.\n", 0, kept)); + + igraph_destroy(mygraph); + igraph_free(mygraph); + VECTOR(Q)[Qptr] = 0; + igraph_i_cohesive_blocks_free_vectors(&separators); + IGRAPH_FINALLY_CLEAN(1); + + Qptr++; + } + + igraph_vector_long_destroy(&components); + igraph_vector_long_destroy(&compid); + igraph_dqueue_destroy(&bfsQ); + igraph_vector_destroy(&neis); + igraph_vector_bool_destroy(&marked); + igraph_vector_destroy(&compvertices); + igraph_vector_ptr_destroy(&separators); + IGRAPH_FINALLY_CLEAN(7); + + if (blocks || cohesion || parent || block_tree) { + igraph_integer_t noblocks = (igraph_integer_t) Qptr, badblocks = 0; + igraph_vector_bool_t removed; + long int i, resptr = 0; + igraph_vector_long_t rewritemap; + + IGRAPH_CHECK(igraph_vector_bool_init(&removed, noblocks)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &removed); + IGRAPH_CHECK(igraph_vector_long_init(&rewritemap, noblocks)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &rewritemap); + + for (i = 1; i < noblocks; i++) { + long int p = VECTOR(Qparent)[i]; + while (VECTOR(removed)[p]) { + p = VECTOR(Qparent)[p]; + } + if (VECTOR(Qcohesion)[p] >= VECTOR(Qcohesion)[i]) { + VECTOR(removed)[i] = 1; + badblocks++; + } + } + + /* Rewrite the mappings */ + for (i = 1; i < Qptr; i++) { + long int p = VECTOR(Qparent)[i]; + igraph_vector_t *mapping = VECTOR(Qmapping)[i]; + igraph_vector_t *pmapping = VECTOR(Qmapping)[p]; + long int j, n = igraph_vector_size(mapping); + + if (!pmapping) { + continue; + } + for (j = 0; j < n; j++) { + long int v = (long int) VECTOR(*mapping)[j]; + VECTOR(*mapping)[j] = VECTOR(*pmapping)[v]; + } + } + + /* Because we also put the separator vertices in the queue, it is + not ensured that the found blocks are not subsets of each other. + We check this now. */ + for (i = 1; i < noblocks; i++) { + long int j, ic; + igraph_vector_t *ivec; + if (!VECTOR(Qcheck)[i] || VECTOR(removed)[i]) { + continue; + } + ivec = VECTOR(Qmapping)[i]; + ic = VECTOR(Qcohesion)[i]; + for (j = 1; j < noblocks; j++) { + igraph_vector_t *jvec; + long int jc; + if (j == i || !VECTOR(Qcheck)[j] || VECTOR(removed)[j]) { + continue; + } + jvec = VECTOR(Qmapping)[j]; + jc = VECTOR(Qcohesion)[j]; + if (igraph_i_cb_isin(ivec, jvec) && jc >= ic) { + badblocks++; + VECTOR(removed)[i] = 1; + break; + } + } + } + + noblocks -= badblocks; + + if (blocks) { + IGRAPH_CHECK(igraph_vector_ptr_resize(blocks, noblocks)); + } + if (cohesion) { + IGRAPH_CHECK(igraph_vector_resize(cohesion, noblocks)); + } + if (parent) { + IGRAPH_CHECK(igraph_vector_resize(parent, noblocks)); + } + + for (i = 0; i < Qptr; i++) { + if (VECTOR(removed)[i]) { + IGRAPH_STATUSF(("Candidate %li ignored.\n", 0, i)); + continue; + } else { + IGRAPH_STATUSF(("Candidate %li is a cohesive (sub)block\n", 0, i)); + } + VECTOR(rewritemap)[i] = resptr; + if (cohesion) { + VECTOR(*cohesion)[resptr] = VECTOR(Qcohesion)[i]; + } + if (parent || block_tree) { + long int p = VECTOR(Qparent)[i]; + while (p >= 0 && VECTOR(removed)[p]) { + p = VECTOR(Qparent)[p]; + } + if (p >= 0) { + p = VECTOR(rewritemap)[p]; + } + VECTOR(Qparent)[i] = p; + if (parent) { + VECTOR(*parent)[resptr] = p; + } + } + if (blocks) { + VECTOR(*blocks)[resptr] = VECTOR(Qmapping)[i]; + VECTOR(Qmapping)[i] = 0; + } + resptr++; + } + + /* Plus the original graph */ + if (blocks) { + igraph_vector_t *orig = IGRAPH_CALLOC(1, igraph_vector_t); + if (!orig) { + IGRAPH_ERROR("Cannot do cohesive blocking", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, orig); + IGRAPH_CHECK(igraph_vector_init_seq(orig, 0, igraph_vcount(graph) - 1)); + VECTOR(*blocks)[0] = orig; + IGRAPH_FINALLY_CLEAN(1); + } + + if (block_tree) { + igraph_vector_t edges; + long int eptr = 0; + IGRAPH_VECTOR_INIT_FINALLY(&edges, noblocks * 2 - 2); + for (i = 1; i < Qptr; i++) { + if (VECTOR(removed)[i]) { + continue; + } + VECTOR(edges)[eptr++] = VECTOR(Qparent)[i]; + VECTOR(edges)[eptr++] = VECTOR(rewritemap)[i]; + } + + IGRAPH_CHECK(igraph_create(block_tree, &edges, noblocks, + IGRAPH_DIRECTED)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_long_destroy(&rewritemap); + igraph_vector_bool_destroy(&removed); + IGRAPH_FINALLY_CLEAN(2); + + } + + igraph_vector_bool_destroy(&Qcheck); + igraph_vector_long_destroy(&Qcohesion); + igraph_vector_long_destroy(&Qparent); + igraph_i_cohesive_blocks_free_vectors(&Qmapping); + IGRAPH_FINALLY_CLEAN(4); + + igraph_vector_ptr_destroy(&Qmapping); + igraph_vector_ptr_destroy(&Q); + IGRAPH_FINALLY_CLEAN(3); /* + the elements of Q, they were + already destroyed */ + + IGRAPH_STATUS("Cohesive blocking done.\n", 0); + + return 0; +} diff --git a/src/rigraph/core/connectivity/components.c b/src/rigraph/core/connectivity/components.c new file mode 100644 index 0000000..a2e923a --- /dev/null +++ b/src/rigraph/core/connectivity/components.c @@ -0,0 +1,1423 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_components.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_progress.h" +#include "igraph_stack.h" +#include "igraph_structural.h" +#include "igraph_vector.h" + +#include "../core/interruption.h" +#include "../operators/subgraph.h" + +#include + +static int igraph_i_clusters_weak(const igraph_t *graph, igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t *no); + +static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t *no); + +/** + * \ingroup structural + * \function igraph_clusters + * \brief Calculates the (weakly or strongly) connected components in a graph. + * + * \param graph The graph object to analyze. + * \param membership First half of the result will be stored here. For + * every vertex the id of its component is given. The vector + * has to be preinitialized and will be resized. Alternatively + * this argument can be \c NULL, in which case it is ignored. + * \param csize The second half of the result. For every component it + * gives its size, the order is defined by the component ids. + * The vector has to be preinitialized and will be resized. + * Alternatively this argument can be \c NULL, in which + * case it is ignored. + * \param no Pointer to an integer, if not \c NULL then the number of + * clusters will be stored here. + * \param mode For directed graph this specifies whether to calculate + * weakly or strongly connected components. Possible values: + * \c IGRAPH_WEAK, + * \c IGRAPH_STRONG. This argument is + * ignored for undirected graphs. + * \return Error code: + * \c IGRAPH_EINVAL: invalid mode argument. + * + * Time complexity: O(|V|+|E|), + * |V| and + * |E| are the number of vertices and + * edges in the graph. + */ + +int igraph_clusters(const igraph_t *graph, igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t *no, + igraph_connectedness_t mode) { + if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) { + return igraph_i_clusters_weak(graph, membership, csize, no); + } else if (mode == IGRAPH_STRONG) { + return igraph_i_clusters_strong(graph, membership, csize, no); + } + + IGRAPH_ERROR("Cannot calculate clusters", IGRAPH_EINVAL); +} + +static int igraph_i_clusters_weak(const igraph_t *graph, igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t *no) { + + long int no_of_nodes = igraph_vcount(graph); + char *already_added; + long int first_node, act_cluster_size = 0, no_of_clusters = 1; + + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + + long int i; + igraph_vector_t neis = IGRAPH_VECTOR_NULL; + + already_added = IGRAPH_CALLOC(no_of_nodes, char); + if (already_added == 0) { + IGRAPH_ERROR("Cannot calculate clusters", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, already_added); + + IGRAPH_DQUEUE_INIT_FINALLY(&q, no_of_nodes > 100000 ? 10000 : no_of_nodes / 10); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + /* Memory for result, csize is dynamically allocated */ + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + } + if (csize) { + igraph_vector_clear(csize); + } + + /* The algorithm */ + + for (first_node = 0; first_node < no_of_nodes; ++first_node) { + if (already_added[first_node] == 1) { + continue; + } + IGRAPH_ALLOW_INTERRUPTION(); + + already_added[first_node] = 1; + act_cluster_size = 1; + if (membership) { + VECTOR(*membership)[first_node] = no_of_clusters - 1; + } + IGRAPH_CHECK(igraph_dqueue_push(&q, first_node)); + + while ( !igraph_dqueue_empty(&q) ) { + long int act_node = (long int) igraph_dqueue_pop(&q); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, + (igraph_integer_t) act_node, IGRAPH_ALL)); + for (i = 0; i < igraph_vector_size(&neis); i++) { + long int neighbor = (long int) VECTOR(neis)[i]; + if (already_added[neighbor] == 1) { + continue; + } + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + already_added[neighbor] = 1; + act_cluster_size++; + if (membership) { + VECTOR(*membership)[neighbor] = no_of_clusters - 1; + } + } + } + no_of_clusters++; + if (csize) { + IGRAPH_CHECK(igraph_vector_push_back(csize, act_cluster_size)); + } + } + + /* Cleaning up */ + + if (no) { + *no = (igraph_integer_t) no_of_clusters - 1; + } + + IGRAPH_FREE(already_added); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t *no) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t next_nei = IGRAPH_VECTOR_NULL; + + long int i, n, num_seen; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + + long int no_of_clusters = 1; + long int act_cluster_size; + + igraph_vector_t out = IGRAPH_VECTOR_NULL; + const igraph_vector_int_t* tmp; + + igraph_adjlist_t adjlist; + + /* The result */ + + IGRAPH_VECTOR_INIT_FINALLY(&next_nei, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&out, 0); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + if (membership) { + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + } + IGRAPH_CHECK(igraph_vector_reserve(&out, no_of_nodes)); + + igraph_vector_null(&out); + if (csize) { + igraph_vector_clear(csize); + } + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + num_seen = 0; + for (i = 0; i < no_of_nodes; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + + tmp = igraph_adjlist_get(&adjlist, i); + if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) { + continue; + } + + IGRAPH_CHECK(igraph_dqueue_push(&q, i)); + while (!igraph_dqueue_empty(&q)) { + long int act_node = (long int) igraph_dqueue_back(&q); + tmp = igraph_adjlist_get(&adjlist, act_node); + if (VECTOR(next_nei)[act_node] == 0) { + /* this is the first time we've met this vertex */ + VECTOR(next_nei)[act_node]++; + } else if (VECTOR(next_nei)[act_node] <= igraph_vector_int_size(tmp)) { + /* we've already met this vertex but it has more children */ + long int neighbor = (long int) VECTOR(*tmp)[(long int) + VECTOR(next_nei)[act_node] - 1]; + if (VECTOR(next_nei)[neighbor] == 0) { + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + } + VECTOR(next_nei)[act_node]++; + } else { + /* we've met this vertex and it has no more children */ + IGRAPH_CHECK(igraph_vector_push_back(&out, act_node)); + igraph_dqueue_pop_back(&q); + num_seen++; + + if (num_seen % 10000 == 0) { + /* time to report progress and allow the user to interrupt */ + IGRAPH_PROGRESS("Strongly connected components: ", + num_seen * 50.0 / no_of_nodes, NULL); + IGRAPH_ALLOW_INTERRUPTION(); + } + } + } /* while q */ + } /* for */ + + IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL); + + igraph_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + /* OK, we've the 'out' values for the nodes, let's use them in + decreasing order with the help of a heap */ + + igraph_vector_null(&next_nei); /* mark already added vertices */ + num_seen = 0; + + while (!igraph_vector_empty(&out)) { + long int grandfather = (long int) igraph_vector_pop_back(&out); + + if (VECTOR(next_nei)[grandfather] != 0) { + continue; + } + VECTOR(next_nei)[grandfather] = 1; + act_cluster_size = 1; + if (membership) { + VECTOR(*membership)[grandfather] = no_of_clusters - 1; + } + IGRAPH_CHECK(igraph_dqueue_push(&q, grandfather)); + + num_seen++; + if (num_seen % 10000 == 0) { + /* time to report progress and allow the user to interrupt */ + IGRAPH_PROGRESS("Strongly connected components: ", + 50.0 + num_seen * 50.0 / no_of_nodes, NULL); + IGRAPH_ALLOW_INTERRUPTION(); + } + + while (!igraph_dqueue_empty(&q)) { + long int act_node = (long int) igraph_dqueue_pop_back(&q); + tmp = igraph_adjlist_get(&adjlist, act_node); + n = igraph_vector_int_size(tmp); + for (i = 0; i < n; i++) { + long int neighbor = (long int) VECTOR(*tmp)[i]; + if (VECTOR(next_nei)[neighbor] != 0) { + continue; + } + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + VECTOR(next_nei)[neighbor] = 1; + act_cluster_size++; + if (membership) { + VECTOR(*membership)[neighbor] = no_of_clusters - 1; + } + + num_seen++; + if (num_seen % 10000 == 0) { + /* time to report progress and allow the user to interrupt */ + IGRAPH_PROGRESS("Strongly connected components: ", + 50.0 + num_seen * 50.0 / no_of_nodes, NULL); + IGRAPH_ALLOW_INTERRUPTION(); + } + } + } + + no_of_clusters++; + if (csize) { + IGRAPH_CHECK(igraph_vector_push_back(csize, act_cluster_size)); + } + } + + IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL); + + if (no) { + *no = (igraph_integer_t) no_of_clusters - 1; + } + + /* Clean up, return */ + + igraph_adjlist_destroy(&adjlist); + igraph_vector_destroy(&out); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&next_nei); + IGRAPH_FINALLY_CLEAN(4); + + return 0; +} + +static int igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res); + +/** + * \ingroup structural + * \function igraph_is_connected + * \brief Decides whether the graph is (weakly or strongly) connected. + * + * A graph is considered connected when any of its vertices is reachable + * from any other. A directed graph with this property is called + * \em strongly connected. A directed graph that would be connected when + * ignoring the directions of its edges is called \em weakly connected. + * + * + * A graph with zero vertices (i.e. the null graph) is \em not connected by + * definition. This behaviour changed in igraph 0.9; earlier versions assumed + * that the null graph is connected. See the following issue on Github for the + * argument that led us to change the definition: + * https://github.com/igraph/igraph/issues/1539 + * + * \param graph The graph object to analyze. + * \param res Pointer to a logical variable, the result will be stored + * here. + * \param mode For a directed graph this specifies whether to calculate + * weak or strong connectedness. Possible values: + * \c IGRAPH_WEAK, + * \c IGRAPH_STRONG. This argument is + * ignored for undirected graphs. + * \return Error code: + * \c IGRAPH_EINVAL: invalid mode argument. + * + * Time complexity: O(|V|+|E|), the + * number of vertices + * plus the number of edges in the graph. + */ + +int igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, + igraph_connectedness_t mode) { + + long int no_of_nodes = igraph_vcount(graph); + + if (no_of_nodes == 0) { + /* Changed in igraph 0.9; see https://github.com/igraph/igraph/issues/1539 + * for the reasoning behind the change */ + *res = 0; + return IGRAPH_SUCCESS; + } + + if (no_of_nodes == 1) { + *res = 1; + return IGRAPH_SUCCESS; + } + + if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) { + return igraph_is_connected_weak(graph, res); + } else if (mode == IGRAPH_STRONG) { + int retval; + igraph_integer_t no; + + /* A strongly connected graph has at least as many edges as vertices, + * except for the singleton graph, which is handled above. */ + if (igraph_ecount(graph) < no_of_nodes) { + *res = 0; + return IGRAPH_SUCCESS; + } + + retval = igraph_i_clusters_strong(graph, NULL, NULL, &no); + *res = (no == 1); + return retval; + } + + IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL); +} + +static int igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res) { + + long int no_of_nodes = igraph_vcount(graph), no_of_edges = igraph_ecount(graph); + long int added_count; + char *already_added; + igraph_vector_t neis = IGRAPH_VECTOR_NULL; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + + /* By convention, the null graph is not considered connected. + * See https://github.com/igraph/igraph/issues/1538 */ + if (no_of_nodes == 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + + /* A connected graph has at least |V| - 1 edges. */ + if (no_of_edges < no_of_nodes - 1) { + *res = 0; + return IGRAPH_SUCCESS; + } + + already_added = IGRAPH_CALLOC(no_of_nodes, char); + if (already_added == 0) { + IGRAPH_ERROR("Weak connectedness check failed.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, already_added); + + IGRAPH_DQUEUE_INIT_FINALLY(&q, 10); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + /* Try to find at least two clusters */ + already_added[0] = 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + + added_count = 1; + while ( !igraph_dqueue_empty(&q)) { + IGRAPH_ALLOW_INTERRUPTION(); + + long int actnode = (long int) igraph_dqueue_pop(&q); + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, IGRAPH_ALL)); + long int nei_count = igraph_vector_size(&neis); + + for (long int i = 0; i < nei_count; i++) { + long int neighbor = (long int) VECTOR(neis)[i]; + if (already_added[neighbor]) { + continue; + } + + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + added_count++; + already_added[neighbor] = 1; + + if (added_count == no_of_nodes) { + /* We have already reached all nodes: the graph is connected. + * We can stop the traversal now. */ + igraph_dqueue_clear(&q); + break; + } + } + } + + /* Connected? */ + *res = (added_count == no_of_nodes); + + IGRAPH_FREE(already_added); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_decompose_destroy + * \brief Free the memory allocated by \ref igraph_decompose(). + * + * This function destroys and frees all igraph_t + * objects held in \p complist. However, it does not destroy + * \p complist itself, as it was not allocated by \ref igraph_decompose(). + * Use \ref igraph_vector_ptr_destroy() to destroy \p complist. + * + * \param complist The list of graph components, as returned by + * \ref igraph_decompose(). + * + * Time complexity: O(c), c is the number of components. + */ + +void igraph_decompose_destroy(igraph_vector_ptr_t *complist) { + long int i; + for (i = 0; i < igraph_vector_ptr_size(complist); i++) { + if (VECTOR(*complist)[i] != 0) { + igraph_destroy(VECTOR(*complist)[i]); + igraph_free(VECTOR(*complist)[i]); + } + } +} + +static int igraph_i_decompose_weak(const igraph_t *graph, + igraph_vector_ptr_t *components, + long int maxcompno, long int minelements); + +static int igraph_i_decompose_strong(const igraph_t *graph, + igraph_vector_ptr_t *components, + long int maxcompno, long int minelements); + +/** + * \function igraph_decompose + * \brief Decompose a graph into connected components. + * + * Create separate graph for each component of a graph. Note that the + * vertex ids in the new graphs will be different than in the original + * graph. (Except if there is only one component in the original graph.) + * + * \param graph The original graph. + * \param components This pointer vector will contain pointers to the + * subcomponent graphs. It should be initialized before calling this + * function and will be resized to hold the graphs. Don't forget to + * call \ref igraph_destroy() and \ref igraph_free() on the elements of + * this pointer vector to free unneeded memory. Alternatively, you can + * simply call \ref igraph_decompose_destroy() that does this for you. + * \param mode Either \c IGRAPH_WEAK or \c IGRAPH_STRONG for weakly + * and strongly connected components respectively. + * \param maxcompno The maximum number of components to return. The + * first \p maxcompno components will be returned (which hold at + * least \p minelements vertices, see the next parameter), the + * others will be ignored. Supply -1 here if you don't want to limit + * the number of components. + * \param minelements The minimum number of vertices a component + * should contain in order to place it in the \p components + * vector. Eg. supply 2 here to ignore isolated vertices. + * \return Error code, \c IGRAPH_ENOMEM if there is not enough memory + * to perform the operation. + * + * Added in version 0.2. + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number + * of edges. + * + * \example examples/simple/igraph_decompose.c + */ + +int igraph_decompose(const igraph_t *graph, igraph_vector_ptr_t *components, + igraph_connectedness_t mode, + long int maxcompno, long int minelements) { + if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) { + return igraph_i_decompose_weak(graph, components, maxcompno, minelements); + } else if (mode == IGRAPH_STRONG) { + return igraph_i_decompose_strong(graph, components, maxcompno, minelements); + } + + IGRAPH_ERROR("Cannot decompose graph", IGRAPH_EINVAL); +} + +static int igraph_i_decompose_weak(const igraph_t *graph, + igraph_vector_ptr_t *components, + long int maxcompno, long int minelements) { + + long int actstart; + long int no_of_nodes = igraph_vcount(graph); + long int resco = 0; /* number of graphs created so far */ + char *already_added; + igraph_dqueue_t q; + igraph_vector_t verts; + igraph_vector_t neis; + igraph_vector_t vids_old2new; + long int i; + igraph_t *newg; + + + if (maxcompno < 0) { + maxcompno = LONG_MAX; + } + + igraph_vector_ptr_clear(components); + IGRAPH_FINALLY(igraph_decompose_destroy, components); + + /* already_added keeps track of what nodes made it into a graph already */ + already_added = IGRAPH_CALLOC(no_of_nodes, char); + if (already_added == 0) { + IGRAPH_ERROR("Cannot decompose graph", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, already_added); + + IGRAPH_CHECK(igraph_dqueue_init(&q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &q); + IGRAPH_VECTOR_INIT_FINALLY(&verts, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vids_old2new, no_of_nodes); + + /* vids_old2new would have been created internally in igraph_induced_subgraph(), + but it is slow if the graph is large and consists of many small components, + so we create it once here and then re-use it */ + + /* add a node and its neighbors at once, recursively + then switch to next node that has not been added already */ + for (actstart = 0; resco < maxcompno && actstart < no_of_nodes; actstart++) { + + if (already_added[actstart]) { + continue; + } + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_vector_clear(&verts); + + /* add the node itself */ + already_added[actstart] = 1; + IGRAPH_CHECK(igraph_vector_push_back(&verts, actstart)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actstart)); + + /* add the neighbors, recursively */ + while (!igraph_dqueue_empty(&q) ) { + /* pop from the queue of this component */ + long int actvert = (long int) igraph_dqueue_pop(&q); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actvert, + IGRAPH_ALL)); + /* iterate over the neighbors */ + for (i = 0; i < igraph_vector_size(&neis); i++) { + long int neighbor = (long int) VECTOR(neis)[i]; + if (already_added[neighbor] == 1) { + continue; + } + /* add neighbor */ + already_added[neighbor] = 1; + + /* recursion: append neighbor to the queues */ + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_vector_push_back(&verts, neighbor)); + } + } + + /* ok, we have a component */ + if (igraph_vector_size(&verts) < minelements) { + continue; + } + + newg = IGRAPH_CALLOC(1, igraph_t); + if (newg == 0) { + IGRAPH_ERROR("Cannot decompose graph", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(components, newg)); + IGRAPH_CHECK(igraph_i_induced_subgraph_map( + graph, newg, igraph_vss_vector(&verts), + IGRAPH_SUBGRAPH_AUTO, &vids_old2new, + /* invmap = */ 0, /* map_is_prepared = */ 1 + )); + resco++; + + /* vids_old2new does not have to be cleaned up here; since we are doing + * weak decomposition, each vertex will appear in only one of the + * connected components so we won't ever touch an item in vids_old2new + * if it was already set to a non-zero value in a previous component */ + + } /* for actstart++ */ + + igraph_vector_destroy(&vids_old2new); + igraph_vector_destroy(&neis); + igraph_vector_destroy(&verts); + igraph_dqueue_destroy(&q); + IGRAPH_FREE(already_added); + IGRAPH_FINALLY_CLEAN(6); /* + components */ + + return 0; +} + +static int igraph_i_decompose_strong(const igraph_t *graph, + igraph_vector_ptr_t *components, + long int maxcompno, long int minelements) { + + + long int no_of_nodes = igraph_vcount(graph); + + /* this is a heap used twice for checking what nodes have + * been counted already */ + igraph_vector_t next_nei = IGRAPH_VECTOR_NULL; + + long int i, n, num_seen; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + + long int no_of_clusters = 0; + long int act_cluster_size; + + igraph_vector_t out = IGRAPH_VECTOR_NULL; + const igraph_vector_int_t* tmp; + + igraph_adjlist_t adjlist; + igraph_vector_t verts; + igraph_vector_t vids_old2new; + igraph_t *newg; + + if (maxcompno < 0) { + maxcompno = LONG_MAX; + } + + igraph_vector_ptr_clear(components); + IGRAPH_FINALLY(igraph_decompose_destroy, components); + + /* The result */ + + IGRAPH_VECTOR_INIT_FINALLY(&vids_old2new, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&verts, 0); + IGRAPH_VECTOR_INIT_FINALLY(&next_nei, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&out, 0); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_vector_reserve(&out, no_of_nodes)); + + igraph_vector_null(&out); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + /* vids_old2new would have been created internally in igraph_induced_subgraph(), + but it is slow if the graph is large and consists of many small components, + so we create it once here and then re-use it */ + + /* number of components seen */ + num_seen = 0; + /* populate the 'out' vector by browsing a node and following up + all its neighbors recursively, then switching to the next + unassigned node */ + for (i = 0; i < no_of_nodes; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* get all the 'out' neighbors of this node + * NOTE: next_nei is initialized [0, 0, ...] */ + tmp = igraph_adjlist_get(&adjlist, i); + if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) { + continue; + } + + /* add this node to the queue for this component */ + IGRAPH_CHECK(igraph_dqueue_push(&q, i)); + + /* consume the tree from this node ("root") recursively + * until there is no more */ + while (!igraph_dqueue_empty(&q)) { + /* this looks up but does NOT consume the queue */ + long int act_node = (long int) igraph_dqueue_back(&q); + + /* get all neighbors of this node */ + tmp = igraph_adjlist_get(&adjlist, act_node); + if (VECTOR(next_nei)[act_node] == 0) { + /* this is the first time we've met this vertex, + * because next_nei is initialized [0, 0, ...] */ + VECTOR(next_nei)[act_node]++; + /* back to the queue, same vertex is up again */ + + } else if (VECTOR(next_nei)[act_node] <= igraph_vector_int_size(tmp)) { + /* we've already met this vertex but it has more children */ + long int neighbor = (long int) VECTOR(*tmp)[(long int) + VECTOR(next_nei)[act_node] - 1]; + if (VECTOR(next_nei)[neighbor] == 0) { + /* add the root of the other children to the queue */ + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + } + VECTOR(next_nei)[act_node]++; + } else { + /* we've met this vertex and it has no more children */ + IGRAPH_CHECK(igraph_vector_push_back(&out, act_node)); + /* this consumes the queue, since there's nowhere to go */ + igraph_dqueue_pop_back(&q); + num_seen++; + + if (num_seen % 10000 == 0) { + /* time to report progress and allow the user to interrupt */ + IGRAPH_PROGRESS("Strongly connected components: ", + num_seen * 50.0 / no_of_nodes, NULL); + IGRAPH_ALLOW_INTERRUPTION(); + } + } + } /* while q */ + } /* for */ + + IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL); + + igraph_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + /* OK, we've the 'out' values for the nodes, let's use them in + * decreasing order with the help of the next_nei heap */ + + igraph_vector_null(&next_nei); /* mark already added vertices */ + + /* number of components built */ + num_seen = 0; + while (!igraph_vector_empty(&out) && no_of_clusters < maxcompno) { + /* consume the vector from the last element */ + long int grandfather = (long int) igraph_vector_pop_back(&out); + + /* been here, done that + * NOTE: next_nei is initialized as [0, 0, ...] */ + if (VECTOR(next_nei)[grandfather] != 0) { + continue; + } + + /* collect all the members of this component */ + igraph_vector_clear(&verts); + + /* this node is gone for any future components */ + VECTOR(next_nei)[grandfather] = 1; + act_cluster_size = 1; + + /* add to component */ + IGRAPH_CHECK(igraph_vector_push_back(&verts, grandfather)); + IGRAPH_CHECK(igraph_dqueue_push(&q, grandfather)); + + num_seen++; + if (num_seen % 10000 == 0) { + /* time to report progress and allow the user to interrupt */ + IGRAPH_PROGRESS("Strongly connected components: ", + 50.0 + num_seen * 50.0 / no_of_nodes, NULL); + IGRAPH_ALLOW_INTERRUPTION(); + } + + while (!igraph_dqueue_empty(&q)) { + /* consume the queue from this node */ + long int act_node = (long int) igraph_dqueue_pop_back(&q); + tmp = igraph_adjlist_get(&adjlist, act_node); + n = igraph_vector_int_size(tmp); + for (i = 0; i < n; i++) { + long int neighbor = (long int) VECTOR(*tmp)[i]; + if (VECTOR(next_nei)[neighbor] != 0) { + continue; + } + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + VECTOR(next_nei)[neighbor] = 1; + act_cluster_size++; + + /* add to component */ + IGRAPH_CHECK(igraph_vector_push_back(&verts, neighbor)); + + num_seen++; + if (num_seen % 10000 == 0) { + /* time to report progress and allow the user to interrupt */ + IGRAPH_PROGRESS("Strongly connected components: ", + 50.0 + num_seen * 50.0 / no_of_nodes, NULL); + IGRAPH_ALLOW_INTERRUPTION(); + } + } + } + + /* ok, we have a component */ + if (igraph_vector_size(&verts) < minelements) { + continue; + } + + newg = IGRAPH_CALLOC(1, igraph_t); + if (newg == 0) { + IGRAPH_ERROR("Cannot decompose graph", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(components, newg)); + IGRAPH_CHECK(igraph_i_induced_subgraph_map( + graph, newg, igraph_vss_vector(&verts), + IGRAPH_SUBGRAPH_AUTO, &vids_old2new, + /* invmap = */ 0, /* map_is_prepared = */ 1 + )); + + /* vids_old2new has to be cleaned up here because a vertex may appear + * in multiple strongly connected components. Simply calling + * igraph_vector_fill() would be an O(n) operation where n is the number + * of vertices in the large graph so we cannot do that; we have to + * iterate over 'verts' instead */ + n = igraph_vector_size(&verts); + for (i = 0; i < n; i++) { + VECTOR(vids_old2new)[(igraph_integer_t) VECTOR(verts)[i]] = 0; + } + + no_of_clusters++; + } + + IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL); + + /* Clean up, return */ + + igraph_vector_destroy(&vids_old2new); + igraph_vector_destroy(&verts); + igraph_adjlist_destroy(&adjlist); + igraph_vector_destroy(&out); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&next_nei); + IGRAPH_FINALLY_CLEAN(7); /* + components */ + + return 0; + +} + +/** + * \function igraph_articulation_points + * Find the articulation points in a graph. + * + * A vertex is an articulation point if its removal increases + * the number of connected components in the graph. + * \param graph The input graph. + * \param res Pointer to an initialized vector, the + * articulation points will be stored here. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and edges. + * + * \sa \ref igraph_biconnected_components(), \ref igraph_clusters(), \ref igraph_bridges() + */ + +int igraph_articulation_points(const igraph_t *graph, + igraph_vector_t *res) { + + igraph_integer_t no; + return igraph_biconnected_components(graph, &no, 0, 0, 0, res); +} + +void igraph_i_free_vectorlist(igraph_vector_ptr_t *list); + +void igraph_i_free_vectorlist(igraph_vector_ptr_t *list) { + long int i, n = igraph_vector_ptr_size(list); + for (i = 0; i < n; i++) { + igraph_vector_t *v = VECTOR(*list)[i]; + if (v) { + igraph_vector_destroy(v); + IGRAPH_FREE(v); + } + } + igraph_vector_ptr_destroy(list); +} + +/** + * \function igraph_biconnected_components + * Calculate biconnected components + * + * A graph is biconnected if the removal of any single vertex (and + * its incident edges) does not disconnect it. + * + * + * A biconnected component of a graph is a maximal biconnected + * subgraph of it. The biconnected components of a graph can be given + * by the partition of its edges: every edge is a member of exactly + * one biconnected component. Note that this is not true for + * vertices: the same vertex can be part of many biconnected + * components. + * + * + * Somewhat arbitrarily, igraph does not consider components containing + * a single vertex only as being biconnected. Isolated vertices will + * not be part of any of the biconnected components. + * + * \param graph The input graph. + * \param no The number of biconnected components will be stored here. + * \param tree_edges If not a NULL pointer, then the found components + * are stored here, in a list of vectors. Every vector in the list + * is a biconnected component, represented by its edges. More precisely, + * a spanning tree of the biconnected component is returned. + * Note you'll have to + * destroy each vector first by calling \ref igraph_vector_destroy() + * and then \ref igraph_free() on it, plus you need to call + * \ref igraph_vector_ptr_destroy() on the list to regain all + * allocated memory. + * \param component_edges If not a NULL pointer, then the edges of the + * biconnected components are stored here, in the same form as for + * \c tree_edges. + * \param components If not a NULL pointer, then the vertices of the + * biconnected components are stored here, in the same format as + * for the previous two arguments. + * \param articulation_points If not a NULL pointer, then the + * articulation points of the graph are stored in this vector. + * A vertex is an articulation point if its removal increases the + * number of (weakly) connected components in the graph. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges, but only if you do not calculate \c components and + * \c component_edges. If you calculate \c components, then it is + * quadratic in the number of vertices. If you calculate \c + * component_edges as well, then it is cubic in the number of + * vertices. + * + * \sa \ref igraph_articulation_points(), \ref igraph_clusters(). + * + * \example examples/simple/igraph_biconnected_components.c + */ + +int igraph_biconnected_components(const igraph_t *graph, + igraph_integer_t *no, + igraph_vector_ptr_t *tree_edges, + igraph_vector_ptr_t *component_edges, + igraph_vector_ptr_t *components, + igraph_vector_t *articulation_points) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_long_t nextptr; + igraph_vector_long_t num, low; + igraph_vector_bool_t found; + igraph_vector_int_t *adjedges; + igraph_stack_t path; + igraph_vector_t edgestack; + igraph_inclist_t inclist; + long int i, counter, rootdfs = 0; + igraph_vector_long_t vertex_added; + long int comps = 0; + igraph_vector_ptr_t *mycomponents = components, vcomponents; + + IGRAPH_CHECK(igraph_vector_long_init(&nextptr, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &nextptr); + IGRAPH_CHECK(igraph_vector_long_init(&num, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &num); + IGRAPH_CHECK(igraph_vector_long_init(&low, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &low); + IGRAPH_CHECK(igraph_vector_bool_init(&found, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &found); + + IGRAPH_CHECK(igraph_stack_init(&path, 100)); + IGRAPH_FINALLY(igraph_stack_destroy, &path); + IGRAPH_VECTOR_INIT_FINALLY(&edgestack, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edgestack, 100)); + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + + IGRAPH_CHECK(igraph_vector_long_init(&vertex_added, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &vertex_added); + + if (no) { + *no = 0; + } + if (tree_edges) { + igraph_vector_ptr_clear(tree_edges); + } + if (components) { + igraph_vector_ptr_clear(components); + } + if (component_edges) { + igraph_vector_ptr_clear(component_edges); + } + if (articulation_points) { + igraph_vector_clear(articulation_points); + } + if (component_edges && !components) { + mycomponents = &vcomponents; + IGRAPH_CHECK(igraph_vector_ptr_init(mycomponents, 0)); + IGRAPH_FINALLY(igraph_i_free_vectorlist, mycomponents); + } + + for (i = 0; i < no_of_nodes; i++) { + + if (VECTOR(low)[i] != 0) { + continue; /* already visited */ + } + + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_stack_push(&path, i)); + counter = 1; + rootdfs = 0; + VECTOR(low)[i] = VECTOR(num)[i] = counter++; + while (!igraph_stack_empty(&path)) { + long int n; + long int act = (long int) igraph_stack_top(&path); + long int actnext = VECTOR(nextptr)[act]; + + adjedges = igraph_inclist_get(&inclist, act); + n = igraph_vector_int_size(adjedges); + if (actnext < n) { + /* Step down (maybe) */ + long int edge = (long int) VECTOR(*adjedges)[actnext]; + long int nei = IGRAPH_OTHER(graph, edge, act); + if (VECTOR(low)[nei] == 0) { + if (act == i) { + rootdfs++; + } + IGRAPH_CHECK(igraph_vector_push_back(&edgestack, edge)); + IGRAPH_CHECK(igraph_stack_push(&path, nei)); + VECTOR(low)[nei] = VECTOR(num)[nei] = counter++; + } else { + /* Update low value if needed */ + if (VECTOR(num)[nei] < VECTOR(low)[act]) { + VECTOR(low)[act] = VECTOR(num)[nei]; + } + } + VECTOR(nextptr)[act] += 1; + } else { + /* Step up */ + igraph_stack_pop(&path); + if (!igraph_stack_empty(&path)) { + long int prev = (long int) igraph_stack_top(&path); + /* Update LOW value if needed */ + if (VECTOR(low)[act] < VECTOR(low)[prev]) { + VECTOR(low)[prev] = VECTOR(low)[act]; + } + /* Check for articulation point */ + if (VECTOR(low)[act] >= VECTOR(num)[prev]) { + if (articulation_points && !VECTOR(found)[prev] + && prev != i /* the root */) { + IGRAPH_CHECK(igraph_vector_push_back(articulation_points, prev)); + VECTOR(found)[prev] = 1; + } + if (no) { + *no += 1; + } + + /*------------------------------------*/ + /* Record the biconnected component just found */ + if (tree_edges || mycomponents) { + igraph_vector_t *v = 0, *v2 = 0; + comps++; + if (tree_edges) { + v = IGRAPH_CALLOC(1, igraph_vector_t); + if (!v) { + IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(v, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, v); + } + if (mycomponents) { + v2 = IGRAPH_CALLOC(1, igraph_vector_t); + if (!v2) { + IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(v2, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, v2); + } + + while (!igraph_vector_empty(&edgestack)) { + long int e = (long int) igraph_vector_pop_back(&edgestack); + long int from = IGRAPH_FROM(graph, e); + long int to = IGRAPH_TO(graph, e); + if (tree_edges) { + IGRAPH_CHECK(igraph_vector_push_back(v, e)); + } + if (mycomponents) { + if (VECTOR(vertex_added)[from] != comps) { + VECTOR(vertex_added)[from] = comps; + IGRAPH_CHECK(igraph_vector_push_back(v2, from)); + } + if (VECTOR(vertex_added)[to] != comps) { + VECTOR(vertex_added)[to] = comps; + IGRAPH_CHECK(igraph_vector_push_back(v2, to)); + } + } + if (from == prev || to == prev) { + break; + } + } + + if (mycomponents) { + IGRAPH_CHECK(igraph_vector_ptr_push_back(mycomponents, v2)); + IGRAPH_FINALLY_CLEAN(1); + } + if (tree_edges) { + IGRAPH_CHECK(igraph_vector_ptr_push_back(tree_edges, v)); + IGRAPH_FINALLY_CLEAN(1); + } + if (component_edges) { + igraph_vector_t *nodes = VECTOR(*mycomponents)[comps - 1]; + igraph_vector_t *vv = IGRAPH_CALLOC(1, igraph_vector_t); + long int ii, no_vert = igraph_vector_size(nodes); + if (!vv) { + IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(vv, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, vv); + for (ii = 0; ii < no_vert; ii++) { + long int vert = (long int) VECTOR(*nodes)[ii]; + igraph_vector_int_t *edges = igraph_inclist_get(&inclist, + vert); + long int j, nn = igraph_vector_int_size(edges); + for (j = 0; j < nn; j++) { + long int e = (long int) VECTOR(*edges)[j]; + long int nei = IGRAPH_OTHER(graph, e, vert); + if (VECTOR(vertex_added)[nei] == comps && nei < vert) { + IGRAPH_CHECK(igraph_vector_push_back(vv, e)); + } + } + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(component_edges, vv)); + IGRAPH_FINALLY_CLEAN(1); + } + } /* record component if requested */ + /*------------------------------------*/ + + } + } /* !igraph_stack_empty(&path) */ + } + + } /* !igraph_stack_empty(&path) */ + + if (articulation_points && rootdfs >= 2) { + IGRAPH_CHECK(igraph_vector_push_back(articulation_points, i)); + } + + } /* i < no_of_nodes */ + + if (mycomponents != components) { + igraph_i_free_vectorlist(mycomponents); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_long_destroy(&vertex_added); + igraph_inclist_destroy(&inclist); + igraph_vector_destroy(&edgestack); + igraph_stack_destroy(&path); + igraph_vector_bool_destroy(&found); + igraph_vector_long_destroy(&low); + igraph_vector_long_destroy(&num); + igraph_vector_long_destroy(&nextptr); + IGRAPH_FINALLY_CLEAN(8); + + return 0; +} + + +/* igraph_bridges -- find all bridges in the graph */ +/* The algorithm is based on https://www.geeksforgeeks.org/bridge-in-a-graph/ + but instead of keeping track of the parent of each vertex in the DFS tree + we keep track of its incoming edge. This is necessary to support multigraphs. */ + +static int igraph_i_bridges_rec( + const igraph_t *graph, const igraph_inclist_t *il, igraph_integer_t u, + igraph_integer_t *time, igraph_vector_t *bridges, igraph_vector_bool_t *visited, + igraph_vector_int_t *disc, igraph_vector_int_t *low, igraph_vector_int_t *incoming_edge) +{ + igraph_vector_int_t *incedges; + long nc; /* neighbour count */ + long i; + + VECTOR(*visited)[u] = 1; + + *time += 1; + + VECTOR(*disc)[u] = *time; + VECTOR(*low)[u] = *time; + + incedges = igraph_inclist_get(il, u); + nc = igraph_vector_int_size(incedges); + for (i = 0; i < nc; ++i) { + long edge = (long) VECTOR(*incedges)[i]; + igraph_integer_t v = IGRAPH_TO(graph, edge) == u ? IGRAPH_FROM(graph, edge) : IGRAPH_TO(graph, edge); + + if (! VECTOR(*visited)[v]) { + VECTOR(*incoming_edge)[v] = edge; + IGRAPH_CHECK(igraph_i_bridges_rec(graph, il, v, time, bridges, visited, disc, low, incoming_edge)); + + VECTOR(*low)[u] = VECTOR(*low)[u] < VECTOR(*low)[v] ? VECTOR(*low)[u] : VECTOR(*low)[v]; + + if (VECTOR(*low)[v] > VECTOR(*disc)[u]) { + IGRAPH_CHECK(igraph_vector_push_back(bridges, edge)); + } + } else if (edge != VECTOR(*incoming_edge)[u]) { + VECTOR(*low)[u] = VECTOR(*low)[u] < VECTOR(*disc)[v] ? VECTOR(*low)[u] : VECTOR(*disc)[v]; + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_bridges + * Find all bridges in a graph. + * + * An edge is a bridge if its removal increases the number of (weakly) + * connected components in the graph. + * + * \param graph The input graph. + * \param res Pointer to an initialized vector, the + * bridges will be stored here as edge indices. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and edges. + * + * \sa \ref igraph_articulation_points(), \ref igraph_biconnected_components(), \ref igraph_clusters() + */ + +int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges) { + igraph_inclist_t il; + igraph_vector_bool_t visited; + igraph_vector_int_t disc, low; + igraph_vector_int_t incoming_edge; + long n; + long i; + igraph_integer_t time; + + n = igraph_vcount(graph); + + IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + + IGRAPH_CHECK(igraph_vector_bool_init(&visited, n)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited); + + IGRAPH_CHECK(igraph_vector_int_init(&disc, n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &disc); + + IGRAPH_CHECK(igraph_vector_int_init(&low, n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &low); + + IGRAPH_CHECK(igraph_vector_int_init(&incoming_edge, n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &incoming_edge); + for (i = 0; i < n; ++i) { + VECTOR(incoming_edge)[i] = -1; + } + + igraph_vector_clear(bridges); + + time = 0; + for (i = 0; i < n; ++i) + if (! VECTOR(visited)[i]) { + IGRAPH_CHECK(igraph_i_bridges_rec(graph, &il, i, &time, bridges, &visited, &disc, &low, &incoming_edge)); + } + + igraph_vector_int_destroy(&incoming_edge); + igraph_vector_int_destroy(&low); + igraph_vector_int_destroy(&disc); + igraph_vector_bool_destroy(&visited); + igraph_inclist_destroy(&il); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_subcomponent + * \brief The vertices in the same component as a given vertex. + * + * \param graph The graph object. + * \param res The result, vector with the ids of the vertices in the + * same component. + * \param vertex The id of the vertex of which the component is + * searched. + * \param mode Type of the component for directed graphs, possible + * values: + * \clist + * \cli IGRAPH_OUT + * the set of vertices reachable \em from the + * \p vertex, + * \cli IGRAPH_IN + * the set of vertices from which the + * \p vertex is reachable. + * \cli IGRAPH_ALL + * the graph is considered as an + * undirected graph. Note that this is \em not the same + * as the union of the previous two. + * \endclist + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * \p vertex is an invalid vertex id + * \cli IGRAPH_EINVMODE + * invalid mode argument passed. + * \endclist + * + * Time complexity: O(|V|+|E|), + * |V| and + * |E| are the number of vertices and + * edges in the graph. + * + * \sa \ref igraph_induced_subgraph() if you want a graph object consisting only + * a given set of vertices and the edges between them. + */ +int igraph_subcomponent(const igraph_t *graph, igraph_vector_t *res, igraph_real_t vertex, + igraph_neimode_t mode) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + char *already_added; + long int i, vsize; + igraph_vector_t tmp = IGRAPH_VECTOR_NULL; + + if (!IGRAPH_FINITE(vertex) || vertex < 0 || vertex >= no_of_nodes) { + IGRAPH_ERROR("Vertex id out of range.", IGRAPH_EINVVID); + } + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && + mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE); + } + + already_added = IGRAPH_CALLOC(no_of_nodes, char); + if (already_added == 0) { + IGRAPH_ERROR("Subcomponent failed.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, already_added); + + igraph_vector_clear(res); + + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_dqueue_push(&q, vertex)); + IGRAPH_CHECK(igraph_vector_push_back(res, vertex)); + already_added[(long int)vertex] = 1; + + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_neighbors(graph, &tmp, (igraph_integer_t) actnode, + mode)); + vsize = igraph_vector_size(&tmp); + for (i = 0; i < vsize; i++) { + long int neighbor = (long int) VECTOR(tmp)[i]; + + if (already_added[neighbor]) { + continue; + } + already_added[neighbor] = 1; + IGRAPH_CHECK(igraph_vector_push_back(res, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + } + } + + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&tmp); + IGRAPH_FREE(already_added); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/connectivity/separators.c b/src/rigraph/core/connectivity/separators.c new file mode 100644 index 0000000..1857a76 --- /dev/null +++ b/src/rigraph/core/connectivity/separators.c @@ -0,0 +1,853 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_separators.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_flow.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_structural.h" +#include "igraph_vector.h" + +#include "../core/interruption.h" + +static int igraph_i_is_separator(const igraph_t *graph, + igraph_vit_t *vit, + long int except, + igraph_bool_t *res, + igraph_vector_bool_t *removed, + igraph_dqueue_t *Q, + igraph_vector_t *neis, + long int no_of_nodes) { + + long int start = 0; + + if (IGRAPH_VIT_SIZE(*vit) >= no_of_nodes - 1) { + /* Just need to check that we really have at least n-1 vertices in it */ + igraph_vector_bool_t hit; + long int nohit = 0; + IGRAPH_CHECK(igraph_vector_bool_init(&hit, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &hit); + for (IGRAPH_VIT_RESET(*vit); + !IGRAPH_VIT_END(*vit); + IGRAPH_VIT_NEXT(*vit)) { + long int v = IGRAPH_VIT_GET(*vit); + if (!VECTOR(hit)[v]) { + nohit++; + VECTOR(hit)[v] = 1; + } + } + igraph_vector_bool_destroy(&hit); + IGRAPH_FINALLY_CLEAN(1); + if (nohit >= no_of_nodes - 1) { + *res = 0; + return 0; + } + } + + /* Remove the given vertices from the graph, do a breadth-first + search and check the number of components */ + + if (except < 0) { + for (IGRAPH_VIT_RESET(*vit); + !IGRAPH_VIT_END(*vit); + IGRAPH_VIT_NEXT(*vit)) { + VECTOR(*removed)[ (long int) IGRAPH_VIT_GET(*vit) ] = 1; + } + } else { + /* There is an exception */ + long int i; + for (i = 0, IGRAPH_VIT_RESET(*vit); + i < except; + i++, IGRAPH_VIT_NEXT(*vit)) { + VECTOR(*removed)[ (long int) IGRAPH_VIT_GET(*vit) ] = 1; + } + for (IGRAPH_VIT_NEXT(*vit); + !IGRAPH_VIT_END(*vit); + IGRAPH_VIT_NEXT(*vit)) { + VECTOR(*removed)[ (long int) IGRAPH_VIT_GET(*vit) ] = 1; + } + } + + /* Look for the first node that is not removed */ + while (start < no_of_nodes && VECTOR(*removed)[start]) { + start++; + } + + if (start == no_of_nodes) { + IGRAPH_ERROR("All vertices are included in the separator", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_dqueue_push(Q, start)); + VECTOR(*removed)[start] = 1; + while (!igraph_dqueue_empty(Q)) { + long int node = (long int) igraph_dqueue_pop(Q); + long int j, n; + IGRAPH_CHECK(igraph_neighbors(graph, neis, (igraph_integer_t) node, IGRAPH_ALL)); + n = igraph_vector_size(neis); + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + if (!VECTOR(*removed)[nei]) { + IGRAPH_CHECK(igraph_dqueue_push(Q, nei)); + VECTOR(*removed)[nei] = 1; + } + } + } + + /* Look for the next node that was neighter removed, not visited */ + while (start < no_of_nodes && VECTOR(*removed)[start]) { + start++; + } + + /* If there is another component, then we have a separator */ + *res = (start < no_of_nodes); + + return 0; +} + +/** + * \function igraph_is_separator + * \brief Would removing this set of vertices disconnect the graph? + * + * \param graph The input graph. It may be directed, but edge + * directions are ignored. + * \param candidate The candidate separator. It must not contain all + * vertices. + * \param res Pointer to a Boolean variable, the result is stored here. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number vertices and edges. + * + * \example examples/simple/igraph_is_separator.c + */ + +int igraph_is_separator(const igraph_t *graph, + const igraph_vs_t candidate, + igraph_bool_t *res) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_bool_t removed; + igraph_dqueue_t Q; + igraph_vector_t neis; + igraph_vit_t vit; + + IGRAPH_CHECK(igraph_vit_create(graph, candidate, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_CHECK(igraph_vector_bool_init(&removed, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &removed); + IGRAPH_CHECK(igraph_dqueue_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &Q); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + IGRAPH_CHECK(igraph_i_is_separator(graph, &vit, -1, res, &removed, + &Q, &neis, no_of_nodes)); + + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&Q); + igraph_vector_bool_destroy(&removed); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(4); + + return 0; +} + +/** + * \function igraph_is_minimal_separator + * \brief Decides whether a set of vertices is a minimal separator. + * + * A set of vertices is a minimal separator, if the removal of the + * vertices disconnects the graph, and this is not true for any subset + * of the set. + * + * This implementation first checks that the given + * candidate is a separator, by calling \ref + * igraph_is_separator(). If it is a separator, then it checks that + * each subset of size n-1, where n is the size of the candidate, is + * not a separator. + * + * \param graph The input graph. It may be directed, but edge + * directions are ignored. + * \param candidate Pointer to a vector of long integers, the + * candidate minimal separator. + * \param res Pointer to a boolean variable, the result is stored + * here. + * \return Error code. + * + * Time complexity: O(n(|V|+|E|)), |V| is the number of vertices, |E| + * is the number of edges, n is the number vertices in the candidate + * separator. + * + * \example examples/simple/igraph_is_minimal_separator.c + */ + +int igraph_is_minimal_separator(const igraph_t *graph, + const igraph_vs_t candidate, + igraph_bool_t *res) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_bool_t removed; + igraph_dqueue_t Q; + igraph_vector_t neis; + long int candsize; + igraph_vit_t vit; + + IGRAPH_CHECK(igraph_vit_create(graph, candidate, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + candsize = IGRAPH_VIT_SIZE(vit); + + IGRAPH_CHECK(igraph_vector_bool_init(&removed, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &removed); + IGRAPH_CHECK(igraph_dqueue_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &Q); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + /* Is it a separator at all? */ + IGRAPH_CHECK(igraph_i_is_separator(graph, &vit, -1, res, &removed, + &Q, &neis, no_of_nodes)); + if (!(*res)) { + /* Not a separator at all, nothing to do, *res is already set */ + } else if (candsize == 0) { + /* Nothing to do, minimal, *res is already set */ + } else { + /* General case, we need to remove each vertex from 'candidate' + * and check whether the remainder is a separator. If this is + * false for all vertices, then 'candidate' is a minimal + * separator. + */ + long int i; + for (i = 0, *res = 0; i < candsize && (!*res); i++) { + igraph_vector_bool_null(&removed); + IGRAPH_CHECK(igraph_i_is_separator(graph, &vit, i, res, &removed, + &Q, &neis, no_of_nodes)); + } + (*res) = (*res) ? 0 : 1; /* opposite */ + } + + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&Q); + igraph_vector_bool_destroy(&removed); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(4); + + return 0; +} + +/* --------------------------------------------------------------------*/ + +#define UPDATEMARK() do { \ + (*mark)++; \ + if (!(*mark)) { \ + igraph_vector_null(leaveout); \ + (*mark)=1; \ + } \ + } while (0) + +static int igraph_i_clusters_leaveout(const igraph_adjlist_t *adjlist, + igraph_vector_t *components, + igraph_vector_t *leaveout, + unsigned long int *mark, + igraph_dqueue_t *Q) { + + /* Another trick: we use the same 'leaveout' vector to mark the + * vertices that were already found in the BFS + */ + + long int i, no_of_nodes = igraph_adjlist_size(adjlist); + + igraph_dqueue_clear(Q); + igraph_vector_clear(components); + + for (i = 0; i < no_of_nodes; i++) { + + if (VECTOR(*leaveout)[i] == *mark) { + continue; + } + + VECTOR(*leaveout)[i] = *mark; + igraph_dqueue_push(Q, i); + igraph_vector_push_back(components, i); + + while (!igraph_dqueue_empty(Q)) { + long int act_node = (long int) igraph_dqueue_pop(Q); + igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, act_node); + long int j, n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + if (VECTOR(*leaveout)[nei] == *mark) { + continue; + } + IGRAPH_CHECK(igraph_dqueue_push(Q, nei)); + VECTOR(*leaveout)[nei] = *mark; + igraph_vector_push_back(components, nei); + } + } + + igraph_vector_push_back(components, -1); + } + + UPDATEMARK(); + + return 0; +} + +static igraph_bool_t igraph_i_separators_newsep(const igraph_vector_ptr_t *comps, + const igraph_vector_t *newc) { + + long int co, nocomps = igraph_vector_ptr_size(comps); + + for (co = 0; co < nocomps; co++) { + igraph_vector_t *act = VECTOR(*comps)[co]; + if (igraph_vector_all_e(act, newc)) { + return 0; + } + } + + /* If not found, then it is new */ + return 1; +} + +static int igraph_i_separators_store(igraph_vector_ptr_t *separators, + const igraph_adjlist_t *adjlist, + igraph_vector_t *components, + igraph_vector_t *leaveout, + unsigned long int *mark, + igraph_vector_t *sorter) { + + /* We need to stote N(C), the neighborhood of C, but only if it is + * not already stored among the separators. + */ + + long int cptr = 0, next, complen = igraph_vector_size(components); + + while (cptr < complen) { + long int saved = cptr; + igraph_vector_clear(sorter); + + /* Calculate N(C) for the next C */ + + while ( (next = (long int) VECTOR(*components)[cptr++]) != -1) { + VECTOR(*leaveout)[next] = *mark; + } + cptr = saved; + + while ( (next = (long int) VECTOR(*components)[cptr++]) != -1) { + igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, next); + long int j, nn = igraph_vector_int_size(neis); + for (j = 0; j < nn; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + if (VECTOR(*leaveout)[nei] != *mark) { + igraph_vector_push_back(sorter, nei); + VECTOR(*leaveout)[nei] = *mark; + } + } + } + igraph_vector_sort(sorter); + + UPDATEMARK(); + + /* Add it to the list of separators, if it is new */ + + if (igraph_i_separators_newsep(separators, sorter)) { + igraph_vector_t *newc = IGRAPH_CALLOC(1, igraph_vector_t); + if (!newc) { + IGRAPH_ERROR("Cannot calculate minimal separators", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newc); + igraph_vector_copy(newc, sorter); + IGRAPH_FINALLY(igraph_vector_destroy, newc); + IGRAPH_CHECK(igraph_vector_ptr_push_back(separators, newc)); + IGRAPH_FINALLY_CLEAN(2); + } + } /* while cptr < complen */ + + return 0; +} + +static void igraph_i_separators_free(igraph_vector_ptr_t *separators) { + long int i, n = igraph_vector_ptr_size(separators); + for (i = 0; i < n; i++) { + igraph_vector_t *vec = VECTOR(*separators)[i]; + if (vec) { + igraph_vector_destroy(vec); + IGRAPH_FREE(vec); + } + } +} + +/** + * \function igraph_all_minimal_st_separators + * \brief List all vertex sets that are minimal (s,t) separators for some s and t. + * + * This function lists all vertex sets that are minimal (s,t) + * separators for some (s,t) vertex pair. + * + * + * Note that some vertex sets returned by this function may not be minimal + * with respect to disconnecting the graph (or increasing the number of + * connected components). Take for example the 5-vertex graph with edges + * 0-1-2-3-4-1. This function returns the vertex sets + * {1}, {2,4} and {1,3}. + * Notice that {1,3} is not minimal with respect to disconnecting + * the graph, as {1} would be sufficient for that. However, it is + * minimal with respect to separating vertices \c 2 and \c 4. + * + * + * See more about the implemented algorithm in + * Anne Berry, Jean-Paul Bordat and Olivier Cogis: Generating All the + * Minimal Separators of a Graph, In: Peter Widmayer, Gabriele Neyer + * and Stephan Eidenbenz (editors): Graph-theoretic concepts in + * computer science, 1665, 167--172, 1999. Springer. + * https://doi.org/10.1007/3-540-46784-X_17 + * + * \param graph The input graph. It may be directed, but edge + * directions are ignored. + * \param separators An initialized pointer vector, the separators + * are stored here. It is a list of pointers to igraph_vector_t + * objects. Each vector will contain the ids of the vertices in + * the separator. + * To free all memory allocated for \p separators, you need call + * \ref igraph_vector_destroy() and then \ref igraph_free() on + * each element, before destroying the pointer vector itself. + * \return Error code. + * + * \sa \ref igraph_minimum_size_separators() + * + * Time complexity: O(n|V|^3), |V| is the number of vertices, n is the + * number of separators. + * + * \example examples/simple/igraph_minimal_separators.c + */ + +int igraph_all_minimal_st_separators(const igraph_t *graph, + igraph_vector_ptr_t *separators) { + + /* + * Some notes about the tricks used here. For finding the components + * of the graph after removing some vertices, we do the + * following. First we mark the vertices with the actual mark stamp + * (mark), then run breadth-first search on the graph, but not + * considering the marked vertices. Then we increase the mark. If + * there is integer overflow here, then we zero out the mark and set + * it to one. (We might as well just always zero it out.) + * + * For each separator the vertices are stored in vertex id order. + * This facilitates the comparison of the separators when we find a + * potential new candidate. + * + * To keep track of which separator we already used as a basis, we + * keep a boolean vector (already_tried). The try_next pointer show + * the next separator to try as a basis. + */ + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t leaveout; + igraph_vector_bool_t already_tried; + long int try_next = 0; + unsigned long int mark = 1; + long int v; + + igraph_adjlist_t adjlist; + igraph_vector_t components; + igraph_dqueue_t Q; + igraph_vector_t sorter; + + igraph_vector_ptr_clear(separators); + IGRAPH_FINALLY(igraph_i_separators_free, separators); + + IGRAPH_CHECK(igraph_vector_init(&leaveout, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_destroy, &leaveout); + IGRAPH_CHECK(igraph_vector_bool_init(&already_tried, 0)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &already_tried); + IGRAPH_CHECK(igraph_vector_init(&components, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &components); + IGRAPH_CHECK(igraph_vector_reserve(&components, no_of_nodes * 2)); + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + IGRAPH_CHECK(igraph_dqueue_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &Q); + IGRAPH_CHECK(igraph_vector_init(&sorter, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &sorter); + IGRAPH_CHECK(igraph_vector_reserve(&sorter, no_of_nodes)); + + /* --------------------------------------------------------------- + * INITIALIZATION, we check whether the neighborhoods of the + * vertices separate the graph. The ones that do will form the + * initial basis. + */ + + for (v = 0; v < no_of_nodes; v++) { + + /* Mark v and its neighbors */ + igraph_vector_int_t *neis = igraph_adjlist_get(&adjlist, v); + long int i, n = igraph_vector_int_size(neis); + VECTOR(leaveout)[v] = mark; + for (i = 0; i < n; i++) { + long int nei = (long int) VECTOR(*neis)[i]; + VECTOR(leaveout)[nei] = mark; + } + + /* Find the components */ + IGRAPH_CHECK(igraph_i_clusters_leaveout(&adjlist, &components, &leaveout, + &mark, &Q)); + + /* Store the corresponding separators, N(C) for each component C */ + IGRAPH_CHECK(igraph_i_separators_store(separators, &adjlist, &components, + &leaveout, &mark, &sorter)); + + } + + /* --------------------------------------------------------------- + * GENERATION, we need to use all already found separators as + * basis and see if they generate more separators + */ + + while (try_next < igraph_vector_ptr_size(separators)) { + igraph_vector_t *basis = VECTOR(*separators)[try_next]; + long int b, basislen = igraph_vector_size(basis); + for (b = 0; b < basislen; b++) { + + /* Remove N(x) U basis */ + long int x = (long int) VECTOR(*basis)[b]; + igraph_vector_int_t *neis = igraph_adjlist_get(&adjlist, x); + long int i, n = igraph_vector_int_size(neis); + for (i = 0; i < basislen; i++) { + long int sn = (long int) VECTOR(*basis)[i]; + VECTOR(leaveout)[sn] = mark; + } + for (i = 0; i < n; i++) { + long int nei = (long int) VECTOR(*neis)[i]; + VECTOR(leaveout)[nei] = mark; + } + + /* Find the components */ + IGRAPH_CHECK(igraph_i_clusters_leaveout(&adjlist, &components, + &leaveout, &mark, &Q)); + + /* Store the corresponding separators, N(C) for each component C */ + IGRAPH_CHECK(igraph_i_separators_store(separators, &adjlist, + &components, &leaveout, &mark, + &sorter)); + } + + try_next++; + } + + /* --------------------------------------------------------------- */ + + igraph_vector_destroy(&sorter); + igraph_dqueue_destroy(&Q); + igraph_adjlist_destroy(&adjlist); + igraph_vector_destroy(&components); + igraph_vector_bool_destroy(&already_tried); + igraph_vector_destroy(&leaveout); + IGRAPH_FINALLY_CLEAN(7); /* +1 for separators */ + + return 0; +} + +#undef UPDATEMARK + +static int igraph_i_minimum_size_separators_append(igraph_vector_ptr_t *old, + igraph_vector_ptr_t *new) { + + long int olen = igraph_vector_ptr_size(old); + long int nlen = igraph_vector_ptr_size(new); + long int i; + + for (i = 0; i < nlen; i++) { + igraph_vector_t *newvec = VECTOR(*new)[i]; + long int j; + for (j = 0; j < olen; j++) { + igraph_vector_t *oldvec = VECTOR(*old)[j]; + if (igraph_vector_all_e(oldvec, newvec)) { + break; + } + } + if (j == olen) { + IGRAPH_CHECK(igraph_vector_ptr_push_back(old, newvec)); + olen++; + } else { + igraph_vector_destroy(newvec); + igraph_free(newvec); + } + VECTOR(*new)[i] = 0; + } + igraph_vector_ptr_clear(new); + + return 0; +} + +static int igraph_i_minimum_size_separators_topkdeg(const igraph_t *graph, + igraph_vector_t *res, + long int k) { + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t deg, order; + long int i; + + IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), IGRAPH_ALL, + /*loops=*/ 0)); + + IGRAPH_CHECK(igraph_vector_order1(°, &order, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(res, k)); + for (i = 0; i < k; i++) { + VECTOR(*res)[i] = VECTOR(order)[no_of_nodes - 1 - i]; + } + + igraph_vector_destroy(&order); + igraph_vector_destroy(°); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +static void igraph_i_separators_stcuts_free(igraph_vector_ptr_t *p) { + long int i, n = igraph_vector_ptr_size(p); + for (i = 0; i < n; i++) { + igraph_vector_t *v = VECTOR(*p)[i]; + if (v) { + igraph_vector_destroy(v); + igraph_free(v); + VECTOR(*p)[i] = 0; + } + } + igraph_vector_ptr_destroy(p); +} + +/** + * \function igraph_minimum_size_separators + * \brief Find all minimum size separating vertex sets. + * + * This function lists all separator vertex sets of minimum size. + * A vertex set is a separator if its removal disconnects the graph. + * + * + * The implementation is based on the following paper: + * Arkady Kanevsky: Finding all minimum-size separating vertex sets in + * a graph, Networks 23, 533--541, 1993. + * + * \param graph The input graph, which must be undirected. + * \param separators An initialized pointer vector, the separators + * are stored here. It is a list of pointers to igraph_vector_t + * objects. Each vector will contain the ids of the vertices in + * the separator. + * To free all memory allocated for \c separators, you need call + * \ref igraph_vector_destroy() and then \ref igraph_free() on + * each element, before destroying the pointer vector itself. + * \return Error code. + * + * Time complexity: TODO. + * + * \example examples/simple/igraph_minimum_size_separators.c + */ + +int igraph_minimum_size_separators(const igraph_t *graph, + igraph_vector_ptr_t *separators) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_integer_t conn; long int k; + igraph_vector_t X; + long int i, j; + igraph_bool_t issepX; + igraph_t Gbar; + igraph_vector_t phi; + igraph_t graph_copy; + igraph_vector_t capacity; + igraph_maxflow_stats_t stats; + + if (igraph_is_directed(graph)) { + IGRAPH_ERROR("Minimum size separators currently only works on undirected graphs", + IGRAPH_EINVAL); + } + + igraph_vector_ptr_clear(separators); + IGRAPH_FINALLY(igraph_i_separators_free, separators); + + /* ---------------------------------------------------------------- */ + /* 1 Find the vertex connectivity of 'graph' */ + IGRAPH_CHECK(igraph_vertex_connectivity(graph, &conn, + /* checks= */ 1)); k = conn; + + /* Special cases for low connectivity, two exits here! */ + if (conn == 0) { + /* Nothing to do */ + IGRAPH_FINALLY_CLEAN(1); /* separators */ + return 0; + } else if (conn == 1) { + igraph_vector_t ap; + long int i, n; + IGRAPH_VECTOR_INIT_FINALLY(&ap, 0); + IGRAPH_CHECK(igraph_articulation_points(graph, &ap)); + n = igraph_vector_size(&ap); + IGRAPH_CHECK(igraph_vector_ptr_resize(separators, n)); + igraph_vector_ptr_null(separators); + for (i = 0; i < n; i++) { + igraph_vector_t *v = IGRAPH_CALLOC(1, igraph_vector_t); + if (!v) { + IGRAPH_ERROR("Minimum size separators failed", IGRAPH_ENOMEM); + } + IGRAPH_VECTOR_INIT_FINALLY(v, 1); + VECTOR(*v)[0] = VECTOR(ap)[i]; + VECTOR(*separators)[i] = v; + IGRAPH_FINALLY_CLEAN(1); + } + igraph_vector_destroy(&ap); + IGRAPH_FINALLY_CLEAN(2); /* +1 for separators */ + return 0; + } else if (conn == no_of_nodes - 1) { + long int k; + IGRAPH_CHECK(igraph_vector_ptr_resize(separators, no_of_nodes)); + igraph_vector_ptr_null(separators); + for (i = 0; i < no_of_nodes; i++) { + igraph_vector_t *v = IGRAPH_CALLOC(1, igraph_vector_t); + if (!v) { + IGRAPH_ERROR("Cannot list minimum size separators", IGRAPH_ENOMEM); + } + IGRAPH_VECTOR_INIT_FINALLY(v, no_of_nodes - 1); + for (j = 0, k = 0; j < no_of_nodes; j++) { + if (j != i) { + VECTOR(*v)[k++] = j; + } + } + VECTOR(*separators)[i] = v; + IGRAPH_FINALLY_CLEAN(1); + } + IGRAPH_FINALLY_CLEAN(1); /* separators */ + return 0; + } + + /* Work on a copy of 'graph' */ + IGRAPH_CHECK(igraph_copy(&graph_copy, graph)); + IGRAPH_FINALLY(igraph_destroy, &graph_copy); + IGRAPH_CHECK(igraph_simplify(&graph_copy, /* multiple */ 1, /* loops */ 1, NULL)); + + /* ---------------------------------------------------------------- */ + /* 2 Find k vertices with the largest degrees (x1;..,xk). Check + if these k vertices form a separating k-set of G */ + IGRAPH_CHECK(igraph_vector_init(&X, conn)); + IGRAPH_FINALLY(igraph_vector_destroy, &X); + IGRAPH_CHECK(igraph_i_minimum_size_separators_topkdeg(&graph_copy, &X, k)); + IGRAPH_CHECK(igraph_is_separator(&graph_copy, igraph_vss_vector(&X), + &issepX)); + if (issepX) { + igraph_vector_t *v = IGRAPH_CALLOC(1, igraph_vector_t); + if (!v) { + IGRAPH_ERROR("Cannot find minimal size separators", IGRAPH_ENOMEM); + } + IGRAPH_VECTOR_INIT_FINALLY(v, k); + for (i = 0; i < k; i++) { + VECTOR(*v)[i] = VECTOR(X)[i]; + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(separators, v)); + IGRAPH_FINALLY_CLEAN(1); + } + + /* Create Gbar, the Even-Tarjan reduction of graph */ + IGRAPH_VECTOR_INIT_FINALLY(&capacity, 0); + IGRAPH_CHECK(igraph_even_tarjan_reduction(&graph_copy, &Gbar, &capacity)); + IGRAPH_FINALLY(igraph_destroy, &Gbar); + + IGRAPH_VECTOR_INIT_FINALLY(&phi, no_of_edges); + + /* ---------------------------------------------------------------- */ + /* 3 If v[j] != x[i] and v[j] is not adjacent to x[i] then */ + for (i = 0; i < k; i++) { + + IGRAPH_ALLOW_INTERRUPTION(); + + for (j = 0; j < no_of_nodes; j++) { + long int ii = (long int) VECTOR(X)[i]; + igraph_real_t phivalue; + igraph_bool_t conn; + + if (ii == j) { + continue; /* the same vertex */ + } + IGRAPH_CHECK(igraph_are_connected(&graph_copy, (igraph_integer_t) ii, + (igraph_integer_t) j, &conn)); + if (conn) { + continue; /* they are connected */ + } + + /* --------------------------------------------------------------- */ + /* 4 Compute a maximum flow phi in Gbar from x[i] to v[j]. + If |phi|=k, then */ + IGRAPH_CHECK(igraph_maxflow(&Gbar, &phivalue, &phi, /*cut=*/ 0, + /*partition=*/ 0, /*partition2=*/ 0, + /* source= */ + (igraph_integer_t) (ii + no_of_nodes), + /* target= */ (igraph_integer_t) j, + &capacity, &stats)); + + if (phivalue == k) { + + /* ------------------------------------------------------------- */ + /* 5-6-7. Find all k-sets separating x[i] and v[j]. */ + igraph_vector_ptr_t stcuts; + IGRAPH_CHECK(igraph_vector_ptr_init(&stcuts, 0)); + IGRAPH_FINALLY(igraph_i_separators_stcuts_free, &stcuts); + IGRAPH_CHECK(igraph_all_st_mincuts(&Gbar, /*value=*/ 0, + /*cuts=*/ &stcuts, + /*partition1s=*/ 0, + /*source=*/ (igraph_integer_t) + (ii + no_of_nodes), + /*target=*/ (igraph_integer_t) j, + /*capacity=*/ &capacity)); + + IGRAPH_CHECK(igraph_i_minimum_size_separators_append(separators, + &stcuts)); + igraph_vector_ptr_destroy(&stcuts); + IGRAPH_FINALLY_CLEAN(1); + + } /* if phivalue == k */ + + /* --------------------------------------------------------------- */ + /* 8 Add edge (x[i],v[j]) to G. */ + IGRAPH_CHECK(igraph_add_edge(&graph_copy, (igraph_integer_t) ii, + (igraph_integer_t) j)); + IGRAPH_CHECK(igraph_add_edge(&Gbar, (igraph_integer_t) (ii + no_of_nodes), + (igraph_integer_t) j)); + IGRAPH_CHECK(igraph_add_edge(&Gbar, (igraph_integer_t) (j + no_of_nodes), + (igraph_integer_t) ii)); + IGRAPH_CHECK(igraph_vector_push_back(&capacity, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_push_back(&capacity, no_of_nodes)); + + } /* for j M2) { + M1 = M2; + } + for (k = 0; k < M1; k++) { + IGRAPH_CHECK(igraph_vector_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(edges, j)); + } + } + } + + return 0; +} + +/** + * \ingroup generators + * \function igraph_adjacency + * \brief Creates a graph from an adjacency matrix. + * + * The order of the vertices in the matrix is preserved, i.e. the vertex + * corresponding to the first row/column will be vertex with id 0, the + * next row is for vertex 1, etc. + * \param graph Pointer to an uninitialized graph object. + * \param adjmatrix The adjacency matrix. How it is interpreted + * depends on the \p mode argument. + * \param mode Constant to specify how the given matrix is interpreted + * as an adjacency matrix. Possible values + * (A(i,j) + * is the element in row i and column + * j in the adjacency matrix + * \p adjmatrix): + * \clist + * \cli IGRAPH_ADJ_DIRECTED + * the graph will be directed and + * an element gives the number of edges between two vertices. + * \cli IGRAPH_ADJ_UNDIRECTED + * this is the same as \c IGRAPH_ADJ_MAX, + * for convenience. + * \cli IGRAPH_ADJ_MAX + * undirected graph will be created + * and the number of edges between vertices + * i and + * j is + * max(A(i,j), A(j,i)). + * \cli IGRAPH_ADJ_MIN + * undirected graph will be created + * with min(A(i,j), A(j,i)) + * edges between vertices + * i and + * j. + * \cli IGRAPH_ADJ_PLUS + * undirected graph will be created + * with A(i,j)+A(j,i) edges + * between vertices + * i and + * j. + * \cli IGRAPH_ADJ_UPPER + * undirected graph will be created, + * only the upper right triangle (including the diagonal) is + * used for the number of edges. + * \cli IGRAPH_ADJ_LOWER + * undirected graph will be created, + * only the lower left triangle (including the diagonal) is + * used for creating the edges. + * \endclist + * \return Error code, + * \c IGRAPH_NONSQUARE: non-square matrix. + * + * Time complexity: O(|V||V|), + * |V| is the number of vertices in the graph. + * + * \example examples/simple/igraph_adjacency.c + */ +int igraph_adjacency(igraph_t *graph, const igraph_matrix_t *adjmatrix, + igraph_adjacency_t mode) { + + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + long int no_of_nodes; + + /* Some checks */ + if (igraph_matrix_nrow(adjmatrix) != igraph_matrix_ncol(adjmatrix)) { + IGRAPH_ERROR("Non-square matrix", IGRAPH_NONSQUARE); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + /* Collect the edges */ + no_of_nodes = igraph_matrix_nrow(adjmatrix); + switch (mode) { + case IGRAPH_ADJ_DIRECTED: + IGRAPH_CHECK(igraph_i_adjacency_directed(adjmatrix, &edges)); + break; + case IGRAPH_ADJ_MAX: + IGRAPH_CHECK(igraph_i_adjacency_max(adjmatrix, &edges)); + break; + case IGRAPH_ADJ_UPPER: + IGRAPH_CHECK(igraph_i_adjacency_upper(adjmatrix, &edges)); + break; + case IGRAPH_ADJ_LOWER: + IGRAPH_CHECK(igraph_i_adjacency_lower(adjmatrix, &edges)); + break; + case IGRAPH_ADJ_MIN: + IGRAPH_CHECK(igraph_i_adjacency_min(adjmatrix, &edges)); + break; + case IGRAPH_ADJ_PLUS: + IGRAPH_CHECK(igraph_i_adjacency_directed(adjmatrix, &edges)); + break; + } + + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + (mode == IGRAPH_ADJ_DIRECTED))); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_weighted_adjacency_directed( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_plus( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_max( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_upper( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_lower( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_min( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops); + +static int igraph_i_weighted_adjacency_directed( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops) { + + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j; + + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < no_of_nodes; j++) { + igraph_real_t M = MATRIX(*adjmatrix, i, j); + if (M == 0.0) { + continue; + } + if (i == j && !loops) { + continue; + } + IGRAPH_CHECK(igraph_vector_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + } + } + + return 0; +} + +static int igraph_i_weighted_adjacency_plus( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops) { + + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j; + + for (i = 0; i < no_of_nodes; i++) { + for (j = i; j < no_of_nodes; j++) { + igraph_real_t M = MATRIX(*adjmatrix, i, j) + MATRIX(*adjmatrix, j, i); + if (M == 0.0) { + continue; + } + if (i == j && !loops) { + continue; + } + if (i == j) { + M /= 2; + } + IGRAPH_CHECK(igraph_vector_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + } + } + + return 0; +} + +static int igraph_i_weighted_adjacency_max( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops) { + + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j; + + for (i = 0; i < no_of_nodes; i++) { + for (j = i; j < no_of_nodes; j++) { + igraph_real_t M1 = MATRIX(*adjmatrix, i, j); + igraph_real_t M2 = MATRIX(*adjmatrix, j, i); + if (M1 < M2) { + M1 = M2; + } + if (M1 == 0.0) { + continue; + } + if (i == j && !loops) { + continue; + } + IGRAPH_CHECK(igraph_vector_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); + } + } + return 0; +} + +static int igraph_i_weighted_adjacency_upper( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops) { + + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j; + + for (i = 0; i < no_of_nodes; i++) { + for (j = i; j < no_of_nodes; j++) { + igraph_real_t M = MATRIX(*adjmatrix, i, j); + if (M == 0.0) { + continue; + } + if (i == j && !loops) { + continue; + } + IGRAPH_CHECK(igraph_vector_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + } + } + return 0; +} + +static int igraph_i_weighted_adjacency_lower( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops) { + + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j; + + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j <= i; j++) { + igraph_real_t M = MATRIX(*adjmatrix, i, j); + if (M == 0.0) { + continue; + } + if (i == j && !loops) { + continue; + } + IGRAPH_CHECK(igraph_vector_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + } + } + return 0; +} + +static int igraph_i_weighted_adjacency_min( + const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges, + igraph_vector_t *weights, + igraph_bool_t loops) { + + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j; + + for (i = 0; i < no_of_nodes; i++) { + for (j = i; j < no_of_nodes; j++) { + igraph_real_t M1 = MATRIX(*adjmatrix, i, j); + igraph_real_t M2 = MATRIX(*adjmatrix, j, i); + if (M1 > M2) { + M1 = M2; + } + if (M1 == 0.0) { + continue; + } + if (i == j && !loops) { + continue; + } + IGRAPH_CHECK(igraph_vector_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); + } + } + + return 0; +} + +/** + * \ingroup generators + * \function igraph_weighted_adjacency + * \brief Creates a graph from a weighted adjacency matrix. + * + * The order of the vertices in the matrix is preserved, i.e. the vertex + * corresponding to the first row/column will be vertex with id 0, the + * next row is for vertex 1, etc. + * \param graph Pointer to an uninitialized graph object. + * \param adjmatrix The weighted adjacency matrix. How it is interpreted + * depends on the \p mode argument. The common feature is that + * edges with zero weights are considered nonexistent (however, + * negative weights are permitted). + * \param mode Constant to specify how the given matrix is interpreted + * as an adjacency matrix. Possible values + * (A(i,j) + * is the element in row i and column + * j in the adjacency matrix + * \p adjmatrix): + * \clist + * \cli IGRAPH_ADJ_DIRECTED + * the graph will be directed and + * an element gives the weight of the edge between two vertices. + * \cli IGRAPH_ADJ_UNDIRECTED + * this is the same as \c IGRAPH_ADJ_MAX, + * for convenience. + * \cli IGRAPH_ADJ_MAX + * undirected graph will be created + * and the weight of the edge between vertices + * i and + * j is + * max(A(i,j), A(j,i)). + * \cli IGRAPH_ADJ_MIN + * undirected graph will be created + * with edge weight min(A(i,j), A(j,i)) + * between vertices + * i and + * j. + * \cli IGRAPH_ADJ_PLUS + * undirected graph will be created + * with edge weight A(i,j)+A(j,i) + * between vertices + * i and + * j. + * \cli IGRAPH_ADJ_UPPER + * undirected graph will be created, + * only the upper right triangle (including the diagonal) is + * used for the edge weights. + * \cli IGRAPH_ADJ_LOWER + * undirected graph will be created, + * only the lower left triangle (including the diagonal) is + * used for the edge weights. + * \endclist + * \param attr the name of the attribute that will store the edge weights. + * If \c NULL , it will use \c weight as the attribute name. + * \param loops Logical scalar, whether to ignore the diagonal elements + * in the adjacency matrix. + * \return Error code, + * \c IGRAPH_NONSQUARE: non-square matrix. + * + * Time complexity: O(|V||V|), + * |V| is the number of vertices in the graph. + * + * \example examples/simple/igraph_weighted_adjacency.c + */ +int igraph_weighted_adjacency(igraph_t *graph, const igraph_matrix_t *adjmatrix, + igraph_adjacency_t mode, const char* attr, + igraph_bool_t loops) { + + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + igraph_vector_t weights = IGRAPH_VECTOR_NULL; + const char* default_attr = "weight"; + igraph_vector_ptr_t attr_vec; + igraph_attribute_record_t attr_rec; + long int no_of_nodes; + + /* Some checks */ + if (igraph_matrix_nrow(adjmatrix) != igraph_matrix_ncol(adjmatrix)) { + IGRAPH_ERROR("Non-square matrix", IGRAPH_NONSQUARE); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&weights, 0); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&attr_vec, 1); + + /* Collect the edges */ + no_of_nodes = igraph_matrix_nrow(adjmatrix); + switch (mode) { + case IGRAPH_ADJ_DIRECTED: + IGRAPH_CHECK(igraph_i_weighted_adjacency_directed(adjmatrix, &edges, + &weights, loops)); + break; + case IGRAPH_ADJ_MAX: + IGRAPH_CHECK(igraph_i_weighted_adjacency_max(adjmatrix, &edges, + &weights, loops)); + break; + case IGRAPH_ADJ_UPPER: + IGRAPH_CHECK(igraph_i_weighted_adjacency_upper(adjmatrix, &edges, + &weights, loops)); + break; + case IGRAPH_ADJ_LOWER: + IGRAPH_CHECK(igraph_i_weighted_adjacency_lower(adjmatrix, &edges, + &weights, loops)); + break; + case IGRAPH_ADJ_MIN: + IGRAPH_CHECK(igraph_i_weighted_adjacency_min(adjmatrix, &edges, + &weights, loops)); + break; + case IGRAPH_ADJ_PLUS: + IGRAPH_CHECK(igraph_i_weighted_adjacency_plus(adjmatrix, &edges, + &weights, loops)); + break; + } + + /* Prepare attribute record */ + attr_rec.name = attr ? attr : default_attr; + attr_rec.type = IGRAPH_ATTRIBUTE_NUMERIC; + attr_rec.value = &weights; + VECTOR(attr_vec)[0] = &attr_rec; + + /* Create graph */ + IGRAPH_CHECK(igraph_empty(graph, (igraph_integer_t) no_of_nodes, + (mode == IGRAPH_ADJ_DIRECTED))); + IGRAPH_FINALLY(igraph_destroy, graph); + if (igraph_vector_size(&edges) > 0) { + IGRAPH_CHECK(igraph_add_edges(graph, &edges, &attr_vec)); + } + IGRAPH_FINALLY_CLEAN(1); + + /* Cleanup */ + igraph_vector_destroy(&edges); + igraph_vector_destroy(&weights); + igraph_vector_ptr_destroy(&attr_vec); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \function igraph_adjlist + * \brief Creates a graph from an adjacency list. + * + * An adjacency list is a list of vectors, containing the neighbors + * of all vertices. For operations that involve many changes to the + * graph structure, it is recommended that you convert the graph into + * an adjacency list via \ref igraph_adjlist_init(), perform the + * modifications (these are cheap for an adjacency list) and then + * recreate the igraph graph via this function. + * + * \param graph Pointer to an uninitialized graph object. + * \param adjlist The adjacency list. + * \param mode Whether or not to create a directed graph. \c IGRAPH_ALL + * means an undirected graph, \c IGRAPH_OUT means a + * directed graph from an out-adjacency list (i.e. each + * list contains the successors of the corresponding + * vertices), \c IGRAPH_IN means a directed graph from an + * in-adjacency list + * \param duplicate Logical, for undirected graphs this specified + * whether each edge is included twice, in the vectors of + * both adjacent vertices. If this is false (0), then it is + * assumed that every edge is included only once. This argument + * is ignored for directed graphs. + * \return Error code. + * + * \sa \ref igraph_adjlist_init() for the opposite operation. + * + * Time complexity: O(|V|+|E|). + * + */ +int igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, + igraph_neimode_t mode, igraph_bool_t duplicate) { + + long int no_of_nodes = igraph_adjlist_size(adjlist); + long int no_of_edges = 0; + long int i; + + igraph_vector_t edges; + long int edgeptr = 0; + + duplicate = duplicate && (mode == IGRAPH_ALL); /* only duplicate if undirected */ + + for (i = 0; i < no_of_nodes; i++) { + no_of_edges += igraph_vector_int_size(igraph_adjlist_get(adjlist, i)); + } + + if (duplicate) { + no_of_edges /= 2; + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * no_of_edges); + + for (i = 0; i < no_of_nodes; i++) { + igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, i); + long int j, n = igraph_vector_int_size(neis); + long int loops = 0; + + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + if (nei == i) { + loops++; + } else { + if (! duplicate || nei > i) { + if (edgeptr + 2 > 2 * no_of_edges) { + IGRAPH_ERROR("Invalid adjacency list, most probably not correctly" + " duplicated edges for an undirected graph", IGRAPH_EINVAL); + } + if (mode == IGRAPH_IN) { + VECTOR(edges)[edgeptr++] = nei; + VECTOR(edges)[edgeptr++] = i; + } else { + VECTOR(edges)[edgeptr++] = i; + VECTOR(edges)[edgeptr++] = nei; + } + } + } + } + /* loops */ + if (duplicate) { + loops = loops / 2; + } + if (edgeptr + 2 * loops > 2 * no_of_edges) { + IGRAPH_ERROR("Invalid adjacency list, most probably not correctly" + " duplicated edges for an undirected graph", IGRAPH_EINVAL); + } + for (j = 0; j < loops; j++) { + VECTOR(edges)[edgeptr++] = i; + VECTOR(edges)[edgeptr++] = i; + } + } + + if (mode == IGRAPH_ALL) + IGRAPH_CHECK(igraph_create(graph, &edges, + (igraph_integer_t) no_of_nodes, 0)); + else + IGRAPH_CHECK(igraph_create(graph, &edges, + (igraph_integer_t) no_of_nodes, 1)); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} diff --git a/src/rigraph/core/constructors/atlas-edges.h b/src/rigraph/core/constructors/atlas-edges.h new file mode 100644 index 0000000..229e8f1 --- /dev/null +++ b/src/rigraph/core/constructors/atlas-edges.h @@ -0,0 +1,1296 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus + #define __BEGIN_DECLS extern "C" { + #define __END_DECLS } +#else + #define __BEGIN_DECLS /* empty */ + #define __END_DECLS /* empty */ +#endif + +__BEGIN_DECLS + +#include "igraph_types.h" + +const igraph_real_t igraph_i_atlas_edges[] = { + 0, 0, + 1, 0, + 2, 0, + 2, 1, 0, 1, + 3, 0, + 3, 1, 1, 2, + 3, 2, 0, 1, 0, 2, + 3, 3, 0, 1, 0, 2, 1, 2, + 4, 0, + 4, 1, 3, 2, + 4, 2, 3, 2, 3, 1, + 4, 2, 0, 1, 3, 2, + 4, 3, 3, 2, 1, 2, 3, 1, + 4, 3, 3, 0, 3, 1, 3, 2, + 4, 3, 0, 1, 1, 2, 0, 3, + 4, 4, 3, 2, 1, 2, 3, 1, 3, 0, + 4, 4, 0, 1, 1, 2, 2, 3, 0, 3, + 4, 5, 0, 1, 0, 2, 0, 3, 1, 2, 2, 3, + 4, 6, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, + 5, 0, + 5, 1, 4, 3, + 5, 2, 1, 2, 0, 1, + 5, 2, 0, 2, 4, 3, + 5, 3, 1, 2, 0, 1, 2, 0, + 5, 3, 4, 3, 3, 2, 3, 1, + 5, 3, 3, 2, 4, 3, 0, 4, + 5, 3, 1, 2, 0, 1, 4, 3, + 5, 4, 4, 3, 1, 2, 3, 1, 3, 2, + 5, 4, 0, 3, 1, 0, 2, 1, 3, 2, + 5, 4, 4, 3, 4, 0, 4, 1, 4, 2, + 5, 4, 4, 0, 3, 1, 4, 3, 3, 2, + 5, 4, 2, 3, 1, 2, 0, 1, 4, 0, + 5, 4, 1, 2, 0, 1, 2, 0, 4, 3, + 5, 5, 0, 3, 2, 0, 3, 2, 1, 0, 2, 1, + 5, 5, 4, 2, 4, 3, 2, 3, 4, 1, 4, 0, + 5, 5, 0, 1, 1, 2, 2, 3, 0, 4, 0, 2, + 5, 5, 4, 0, 1, 2, 4, 3, 3, 2, 3, 1, + 5, 5, 1, 0, 4, 1, 2, 4, 3, 2, 1, 3, + 5, 5, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, + 5, 6, 1, 0, 4, 1, 4, 0, 0, 3, 1, 3, 3, 4, + 5, 6, 1, 0, 4, 1, 2, 4, 3, 2, 1, 3, 2, 1, + 5, 6, 1, 0, 4, 1, 2, 4, 3, 2, 1, 3, 3, 4, + 5, 6, 0, 1, 4, 3, 2, 3, 4, 2, 4, 0, 4, 1, + 5, 6, 0, 4, 3, 0, 4, 3, 2, 3, 1, 2, 0, 1, + 5, 6, 2, 1, 0, 2, 3, 0, 1, 3, 4, 1, 0, 4, + 5, 7, 4, 0, 1, 2, 4, 3, 3, 2, 3, 1, 4, 1, 2, 4, + 5, 7, 4, 1, 2, 4, 3, 2, 1, 3, 3, 4, 0, 3, 4, 0, + 5, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, + 5, 7, 2, 1, 0, 2, 3, 0, 1, 3, 4, 1, 0, 4, 2, 4, + 5, 8, 1, 0, 4, 1, 2, 4, 3, 2, 1, 3, 4, 0, 3, 4, 0, 3, + 5, 8, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, + 5, 9, 0, 1, 3, 4, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, + 5, 10, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, + 6, 0, + 6, 1, 5, 4, + 6, 2, 0, 3, 5, 4, + 6, 2, 1, 3, 1, 2, + 6, 3, 1, 3, 2, 1, 3, 2, + 6, 3, 0, 3, 5, 0, 4, 0, + 6, 3, 4, 3, 5, 4, 0, 5, + 6, 3, 4, 3, 5, 1, 5, 2, + 6, 3, 1, 2, 3, 0, 5, 4, + 6, 4, 0, 3, 4, 0, 5, 4, 0, 5, + 6, 4, 3, 0, 5, 3, 4, 5, 0, 4, + 6, 4, 5, 1, 5, 3, 5, 2, 0, 5, + 6, 4, 4, 3, 3, 1, 4, 0, 3, 2, + 6, 4, 0, 2, 1, 3, 2, 1, 5, 3, + 6, 4, 1, 3, 2, 1, 3, 2, 0, 5, + 6, 4, 1, 2, 0, 3, 5, 0, 4, 0, + 6, 4, 4, 5, 1, 2, 0, 5, 3, 4, + 6, 4, 0, 2, 4, 0, 3, 1, 5, 3, + 6, 5, 3, 0, 5, 3, 4, 5, 0, 4, 5, 0, + 6, 5, 5, 3, 3, 1, 3, 2, 4, 3, 4, 5, + 6, 5, 5, 3, 5, 4, 2, 3, 3, 4, 0, 4, + 6, 5, 4, 3, 1, 2, 4, 0, 3, 2, 3, 1, + 6, 5, 1, 4, 3, 4, 4, 0, 2, 1, 3, 2, + 6, 5, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, + 6, 5, 5, 3, 5, 4, 5, 0, 5, 1, 5, 2, + 6, 5, 1, 4, 5, 1, 1, 0, 2, 1, 2, 3, + 6, 5, 0, 1, 3, 4, 0, 2, 3, 0, 5, 3, + 6, 5, 1, 0, 2, 1, 2, 4, 1, 3, 5, 3, + 6, 5, 4, 3, 0, 5, 4, 0, 3, 2, 3, 1, + 6, 5, 1, 2, 0, 1, 4, 5, 1, 3, 2, 3, + 6, 5, 0, 1, 0, 5, 2, 3, 3, 4, 4, 5, + 6, 5, 4, 3, 5, 1, 5, 2, 0, 3, 4, 0, + 6, 5, 1, 2, 3, 0, 5, 3, 4, 5, 0, 4, + 6, 6, 0, 3, 5, 0, 4, 5, 3, 4, 5, 3, 4, 0, + 6, 6, 1, 4, 2, 4, 4, 0, 2, 3, 3, 1, 3, 4, + 6, 6, 1, 4, 2, 4, 4, 0, 2, 1, 3, 1, 2, 3, + 6, 6, 2, 0, 5, 4, 4, 3, 5, 3, 4, 0, 2, 4, + 6, 6, 3, 2, 4, 3, 0, 4, 1, 0, 2, 1, 0, 3, + 6, 6, 4, 1, 3, 1, 4, 2, 3, 2, 2, 0, 1, 0, + 6, 6, 5, 2, 5, 3, 5, 4, 3, 4, 5, 1, 5, 0, + 6, 6, 4, 3, 4, 2, 4, 0, 1, 4, 3, 0, 5, 3, + 6, 6, 4, 3, 3, 5, 5, 4, 5, 1, 3, 2, 4, 0, + 6, 6, 4, 2, 1, 2, 4, 3, 4, 1, 4, 0, 0, 5, + 6, 6, 1, 2, 3, 1, 0, 3, 2, 0, 4, 0, 5, 0, + 6, 6, 2, 0, 4, 2, 1, 4, 2, 1, 3, 1, 5, 3, + 6, 6, 1, 2, 3, 1, 0, 3, 2, 0, 4, 0, 5, 3, + 6, 6, 5, 3, 2, 5, 2, 0, 4, 2, 4, 3, 3, 1, + 6, 6, 0, 2, 3, 4, 1, 0, 5, 3, 4, 5, 3, 0, + 6, 6, 1, 2, 3, 0, 5, 3, 4, 5, 0, 4, 5, 0, + 6, 6, 4, 3, 1, 2, 4, 0, 3, 2, 3, 1, 5, 0, + 6, 6, 1, 4, 2, 4, 4, 0, 0, 5, 3, 1, 2, 3, + 6, 6, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 5, + 6, 6, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, + 6, 6, 1, 3, 2, 1, 3, 2, 0, 4, 5, 0, 4, 5, + 6, 7, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 0, 5, + 6, 7, 1, 4, 2, 4, 2, 1, 3, 1, 2, 3, 2, 0, 0, 1, + 6, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, + 6, 7, 0, 1, 3, 2, 0, 2, 3, 0, 3, 1, 5, 1, 5, 2, + 6, 7, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 3, 4, + 6, 7, 1, 0, 4, 1, 2, 4, 3, 2, 5, 1, 2, 5, 1, 2, + 6, 7, 0, 4, 2, 0, 1, 2, 3, 1, 5, 3, 3, 0, 2, 3, + 6, 7, 1, 4, 2, 4, 2, 3, 2, 1, 3, 1, 4, 5, 0, 4, + 6, 7, 1, 0, 4, 1, 2, 4, 3, 2, 5, 1, 2, 5, 4, 5, + 6, 7, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 4, + 6, 7, 0, 5, 4, 0, 5, 4, 0, 2, 3, 0, 3, 2, 0, 1, + 6, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 5, 4, 1, + 6, 7, 0, 1, 4, 0, 1, 4, 0, 2, 3, 0, 3, 2, 3, 5, + 6, 7, 1, 4, 2, 4, 4, 0, 0, 5, 3, 1, 2, 3, 3, 4, + 6, 7, 2, 0, 3, 2, 4, 3, 5, 4, 2, 5, 1, 2, 4, 1, + 6, 7, 1, 5, 0, 1, 4, 0, 3, 4, 2, 3, 1, 2, 0, 3, + 6, 7, 1, 4, 2, 4, 4, 0, 0, 5, 3, 1, 2, 3, 2, 1, + 6, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 0, 2, 5, 1, + 6, 7, 2, 0, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3, + 6, 7, 5, 0, 3, 5, 2, 3, 0, 2, 1, 3, 4, 1, 3, 4, + 6, 7, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 2, 3, + 6, 7, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, + 6, 7, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, + 6, 7, 1, 2, 0, 1, 2, 0, 3, 0, 4, 3, 5, 4, 3, 5, + 6, 8, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, + 6, 8, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, + 6, 8, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 0, 0, 4, + 6, 8, 1, 2, 3, 1, 0, 3, 1, 0, 2, 0, 3, 2, 5, 3, 4, 0, + 6, 8, 0, 1, 2, 4, 0, 2, 5, 2, 3, 1, 3, 2, 2, 1, 4, 1, + 6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 1, 5, + 6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, + 6, 8, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 2, 1, 5, 1, + 6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 0, + 6, 8, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 3, 0, 5, 1, + 6, 8, 2, 0, 3, 2, 4, 3, 5, 4, 2, 5, 1, 2, 4, 1, 5, 3, + 6, 8, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 0, 5, 5, 4, + 6, 8, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 5, 1, 5, 3, + 6, 8, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 3, 4, + 6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 2, 0, 2, + 6, 8, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, + 6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, + 6, 8, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 1, 4, 0, 1, + 6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, + 6, 8, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 2, 1, + 6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 5, 5, 3, 1, 5, + 6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 1, + 6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 5, 5, 2, 5, 0, + 6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 1, 5, 2, + 6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, + 6, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 2, + 6, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 4, + 6, 9, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 4, 5, + 6, 9, 2, 0, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3, 3, 2, 4, 3, + 6, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 5, + 6, 9, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, + 6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0, + 6, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 0, 4, 1, 0, 4, 1, + 6, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 1, 0, 5, 1, + 6, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 4, 4, 0, 5, 0, + 6, 9, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, + 6, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, + 6, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, + 6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, + 6, 9, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 2, 0, + 6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, + 6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1, + 6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 4, 2, 5, 2, + 6, 9, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 5, 2, + 6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 5, 2, 4, 1, + 6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2, + 6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 4, 5, + 6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 5, + 6, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, 1, 0, + 6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 5, 1, 5, + 6, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 2, 4, + 6, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2, + 6, 10, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2, + 6, 10, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 5, 2, + 6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1, + 6, 10, 0, 1, 2, 4, 0, 2, 4, 5, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, + 6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4, + 6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 0, 2, 1, 3, 5, 1, + 6, 10, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 3, 2, 0, 3, + 6, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 5, 3, 2, 5, 1, 0, + 6, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2, 1, 5, + 6, 11, 0, 1, 2, 4, 0, 2, 2, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, + 6, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5, + 6, 11, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 0, 2, + 6, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 5, 3, 2, 5, 1, 0, 5, 1, + 6, 11, 1, 3, 4, 1, 3, 4, 2, 3, 0, 2, 4, 0, 5, 4, 2, 5, 4, 2, 0, 5, 1, 5, + 6, 11, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, + 6, 11, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, + 6, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 0, 3, + 6, 12, 0, 1, 1, 2, 0, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 4, 3, + 6, 12, 3, 2, 1, 3, 2, 1, 0, 2, 5, 0, 2, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 0, 4, + 6, 12, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, 4, 5, + 6, 12, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 2, 3, + 6, 12, 0, 1, 1, 2, 0, 2, 3, 2, 3, 1, 4, 0, 2, 4, 5, 1, 0, 5, 4, 5, 3, 4, 5, 3, + 6, 13, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 2, 3, 0, 4, + 6, 13, 0, 1, 1, 2, 0, 2, 3, 2, 3, 1, 4, 0, 2, 4, 5, 1, 0, 5, 4, 5, 3, 4, 5, 3, 3, 0, + 6, 14, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 1, 3, 2, 0, 4, 0, 5, 3, + 6, 15, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 4, 5, + 7, 0, + 7, 1, 6, 5, + 7, 2, 2, 3, 1, 2, + 7, 2, 5, 4, 6, 0, + 7, 3, 0, 4, 4, 2, 2, 0, + 7, 3, 0, 1, 0, 6, 0, 5, + 7, 3, 5, 4, 6, 0, 5, 6, + 7, 3, 3, 2, 1, 2, 5, 6, + 7, 3, 3, 1, 5, 6, 0, 4, + 7, 4, 2, 5, 6, 2, 5, 6, 1, 2, + 7, 4, 1, 2, 4, 1, 5, 4, 2, 5, + 7, 4, 1, 0, 5, 1, 1, 2, 4, 1, + 7, 4, 1, 0, 2, 1, 5, 2, 6, 2, + 7, 4, 3, 4, 2, 3, 1, 2, 0, 1, + 7, 4, 4, 2, 0, 4, 2, 0, 5, 6, + 7, 4, 0, 1, 6, 0, 0, 5, 4, 2, + 7, 4, 3, 1, 5, 4, 6, 5, 0, 6, + 7, 4, 0, 4, 3, 0, 2, 5, 6, 2, + 7, 4, 2, 3, 1, 2, 6, 0, 5, 4, + 7, 5, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, + 7, 5, 2, 5, 6, 2, 5, 6, 4, 2, 3, 2, + 7, 5, 4, 2, 4, 0, 2, 0, 5, 4, 6, 0, + 7, 5, 2, 5, 6, 2, 5, 6, 1, 2, 0, 1, + 7, 5, 4, 1, 0, 4, 3, 0, 1, 3, 2, 1, + 7, 5, 1, 2, 0, 1, 4, 0, 3, 4, 2, 3, + 7, 5, 5, 1, 5, 0, 2, 5, 3, 5, 4, 5, + 7, 5, 1, 5, 6, 1, 1, 0, 2, 1, 3, 2, + 7, 5, 1, 5, 4, 1, 2, 3, 6, 2, 2, 1, + 7, 5, 1, 5, 6, 1, 1, 2, 2, 3, 4, 3, + 7, 5, 2, 1, 3, 2, 4, 3, 5, 4, 3, 6, + 7, 5, 6, 5, 2, 6, 1, 2, 5, 2, 3, 4, + 7, 5, 4, 3, 5, 4, 6, 5, 0, 6, 1, 0, + 7, 5, 0, 4, 3, 0, 2, 5, 6, 2, 5, 6, + 7, 5, 4, 1, 5, 2, 6, 5, 3, 6, 2, 3, + 7, 5, 1, 4, 3, 1, 1, 0, 2, 1, 6, 5, + 7, 5, 0, 4, 3, 0, 1, 0, 2, 1, 6, 5, + 7, 5, 0, 4, 3, 0, 2, 1, 5, 2, 6, 2, + 7, 5, 6, 5, 3, 4, 2, 3, 1, 2, 0, 1, + 7, 5, 2, 3, 1, 2, 6, 0, 5, 6, 5, 4, + 7, 5, 0, 1, 4, 6, 5, 4, 3, 2, 6, 5, + 7, 6, 1, 5, 6, 1, 5, 6, 2, 5, 1, 2, 6, 2, + 7, 6, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1, + 7, 6, 0, 4, 3, 0, 1, 3, 2, 1, 1, 4, 3, 4, + 7, 6, 5, 2, 4, 5, 2, 4, 3, 2, 6, 3, 2, 6, + 7, 6, 1, 2, 4, 1, 5, 4, 2, 5, 0, 1, 4, 0, + 7, 6, 1, 2, 5, 1, 4, 5, 2, 4, 0, 2, 5, 0, + 7, 6, 2, 5, 6, 2, 5, 6, 2, 4, 1, 2, 3, 2, + 7, 6, 1, 4, 3, 1, 2, 3, 1, 2, 2, 5, 6, 2, + 7, 6, 5, 4, 6, 5, 1, 6, 5, 1, 3, 6, 0, 1, + 7, 6, 6, 5, 1, 6, 5, 1, 3, 1, 0, 3, 1, 4, + 7, 6, 0, 4, 3, 0, 2, 3, 4, 2, 2, 5, 6, 2, + 7, 6, 1, 4, 3, 1, 2, 3, 1, 2, 2, 5, 6, 5, + 7, 6, 2, 3, 1, 2, 3, 6, 5, 4, 6, 5, 5, 2, + 7, 6, 2, 5, 6, 2, 5, 6, 1, 4, 3, 1, 2, 1, + 7, 6, 4, 5, 0, 4, 3, 0, 2, 3, 4, 2, 6, 3, + 7, 6, 0, 4, 3, 0, 1, 3, 6, 5, 1, 4, 1, 0, + 7, 6, 1, 4, 3, 1, 2, 3, 5, 2, 6, 5, 2, 6, + 7, 6, 6, 3, 5, 6, 4, 5, 1, 4, 2, 1, 5, 2, + 7, 6, 1, 0, 3, 1, 6, 3, 5, 6, 4, 5, 1, 4, + 7, 6, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, + 7, 6, 0, 4, 3, 0, 4, 3, 2, 5, 6, 2, 5, 6, + 7, 6, 6, 3, 0, 6, 6, 2, 5, 6, 6, 1, 4, 6, + 7, 6, 2, 4, 5, 2, 2, 3, 6, 2, 1, 2, 1, 0, + 7, 6, 1, 0, 2, 1, 5, 2, 1, 4, 3, 1, 6, 2, + 7, 6, 1, 0, 2, 1, 3, 6, 1, 3, 4, 1, 5, 4, + 7, 6, 1, 0, 2, 1, 5, 2, 6, 5, 1, 4, 3, 1, + 7, 6, 1, 0, 2, 4, 5, 2, 6, 5, 2, 6, 3, 2, + 7, 6, 4, 0, 1, 4, 3, 1, 2, 1, 5, 2, 6, 2, + 7, 6, 6, 5, 1, 2, 0, 1, 2, 0, 3, 2, 0, 4, + 7, 6, 0, 4, 3, 0, 1, 0, 2, 1, 5, 2, 6, 2, + 7, 6, 1, 0, 3, 1, 6, 3, 2, 6, 4, 1, 5, 4, + 7, 6, 2, 5, 6, 2, 4, 2, 1, 4, 3, 1, 0, 3, + 7, 6, 0, 4, 3, 0, 2, 3, 4, 2, 1, 2, 6, 5, + 7, 6, 0, 4, 3, 0, 2, 1, 5, 2, 6, 5, 2, 6, + 7, 6, 3, 4, 1, 0, 2, 1, 5, 2, 6, 5, 2, 6, + 7, 6, 4, 5, 0, 4, 3, 0, 6, 3, 1, 0, 2, 1, + 7, 6, 2, 5, 6, 2, 5, 6, 1, 4, 3, 1, 1, 0, + 7, 6, 4, 5, 3, 4, 2, 3, 1, 2, 0, 1, 6, 0, + 7, 6, 6, 4, 5, 6, 4, 5, 2, 3, 1, 2, 0, 1, + 7, 6, 0, 1, 4, 0, 2, 3, 5, 2, 6, 5, 3, 6, + 7, 6, 1, 2, 0, 1, 4, 0, 3, 4, 2, 3, 6, 5, + 7, 7, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1, 3, 4, + 7, 7, 1, 2, 5, 1, 4, 5, 2, 4, 0, 2, 5, 0, 5, 2, + 7, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, + 7, 7, 1, 2, 5, 1, 4, 5, 2, 4, 0, 2, 5, 0, 1, 0, + 7, 7, 0, 4, 3, 0, 2, 3, 4, 2, 2, 5, 6, 2, 2, 0, + 7, 7, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1, 2, 6, + 7, 7, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 3, 4, 6, 3, + 7, 7, 0, 4, 3, 0, 2, 3, 4, 2, 2, 5, 6, 2, 3, 4, + 7, 7, 0, 4, 3, 0, 1, 3, 3, 6, 1, 4, 1, 0, 5, 4, + 7, 7, 0, 4, 3, 0, 1, 3, 6, 5, 1, 4, 1, 0, 3, 4, + 7, 7, 5, 2, 4, 5, 2, 4, 3, 2, 6, 3, 2, 6, 2, 1, + 7, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 0, 2, 2, 5, + 7, 7, 5, 2, 4, 5, 2, 4, 3, 2, 6, 3, 2, 6, 3, 1, + 7, 7, 1, 4, 3, 1, 2, 3, 4, 2, 2, 0, 2, 1, 6, 0, + 7, 7, 1, 2, 5, 1, 4, 5, 2, 4, 0, 2, 5, 0, 3, 5, + 7, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 0, 2, 3, 5, + 7, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 0, 2, 1, 5, + 7, 7, 3, 2, 4, 3, 3, 5, 2, 4, 5, 2, 6, 1, 6, 4, + 7, 7, 1, 2, 5, 1, 4, 5, 2, 4, 0, 2, 5, 0, 0, 3, + 7, 7, 3, 4, 1, 3, 2, 1, 6, 2, 5, 6, 1, 5, 4, 1, + 7, 7, 0, 1, 4, 0, 1, 4, 2, 1, 3, 2, 5, 3, 4, 5, + 7, 7, 6, 3, 5, 6, 1, 5, 2, 1, 3, 2, 4, 2, 5, 4, + 7, 7, 1, 2, 4, 1, 5, 4, 6, 5, 3, 6, 2, 3, 5, 2, + 7, 7, 4, 1, 3, 4, 1, 3, 2, 1, 6, 2, 5, 6, 2, 5, + 7, 7, 3, 0, 6, 3, 0, 6, 1, 0, 0, 2, 5, 0, 0, 4, + 7, 7, 1, 5, 6, 1, 1, 2, 3, 1, 4, 3, 1, 4, 4, 0, + 7, 7, 5, 0, 6, 5, 0, 6, 5, 2, 1, 5, 6, 3, 4, 6, + 7, 7, 4, 1, 0, 4, 1, 0, 2, 1, 0, 3, 6, 0, 4, 5, + 7, 7, 5, 2, 6, 5, 2, 6, 2, 4, 3, 2, 1, 0, 2, 1, + 7, 7, 4, 1, 0, 4, 3, 0, 1, 3, 2, 1, 1, 5, 6, 1, + 7, 7, 1, 0, 4, 1, 0, 4, 5, 4, 2, 1, 3, 2, 6, 1, + 7, 7, 0, 1, 4, 0, 1, 4, 2, 1, 3, 2, 5, 4, 6, 4, + 7, 7, 2, 3, 5, 2, 6, 5, 3, 6, 1, 2, 4, 5, 0, 5, + 7, 7, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 2, 1, 6, 5, + 7, 7, 2, 5, 6, 2, 5, 6, 4, 2, 1, 2, 0, 1, 3, 1, + 7, 7, 2, 5, 6, 2, 4, 2, 1, 4, 3, 1, 2, 3, 0, 1, + 7, 7, 6, 2, 5, 6, 2, 5, 1, 2, 0, 1, 4, 1, 3, 1, + 7, 7, 0, 4, 3, 0, 1, 3, 4, 1, 5, 4, 2, 1, 6, 3, + 7, 7, 2, 5, 6, 2, 5, 6, 4, 5, 3, 6, 1, 2, 0, 1, + 7, 7, 2, 5, 6, 2, 1, 4, 1, 2, 0, 1, 4, 0, 0, 3, + 7, 7, 6, 5, 1, 2, 4, 1, 0, 4, 3, 0, 1, 3, 3, 4, + 7, 7, 4, 1, 0, 4, 1, 0, 3, 6, 2, 3, 0, 2, 5, 0, + 7, 7, 4, 1, 0, 4, 3, 0, 1, 3, 2, 1, 5, 2, 6, 1, + 7, 7, 4, 1, 0, 4, 1, 0, 2, 3, 0, 2, 5, 0, 6, 5, + 7, 7, 0, 1, 5, 0, 6, 5, 3, 6, 2, 3, 0, 2, 4, 0, + 7, 7, 1, 0, 4, 1, 2, 4, 3, 2, 4, 3, 0, 4, 6, 5, + 7, 7, 3, 6, 2, 3, 1, 2, 0, 1, 4, 0, 1, 4, 5, 4, + 7, 7, 1, 0, 5, 1, 6, 5, 2, 6, 1, 2, 3, 2, 4, 3, + 7, 7, 2, 3, 1, 2, 0, 1, 4, 0, 5, 4, 6, 5, 4, 1, + 7, 7, 5, 2, 6, 5, 2, 6, 1, 2, 4, 1, 0, 4, 3, 1, + 7, 7, 2, 3, 1, 2, 0, 1, 4, 0, 5, 4, 6, 5, 5, 2, + 7, 7, 1, 4, 0, 1, 2, 0, 3, 2, 5, 3, 0, 5, 6, 3, + 7, 7, 2, 1, 3, 2, 6, 3, 5, 6, 0, 5, 2, 0, 5, 4, + 7, 7, 5, 2, 6, 5, 2, 6, 1, 2, 0, 1, 4, 0, 3, 0, + 7, 7, 4, 1, 0, 4, 3, 0, 1, 3, 2, 1, 5, 2, 6, 2, + 7, 7, 1, 0, 2, 1, 5, 2, 4, 5, 0, 4, 4, 1, 6, 3, + 7, 7, 2, 5, 6, 2, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, + 7, 7, 6, 5, 0, 4, 3, 0, 1, 3, 4, 1, 2, 4, 3, 2, + 7, 7, 2, 1, 5, 2, 4, 5, 0, 4, 3, 0, 6, 3, 2, 6, + 7, 7, 4, 0, 3, 4, 1, 3, 2, 1, 5, 2, 6, 5, 2, 6, + 7, 7, 6, 5, 2, 6, 1, 2, 4, 1, 0, 4, 3, 0, 1, 3, + 7, 7, 4, 1, 0, 4, 2, 0, 3, 2, 6, 3, 5, 6, 0, 5, + 7, 7, 0, 4, 3, 0, 4, 3, 2, 1, 5, 2, 6, 5, 2, 6, + 7, 7, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 0, 6, + 7, 7, 1, 0, 4, 1, 0, 4, 5, 2, 6, 5, 3, 6, 2, 3, + 7, 8, 0, 1, 4, 0, 5, 4, 2, 5, 1, 2, 5, 1, 4, 1, 2, 4, + 7, 8, 4, 1, 5, 4, 2, 5, 1, 2, 0, 1, 5, 0, 0, 4, 2, 0, + 7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 3, 4, 5, 1, 6, 1, + 7, 8, 4, 1, 5, 4, 2, 5, 1, 2, 5, 1, 6, 5, 2, 4, 3, 2, + 7, 8, 1, 3, 0, 1, 4, 0, 2, 4, 1, 2, 4, 1, 5, 4, 1, 5, + 7, 8, 2, 0, 3, 2, 6, 3, 5, 6, 0, 5, 3, 0, 0, 6, 4, 0, + 7, 8, 1, 0, 2, 1, 5, 2, 4, 5, 0, 4, 2, 0, 5, 0, 6, 5, + 7, 8, 1, 0, 2, 1, 3, 2, 1, 3, 4, 3, 2, 4, 5, 2, 3, 5, + 7, 8, 2, 0, 3, 2, 6, 3, 5, 6, 0, 5, 3, 0, 6, 0, 4, 5, + 7, 8, 1, 0, 2, 1, 4, 3, 1, 5, 4, 1, 2, 4, 5, 2, 3, 5, + 7, 8, 3, 5, 2, 1, 4, 3, 1, 5, 4, 1, 2, 4, 5, 2, 4, 6, + 7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 3, 4, 2, 1, 5, 2, + 7, 8, 3, 5, 2, 1, 4, 3, 1, 5, 4, 1, 2, 4, 5, 2, 0, 3, + 7, 8, 4, 0, 2, 4, 0, 2, 3, 0, 2, 3, 5, 2, 6, 5, 2, 6, + 7, 8, 3, 2, 6, 3, 5, 6, 2, 5, 0, 2, 5, 0, 4, 5, 2, 4, + 7, 8, 0, 5, 4, 0, 2, 4, 5, 2, 1, 5, 4, 1, 3, 4, 5, 3, + 7, 8, 2, 3, 1, 2, 4, 1, 5, 4, 1, 5, 5, 2, 6, 5, 3, 6, + 7, 8, 5, 2, 4, 5, 0, 4, 3, 0, 6, 3, 2, 6, 4, 2, 3, 2, + 7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 2, 4, 3, 2, 5, 4, 2, 5, + 7, 8, 5, 6, 2, 5, 6, 2, 3, 2, 4, 3, 0, 4, 3, 0, 2, 4, + 7, 8, 1, 0, 5, 0, 3, 2, 1, 3, 5, 2, 6, 1, 6, 2, 6, 5, + 7, 8, 5, 4, 6, 5, 3, 6, 0, 3, 4, 0, 2, 4, 3, 2, 0, 2, + 7, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 2, 1, 5, + 7, 8, 5, 0, 6, 2, 0, 6, 1, 0, 2, 1, 5, 2, 4, 5, 4, 6, + 7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 2, 1, 1, 5, 6, 1, + 7, 8, 0, 2, 4, 0, 1, 4, 0, 1, 3, 0, 1, 3, 5, 1, 6, 1, + 7, 8, 4, 2, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 1, 5, 6, 1, + 7, 8, 0, 4, 3, 0, 4, 3, 1, 4, 3, 1, 1, 5, 2, 1, 6, 1, + 7, 8, 2, 1, 0, 2, 3, 0, 5, 3, 2, 5, 3, 2, 4, 3, 6, 5, + 7, 8, 4, 2, 0, 4, 3, 0, 1, 3, 4, 1, 3, 4, 1, 5, 6, 1, + 7, 8, 2, 1, 0, 2, 3, 0, 5, 3, 2, 5, 6, 5, 4, 3, 5, 0, + 7, 8, 1, 0, 2, 1, 3, 2, 1, 3, 4, 2, 3, 4, 4, 5, 6, 4, + 7, 8, 6, 5, 1, 2, 4, 1, 0, 4, 3, 0, 1, 3, 0, 1, 3, 4, + 7, 8, 0, 1, 6, 5, 2, 3, 6, 4, 6, 3, 6, 2, 6, 0, 6, 1, + 7, 8, 6, 4, 1, 2, 2, 3, 6, 5, 4, 5, 6, 2, 6, 0, 6, 1, + 7, 8, 0, 1, 1, 2, 2, 3, 6, 5, 6, 4, 6, 3, 6, 0, 6, 2, + 7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 6, 1, 5, 1, 2, 5, + 7, 8, 3, 0, 2, 3, 4, 2, 0, 4, 1, 0, 2, 1, 5, 2, 6, 2, + 7, 8, 2, 1, 3, 2, 6, 3, 5, 6, 0, 5, 2, 0, 5, 2, 4, 5, + 7, 8, 1, 0, 2, 1, 3, 2, 4, 3, 5, 2, 1, 5, 6, 1, 2, 6, + 7, 8, 2, 5, 4, 2, 1, 4, 3, 1, 0, 3, 1, 0, 2, 1, 6, 2, + 7, 8, 4, 5, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 6, 3, + 7, 8, 0, 1, 4, 0, 1, 4, 2, 1, 4, 2, 5, 4, 1, 5, 6, 3, + 7, 8, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 1, 6, 5, 0, + 7, 8, 4, 5, 0, 4, 1, 0, 4, 1, 3, 0, 1, 3, 6, 1, 2, 6, + 7, 8, 2, 5, 4, 2, 0, 4, 1, 0, 4, 1, 3, 0, 1, 3, 6, 1, + 7, 8, 1, 6, 2, 1, 0, 2, 1, 0, 4, 1, 3, 4, 2, 3, 4, 5, + 7, 8, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 1, 6, 5, 3, + 7, 8, 0, 4, 3, 0, 4, 3, 1, 4, 3, 1, 5, 1, 6, 2, 1, 6, + 7, 8, 2, 3, 1, 2, 0, 1, 5, 0, 4, 5, 0, 4, 2, 0, 6, 5, + 7, 8, 4, 5, 0, 4, 3, 0, 1, 3, 4, 1, 2, 4, 3, 2, 6, 2, + 7, 8, 2, 3, 1, 2, 0, 1, 4, 0, 5, 4, 4, 1, 2, 6, 5, 2, + 7, 8, 0, 1, 1, 2, 2, 3, 6, 3, 4, 5, 6, 2, 6, 0, 6, 1, + 7, 8, 4, 1, 0, 4, 3, 0, 1, 3, 0, 1, 2, 1, 5, 2, 6, 2, + 7, 8, 0, 1, 1, 2, 2, 3, 6, 5, 4, 5, 6, 2, 6, 4, 6, 1, + 7, 8, 0, 1, 4, 0, 0, 2, 5, 0, 6, 5, 3, 6, 2, 3, 5, 2, + 7, 8, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 2, 5, 6, 2, + 7, 8, 4, 5, 3, 4, 1, 3, 2, 1, 6, 2, 4, 6, 3, 2, 0, 1, + 7, 8, 1, 0, 2, 6, 3, 2, 4, 3, 5, 2, 1, 5, 6, 1, 6, 5, + 7, 8, 2, 3, 1, 2, 0, 1, 4, 0, 5, 4, 6, 5, 5, 2, 4, 1, + 7, 8, 4, 1, 0, 4, 3, 0, 1, 3, 3, 4, 2, 1, 2, 5, 6, 2, + 7, 8, 0, 6, 4, 0, 1, 4, 3, 1, 0, 3, 2, 4, 3, 2, 5, 2, + 7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 2, 4, 3, 2, 1, 0, 6, 5, + 7, 8, 0, 1, 4, 0, 3, 2, 6, 3, 5, 6, 2, 5, 6, 2, 3, 5, + 7, 8, 5, 2, 6, 5, 2, 6, 4, 2, 0, 4, 3, 0, 2, 3, 1, 2, + 7, 8, 2, 0, 1, 2, 0, 1, 5, 0, 4, 5, 0, 4, 6, 0, 3, 6, + 7, 8, 0, 1, 2, 0, 3, 2, 2, 1, 1, 4, 5, 4, 5, 3, 1, 6, + 7, 8, 1, 6, 2, 1, 0, 2, 1, 0, 4, 1, 3, 4, 2, 3, 5, 6, + 7, 8, 6, 1, 0, 6, 1, 0, 5, 1, 0, 5, 2, 1, 3, 2, 4, 3, + 7, 8, 6, 5, 2, 6, 1, 2, 4, 1, 3, 4, 0, 3, 4, 0, 2, 4, + 7, 8, 1, 6, 0, 1, 5, 0, 1, 5, 3, 0, 4, 3, 2, 4, 0, 2, + 7, 8, 2, 6, 4, 2, 0, 4, 1, 0, 4, 1, 3, 4, 5, 3, 2, 5, + 7, 8, 1, 0, 2, 1, 6, 2, 5, 6, 1, 5, 4, 1, 3, 4, 2, 3, + 7, 8, 6, 1, 4, 3, 1, 0, 5, 1, 3, 2, 2, 1, 4, 6, 5, 4, + 7, 8, 4, 2, 0, 4, 1, 0, 4, 1, 3, 4, 6, 3, 5, 6, 3, 5, + 7, 8, 4, 1, 2, 4, 0, 2, 6, 0, 3, 6, 0, 3, 5, 0, 4, 5, + 7, 8, 5, 6, 4, 5, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, + 7, 8, 6, 3, 5, 6, 4, 5, 0, 4, 1, 0, 2, 1, 5, 2, 4, 1, + 7, 8, 0, 1, 2, 0, 3, 2, 2, 1, 1, 4, 5, 4, 5, 3, 4, 6, + 7, 8, 4, 0, 3, 4, 2, 3, 6, 2, 5, 6, 1, 5, 4, 1, 2, 1, + 7, 8, 6, 1, 0, 6, 4, 3, 5, 1, 0, 5, 2, 1, 3, 2, 5, 6, + 7, 8, 6, 2, 5, 6, 3, 5, 6, 3, 4, 3, 0, 4, 1, 0, 4, 1, + 7, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 6, 5, 1, + 7, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 6, 4, 2, + 7, 8, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 6, 0, 5, + 7, 8, 4, 0, 2, 4, 3, 2, 6, 3, 5, 6, 4, 5, 1, 2, 5, 1, + 7, 8, 5, 1, 2, 4, 3, 2, 0, 3, 5, 0, 4, 5, 1, 2, 0, 6, + 7, 8, 5, 6, 2, 5, 4, 2, 0, 4, 3, 0, 2, 3, 1, 4, 3, 1, + 7, 8, 0, 4, 1, 0, 4, 1, 3, 4, 5, 3, 6, 5, 2, 6, 4, 2, + 7, 8, 0, 1, 6, 5, 2, 3, 3, 4, 6, 4, 0, 5, 6, 2, 6, 1, + 7, 8, 1, 2, 0, 1, 4, 0, 5, 4, 2, 5, 3, 2, 6, 3, 5, 6, + 7, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 6, 1, 6, + 7, 8, 6, 2, 5, 6, 2, 5, 1, 2, 4, 1, 0, 4, 3, 0, 1, 3, + 7, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 5, 6, 1, + 7, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 3, + 7, 8, 0, 4, 1, 0, 3, 2, 1, 4, 2, 5, 5, 3, 6, 4, 6, 3, + 7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 6, 2, 5, 6, 2, 5, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, + 7, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 2, + 7, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 4, + 7, 9, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 4, 5, + 7, 9, 2, 0, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3, 3, 2, 4, 3, + 7, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 5, + 7, 9, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0, + 7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 0, 4, 1, 0, 4, 1, + 7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 1, 0, 5, 1, + 7, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 4, 4, 0, 5, 0, + 7, 9, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, + 7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, + 7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, + 7, 9, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 2, 0, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 4, 2, 5, 2, + 7, 9, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 5, 2, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 5, 2, 4, 1, + 7, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 0, 0, 4, 0, 6, + 7, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 0, 0, 4, 2, 6, + 7, 9, 1, 2, 3, 1, 0, 3, 1, 0, 2, 0, 3, 2, 5, 3, 4, 0, 1, 6, + 7, 9, 0, 1, 2, 4, 0, 2, 3, 1, 3, 2, 2, 1, 4, 1, 5, 2, 2, 6, + 7, 9, 0, 1, 2, 4, 0, 2, 5, 2, 3, 1, 3, 2, 2, 1, 4, 1, 1, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 1, 5, 1, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 1, 5, 4, 6, + 7, 9, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 2, 1, 5, 1, 1, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 4, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 6, + 7, 9, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 0, 1, 6, + 7, 9, 2, 0, 3, 2, 4, 3, 5, 4, 2, 5, 1, 2, 4, 1, 5, 3, 2, 6, + 7, 9, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 3, 0, 5, 1, 0, 6, + 7, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 0, 0, 4, 5, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 0, 4, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 2, 6, + 7, 9, 2, 0, 3, 2, 4, 3, 5, 4, 2, 5, 1, 2, 4, 1, 5, 3, 5, 6, + 7, 9, 1, 2, 3, 1, 0, 3, 1, 0, 2, 0, 3, 2, 4, 0, 6, 5, 6, 3, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 0, 0, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 6, 5, 4, 0, 3, 2, 4, + 7, 9, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 2, 1, 5, 1, 5, 6, + 7, 9, 2, 0, 3, 2, 4, 3, 5, 4, 2, 5, 1, 2, 4, 1, 5, 3, 4, 6, + 7, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 0, 2, 6, + 7, 9, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 3, 0, 5, 1, 5, 6, + 7, 9, 0, 1, 2, 5, 0, 2, 5, 4, 3, 1, 3, 2, 3, 0, 5, 1, 2, 6, + 7, 9, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 5, 1, 5, 3, 0, 6, + 7, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 0, 5, 5, 4, 5, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 6, + 7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 3, 4, 4, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 2, 0, 2, 0, 6, + 7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 3, 4, 3, 6, + 7, 9, 0, 1, 2, 4, 0, 2, 5, 2, 3, 1, 3, 2, 2, 1, 4, 1, 5, 6, + 7, 9, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 2, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 6, 5, 6, 1, + 7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 3, 4, 2, 6, + 7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 3, 4, 0, 6, + 7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 3, 6, 1, 6, 0, 1, 1, 4, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 0, 6, + 7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 2, 1, 4, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 5, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 4, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 5, 6, + 7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 3, 6, 1, 6, 0, 1, 0, 4, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 2, 0, 2, 1, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 2, 0, 2, 4, 6, + 7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 2, 1, 2, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 3, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 2, 6, + 7, 9, 0, 1, 2, 5, 0, 2, 5, 1, 3, 1, 3, 2, 2, 1, 6, 0, 6, 4, + 7, 9, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 1, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 0, 5, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 0, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 3, 6, + 7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 3, 6, 1, 6, 0, 1, 2, 4, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 5, 5, 3, 1, 5, 3, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 1, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 1, 4, 6, + 7, 9, 0, 1, 2, 5, 0, 2, 5, 1, 3, 1, 3, 2, 3, 0, 6, 4, 6, 0, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 5, 5, 2, 5, 0, 1, 6, + 7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 2, 1, 6, 0, + 7, 9, 5, 3, 3, 2, 4, 3, 5, 4, 2, 5, 1, 2, 4, 1, 6, 0, 6, 2, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 5, 5, 2, 5, 0, 0, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 5, 5, 3, 1, 5, 5, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 4, 6, + 7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 3, 6, 1, 6, 0, 1, 5, 4, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 1, 5, 2, 5, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 5, 5, 3, 1, 5, 1, 6, + 7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 2, 1, 3, 6, + 7, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 0, 5, 5, 4, 4, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 5, 5, 3, 1, 5, 0, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 5, 5, 2, 5, 0, 4, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 1, 0, 6, + 7, 9, 0, 1, 2, 5, 0, 2, 5, 3, 3, 1, 3, 2, 5, 1, 6, 4, 6, 0, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 1, 5, 2, 0, 6, + 7, 9, 6, 3, 1, 2, 6, 5, 3, 4, 6, 4, 0, 5, 6, 0, 6, 1, 6, 2, + 7, 9, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 6, 2, 5, 6, 2, 5, + 7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 3, 4, 6, 5, 6, 0, + 7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 6, 4, 6, 3, + 7, 9, 4, 1, 5, 4, 6, 5, 3, 6, 2, 3, 1, 2, 5, 2, 0, 5, 2, 0, + 7, 9, 4, 1, 3, 1, 2, 3, 4, 0, 5, 0, 5, 2, 5, 4, 6, 4, 5, 6, + 7, 9, 1, 0, 2, 1, 6, 2, 3, 6, 5, 3, 4, 5, 3, 4, 2, 3, 0, 2, + 7, 9, 0, 2, 5, 0, 1, 5, 2, 1, 4, 2, 5, 4, 6, 5, 3, 6, 2, 3, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 0, 6, 1, 3, 4, 1, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 4, 5, 1, 6, 1, 0, 6, + 7, 9, 0, 4, 1, 0, 4, 1, 3, 4, 2, 3, 6, 2, 5, 6, 1, 5, 2, 1, + 7, 9, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 3, 6, 5, 3, 6, + 7, 9, 6, 5, 3, 6, 2, 3, 0, 4, 0, 5, 1, 0, 2, 0, 1, 2, 5, 4, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 3, 5, 1, 6, 1, 0, 6, + 7, 9, 5, 2, 6, 5, 3, 6, 2, 3, 0, 2, 4, 0, 5, 4, 1, 5, 0, 1, + 7, 9, 2, 4, 1, 2, 4, 1, 5, 4, 0, 5, 1, 0, 6, 4, 3, 6, 2, 3, + 7, 9, 6, 2, 5, 6, 2, 5, 1, 2, 0, 1, 4, 0, 1, 4, 3, 1, 0, 3, + 7, 9, 0, 5, 6, 0, 1, 6, 4, 1, 2, 4, 3, 2, 1, 3, 5, 1, 6, 5, + 7, 9, 6, 5, 3, 6, 2, 3, 5, 2, 0, 5, 1, 0, 4, 1, 0, 4, 2, 0, + 7, 9, 0, 4, 3, 0, 1, 3, 4, 1, 2, 4, 6, 2, 5, 6, 2, 5, 3, 2, + 7, 9, 1, 0, 4, 1, 5, 4, 0, 5, 6, 0, 3, 6, 2, 3, 0, 2, 3, 4, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 3, + 7, 9, 0, 1, 1, 2, 2, 3, 0, 3, 4, 1, 5, 4, 5, 3, 6, 0, 6, 4, + 7, 9, 0, 1, 4, 0, 1, 4, 2, 1, 5, 2, 4, 5, 6, 5, 3, 6, 2, 3, + 7, 9, 1, 0, 6, 3, 0, 4, 5, 0, 3, 5, 5, 6, 1, 2, 1, 4, 6, 2, + 7, 9, 6, 2, 5, 6, 2, 5, 1, 2, 0, 3, 4, 0, 1, 4, 3, 1, 3, 4, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 1, 5, 6, 6, 0, + 7, 9, 0, 4, 1, 0, 2, 1, 3, 2, 0, 3, 2, 4, 5, 4, 6, 5, 3, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 2, 6, 1, 5, 6, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 3, 5, 4, 6, 1, 6, 5, + 7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 4, 6, 2, + 7, 9, 0, 4, 3, 0, 4, 3, 6, 1, 5, 6, 1, 5, 2, 1, 5, 2, 6, 2, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 4, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, + 7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, 1, 0, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 5, 1, 5, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 2, 4, + 7, 10, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1, + 7, 10, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 5, 2, + 7, 10, 0, 1, 2, 4, 0, 2, 4, 5, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 0, 2, 1, 3, 5, 1, + 7, 10, 0, 1, 1, 2, 3, 4, 0, 2, 3, 0, 2, 4, 5, 2, 1, 5, 4, 1, 3, 5, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 5, 3, 2, 5, 1, 0, + 7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 2, 2, 6, + 7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 2, 1, 6, + 7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 4, 1, 6, + 7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 4, 0, 6, + 7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 4, 3, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 4, 5, 4, 6, + 7, 10, 2, 0, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3, 3, 2, 4, 3, 3, 6, + 7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 5, 2, 6, + 7, 10, 2, 0, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3, 3, 2, 4, 3, 2, 6, + 7, 10, 2, 3, 1, 2, 4, 1, 5, 4, 2, 5, 0, 2, 4, 0, 0, 1, 5, 0, 6, 5, + 7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 4, 5, 6, + 7, 10, 2, 0, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3, 3, 2, 4, 3, 4, 6, + 7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 5, 6, 5, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 6, + 7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, 6, 5, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0, 0, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0, 2, 6, + 7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, 1, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0, 3, 6, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 0, 4, 1, 0, 4, 1, 0, 6, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 1, 0, 5, 1, 1, 6, + 7, 10, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 4, 4, 0, 5, 0, 0, 6, + 7, 10, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 3, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0, 5, 6, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 3, 6, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 1, 0, 5, 1, 0, 6, + 7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 2, 0, 5, 3, 2, 3, 6, 2, + 7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 6, 4, 6, 2, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 2, 6, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 1, 0, 5, 1, 5, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0, 4, 6, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 3, 6, + 7, 10, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 4, 4, 0, 5, 0, 2, 6, + 7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 2, 0, 5, 6, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 2, 6, + 7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 2, 0, 5, 3, 2, 3, 0, 6, + 7, 10, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 1, 6, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 4, 6, + 7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 6, 4, 6, 0, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 5, 6, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 0, 4, 1, 0, 4, 1, 5, 6, + 7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 2, 0, 0, 6, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 1, 0, 5, 1, 2, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 4, 2, 5, 2, 2, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, 5, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1, 0, 6, + 7, 10, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 4, 4, 0, 5, 0, 5, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 6, 5, 6, 4, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 1, 6, + 7, 10, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 4, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, 4, 6, + 7, 10, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 2, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, 0, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 4, 2, 5, 2, 5, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1, 1, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 3, 6, + 7, 10, 4, 3, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3, 3, 2, 6, 0, 6, 2, + 7, 10, 1, 0, 2, 1, 3, 2, 4, 3, 5, 4, 1, 5, 6, 1, 4, 6, 2, 6, 5, 2, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 4, 2, 5, 2, 0, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1, 3, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1, 2, 6, + 7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 6, 5, 6, 4, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 1, 6, + 7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 6, + 7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 2, 0, 1, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, 1, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 4, 2, 5, 2, 1, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1, 4, 6, + 7, 10, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 5, 2, 4, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 5, 2, 4, 1, 0, 6, + 7, 10, 4, 0, 1, 4, 3, 1, 2, 3, 1, 2, 6, 1, 0, 6, 5, 0, 1, 5, 0, 1, + 7, 10, 3, 2, 6, 3, 5, 6, 0, 5, 2, 0, 5, 2, 1, 5, 2, 1, 4, 2, 5, 4, + 7, 10, 2, 0, 1, 2, 3, 1, 0, 3, 6, 0, 1, 6, 5, 1, 0, 5, 4, 0, 1, 4, + 7, 10, 6, 4, 1, 2, 6, 5, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 2, 6, 3, + 7, 10, 0, 1, 6, 5, 2, 3, 3, 4, 6, 4, 0, 5, 6, 0, 6, 1, 6, 2, 6, 3, + 7, 10, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 0, 5, 5, 2, 6, 1, 2, 6, + 7, 10, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 0, 5, 2, 6, 2, 0, 6, + 7, 10, 6, 4, 1, 2, 6, 5, 3, 4, 4, 5, 0, 5, 6, 3, 6, 1, 6, 2, 0, 4, + 7, 10, 1, 0, 2, 1, 0, 2, 3, 2, 4, 3, 2, 4, 5, 2, 4, 5, 6, 4, 1, 6, + 7, 10, 0, 1, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 5, 2, 6, + 7, 10, 0, 2, 5, 0, 4, 5, 2, 4, 1, 2, 5, 1, 6, 5, 3, 6, 2, 3, 2, 6, + 7, 10, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 1, 0, 5, 6, 0, 2, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 2, 6, 4, 0, 2, 4, 0, + 7, 10, 0, 4, 3, 0, 2, 3, 5, 2, 6, 5, 2, 6, 4, 2, 1, 4, 3, 1, 4, 3, + 7, 10, 1, 6, 2, 1, 0, 2, 1, 0, 4, 1, 3, 4, 2, 3, 5, 6, 4, 5, 3, 1, + 7, 10, 6, 5, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 2, 6, 4, + 7, 10, 0, 1, 6, 5, 2, 3, 3, 4, 6, 4, 0, 5, 6, 0, 6, 1, 6, 2, 5, 3, + 7, 10, 0, 1, 1, 2, 2, 3, 5, 4, 0, 4, 5, 0, 5, 3, 5, 2, 6, 5, 6, 1, + 7, 10, 0, 3, 2, 0, 1, 2, 3, 1, 4, 3, 2, 4, 0, 4, 6, 0, 5, 6, 0, 5, + 7, 10, 0, 3, 2, 0, 1, 2, 3, 1, 4, 3, 0, 5, 0, 4, 6, 0, 5, 6, 1, 4, + 7, 10, 0, 1, 6, 5, 2, 3, 3, 4, 6, 4, 0, 5, 6, 0, 6, 1, 6, 2, 4, 2, + 7, 10, 1, 2, 5, 1, 6, 5, 2, 6, 1, 6, 5, 2, 4, 1, 0, 4, 3, 0, 1, 3, + 7, 10, 4, 2, 6, 2, 5, 3, 4, 1, 2, 0, 6, 3, 5, 2, 0, 1, 0, 4, 6, 0, + 7, 10, 4, 2, 3, 6, 5, 3, 5, 1, 2, 0, 6, 0, 5, 2, 1, 4, 0, 4, 5, 4, + 7, 10, 4, 0, 5, 4, 4, 1, 2, 1, 3, 2, 0, 3, 3, 4, 5, 3, 6, 1, 6, 5, + 7, 10, 0, 4, 1, 0, 2, 1, 4, 2, 3, 4, 5, 3, 4, 5, 5, 2, 6, 3, 2, 6, + 7, 10, 1, 6, 2, 1, 0, 2, 1, 0, 4, 1, 3, 4, 2, 3, 5, 6, 4, 5, 4, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 3, 6, 1, 6, 5, 5, 1, + 7, 10, 1, 0, 4, 1, 0, 4, 2, 0, 3, 2, 6, 3, 5, 6, 0, 5, 5, 2, 6, 2, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 3, 5, 1, 5, 4, 6, 1, 5, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 4, 1, 6, 1, 6, 5, + 7, 10, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 3, 2, 5, 6, 1, 4, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 2, 5, 4, 6, 5, 6, 0, 0, 2, + 7, 10, 2, 0, 5, 2, 1, 5, 0, 1, 3, 0, 5, 3, 6, 5, 4, 6, 0, 4, 4, 3, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 2, 1, 5, 6, 2, 6, 5, + 7, 10, 5, 0, 6, 5, 2, 6, 3, 2, 0, 3, 4, 0, 2, 4, 4, 3, 1, 4, 3, 1, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 6, 3, 5, 6, 3, 5, 4, 5, 4, 6, + 7, 10, 5, 2, 2, 1, 3, 2, 4, 3, 1, 4, 5, 0, 6, 1, 6, 0, 1, 5, 2, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 2, 6, 4, 4, 2, 5, 1, + 7, 10, 4, 2, 2, 3, 4, 1, 0, 1, 3, 0, 6, 4, 0, 6, 5, 0, 4, 5, 1, 5, + 7, 10, 2, 1, 5, 2, 3, 5, 0, 3, 4, 0, 6, 4, 3, 6, 1, 3, 4, 1, 5, 4, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 6, 5, 6, 3, + 7, 10, 0, 1, 4, 0, 1, 4, 2, 1, 5, 2, 4, 5, 6, 5, 3, 6, 2, 3, 5, 3, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 5, 6, 1, 6, 2, 4, 2, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 3, 5, 2, 6, 5, 6, 4, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 3, 6, 2, 4, 0, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 4, 6, 5, 6, 3, + 7, 10, 0, 5, 6, 0, 1, 6, 0, 1, 1, 5, 2, 1, 3, 2, 4, 3, 6, 4, 4, 5, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 5, 6, 4, 2, 0, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 5, 6, 3, + 7, 10, 2, 1, 2, 0, 3, 2, 4, 3, 1, 4, 5, 1, 5, 0, 6, 0, 1, 6, 6, 5, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 3, 5, 1, 6, 1, 6, 4, 6, 5, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 2, 4, 1, 6, 0, 5, 6, + 7, 10, 3, 1, 0, 3, 5, 0, 1, 5, 2, 1, 6, 2, 0, 6, 0, 4, 4, 2, 4, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 4, 6, 1, 6, 2, 6, 5, + 7, 10, 0, 3, 2, 0, 1, 2, 3, 1, 4, 3, 2, 4, 5, 4, 5, 0, 6, 4, 6, 1, + 7, 10, 0, 1, 6, 5, 2, 3, 3, 4, 6, 4, 0, 5, 6, 2, 6, 1, 4, 2, 5, 1, + 7, 10, 5, 2, 6, 5, 2, 6, 4, 2, 0, 4, 3, 0, 2, 3, 1, 0, 1, 3, 4, 1, + 7, 10, 3, 4, 1, 3, 4, 1, 0, 4, 3, 0, 1, 0, 2, 1, 5, 2, 6, 5, 2, 6, + 7, 10, 5, 6, 2, 5, 6, 2, 3, 6, 0, 3, 4, 0, 5, 4, 1, 4, 3, 1, 2, 1, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 5, 4, 2, + 7, 10, 1, 0, 2, 1, 3, 2, 0, 3, 5, 0, 1, 5, 4, 5, 6, 4, 3, 6, 2, 6, + 7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 1, 2, 5, 6, 0, 3, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2, 1, 5, + 7, 11, 0, 1, 2, 4, 0, 2, 2, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5, + 7, 11, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 0, 2, + 7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 5, 1, + 7, 11, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 11, 3, 6, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 2, 6, 5, 1, 0, 3, 1, 6, 0, 1, + 7, 11, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 0, 3, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 4, 6, 4, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 6, 4, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 2, 4, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 2, 5, 6, 2, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 2, 0, 6, + 7, 11, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 6, 5, + 7, 11, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, 1, 0, 4, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 5, 1, 5, 1, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 5, 1, 5, 6, 4, + 7, 11, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, 1, 0, 1, 6, + 7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 2, 4, 2, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 5, 1, 5, 5, 6, + 7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2, 2, 6, + 7, 11, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2, 6, 1, + 7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2, 3, 6, + 7, 11, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2, 6, 4, + 7, 11, 0, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 11, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 5, 2, 0, 6, + 7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2, 0, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1, 1, 6, + 7, 11, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2, 5, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 5, 1, 5, 0, 6, + 7, 11, 0, 1, 2, 4, 0, 2, 4, 5, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 2, 6, + 7, 11, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2, 3, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1, 2, 6, + 7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2, 5, 6, + 7, 11, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 5, 2, 5, 6, + 7, 11, 0, 1, 2, 4, 0, 2, 4, 5, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 5, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 4, 5, 6, + 7, 11, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 5, 2, 1, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1, 4, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1, 5, 6, + 7, 11, 0, 1, 2, 4, 0, 2, 4, 5, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 4, 6, + 7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 2, 4, 1, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 6, 5, 6, 2, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4, 5, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 0, 2, 1, 3, 5, 1, 1, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4, 1, 6, + 7, 11, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2, 2, 6, + 7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2, 1, 6, + 7, 11, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 5, 6, + 7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 5, 3, 2, 5, 1, 0, 1, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 0, 2, 1, 3, 5, 1, 5, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 0, 2, 1, 3, 5, 1, 6, 3, + 7, 11, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 5, 2, 4, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1, 6, 3, + 7, 11, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 3, 2, 0, 3, 1, 6, + 7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 5, 3, 2, 5, 1, 0, 6, 2, + 7, 11, 0, 1, 2, 4, 0, 2, 4, 5, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 6, + 7, 11, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, + 7, 11, 6, 5, 0, 6, 5, 0, 1, 5, 6, 1, 2, 6, 5, 2, 3, 5, 6, 3, 4, 6, 5, 4, + 7, 11, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 1, 2, 5, 6, 2, 1, 6, 3, 1, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 1, 3, 5, 6, 1, 4, 6, 1, 4, 3, 1, + 7, 11, 1, 4, 2, 3, 4, 2, 0, 6, 4, 5, 6, 5, 3, 1, 6, 4, 3, 0, 3, 6, 4, 3, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 2, 6, 4, 2, 6, 5, 2, 0, 2, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 0, 3, 0, 2, 0, 6, 0, 3, 6, + 7, 11, 0, 1, 2, 0, 5, 2, 6, 5, 2, 6, 1, 2, 4, 1, 2, 4, 3, 2, 4, 3, 3, 1, + 7, 11, 4, 5, 1, 4, 2, 1, 3, 2, 6, 3, 5, 6, 2, 5, 4, 2, 3, 5, 0, 5, 2, 0, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 2, 5, 2, 5, 0, 6, 2, 4, 6, 5, 4, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 6, 1, 2, 6, 0, 2, 6, 0, 5, 2, 0, 5, + 7, 11, 0, 5, 6, 0, 1, 6, 5, 1, 2, 5, 6, 2, 4, 3, 3, 2, 4, 5, 6, 4, 6, 5, + 7, 11, 0, 5, 6, 0, 1, 6, 5, 1, 2, 5, 6, 2, 3, 6, 5, 3, 4, 5, 6, 4, 4, 3, + 7, 11, 0, 5, 0, 6, 1, 2, 1, 6, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 0, 2, 2, 4, 5, 2, 4, 5, 6, 5, 6, 0, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 0, 6, 4, 2, 0, 4, 2, 0, 6, 4, + 7, 11, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 5, 1, 5, 2, 6, 1, 2, 6, 4, 2, 5, 4, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 1, 2, 5, 4, 2, 6, 2, 4, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 4, 6, 2, 0, 2, 4, 0, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 1, 5, 3, 4, 5, 1, 4, 6, 5, 6, 1, + 7, 11, 0, 4, 3, 0, 4, 3, 2, 4, 3, 2, 1, 3, 2, 1, 4, 1, 5, 2, 6, 5, 2, 6, + 7, 11, 0, 5, 0, 6, 1, 4, 1, 6, 2, 3, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 11, 0, 1, 4, 0, 5, 4, 6, 5, 3, 6, 2, 3, 1, 2, 4, 1, 2, 4, 5, 2, 1, 5, + 7, 11, 0, 4, 3, 0, 4, 3, 2, 4, 6, 2, 1, 6, 5, 1, 2, 5, 3, 2, 1, 3, 4, 1, + 7, 11, 0, 1, 6, 5, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 2, 6, 3, 6, 4, + 7, 11, 4, 1, 0, 4, 1, 0, 3, 1, 0, 3, 5, 1, 6, 5, 1, 6, 2, 1, 5, 2, 6, 2, + 7, 11, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 4, 6, 5, 4, 6, + 7, 11, 1, 0, 2, 1, 3, 2, 4, 3, 0, 4, 2, 0, 5, 2, 6, 5, 3, 6, 6, 0, 0, 5, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 2, 4, 0, 6, 4, 0, 6, 3, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 0, 5, 2, 6, 5, 4, 6, 0, 5, 6, 0, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 2, 6, 3, 6, 0, 3, 4, 0, + 7, 11, 4, 6, 5, 4, 6, 5, 3, 6, 5, 3, 2, 5, 3, 2, 5, 0, 6, 0, 1, 0, 2, 1, + 7, 11, 2, 0, 4, 2, 5, 4, 3, 5, 1, 3, 0, 1, 2, 1, 3, 2, 6, 3, 5, 6, 4, 3, + 7, 11, 4, 3, 4, 2, 1, 4, 3, 1, 0, 3, 1, 0, 3, 2, 3, 5, 2, 5, 6, 0, 4, 6, + 7, 11, 0, 1, 0, 2, 2, 3, 5, 1, 1, 3, 5, 2, 6, 3, 6, 0, 5, 3, 4, 5, 3, 4, + 7, 11, 4, 0, 1, 4, 6, 1, 0, 6, 3, 0, 1, 3, 5, 1, 0, 5, 6, 5, 2, 3, 0, 2, + 7, 11, 0, 1, 5, 0, 4, 5, 1, 4, 2, 1, 3, 2, 4, 3, 4, 2, 6, 4, 2, 6, 3, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 1, 3, 5, 6, 3, 4, 6, 5, 6, + 7, 11, 6, 3, 5, 6, 2, 5, 3, 2, 5, 3, 4, 5, 2, 4, 1, 2, 5, 1, 0, 1, 4, 0, + 7, 11, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 1, 6, 5, 4, 6, + 7, 11, 0, 4, 3, 0, 2, 3, 5, 2, 6, 5, 2, 6, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, + 7, 11, 5, 0, 0, 1, 3, 0, 5, 3, 2, 5, 6, 2, 4, 6, 5, 4, 1, 5, 6, 1, 3, 6, + 7, 11, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 1, 2, 5, 6, 4, 6, 2, 3, 6, + 7, 11, 3, 2, 6, 3, 5, 6, 0, 5, 2, 0, 1, 2, 0, 1, 5, 1, 2, 5, 4, 2, 6, 4, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 1, 5, 3, 5, 1, 6, 4, 6, 5, 3, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 3, 6, 0, 6, 2, 6, 3, 5, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 1, 6, 5, 4, 6, 3, 6, 1, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 1, 6, 5, 1, 6, 2, 6, 4, 2, + 7, 11, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 3, 4, 2, 6, 1, 6, 4, 5, 2, 3, 5, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 1, 6, 1, 4, 6, 6, 5, 2, 6, + 7, 11, 0, 3, 0, 6, 1, 2, 1, 5, 2, 4, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6, + 7, 11, 5, 1, 6, 5, 4, 6, 3, 4, 2, 3, 0, 2, 1, 0, 5, 0, 6, 0, 2, 6, 4, 2, + 7, 11, 1, 0, 2, 1, 3, 2, 4, 3, 0, 4, 5, 2, 3, 5, 6, 0, 6, 5, 6, 2, 3, 6, + 7, 11, 0, 3, 4, 0, 2, 4, 3, 2, 1, 3, 4, 1, 5, 1, 6, 0, 6, 1, 5, 3, 4, 5, + 7, 11, 0, 5, 0, 6, 1, 3, 1, 4, 2, 3, 2, 5, 2, 6, 3, 4, 4, 5, 4, 6, 5, 6, + 7, 11, 0, 2, 1, 0, 2, 1, 0, 3, 3, 1, 5, 4, 5, 3, 6, 4, 6, 2, 6, 0, 1, 6, + 7, 11, 4, 1, 5, 4, 2, 5, 1, 2, 0, 1, 5, 0, 6, 5, 3, 6, 2, 3, 0, 2, 4, 0, + 7, 11, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 6, 1, 6, 2, 5, 1, 3, 5, 5, 2, 4, 5, + 7, 11, 0, 5, 0, 6, 1, 4, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 3, 6, 4, 6, 2, 2, 0, 4, 0, + 7, 11, 0, 2, 1, 0, 2, 1, 0, 3, 3, 1, 5, 4, 5, 3, 6, 4, 6, 2, 4, 0, 1, 4, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 6, 2, 0, 6, 1, 6, + 7, 11, 0, 3, 4, 0, 1, 4, 3, 1, 4, 3, 0, 1, 2, 4, 6, 2, 5, 6, 2, 5, 1, 2, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, 6, 1, 6, 5, + 7, 11, 4, 1, 5, 4, 2, 5, 1, 2, 0, 1, 4, 0, 5, 0, 6, 5, 3, 6, 2, 3, 5, 3, + 7, 11, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 3, 4, 2, 5, 1, 5, 4, 6, 5, 4, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 5, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 6, 3, 6, 4, + 7, 11, 0, 4, 3, 0, 1, 3, 4, 1, 3, 4, 5, 1, 6, 5, 1, 6, 2, 1, 5, 2, 6, 2, + 7, 11, 4, 1, 0, 4, 3, 0, 2, 5, 5, 4, 6, 5, 2, 6, 1, 2, 3, 1, 6, 3, 2, 3, + 7, 11, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 2, 5, 4, 5, 3, 6, 0, 6, 5, 3, 6, + 7, 11, 5, 2, 2, 4, 5, 3, 4, 1, 5, 4, 0, 1, 3, 0, 0, 2, 6, 2, 6, 3, 0, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, 6, 1, 0, 6, + 7, 11, 0, 3, 0, 4, 1, 2, 1, 5, 1, 6, 2, 4, 2, 6, 3, 5, 3, 6, 4, 5, 5, 6, + 7, 11, 4, 0, 3, 4, 5, 3, 0, 5, 1, 0, 2, 1, 3, 2, 4, 1, 5, 2, 6, 4, 5, 6, + 7, 11, 2, 3, 4, 2, 0, 4, 5, 0, 1, 5, 4, 1, 3, 4, 5, 3, 1, 0, 6, 5, 6, 2, + 7, 11, 4, 1, 0, 4, 3, 0, 4, 3, 5, 4, 6, 5, 2, 6, 1, 2, 3, 1, 6, 3, 2, 5, + 7, 11, 0, 3, 4, 0, 2, 4, 3, 2, 1, 3, 0, 1, 6, 0, 5, 6, 2, 5, 1, 5, 4, 1, + 7, 11, 0, 3, 0, 4, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 6, 4, 5, 5, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 3, 4, 2, 5, 1, 5, 4, 6, 1, 5, 6, + 7, 11, 4, 1, 5, 4, 6, 5, 3, 6, 2, 3, 1, 2, 0, 1, 5, 0, 4, 0, 5, 2, 6, 2, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 2, 3, 5, 4, 5, 3, 0, 6, 5, 6, 1, + 7, 11, 0, 4, 1, 0, 4, 1, 3, 4, 2, 3, 1, 2, 6, 1, 5, 6, 3, 5, 5, 4, 2, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 1, 4, 2, 6, 2, 3, 6, 4, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 2, 1, 6, 5, 2, 4, 1, 0, 3, + 7, 11, 0, 3, 0, 4, 1, 2, 1, 5, 1, 6, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 11, 0, 1, 1, 2, 2, 3, 5, 4, 0, 4, 5, 3, 5, 1, 6, 3, 6, 4, 4, 2, 0, 3, + 7, 11, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 6, 3, 6, 4, 5, + 7, 11, 4, 3, 2, 4, 3, 2, 0, 3, 2, 1, 5, 4, 5, 0, 6, 4, 6, 1, 1, 5, 0, 6, + 7, 11, 6, 4, 3, 6, 1, 3, 4, 1, 0, 4, 2, 0, 3, 2, 0, 1, 5, 0, 6, 5, 5, 2, + 7, 11, 6, 1, 2, 6, 1, 2, 0, 1, 3, 0, 4, 3, 5, 4, 3, 5, 2, 0, 4, 0, 5, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 4, 3, 0, 2, + 7, 12, 3, 6, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 2, 6, 5, 1, 0, 3, 1, 6, 0, 1, 0, 6, + 7, 12, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 5, 1, 2, 4, + 7, 12, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 2, 3, + 7, 12, 0, 1, 1, 2, 0, 2, 3, 2, 3, 1, 4, 0, 2, 4, 5, 1, 0, 5, 4, 5, 3, 4, 5, 3, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2, 1, 5, 6, 1, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2, 4, 6, 5, 3, + 7, 12, 0, 1, 2, 4, 0, 2, 2, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, 1, 6, + 7, 12, 0, 1, 2, 4, 0, 2, 2, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, 3, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5, 4, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5, 6, 1, + 7, 12, 0, 1, 2, 4, 0, 2, 2, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, 0, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5, 0, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5, 2, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 0, 2, 1, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 0, 2, 4, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2, 6, 1, 6, 5, + 7, 12, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 1, 5, 3, 2, 5, 1, 0, 4, 1, 1, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 3, 5, 0, 3, 5, 6, + 7, 12, 3, 6, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 2, 6, 5, 1, 0, 3, 1, 6, 0, 1, 1, 4, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 3, 5, 0, 3, 3, 6, + 7, 12, 0, 1, 2, 4, 0, 2, 2, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, 4, 6, + 7, 12, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 1, 5, 3, 2, 5, 1, 0, 4, 1, 2, 6, + 7, 12, 3, 6, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 2, 6, 5, 1, 0, 3, 1, 6, 0, 1, 0, 4, + 7, 12, 1, 3, 4, 1, 3, 4, 2, 3, 0, 2, 4, 0, 5, 4, 2, 5, 4, 2, 0, 5, 1, 5, 3, 6, + 7, 12, 1, 3, 4, 1, 3, 4, 2, 3, 0, 2, 4, 0, 5, 4, 2, 5, 4, 2, 0, 5, 1, 5, 0, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5, 5, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 0, 3, 5, 6, + 7, 12, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, 4, 6, + 7, 12, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, 0, 6, + 7, 12, 3, 6, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 2, 6, 5, 1, 0, 3, 1, 6, 0, 1, 4, 5, + 7, 12, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, 3, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 0, 3, 0, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 0, 2, 5, 6, + 7, 12, 0, 3, 6, 0, 5, 6, 3, 5, 1, 3, 6, 1, 4, 6, 3, 4, 2, 3, 6, 2, 3, 6, 5, 4, + 7, 12, 0, 1, 4, 0, 5, 4, 1, 5, 4, 1, 2, 4, 1, 2, 6, 1, 4, 6, 2, 6, 3, 2, 4, 3, + 7, 12, 4, 1, 3, 2, 3, 0, 4, 2, 5, 1, 5, 0, 3, 5, 4, 3, 5, 4, 6, 5, 3, 6, 4, 6, + 7, 12, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 4, 2, 3, 4, 5, 3, 0, 5, 6, 3, 1, 6, + 7, 12, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 6, 3, 0, 6, 5, 0, 1, 5, 4, 1, 2, 4, + 7, 12, 6, 2, 5, 6, 3, 5, 2, 3, 1, 2, 4, 1, 5, 4, 2, 5, 4, 2, 0, 4, 1, 0, 5, 1, + 7, 12, 5, 4, 6, 5, 3, 6, 4, 3, 0, 4, 3, 0, 1, 3, 0, 1, 4, 1, 3, 5, 2, 3, 4, 2, + 7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 5, 0, 1, 5, 6, 1, 0, 6, + 7, 12, 1, 2, 0, 1, 2, 0, 3, 2, 0, 3, 1, 3, 4, 2, 0, 4, 5, 4, 2, 5, 6, 2, 1, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 2, 4, 5, 6, 4, 3, 6, + 7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 5, 0, 1, 5, 6, 1, 2, 6, + 7, 12, 1, 2, 0, 1, 2, 0, 3, 2, 0, 3, 1, 3, 4, 2, 0, 4, 6, 4, 2, 6, 5, 2, 4, 5, + 7, 12, 1, 0, 2, 1, 0, 2, 3, 0, 4, 3, 0, 4, 5, 0, 3, 5, 4, 5, 6, 4, 3, 6, 6, 0, + 7, 12, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 4, 1, 0, 4, 5, 0, 2, 5, 6, 5, 2, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 6, 4, 0, 6, 5, 0, 3, 5, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 1, 5, 6, 1, 0, 6, 5, 0, 4, 5, + 7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 6, 1, 0, 6, 5, 0, 2, 5, + 7, 12, 5, 4, 3, 5, 4, 3, 6, 4, 3, 6, 2, 3, 4, 2, 0, 4, 3, 0, 1, 0, 2, 1, 6, 2, + 7, 12, 4, 1, 3, 2, 3, 0, 4, 2, 5, 1, 5, 0, 3, 5, 4, 3, 5, 4, 6, 5, 0, 6, 3, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 6, 1, 0, 6, 5, 0, 1, 5, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 3, 1, 5, 1, 6, 5, 4, 6, + 7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 5, 4, 3, 5, 6, 3, 4, 6, + 7, 12, 1, 0, 2, 1, 3, 2, 0, 3, 4, 3, 1, 4, 4, 0, 5, 4, 0, 5, 3, 5, 6, 0, 1, 6, + 7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 6, 2, 4, 6, 5, 0, 1, 5, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 5, 4, 2, 5, 1, 5, 6, 1, 5, 6, + 7, 12, 0, 2, 1, 0, 2, 1, 0, 3, 3, 1, 4, 2, 5, 3, 6, 1, 6, 4, 4, 0, 3, 4, 1, 5, + 7, 12, 0, 1, 1, 2, 0, 2, 3, 0, 3, 2, 4, 3, 4, 1, 0, 4, 5, 2, 5, 4, 6, 0, 3, 6, + 7, 12, 5, 0, 2, 5, 6, 2, 3, 6, 2, 3, 1, 2, 0, 1, 4, 0, 1, 4, 5, 1, 6, 5, 0, 2, + 7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 6, 4, 2, 6, 5, 2, 3, 5, + 7, 12, 0, 2, 1, 0, 5, 1, 3, 5, 6, 3, 2, 6, 4, 2, 3, 4, 5, 4, 2, 5, 1, 2, 4, 1, + 7, 12, 0, 2, 1, 0, 2, 1, 0, 3, 3, 1, 4, 0, 1, 4, 4, 2, 3, 4, 5, 4, 6, 5, 3, 6, + 7, 12, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 6, 1, 2, 6, 4, 6, 3, 4, 5, 3, 6, 5, + 7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 5, 0, 6, 5, 0, 6, 3, 4, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 1, 2, 5, 6, 0, 3, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 3, 5, 1, 5, 4, 6, 3, 4, 6, 1, 6, 6, 5, + 7, 12, 0, 5, 0, 6, 1, 3, 1, 4, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 2, 6, 5, 0, 6, 6, 2, 0, 5, 1, 5, 6, 1, + 7, 12, 0, 1, 6, 5, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 2, 6, 3, 6, 4, 5, 1, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4, 6, 1, 5, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 1, 4, 5, 6, 4, 5, 6, + 7, 12, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 1, 5, 3, 2, 5, 1, 0, 4, 1, 6, 3, 6, 1, + 7, 12, 0, 4, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 5, 4, 5, 4, 6, 5, 6, + 7, 12, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6, + 7, 12, 0, 1, 2, 4, 0, 2, 2, 1, 4, 6, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, 6, 1, 2, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 5, 4, 0, 4, 5, 3, 5, 1, 6, 3, 6, 4, 4, 2, 0, 3, 4, 3, + 7, 12, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 6, 1, 6, 5, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 1, 6, 5, 0, 6, 1, 6, + 7, 12, 0, 3, 4, 0, 2, 4, 3, 2, 1, 3, 4, 1, 1, 0, 5, 1, 5, 2, 6, 1, 2, 6, 4, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 5, 4, 4, 1, 1, 6, 5, 3, 1, 5, 6, 2, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 5, 4, 4, 1, 1, 5, 5, 3, 6, 1, 4, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 5, 4, 4, 1, 1, 5, 5, 3, 6, 1, 0, 6, + 7, 12, 4, 3, 1, 2, 0, 1, 0, 3, 4, 0, 6, 4, 4, 2, 5, 4, 6, 2, 6, 3, 3, 2, 5, 1, + 7, 12, 2, 3, 4, 2, 0, 4, 6, 0, 6, 5, 4, 5, 1, 3, 5, 1, 0, 5, 6, 1, 3, 6, 5, 3, + 7, 12, 0, 3, 0, 5, 1, 2, 1, 5, 1, 6, 2, 4, 2, 6, 3, 4, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 12, 0, 3, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 4, 3, 5, 3, 6, 1, 3, 6, 6, 2, 0, 6, 4, 6, + 7, 12, 0, 1, 2, 4, 0, 2, 6, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, 4, 6, + 7, 12, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 6, 3, 1, 6, + 7, 12, 4, 3, 1, 2, 5, 0, 0, 3, 4, 0, 4, 1, 4, 2, 5, 1, 6, 2, 6, 3, 3, 2, 6, 4, + 7, 12, 2, 3, 4, 2, 5, 4, 0, 5, 6, 0, 1, 6, 3, 1, 6, 3, 5, 3, 1, 5, 4, 0, 3, 0, + 7, 12, 0, 3, 0, 5, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 6, 5, 6, + 7, 12, 3, 2, 1, 2, 5, 0, 0, 3, 4, 0, 4, 1, 6, 4, 5, 1, 6, 2, 6, 3, 6, 1, 0, 6, + 7, 12, 0, 5, 0, 6, 1, 3, 1, 4, 1, 6, 2, 3, 2, 4, 2, 6, 3, 5, 4, 5, 4, 6, 5, 6, + 7, 12, 0, 3, 0, 5, 1, 2, 1, 4, 1, 6, 2, 4, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 12, 0, 3, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 4, 6, 5, 6, + 7, 12, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 5, 4, 4, 1, 3, 4, 5, 3, 2, 6, 6, 1, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 5, 4, 4, 1, 3, 4, 5, 3, 6, 1, 6, 5, + 7, 12, 0, 2, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 12, 0, 5, 0, 6, 1, 3, 1, 4, 1, 6, 2, 3, 2, 4, 2, 5, 3, 4, 3, 6, 4, 5, 5, 6, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 1, 2, 4, 5, 2, 0, 5, 4, 3, 6, 5, 6, 4, 3, 5, + 7, 12, 0, 2, 0, 6, 1, 2, 1, 4, 1, 5, 2, 3, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 12, 0, 2, 0, 6, 1, 3, 1, 4, 1, 5, 2, 4, 2, 5, 3, 4, 3, 5, 3, 6, 4, 6, 5, 6, + 7, 12, 0, 2, 0, 6, 1, 3, 1, 4, 1, 5, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 12, 0, 5, 0, 6, 1, 3, 1, 4, 1, 6, 2, 3, 2, 4, 2, 6, 3, 4, 3, 5, 4, 5, 5, 6, + 7, 12, 0, 5, 0, 6, 1, 2, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 12, 3, 0, 2, 3, 4, 2, 0, 4, 5, 1, 5, 2, 6, 1, 6, 0, 3, 6, 5, 3, 4, 5, 6, 4, + 7, 12, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 12, 0, 1, 0, 2, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 12, 3, 0, 2, 3, 4, 2, 0, 4, 5, 1, 5, 2, 6, 1, 6, 0, 3, 6, 5, 3, 6, 4, 1, 3, + 7, 12, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 3, 6, 4, 6, 5, 6, + 7, 12, 2, 3, 4, 2, 5, 2, 4, 1, 6, 0, 3, 0, 3, 1, 6, 3, 5, 6, 1, 5, 4, 0, 3, 4, + 7, 12, 2, 3, 4, 2, 4, 1, 2, 5, 6, 0, 6, 4, 3, 1, 6, 3, 0, 3, 1, 5, 4, 0, 5, 3, + 7, 12, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 6, 2, 3, 2, 6, 3, 5, 4, 5, 4, 6, 5, 6, + 7, 12, 3, 0, 2, 3, 4, 2, 0, 4, 5, 1, 5, 2, 6, 1, 6, 0, 3, 6, 1, 3, 6, 4, 5, 4, + 7, 12, 6, 3, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 0, 6, 4, 1, 3, 4, 6, 5, 5, 1, 0, 4, + 7, 12, 0, 3, 0, 5, 0, 6, 1, 2, 1, 5, 1, 6, 2, 4, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, + 7, 12, 0, 3, 0, 5, 0, 6, 1, 2, 1, 4, 1, 6, 2, 3, 2, 5, 3, 4, 4, 5, 4, 6, 5, 6, + 7, 12, 0, 3, 0, 5, 0, 6, 1, 2, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 4, 5, 4, 6, 5, 6, + 7, 12, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 4, 5, 2, 4, 6, 2, 5, 6, 2, 5, 3, 2, 6, 3, + 7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 5, 2, 5, 4, 6, 4, 6, 3, 6, 2, 5, 3, + 7, 12, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 4, 5, 4, 6, + 7, 12, 3, 0, 4, 2, 3, 1, 4, 0, 5, 2, 5, 1, 4, 3, 5, 4, 3, 5, 6, 1, 0, 6, 2, 6, + 7, 12, 1, 0, 4, 1, 0, 4, 5, 0, 6, 5, 1, 6, 3, 4, 5, 3, 2, 3, 5, 2, 6, 3, 2, 6, + 7, 12, 0, 1, 2, 0, 2, 3, 3, 4, 0, 4, 0, 5, 6, 1, 4, 6, 6, 5, 2, 6, 3, 1, 5, 3, + 7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 2, 6, 3, 6, 4, 6, 5, + 7, 12, 3, 6, 1, 2, 0, 6, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 1, 2, 5, 4, 5, 6, 4, + 7, 13, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 0, 6, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 4, 3, 0, 2, 6, 4, + 7, 13, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 0, 4, 1, 6, + 7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 6, 4, 5, 5, 6, + 7, 13, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 0, 3, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 13, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 5, 6, + 7, 13, 0, 6, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, 4, 5, 1, 6, + 7, 13, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 4, 3, 0, 2, 5, 6, + 7, 13, 0, 5, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 6, 5, 6, + 7, 13, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 13, 5, 6, 0, 5, 6, 0, 4, 6, 5, 4, 1, 5, 6, 1, 3, 6, 5, 3, 2, 5, 6, 2, 1, 0, 2, 1, + 7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 6, + 7, 13, 3, 4, 0, 3, 4, 0, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1, 6, 0, 3, 6, 5, 3, 4, 5, + 7, 13, 3, 4, 0, 3, 4, 0, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1, 6, 0, 3, 6, 5, 3, 0, 5, + 7, 13, 3, 4, 0, 3, 4, 0, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1, 6, 4, 0, 6, 5, 0, 3, 5, + 7, 13, 0, 5, 0, 6, 1, 3, 1, 4, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 5, 4, 6, + 7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 5, 1, 6, 5, 1, 6, + 7, 13, 0, 4, 0, 6, 1, 3, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 0, 5, 0, 6, 1, 4, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 5, 6, + 7, 13, 0, 5, 0, 6, 1, 3, 1, 4, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 5, 3, 6, 5, 4, 6, + 7, 13, 0, 5, 0, 6, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 13, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 6, 1, 6, 5, 5, 1, + 7, 13, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 4, 0, 5, 6, 0, 3, 6, 6, 4, + 7, 13, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 6, 1, 5, 1, 2, 6, + 7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 5, 6, + 7, 13, 1, 0, 2, 1, 4, 2, 1, 4, 3, 1, 0, 3, 2, 3, 5, 2, 1, 5, 0, 5, 6, 0, 1, 6, 2, 6, + 7, 13, 2, 5, 6, 2, 5, 6, 4, 5, 3, 4, 0, 3, 4, 0, 1, 4, 3, 1, 6, 3, 2, 1, 4, 2, 3, 2, + 7, 13, 0, 3, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6, + 7, 13, 2, 4, 3, 2, 1, 3, 4, 1, 0, 4, 3, 0, 6, 3, 1, 6, 5, 1, 4, 5, 6, 4, 3, 5, 1, 2, + 7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 4, 2, 5, 3, 5, 3, 6, + 7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 5, 5, 6, + 7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 3, 2, 5, 3, 6, 4, 5, + 7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 6, 2, 4, 2, 5, 3, 4, 3, 5, + 7, 13, 0, 2, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 2, 5, 1, 2, 5, 1, 4, 5, 3, 4, 0, 3, 4, 0, 3, 2, 4, 2, 1, 3, 6, 3, 2, 6, 1, 6, + 7, 13, 0, 4, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 5, 6, + 7, 13, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 0, 4, 6, 1, 4, 6, + 7, 13, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 5, 2, 5, 0, 6, 5, 1, 6, 4, 0, + 7, 13, 0, 1, 1, 2, 0, 2, 3, 0, 1, 3, 3, 2, 4, 0, 2, 5, 3, 4, 5, 3, 0, 5, 6, 4, 6, 1, + 7, 13, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 5, 2, 6, 2, 5, 6, 0, 5, 1, 2, + 7, 13, 5, 4, 6, 2, 6, 4, 4, 3, 5, 0, 3, 1, 3, 2, 6, 3, 5, 6, 4, 0, 1, 4, 5, 1, 0, 3, + 7, 13, 0, 2, 0, 6, 1, 3, 1, 4, 1, 5, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, 6, 5, 6, 4, + 7, 13, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 0, 4, 0, 6, 4, 6, + 7, 13, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 5, 0, 4, 5, 1, 5, 6, 1, 0, 6, 3, 6, + 7, 13, 0, 5, 0, 6, 1, 2, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 5, 3, 2, 5, 1, 0, 6, 3, 4, 6, 5, 1, + 7, 13, 5, 2, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 6, 2, 6, 3, 1, 2, 3, 1, 4, 0, + 7, 13, 1, 0, 2, 1, 0, 2, 0, 3, 3, 2, 6, 2, 1, 6, 5, 1, 6, 5, 4, 6, 5, 4, 0, 5, 4, 0, + 7, 13, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6, + 7, 13, 0, 5, 0, 6, 1, 3, 1, 4, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 13, 0, 1, 1, 2, 4, 1, 3, 1, 0, 4, 2, 3, 0, 3, 2, 0, 5, 0, 6, 5, 4, 6, 3, 5, 4, 2, + 7, 13, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6, 5, 6, + 7, 13, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 0, 4, 2, 3, 6, 5, 0, 6, + 7, 13, 5, 2, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 6, 2, 6, 3, 2, 3, 5, 0, 4, 0, + 7, 13, 0, 1, 1, 2, 2, 3, 5, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 6, 3, 6, 4, 4, 2, 0, 3, + 7, 13, 0, 1, 0, 6, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6, 5, 6, + 7, 13, 0, 1, 0, 6, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 13, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 6, 2, 0, 6, 5, 0, 2, 5, 5, 3, 4, 5, 6, 4, 3, 6, + 7, 13, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 6, 5, 0, 6, 0, 2, 2, 5, 5, 3, 4, 5, 6, 4, 3, 6, + 7, 13, 3, 4, 0, 3, 4, 0, 3, 6, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1, 5, 2, 3, 5, 6, 2, 5, 6, + 7, 13, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4, 6, 5, 3, 6, 4, 6, + 7, 13, 1, 0, 2, 1, 6, 0, 1, 4, 3, 1, 0, 3, 2, 3, 1, 6, 1, 5, 2, 6, 4, 3, 5, 4, 6, 5, + 7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 5, 2, 6, 3, 5, 4, 6, + 7, 13, 0, 6, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 6, 3, 4, 1, 3, 4, 6, 5, 1, 5, 3, 5, 1, 3, + 7, 13, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 6, 4, 4, 3, 5, 4, 5, 2, 6, 0, 3, 6, 3, 5, + 7, 13, 0, 1, 1, 2, 2, 3, 3, 6, 0, 4, 6, 5, 0, 6, 6, 4, 2, 5, 5, 3, 4, 5, 1, 5, 6, 1, + 7, 13, 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6, + 7, 13, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 4, 5, 4, 6, 5, 6, + 7, 13, 2, 3, 5, 2, 6, 5, 3, 6, 4, 3, 5, 4, 0, 5, 2, 0, 1, 2, 0, 1, 5, 1, 4, 2, 6, 4, + 7, 13, 2, 1, 0, 5, 6, 0, 4, 6, 5, 4, 1, 5, 6, 1, 3, 6, 5, 3, 2, 5, 6, 2, 1, 0, 3, 4, + 7, 13, 0, 1, 2, 0, 2, 3, 3, 4, 0, 4, 0, 5, 6, 1, 4, 6, 6, 5, 2, 6, 3, 1, 5, 3, 6, 0, + 7, 13, 0, 4, 0, 5, 0, 6, 1, 2, 1, 5, 1, 6, 2, 3, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 13, 2, 3, 0, 2, 3, 0, 4, 3, 4, 6, 5, 1, 4, 5, 3, 1, 5, 2, 6, 0, 6, 1, 2, 1, 4, 1, + 7, 13, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 3, 5, 4, 5, 2, 5, 1, 6, 5, 1, 6, 2, 6, + 7, 13, 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6, + 7, 13, 3, 0, 2, 3, 4, 2, 0, 4, 5, 1, 5, 2, 6, 1, 6, 0, 3, 6, 1, 3, 6, 4, 5, 4, 5, 3, + 7, 13, 0, 4, 0, 5, 0, 6, 1, 2, 1, 4, 1, 5, 2, 3, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6, 5, 6, + 7, 13, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 3, 4, 3, 6, 4, 6, 5, 6, + 7, 13, 0, 2, 0, 5, 0, 6, 1, 2, 1, 4, 1, 6, 2, 3, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 3, 4, 5, 0, 3, 5, 0, 6, 6, 4, 2, 3, 4, 2, 1, 0, 2, 1, 1, 6, 5, 1, 2, 5, 6, 2, + 7, 13, 0, 2, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 13, 3, 0, 2, 3, 4, 2, 0, 4, 5, 3, 5, 2, 5, 4, 1, 3, 1, 0, 4, 1, 6, 0, 3, 6, 1, 6, + 7, 13, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 6, 0, 1, 6, 6, 3, 4, 6, 1, 2, 5, 0, + 7, 13, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, + 7, 13, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, + 7, 13, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 5, 3, 5, 4, 6, 4, 6, 2, 6, 5, 5, 2, 3, 6, + 7, 13, 0, 1, 0, 5, 0, 6, 1, 3, 1, 4, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6, + 7, 13, 0, 1, 0, 5, 0, 6, 1, 3, 1, 4, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 13, 0, 1, 2, 0, 2, 3, 3, 4, 0, 4, 0, 5, 6, 1, 4, 6, 6, 5, 2, 6, 3, 1, 5, 3, 2, 1, + 7, 14, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 1, 3, 2, 0, 4, 0, 5, 3, + 7, 14, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 2, 3, 0, 4, 1, 6, + 7, 14, 0, 6, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6, 5, 6, + 7, 14, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6, + 7, 14, 0, 3, 1, 2, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 3, 6, 5, 3, 4, 5, 6, 4, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 6, 2, 1, 6, 5, 1, 0, 5, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 5, 2, 3, 5, 6, 4, 0, 6, + 7, 14, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 0, 4, 6, 4, 0, 6, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 3, 4, 3, 5, 4, 6, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 6, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 14, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 0, 1, 5, 1, 0, 4, 1, 4, 5, 3, 2, 5, 6, 4, 0, 6, + 7, 14, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 6, 1, 5, 1, 2, 6, 0, 4, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 6, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 14, 2, 3, 4, 2, 6, 3, 4, 0, 6, 0, 3, 4, 3, 1, 5, 4, 5, 0, 0, 3, 1, 5, 5, 3, 6, 4, 6, 1, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 5, 3, 6, 5, 4, 6, 5, 4, + 7, 14, 3, 1, 1, 4, 2, 3, 3, 4, 0, 4, 1, 5, 0, 1, 0, 2, 2, 5, 5, 3, 4, 5, 1, 2, 6, 2, 5, 6, + 7, 14, 0, 3, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6, + 7, 14, 0, 3, 0, 6, 1, 2, 1, 4, 1, 5, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 14, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 14, 0, 1, 0, 6, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 14, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 4, 6, 5, 2, 1, 5, 0, 5, 6, 3, + 7, 14, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4, 4, 2, 3, 0, 6, 1, 5, 6, + 7, 14, 0, 4, 0, 6, 1, 2, 1, 3, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6, + 7, 14, 0, 4, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 5, 6, + 7, 14, 0, 5, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6, + 7, 14, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 1, 2, 5, 2, 4, 0, 3, 1, 5, 0, 6, 5, 6, 4, 0, 1, + 7, 14, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 5, 2, 6, 0, 6, 1, 5, 0, 1, 2, 3, 1, 4, 0, + 7, 14, 5, 6, 0, 5, 6, 0, 4, 6, 5, 4, 1, 5, 6, 1, 3, 6, 5, 3, 2, 5, 6, 2, 1, 0, 2, 1, 3, 4, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 5, 1, 6, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6, + 7, 14, 0, 1, 2, 0, 2, 3, 3, 4, 0, 4, 0, 5, 6, 1, 4, 6, 6, 5, 2, 6, 3, 1, 5, 3, 6, 0, 3, 6, + 7, 14, 3, 1, 4, 2, 4, 5, 4, 0, 1, 4, 0, 3, 5, 0, 5, 2, 6, 1, 3, 6, 6, 0, 5, 6, 6, 2, 4, 6, + 7, 14, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 5, 4, 1, 5, 6, 1, 3, 6, 0, 5, 6, 0, + 7, 14, 3, 4, 4, 2, 1, 5, 4, 0, 1, 4, 5, 3, 3, 0, 5, 2, 6, 4, 0, 6, 3, 6, 2, 6, 5, 6, 1, 6, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 2, 3, 2, 6, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 14, 2, 3, 4, 2, 6, 3, 4, 0, 4, 5, 3, 4, 3, 1, 5, 2, 1, 6, 5, 6, 6, 0, 5, 3, 6, 4, 0, 1, + 7, 14, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6, + 7, 14, 0, 4, 0, 5, 0, 6, 1, 2, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 14, 2, 3, 4, 2, 6, 3, 4, 0, 1, 4, 6, 0, 3, 1, 5, 2, 4, 5, 5, 6, 1, 5, 5, 3, 6, 4, 3, 0, + 7, 14, 3, 1, 4, 2, 0, 3, 4, 0, 1, 4, 5, 3, 5, 0, 5, 2, 6, 4, 1, 6, 6, 3, 0, 6, 6, 5, 2, 6, + 7, 14, 0, 1, 4, 2, 3, 0, 4, 0, 4, 5, 5, 3, 1, 3, 5, 2, 6, 4, 2, 6, 6, 5, 3, 6, 6, 1, 0, 6, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 6, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 5, 1, 6, 2, 3, 2, 4, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 14, 2, 3, 4, 2, 6, 3, 4, 0, 1, 4, 6, 1, 3, 1, 5, 2, 5, 0, 5, 6, 4, 5, 5, 3, 6, 0, 3, 0, + 7, 14, 2, 3, 4, 2, 3, 0, 4, 0, 4, 5, 3, 4, 3, 1, 5, 2, 0, 1, 5, 6, 6, 0, 5, 3, 6, 4, 1, 6, + 7, 14, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6, + 7, 14, 0, 3, 0, 4, 0, 6, 1, 2, 1, 4, 1, 5, 2, 3, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 14, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 5, 6, + 7, 14, 0, 1, 0, 5, 0, 6, 1, 4, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 5, 6, + 7, 14, 0, 1, 0, 4, 0, 6, 1, 3, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 14, 2, 3, 4, 2, 6, 3, 4, 0, 1, 4, 4, 5, 3, 1, 5, 2, 5, 0, 6, 1, 0, 6, 5, 3, 6, 4, 3, 0, + 7, 14, 0, 1, 0, 5, 0, 6, 1, 3, 1, 4, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 14, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 3, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 14, 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 5, 6, + 7, 14, 2, 3, 4, 2, 6, 3, 4, 0, 4, 5, 3, 5, 3, 1, 5, 2, 3, 0, 1, 4, 6, 0, 1, 6, 6, 4, 0, 1, + 7, 14, 0, 4, 0, 5, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 6, + 7, 14, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 4, 6, + 7, 14, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 6, 3, 4, 3, 5, 4, 6, 5, 6, + 7, 14, 0, 3, 0, 4, 0, 5, 1, 2, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 6, 5, 6, + 7, 14, 0, 3, 0, 4, 0, 5, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 6, 4, 6, 5, 6, + 7, 14, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 0, 6, 0, 2, 5, 0, 3, 5, 1, 3, 6, 1, 4, 6, 2, 4, + 7, 14, 0, 3, 0, 4, 0, 5, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 6, 4, 5, + 7, 15, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 4, 5, + 7, 15, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 1, 3, 2, 0, 4, 0, 5, 3, 1, 6, + 7, 15, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 1, 3, 2, 0, 4, 0, 5, 3, 0, 6, + 7, 15, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 3, 4, 3, 5, 3, 6, 5, 6, + 7, 15, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 5, 1, 6, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 15, 3, 4, 4, 5, 0, 3, 0, 4, 0, 5, 0, 6, 3, 6, 4, 6, 5, 6, 1, 5, 3, 5, 2, 3, 2, 4, 1, 6, 0, 1, + 7, 15, 3, 4, 4, 5, 0, 3, 0, 4, 0, 5, 0, 6, 3, 6, 1, 3, 1, 4, 1, 5, 3, 5, 2, 3, 2, 4, 6, 1, 4, 6, + 7, 15, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 1, 0, 5, 5, 2, 3, 5, 4, 5, 6, 1, 5, 6, + 7, 15, 4, 3, 4, 5, 5, 3, 0, 1, 0, 5, 0, 3, 2, 4, 1, 5, 1, 3, 6, 5, 3, 6, 6, 0, 1, 6, 6, 2, 4, 6, + 7, 15, 3, 4, 4, 5, 5, 6, 0, 4, 0, 5, 0, 6, 3, 6, 1, 3, 4, 6, 1, 5, 3, 5, 2, 3, 2, 4, 1, 6, 0, 1, + 7, 15, 0, 2, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 15, 6, 1, 4, 5, 0, 3, 0, 4, 0, 5, 0, 6, 3, 6, 1, 3, 1, 4, 1, 5, 3, 5, 2, 3, 2, 4, 5, 6, 4, 6, + 7, 15, 3, 4, 4, 5, 0, 3, 0, 4, 0, 5, 4, 6, 3, 6, 1, 3, 1, 4, 1, 5, 3, 5, 2, 3, 2, 4, 5, 6, 5, 2, + 7, 15, 3, 4, 4, 5, 0, 3, 0, 4, 6, 0, 4, 6, 3, 6, 1, 3, 1, 4, 1, 5, 3, 5, 2, 3, 2, 4, 5, 6, 5, 2, + 7, 15, 0, 1, 1, 2, 0, 2, 3, 0, 1, 3, 2, 3, 5, 1, 3, 5, 4, 3, 2, 4, 6, 2, 3, 6, 6, 1, 0, 5, 4, 0, + 7, 15, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 3, 1, 4, 2, 0, 4, 3, 0, 6, 3, 4, 6, 5, 4, 3, 5, 6, 2, 5, 1, + 7, 15, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 5, 3, 4, 5, 6, 4, 5, 6, 6, 3, + 7, 15, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 3, 4, 2, 6, 1, 2, 6, 5, 2, 4, 5, 6, 4, 0, 6, 6, 5, 3, 6, + 7, 15, 0, 1, 5, 3, 1, 3, 0, 4, 3, 0, 4, 3, 2, 4, 5, 2, 4, 5, 6, 4, 2, 6, 6, 5, 3, 6, 6, 1, 0, 6, + 7, 15, 5, 2, 4, 5, 3, 1, 0, 4, 0, 5, 0, 3, 2, 4, 1, 5, 1, 4, 6, 3, 1, 6, 6, 0, 5, 6, 6, 2, 4, 6, + 7, 15, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 2, 0, 3, 1, 6, 4, 5, 6, 6, 3, 0, 6, 6, 2, 1, 6, + 7, 15, 5, 2, 3, 0, 5, 3, 0, 4, 0, 5, 4, 3, 2, 4, 1, 5, 1, 4, 6, 4, 2, 6, 6, 0, 5, 6, 6, 3, 1, 6, + 7, 15, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 6, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 15, 6, 1, 4, 5, 0, 3, 0, 4, 0, 5, 4, 6, 3, 6, 1, 3, 1, 4, 0, 6, 3, 5, 2, 3, 2, 4, 5, 6, 5, 2, + 7, 15, 3, 4, 0, 1, 0, 3, 0, 4, 0, 5, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 1, 6, 2, 3, 2, 4, 5, 6, 5, 2, + 7, 15, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6, + 7, 15, 5, 2, 4, 5, 5, 3, 0, 4, 0, 1, 1, 3, 2, 4, 3, 0, 1, 4, 6, 4, 1, 6, 6, 0, 3, 6, 6, 2, 5, 6, + 7, 15, 5, 0, 4, 3, 5, 3, 5, 2, 0, 1, 1, 3, 2, 4, 3, 0, 1, 4, 6, 2, 5, 6, 6, 4, 3, 6, 6, 0, 1, 6, + 7, 15, 3, 4, 4, 5, 0, 3, 4, 6, 0, 1, 1, 6, 3, 6, 1, 3, 1, 4, 6, 0, 0, 5, 2, 3, 2, 4, 5, 6, 5, 2, + 7, 15, 0, 2, 0, 3, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6, + 7, 15, 0, 4, 0, 5, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 15, 3, 4, 5, 0, 0, 3, 0, 4, 4, 6, 1, 6, 3, 6, 1, 3, 1, 4, 6, 0, 1, 5, 2, 3, 2, 4, 5, 6, 5, 2, + 7, 15, 6, 4, 5, 2, 0, 3, 0, 4, 2, 4, 1, 6, 3, 6, 1, 3, 1, 4, 6, 0, 3, 5, 2, 3, 0, 1, 5, 6, 4, 5, + 7, 15, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6, + 7, 15, 0, 1, 0, 2, 0, 3, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 15, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 5, 2, 6, 2, 5, 6, 6, 1, 0, 6, 6, 4, 3, 6, + 7, 15, 3, 0, 3, 5, 3, 4, 2, 0, 2, 5, 2, 4, 1, 4, 1, 5, 1, 0, 6, 0, 1, 6, 6, 5, 3, 6, 6, 4, 2, 6, + 7, 15, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6, + 7, 15, 3, 4, 6, 2, 0, 3, 0, 4, 5, 0, 1, 6, 3, 6, 1, 3, 1, 4, 6, 0, 4, 5, 2, 3, 2, 4, 5, 1, 5, 2, + 7, 15, 3, 4, 6, 2, 0, 3, 0, 4, 5, 0, 5, 6, 3, 6, 1, 3, 1, 4, 0, 1, 4, 6, 2, 3, 2, 4, 5, 1, 5, 2, + 7, 15, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 6, 2, 1, 6, 6, 0, 4, 6, 5, 4, 0, 5, 3, 5, 6, 3, 5, 2, 1, 5, + 7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 4, 5, 2, 6, + 7, 16, 3, 0, 4, 1, 4, 3, 1, 3, 4, 0, 2, 5, 6, 2, 5, 6, 1, 5, 4, 5, 3, 5, 0, 5, 0, 6, 3, 6, 4, 6, 6, 1, + 7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 6, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 16, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 16, 3, 4, 5, 1, 0, 3, 0, 4, 5, 0, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 3, 5, 2, 3, 2, 4, 5, 6, 4, 5, 2, 5, + 7, 16, 2, 4, 3, 1, 3, 0, 4, 3, 4, 0, 5, 2, 4, 5, 5, 0, 3, 5, 5, 1, 6, 5, 1, 6, 3, 6, 6, 0, 4, 6, 6, 2, + 7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 16, 2, 4, 4, 1, 3, 0, 3, 1, 4, 0, 5, 2, 4, 5, 5, 0, 3, 5, 6, 5, 1, 5, 6, 1, 3, 6, 4, 6, 6, 2, 6, 0, + 7, 16, 0, 1, 0, 3, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 16, 2, 5, 0, 1, 4, 5, 1, 3, 5, 0, 4, 3, 5, 3, 2, 4, 1, 4, 3, 0, 6, 3, 2, 6, 6, 4, 5, 6, 6, 1, 0, 6, + 7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 3, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6, + 7, 16, 2, 5, 5, 1, 3, 1, 0, 4, 5, 0, 4, 3, 5, 3, 2, 4, 1, 4, 3, 0, 6, 2, 4, 6, 5, 6, 6, 1, 0, 6, 3, 6, + 7, 16, 1, 6, 0, 1, 0, 3, 0, 4, 5, 0, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 3, 5, 2, 3, 2, 4, 5, 6, 4, 5, 2, 5, + 7, 16, 3, 4, 5, 1, 0, 3, 0, 4, 5, 0, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 1, 6, 2, 3, 2, 4, 5, 6, 0, 1, 2, 5, + 7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, + 7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6, + 7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 5, 6, + 7, 16, 2, 5, 5, 1, 3, 5, 0, 4, 0, 1, 4, 3, 3, 2, 2, 4, 1, 4, 0, 5, 6, 4, 2, 6, 6, 3, 5, 6, 6, 1, 0, 6, + 7, 16, 5, 6, 5, 1, 0, 3, 0, 4, 0, 1, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 3, 5, 2, 3, 2, 4, 6, 2, 4, 5, 2, 5, + 7, 16, 3, 4, 5, 1, 0, 3, 0, 4, 0, 1, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 5, 0, 2, 3, 2, 4, 6, 2, 6, 5, 2, 5, + 7, 16, 5, 0, 5, 1, 0, 3, 0, 4, 6, 1, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 3, 5, 2, 3, 2, 4, 6, 2, 4, 5, 2, 5, + 7, 17, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 4, 5, 6, 2, 1, 6, + 7, 17, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 4, 5, 4, 6, + 7, 17, 4, 0, 4, 3, 0, 1, 3, 0, 2, 4, 3, 1, 5, 3, 4, 5, 5, 2, 6, 5, 5, 0, 1, 5, 6, 1, 0, 6, 6, 4, 2, 6, 3, 6, + 7, 17, 0, 1, 5, 1, 5, 3, 0, 4, 5, 0, 4, 3, 3, 1, 2, 5, 1, 4, 3, 0, 2, 4, 6, 2, 5, 6, 6, 3, 1, 6, 6, 0, 4, 6, + 7, 17, 3, 4, 5, 1, 0, 3, 0, 4, 4, 5, 4, 6, 3, 6, 1, 3, 1, 4, 0, 1, 3, 5, 2, 3, 2, 4, 2, 5, 5, 0, 5, 6, 6, 2, + 7, 17, 3, 2, 4, 1, 0, 1, 3, 0, 2, 4, 4, 3, 5, 1, 4, 5, 5, 2, 0, 5, 5, 3, 6, 5, 2, 6, 6, 3, 0, 6, 1, 6, 4, 6, + 7, 17, 3, 2, 4, 1, 4, 0, 3, 0, 2, 4, 3, 1, 5, 2, 4, 5, 5, 0, 3, 5, 5, 1, 6, 5, 2, 6, 6, 0, 3, 6, 6, 4, 1, 6, + 7, 17, 3, 2, 5, 1, 5, 0, 0, 4, 0, 1, 4, 3, 5, 3, 2, 5, 1, 4, 3, 0, 2, 4, 6, 4, 5, 6, 6, 2, 3, 6, 6, 0, 1, 6, + 7, 17, 3, 2, 5, 1, 5, 0, 0, 4, 4, 5, 0, 1, 3, 1, 2, 5, 1, 4, 3, 0, 2, 4, 6, 0, 3, 6, 6, 1, 5, 6, 6, 2, 4, 6, + 7, 17, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, + 7, 18, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 4, 5, 6, 1, 0, 6, 5, 6, + 7, 18, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, + 7, 18, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 18, 0, 1, 0, 2, 0, 3, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 18, 4, 0, 4, 5, 3, 0, 3, 5, 2, 0, 2, 5, 1, 3, 1, 4, 1, 5, 1, 0, 2, 3, 2, 4, 6, 0, 5, 6, 6, 1, 2, 6, 6, 4, 3, 6, + 7, 19, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 19, 0, 1, 0, 2, 0, 3, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 20, 0, 1, 0, 2, 0, 3, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, + 7, 21, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6, +}; + +const long int igraph_i_atlas_edges_pos[] = {0, 2, 4, 6, 10, 12, 16, 22, 30, 32, 36, 42, 48, 56, 64, 72, 82, 92, 104, 118, 120, 124, 130, 136, 144, 152, 160, 168, 178, 188, 198, 208, 218, 228, 240, 252, 264, 276, 288, 300, 314, 328, 342, 356, 370, 384, 400, 416, 432, 448, 466, 484, 504, 526, 528, 532, 538, 544, 552, 560, 568, 576, 584, 594, 604, 614, 624, 634, 644, 654, 664, 674, 686, 698, 710, 722, 734, 746, 758, 770, 782, 794, 806, 818, 830, 842, 854, 868, 882, 896, 910, 924, 938, 952, 966, 980, 994, 1008, 1022, 1036, 1050, 1064, 1078, 1092, 1106, 1120, 1134, 1148, 1164, 1180, 1196, 1212, 1228, 1244, 1260, 1276, 1292, 1308, 1324, 1340, 1356, 1372, 1388, 1404, 1420, 1436, 1452, 1468, 1484, 1500, 1516, 1532, 1550, 1568, 1586, 1604, 1622, 1640, 1658, 1676, 1694, 1712, 1730, 1748, 1766, 1784, 1802, 1820, 1838, 1856, 1874, 1892, 1910, 1928, 1946, 1964, 1984, 2004, 2024, 2044, 2064, 2084, 2104, 2124, 2144, 2164, 2184, 2204, 2224, 2244, 2264, 2284, 2304, 2324, 2344, 2364, 2384, 2406, 2428, 2450, 2472, 2494, 2516, 2538, 2560, 2582, 2604, 2626, 2648, 2670, 2692, 2714, 2738, 2762, 2786, 2810, 2834, 2858, 2882, 2906, 2930, 2956, 2982, 3008, 3034, 3060, 3088, 3116, 3146, 3178, 3180, 3184, 3190, 3196, 3204, 3212, 3220, 3228, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316, 3326, 3336, 3348, 3360, 3372, 3384, 3396, 3408, 3420, 3432, 3444, 3456, 3468, 3480, 3492, 3504, 3516, 3528, 3540, 3552, 3564, 3576, 3588, 3602, 3616, 3630, 3644, 3658, 3672, 3686, 3700, 3714, 3728, 3742, 3756, 3770, 3784, 3798, 3812, 3826, 3840, 3854, 3868, 3882, 3896, 3910, 3924, 3938, 3952, 3966, 3980, 3994, 4008, 4022, 4036, 4050, 4064, 4078, 4092, 4106, 4120, 4134, 4148, 4162, 4178, 4194, 4210, 4226, 4242, 4258, 4274, 4290, 4306, 4322, 4338, 4354, 4370, 4386, 4402, 4418, 4434, 4450, 4466, 4482, 4498, 4514, 4530, 4546, 4562, 4578, 4594, 4610, 4626, 4642, 4658, 4674, 4690, 4706, 4722, 4738, 4754, 4770, 4786, 4802, 4818, 4834, 4850, 4866, 4882, 4898, 4914, 4930, 4946, 4962, 4978, 4994, 5010, 5026, 5042, 5058, 5074, 5090, 5106, 5122, 5138, 5154, 5170, 5186, 5202, 5220, 5238, 5256, 5274, 5292, 5310, 5328, 5346, 5364, 5382, 5400, 5418, 5436, 5454, 5472, 5490, 5508, 5526, 5544, 5562, 5580, 5598, 5616, 5634, 5652, 5670, 5688, 5706, 5724, 5742, 5760, 5778, 5796, 5814, 5832, 5850, 5868, 5886, 5904, 5922, 5940, 5958, 5976, 5994, 6012, 6030, 6048, 6066, 6084, 6102, 6120, 6138, 6156, 6174, 6192, 6210, 6228, 6246, 6264, 6282, 6300, 6318, 6336, 6354, 6372, 6390, 6408, 6426, 6444, 6462, 6480, 6498, 6516, 6534, 6552, 6570, 6588, 6606, 6624, 6642, 6660, 6678, 6696, 6714, 6732, 6750, 6768, 6786, 6804, 6822, 6840, 6858, 6876, 6894, 6912, 6930, 6948, 6968, 6988, 7008, 7028, 7048, 7068, 7088, 7108, 7128, 7148, 7168, 7188, 7208, 7228, 7248, 7268, 7288, 7308, 7328, 7348, 7368, 7388, 7408, 7428, 7448, 7468, 7488, 7508, 7528, 7548, 7568, 7588, 7608, 7628, 7648, 7668, 7688, 7708, 7728, 7748, 7768, 7788, 7808, 7828, 7848, 7868, 7888, 7908, 7928, 7948, 7968, 7988, 8008, 8028, 8048, 8068, 8088, 8108, 8128, 8148, 8168, 8188, 8208, 8228, 8248, 8268, 8288, 8308, 8328, 8348, 8368, 8388, 8408, 8428, 8448, 8468, 8488, 8508, 8528, 8548, 8568, 8588, 8608, 8628, 8648, 8668, 8688, 8708, 8728, 8748, 8768, 8788, 8808, 8828, 8848, 8868, 8888, 8908, 8928, 8948, 8968, 8988, 9008, 9028, 9048, 9068, 9088, 9108, 9128, 9148, 9168, 9188, 9208, 9228, 9248, 9268, 9288, 9308, 9328, 9348, 9368, 9388, 9408, 9428, 9448, 9468, 9488, 9508, 9528, 9548, 9568, 9590, 9612, 9634, 9656, 9678, 9700, 9722, 9744, 9766, 9788, 9810, 9832, 9854, 9876, 9898, 9920, 9942, 9964, 9986, 10008, 10030, 10052, 10074, 10096, 10118, 10140, 10162, 10184, 10206, 10228, 10250, 10272, 10294, 10316, 10338, 10360, 10382, 10404, 10426, 10448, 10470, 10492, 10514, 10536, 10558, 10580, 10602, 10624, 10646, 10668, 10690, 10712, 10734, 10756, 10778, 10800, 10822, 10844, 10866, 10888, 10910, 10932, 10954, 10976, 10998, 11020, 11042, 11064, 11086, 11108, 11130, 11152, 11174, 11196, 11218, 11240, 11262, 11284, 11306, 11328, 11350, 11372, 11394, 11416, 11438, 11460, 11482, 11504, 11526, 11548, 11570, 11592, 11614, 11636, 11658, 11680, 11702, 11724, 11746, 11768, 11790, 11812, 11834, 11856, 11878, 11900, 11922, 11944, 11966, 11988, 12010, 12032, 12054, 12076, 12098, 12120, 12142, 12164, 12186, 12208, 12230, 12252, 12274, 12296, 12318, 12340, 12362, 12384, 12406, 12428, 12450, 12472, 12494, 12516, 12538, 12560, 12582, 12604, 12626, 12648, 12670, 12692, 12714, 12736, 12758, 12780, 12802, 12824, 12848, 12872, 12896, 12920, 12944, 12968, 12992, 13016, 13040, 13064, 13088, 13112, 13136, 13160, 13184, 13208, 13232, 13256, 13280, 13304, 13328, 13352, 13376, 13400, 13424, 13448, 13472, 13496, 13520, 13544, 13568, 13592, 13616, 13640, 13664, 13688, 13712, 13736, 13760, 13784, 13808, 13832, 13856, 13880, 13904, 13928, 13952, 13976, 14000, 14024, 14048, 14072, 14096, 14120, 14144, 14168, 14192, 14216, 14240, 14264, 14288, 14312, 14336, 14360, 14384, 14408, 14432, 14456, 14480, 14504, 14528, 14552, 14576, 14600, 14624, 14648, 14672, 14696, 14720, 14744, 14768, 14792, 14816, 14840, 14864, 14888, 14912, 14936, 14960, 14984, 15008, 15032, 15056, 15080, 15104, 15128, 15152, 15176, 15200, 15224, 15248, 15272, 15296, 15320, 15344, 15368, 15392, 15416, 15440, 15464, 15488, 15512, 15536, 15560, 15584, 15608, 15632, 15656, 15680, 15704, 15728, 15752, 15776, 15800, 15824, 15848, 15872, 15896, 15920, 15944, 15968, 15992, 16016, 16040, 16064, 16088, 16112, 16136, 16160, 16184, 16208, 16232, 16256, 16280, 16304, 16328, 16352, 16376, 16402, 16428, 16454, 16480, 16506, 16532, 16558, 16584, 16610, 16636, 16662, 16688, 16714, 16740, 16766, 16792, 16818, 16844, 16870, 16896, 16922, 16948, 16974, 17000, 17026, 17052, 17078, 17104, 17130, 17156, 17182, 17208, 17234, 17260, 17286, 17312, 17338, 17364, 17390, 17416, 17442, 17468, 17494, 17520, 17546, 17572, 17598, 17624, 17650, 17676, 17702, 17728, 17754, 17780, 17806, 17832, 17858, 17884, 17910, 17936, 17962, 17988, 18014, 18040, 18066, 18092, 18118, 18144, 18170, 18196, 18222, 18248, 18274, 18300, 18326, 18352, 18378, 18404, 18430, 18456, 18482, 18508, 18534, 18560, 18586, 18612, 18638, 18664, 18690, 18716, 18742, 18768, 18794, 18820, 18846, 18872, 18898, 18924, 18950, 18976, 19002, 19028, 19054, 19080, 19106, 19132, 19158, 19184, 19210, 19236, 19262, 19288, 19314, 19340, 19366, 19392, 19418, 19444, 19470, 19496, 19522, 19548, 19574, 19600, 19626, 19652, 19678, 19704, 19730, 19756, 19782, 19810, 19838, 19866, 19894, 19922, 19950, 19978, 20006, 20034, 20062, 20090, 20118, 20146, 20174, 20202, 20230, 20258, 20286, 20314, 20342, 20370, 20398, 20426, 20454, 20482, 20510, 20538, 20566, 20594, 20622, 20650, 20678, 20706, 20734, 20762, 20790, 20818, 20846, 20874, 20902, 20930, 20958, 20986, 21014, 21042, 21070, 21098, 21126, 21154, 21182, 21210, 21238, 21266, 21294, 21322, 21350, 21378, 21406, 21434, 21462, 21490, 21518, 21546, 21574, 21602, 21630, 21658, 21686, 21714, 21742, 21770, 21798, 21826, 21854, 21882, 21910, 21938, 21966, 21994, 22022, 22050, 22078, 22106, 22134, 22162, 22190, 22218, 22246, 22274, 22302, 22330, 22358, 22386, 22414, 22442, 22470, 22498, 22528, 22558, 22588, 22618, 22648, 22678, 22708, 22738, 22768, 22798, 22828, 22858, 22888, 22918, 22948, 22978, 23008, 23038, 23068, 23098, 23128, 23158, 23188, 23218, 23248, 23278, 23308, 23338, 23368, 23398, 23428, 23458, 23488, 23518, 23548, 23578, 23608, 23638, 23668, 23698, 23728, 23758, 23788, 23818, 23848, 23878, 23908, 23938, 23968, 23998, 24028, 24058, 24088, 24118, 24148, 24178, 24208, 24238, 24268, 24298, 24328, 24358, 24388, 24418, 24448, 24480, 24512, 24544, 24576, 24608, 24640, 24672, 24704, 24736, 24768, 24800, 24832, 24864, 24896, 24928, 24960, 24992, 25024, 25056, 25088, 25120, 25152, 25184, 25216, 25248, 25280, 25312, 25344, 25376, 25408, 25440, 25472, 25504, 25536, 25568, 25600, 25632, 25664, 25696, 25728, 25760, 25794, 25828, 25862, 25896, 25930, 25964, 25998, 26032, 26066, 26100, 26134, 26168, 26202, 26236, 26270, 26304, 26338, 26372, 26406, 26440, 26474, 26510, 26546, 26582, 26618, 26654, 26690, 26726, 26762, 26798, 26834, 26872, 26910, 26948, 26986, 27024, 27064, 27104, 27146}; + +__END_DECLS diff --git a/src/rigraph/core/constructors/atlas.c b/src/rigraph/core/constructors/atlas.c new file mode 100644 index 0000000..4b73faa --- /dev/null +++ b/src/rigraph/core/constructors/atlas.c @@ -0,0 +1,82 @@ +/* -*- mode: C -*- */ +/* + IGraph R package. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "atlas-edges.h" + +/** + * \function igraph_atlas + * \brief Create a small graph from the \quote Graph Atlas \endquote. + * + * + * The number of the graph is given as a parameter. + * The graphs are listed: \olist + * \oli in increasing order of number of nodes; + * \oli for a fixed number of nodes, in increasing order of the + * number of edges; + * \oli for fixed numbers of nodes and edges, in increasing + * order of the degree sequence, for example 111223 < 112222; + * \oli for fixed degree sequence, in increasing number of + * automorphisms. + * \endolist + * + * + * The data was converted from the NetworkX software package, + * see http://networkx.github.io . + * + * + * See \emb An Atlas of Graphs \eme by Ronald C. Read and Robin J. Wilson, + * Oxford University Press, 1998. + * + * \param graph Pointer to an uninitialized graph object. + * \param number The number of the graph to generate. + * + * Added in version 0.2. + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number of + * edges. + * + * \example examples/simple/igraph_atlas.c + */ +int igraph_atlas(igraph_t *graph, int number) { + + igraph_integer_t pos, n, e; + igraph_vector_t v = IGRAPH_VECTOR_NULL; + + if (number < 0 || + number >= (int) (sizeof(igraph_i_atlas_edges_pos) / sizeof(long int))) { + IGRAPH_ERROR("No such graph in atlas", IGRAPH_EINVAL); + } + + pos = (igraph_integer_t) igraph_i_atlas_edges_pos[number]; + n = (igraph_integer_t) igraph_i_atlas_edges[pos]; + e = (igraph_integer_t) igraph_i_atlas_edges[pos + 1]; + + IGRAPH_CHECK(igraph_create(graph, + igraph_vector_view(&v, igraph_i_atlas_edges + pos + 2, + e * 2), + n, IGRAPH_UNDIRECTED)); + + return 0; +} diff --git a/src/rigraph/core/constructors/basic_constructors.c b/src/rigraph/core/constructors/basic_constructors.c new file mode 100644 index 0000000..ff5e59c --- /dev/null +++ b/src/rigraph/core/constructors/basic_constructors.c @@ -0,0 +1,149 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +/** + * \section about_generators + * + * Graph generators create graphs. + * + * Almost all functions which create graph objects are documented + * here. The exceptions are \ref igraph_induced_subgraph() and alike, these + * create graphs based on another graph. + */ + + +/** + * \ingroup generators + * \function igraph_create + * \brief Creates a graph with the specified edges. + * + * \param graph An uninitialized graph object. + * \param edges The edges to add, the first two elements are the first + * edge, etc. + * \param n The number of vertices in the graph, if smaller or equal + * to the highest vertex id in the \p edges vector it + * will be increased automatically. So it is safe to give 0 + * here. + * \param directed Boolean, whether to create a directed graph or + * not. If yes, then the first edge points from the first + * vertex id in \p edges to the second, etc. + * \return Error code: + * \c IGRAPH_EINVEVECTOR: invalid edges + * vector (odd number of vertices). + * \c IGRAPH_EINVVID: invalid (negative) + * vertex id. + * + * Time complexity: O(|V|+|E|), + * |V| is the number of vertices, + * |E| the number of edges in the + * graph. + * + * \example examples/simple/igraph_create.c + */ +int igraph_create(igraph_t *graph, const igraph_vector_t *edges, + igraph_integer_t n, igraph_bool_t directed) { + igraph_bool_t has_edges = igraph_vector_size(edges) > 0; + igraph_real_t max = has_edges ? igraph_vector_max(edges) + 1 : 0; + + if (igraph_vector_size(edges) % 2 != 0) { + IGRAPH_ERROR("Invalid (odd) edges vector", IGRAPH_EINVEVECTOR); + } + if (has_edges && !igraph_vector_isininterval(edges, 0, max - 1)) { + IGRAPH_ERROR("Invalid (negative) vertex id", IGRAPH_EINVVID); + } + + IGRAPH_CHECK(igraph_empty(graph, n, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + if (has_edges) { + igraph_integer_t vc = igraph_vcount(graph); + if (vc < max) { + IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) (max - vc), 0)); + } + IGRAPH_CHECK(igraph_add_edges(graph, edges, 0)); + } + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \function igraph_small + * \brief Shorthand to create a small graph, giving the edges as arguments. + * + * + * This function is handy when a relatively small graph needs to be created. + * Instead of giving the edges as a vector, they are given simply as + * arguments and a '-1' needs to be given after the last meaningful + * edge argument. + * + * Note that only graphs which have vertices less than + * the highest value of the 'int' type can be created this way. If you + * give larger values then the result is undefined. + * + * \param graph Pointer to an uninitialized graph object. The result + * will be stored here. + * \param n The number of vertices in the graph; a nonnegative integer. + * \param directed Logical constant; gives whether the graph should be + * directed. Supported values are: + * \clist + * \cli IGRAPH_DIRECTED + * The graph to be created will be \em directed. + * \cli IGRAPH_UNDIRECTED + * The graph to be created will be \em undirected. + * \endclist + * \param ... The additional arguments giving the edges of the + * graph. Don't forget to supply an additional '-1' after the last + * (meaningful) argument. + * \return Error code. + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number + * of edges in the graph to create. + * + * \example examples/simple/igraph_small.c + */ + +int igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + ...) { + igraph_vector_t edges; + va_list ap; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + va_start(ap, directed); + while (1) { + int num = va_arg(ap, int); + if (num == -1) { + break; + } + igraph_vector_push_back(&edges, num); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/constructors/de_bruijn.c b/src/rigraph/core/constructors/de_bruijn.c new file mode 100644 index 0000000..1a1abe0 --- /dev/null +++ b/src/rigraph/core/constructors/de_bruijn.c @@ -0,0 +1,99 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +/** + * \function igraph_de_bruijn + * \brief Generate a de Bruijn graph. + * + * A de Bruijn graph represents relationships between strings. An alphabet + * of \c m letters are used and strings of length \c n are considered. + * A vertex corresponds to every possible string and there is a directed edge + * from vertex \c v to vertex \c w if the string of \c v can be transformed into + * the string of \c w by removing its first letter and appending a letter to it. + * + * + * Please note that the graph will have \c m to the power \c n vertices and + * even more edges, so probably you don't want to supply too big numbers for + * \c m and \c n. + * + * + * De Bruijn graphs have some interesting properties, please see another source, + * e.g. Wikipedia for details. + * + * \param graph Pointer to an uninitialized graph object, the result will be + * stored here. + * \param m Integer, the number of letters in the alphabet. + * \param n Integer, the length of the strings. + * \return Error code. + * + * \sa \ref igraph_kautz(). + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. + */ +int igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { + + /* m - number of symbols */ + /* n - length of strings */ + + long int no_of_nodes, no_of_edges; + igraph_vector_t edges; + long int i, j; + long int mm = m; + + if (m < 0 || n < 0) { + IGRAPH_ERROR("`m' and `n' should be non-negative in a de Bruijn graph", + IGRAPH_EINVAL); + } + + if (n == 0) { + return igraph_empty(graph, 1, IGRAPH_DIRECTED); + } + if (m == 0) { + return igraph_empty(graph, 0, IGRAPH_DIRECTED); + } + + no_of_nodes = (long int) pow(m, n); + no_of_edges = no_of_nodes * m; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + + for (i = 0; i < no_of_nodes; i++) { + long int basis = (i * mm) % no_of_nodes; + for (j = 0; j < m; j++) { + igraph_vector_push_back(&edges, i); + igraph_vector_push_back(&edges, basis + j); + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + IGRAPH_DIRECTED)); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} diff --git a/src/rigraph/core/constructors/famous.c b/src/rigraph/core/constructors/famous.c new file mode 100644 index 0000000..0cf040e --- /dev/null +++ b/src/rigraph/core/constructors/famous.c @@ -0,0 +1,498 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include + +#include "igraph_constructors.h" + +//#include "../internal/hacks.h" + +const igraph_real_t igraph_i_famous_bull[] = { + 5, 5, 0, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 4 +}; + +const igraph_real_t igraph_i_famous_chvatal[] = { + 12, 24, 0, + 5, 6, 6, 7, 7, 8, 8, 9, 5, 9, 4, 5, 4, 8, 2, 8, 2, 6, 0, 6, 0, 9, 3, 9, 3, 7, + 1, 7, 1, 5, 1, 10, 4, 10, 4, 11, 2, 11, 0, 10, 0, 11, 3, 11, 3, 10, 1, 2 +}; + +const igraph_real_t igraph_i_famous_coxeter[] = { + 28, 42, 0, + 0, 1, 0, 2, 0, 7, 1, 4, 1, 13, 2, 3, 2, 8, 3, 6, 3, 9, 4, 5, 4, 12, 5, 6, 5, + 11, 6, 10, 7, 19, 7, 24, 8, 20, 8, 23, 9, 14, 9, 22, 10, 15, 10, 21, 11, 16, + 11, 27, 12, 17, 12, 26, 13, 18, 13, 25, 14, 17, 14, 18, 15, 18, 15, 19, 16, 19, + 16, 20, 17, 20, 21, 23, 21, 26, 22, 24, 22, 27, 23, 25, 24, 26, 25, 27 +}; + +const igraph_real_t igraph_i_famous_cubical[] = { + 8, 12, 0, + 0, 1, 1, 2, 2, 3, 0, 3, 4, 5, 5, 6, 6, 7, 4, 7, 0, 4, 1, 5, 2, 6, 3, 7 +}; + +const igraph_real_t igraph_i_famous_diamond[] = { + 4, 5, 0, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 3 +}; + +const igraph_real_t igraph_i_famous_dodecahedron[] = { + 20, 30, 0, + 0, 1, 0, 4, 0, 5, 1, 2, 1, 6, 2, 3, 2, 7, 3, 4, 3, 8, 4, 9, 5, 10, 5, 11, 6, + 10, 6, 14, 7, 13, 7, 14, 8, 12, 8, 13, 9, 11, 9, 12, 10, 15, 11, 16, 12, 17, + 13, 18, 14, 19, 15, 16, 15, 19, 16, 17, 17, 18, 18, 19 +}; + +const igraph_real_t igraph_i_famous_folkman[] = { + 20, 40, 0, + 0, 5, 0, 8, 0, 10, 0, 13, 1, 7, 1, 9, 1, 12, 1, 14, 2, 6, 2, 8, 2, 11, 2, 13, + 3, 5, 3, 7, 3, 10, 3, 12, 4, 6, 4, 9, 4, 11, 4, 14, 5, 15, 5, 19, 6, 15, 6, 16, + 7, 16, 7, 17, 8, 17, 8, 18, 9, 18, 9, 19, 10, 15, 10, 19, 11, 15, 11, 16, 12, + 16, 12, 17, 13, 17, 13, 18, 14, 18, 14, 19 +}; + +const igraph_real_t igraph_i_famous_franklin[] = { + 12, 18, 0, + 0, 1, 0, 2, 0, 6, 1, 3, 1, 7, 2, 4, 2, 10, 3, 5, 3, 11, 4, 5, 4, 6, 5, 7, 6, 8, + 7, 9, 8, 9, 8, 11, 9, 10, 10, 11 +}; + +const igraph_real_t igraph_i_famous_frucht[] = { + 12, 18, 0, + 0, 1, 0, 2, 0, 11, 1, 3, 1, 6, 2, 5, 2, 10, 3, 4, 3, 6, 4, 8, 4, 11, 5, 9, 5, + 10, 6, 7, 7, 8, 7, 9, 8, 9, 10, 11 +}; + +const igraph_real_t igraph_i_famous_grotzsch[] = { + 11, 20, 0, + 0, 1, 0, 2, 0, 7, 0, 10, 1, 3, 1, 6, 1, 9, 2, 4, 2, 6, 2, 8, 3, 4, 3, 8, 3, 10, + 4, 7, 4, 9, 5, 6, 5, 7, 5, 8, 5, 9, 5, 10 +}; + +const igraph_real_t igraph_i_famous_heawood[] = { + 14, 21, 0, + 0, 1, 0, 5, 0, 13, 1, 2, 1, 10, 2, 3, 2, 7, 3, 4, 3, 12, 4, 5, 4, 9, 5, 6, 6, + 7, 6, 11, 7, 8, 8, 9, 8, 13, 9, 10, 10, 11, 11, 12, 12, 13 +}; + +const igraph_real_t igraph_i_famous_herschel[] = { + 11, 18, 0, + 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 6, 1, 7, 2, 10, 3, 9, 4, 8, 4, 9, 5, 8, + 5, 10, 6, 8, 6, 9, 7, 8, 7, 10 +}; + +const igraph_real_t igraph_i_famous_house[] = { + 5, 6, 0, + 0, 1, 0, 2, 1, 3, 2, 3, 2, 4, 3, 4 +}; + +const igraph_real_t igraph_i_famous_housex[] = { + 5, 8, 0, + 0, 1, 0, 2, 0, 3, 1, 2, 1, 3, 2, 3, 2, 4, 3, 4 +}; + +const igraph_real_t igraph_i_famous_icosahedron[] = { + 12, 30, 0, + 0, 1, 0, 2, 0, 3, 0, 4, 0, 8, 1, 2, 1, 6, 1, 7, 1, 8, 2, 4, 2, 5, 2, 6, 3, 4, + 3, 8, 3, 9, 3, 11, 4, 5, 4, 11, 5, 6, 5, 10, 5, 11, 6, 7, 6, 10, 7, 8, 7, 9, 7, + 10, 8, 9, 9, 10, 9, 11, 10, 11 +}; + +const igraph_real_t igraph_i_famous_krackhardt_kite[] = { + 10, 18, 0, + 0, 1, 0, 2, 0, 3, 0, 5, 1, 3, 1, 4, 1, 6, 2, 3, 2, 5, 3, 4, 3, 5, 3, 6, 4, 6, 5, 6, 5, 7, 6, 7, 7, 8, 8, 9 +}; + +const igraph_real_t igraph_i_famous_levi[] = { + 30, 45, 0, + 0, 1, 0, 7, 0, 29, 1, 2, 1, 24, 2, 3, 2, 11, 3, 4, 3, 16, 4, 5, 4, 21, 5, 6, 5, + 26, 6, 7, 6, 13, 7, 8, 8, 9, 8, 17, 9, 10, 9, 22, 10, 11, 10, 27, 11, 12, 12, + 13, 12, 19, 13, 14, 14, 15, 14, 23, 15, 16, 15, 28, 16, 17, 17, 18, 18, 19, 18, + 25, 19, 20, 20, 21, 20, 29, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29 +}; + +const igraph_real_t igraph_i_famous_mcgee[] = { + 24, 36, 0, + 0, 1, 0, 7, 0, 23, 1, 2, 1, 18, 2, 3, 2, 14, 3, 4, 3, 10, 4, 5, 4, 21, 5, 6, 5, + 17, 6, 7, 6, 13, 7, 8, 8, 9, 8, 20, 9, 10, 9, 16, 10, 11, 11, 12, 11, 23, 12, + 13, 12, 19, 13, 14, 14, 15, 15, 16, 15, 22, 16, 17, 17, 18, 18, 19, 19, 20, 20, + 21, 21, 22, 22, 23 +}; + +const igraph_real_t igraph_i_famous_meredith[] = { + 70, 140, 0, + 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 7, 11, + 7, 12, 7, 13, 8, 11, 8, 12, 8, 13, 9, 11, 9, 12, 9, 13, 10, 11, 10, 12, 10, 13, + 14, 18, 14, 19, 14, 20, 15, 18, 15, 19, 15, 20, 16, 18, 16, 19, 16, 20, 17, 18, + 17, 19, 17, 20, 21, 25, 21, 26, 21, 27, 22, 25, 22, 26, 22, 27, 23, 25, 23, 26, + 23, 27, 24, 25, 24, 26, 24, 27, 28, 32, 28, 33, 28, 34, 29, 32, 29, 33, 29, 34, + 30, 32, 30, 33, 30, 34, 31, 32, 31, 33, 31, 34, 35, 39, 35, 40, 35, 41, 36, 39, + 36, 40, 36, 41, 37, 39, 37, 40, 37, 41, 38, 39, 38, 40, 38, 41, 42, 46, 42, 47, + 42, 48, 43, 46, 43, 47, 43, 48, 44, 46, 44, 47, 44, 48, 45, 46, 45, 47, 45, 48, + 49, 53, 49, 54, 49, 55, 50, 53, 50, 54, 50, 55, 51, 53, 51, 54, 51, 55, 52, 53, + 52, 54, 52, 55, 56, 60, 56, 61, 56, 62, 57, 60, 57, 61, 57, 62, 58, 60, 58, 61, + 58, 62, 59, 60, 59, 61, 59, 62, 63, 67, 63, 68, 63, 69, 64, 67, 64, 68, 64, 69, + 65, 67, 65, 68, 65, 69, 66, 67, 66, 68, 66, 69, 2, 50, 1, 51, 9, 57, 8, 58, 16, + 64, 15, 65, 23, 36, 22, 37, 30, 43, 29, 44, 3, 21, 7, 24, 14, 31, 0, 17, 10, + 28, 38, 42, 35, 66, 59, 63, 52, 56, 45, 49 +}; + +const igraph_real_t igraph_i_famous_noperfectmatching[] = { + 16, 27, 0, + 0, 1, 0, 2, 0, 3, 1, 2, 1, 3, 2, 3, 2, 4, 3, 4, 4, 5, 5, 6, 5, 7, 6, 12, 6, 13, + 7, 8, 7, 9, 8, 9, 8, 10, 8, 11, 9, 10, 9, 11, 10, 11, 12, 13, 12, 14, 12, 15, + 13, 14, 13, 15, 14, 15 +}; + +const igraph_real_t igraph_i_famous_nonline[] = { + 50, 72, 0, + 0, 1, 0, 2, 0, 3, 4, 6, 4, 7, 5, 6, 5, 7, 6, 7, 7, 8, 9, 11, 9, 12, 9, 13, 10, + 11, 10, 12, 10, 13, 11, 12, 11, 13, 12, 13, 14, 15, 15, 16, 15, 17, 16, 17, 16, + 18, 17, 18, 18, 19, 20, 21, 20, 22, 20, 23, 21, 22, 21, 23, 21, 24, 22, 23, 22, + 24, 24, 25, 26, 27, 26, 28, 26, 29, 27, 28, 27, 29, 27, 30, 27, 31, 28, 29, 28, + 30, 28, 31, 30, 31, 32, 34, 32, 35, 32, 36, 33, 34, 33, 35, 33, 37, 34, 35, 36, + 37, 38, 39, 38, 40, 38, 43, 39, 40, 39, 41, 39, 42, 39, 43, 40, 41, 41, 42, 42, + 43, 44, 45, 44, 46, 45, 46, 45, 47, 46, 47, 46, 48, 47, 48, 47, 49, 48, 49 +}; + +const igraph_real_t igraph_i_famous_octahedron[] = { + 6, 12, 0, + 0, 1, 0, 2, 1, 2, 3, 4, 3, 5, 4, 5, 0, 3, 0, 5, 1, 3, 1, 4, 2, 4, 2, 5 +}; + +const igraph_real_t igraph_i_famous_petersen[] = { + 10, 15, 0, + 0, 1, 0, 4, 0, 5, 1, 2, 1, 6, 2, 3, 2, 7, 3, 4, 3, 8, 4, 9, 5, 7, 5, 8, 6, 8, 6, 9, 7, 9 +}; + +const igraph_real_t igraph_i_famous_robertson[] = { + 19, 38, 0, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, + 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 0, 18, 0, 4, 4, 9, 9, 13, 13, + 17, 2, 17, 2, 6, 6, 10, 10, 15, 0, 15, 1, 8, 8, 16, 5, 16, 5, 12, 1, 12, 7, 18, + 7, 14, 3, 14, 3, 11, 11, 18 +}; + +const igraph_real_t igraph_i_famous_smallestcyclicgroup[] = { + 9, 15, 0, + 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 7, 1, 8, 2, 5, 2, 6, 2, 7, 3, 8, + 4, 5, 6, 7 +}; + +const igraph_real_t igraph_i_famous_tetrahedron[] = { + 4, 6, 0, + 0, 3, 1, 3, 2, 3, 0, 1, 1, 2, 0, 2 +}; + +const igraph_real_t igraph_i_famous_thomassen[] = { + 34, 52, 0, + 0, 2, 0, 3, 1, 3, 1, 4, 2, 4, 5, 7, 5, 8, 6, 8, 6, 9, 7, 9, 10, 12, 10, 13, 11, + 13, 11, 14, 12, 14, 15, 17, 15, 18, 16, 18, 16, 19, 17, 19, 9, 19, 4, 14, 24, + 25, 25, 26, 20, 26, 20, 21, 21, 22, 22, 23, 23, 27, 27, 28, 28, 29, 29, 30, 30, + 31, 31, 32, 32, 33, 24, 33, 5, 24, 6, 25, 7, 26, 8, 20, 0, 20, 1, 21, 2, 22, 3, + 23, 10, 27, 11, 28, 12, 29, 13, 30, 15, 30, 16, 31, 17, 32, 18, 33 +}; + +const igraph_real_t igraph_i_famous_tutte[] = { + 46, 69, 0, + 0, 10, 0, 11, 0, 12, 1, 2, 1, 7, 1, 19, 2, 3, 2, 41, 3, 4, 3, 27, 4, 5, 4, 33, + 5, 6, 5, 45, 6, 9, 6, 29, 7, 8, 7, 21, 8, 9, 8, 22, 9, 24, 10, 13, 10, 14, 11, + 26, 11, 28, 12, 30, 12, 31, 13, 15, 13, 21, 14, 15, 14, 18, 15, 16, 16, 17, 16, + 20, 17, 18, 17, 23, 18, 24, 19, 25, 19, 40, 20, 21, 20, 22, 22, 23, 23, 24, 25, + 26, 25, 38, 26, 34, 27, 28, 27, 39, 28, 34, 29, 30, 29, 44, 30, 35, 31, 32, 31, + 35, 32, 33, 32, 42, 33, 43, 34, 36, 35, 37, 36, 38, 36, 39, 37, 42, 37, 44, 38, + 40, 39, 41, 40, 41, 42, 43, 43, 45, 44, 45 +}; + +const igraph_real_t igraph_i_famous_uniquely3colorable[] = { + 12, 22, 0, + 0, 1, 0, 3, 0, 6, 0, 8, 1, 4, 1, 7, 1, 9, 2, 3, 2, 6, 2, 7, 2, 9, 2, 11, 3, 4, + 3, 10, 4, 5, 4, 11, 5, 6, 5, 7, 5, 8, 5, 10, 8, 11, 9, 10 +}; + +const igraph_real_t igraph_i_famous_walther[] = { + 25, 31, 0, + 0, 1, 1, 2, 1, 8, 2, 3, 2, 13, 3, 4, 3, 16, 4, 5, 5, 6, 5, 19, 6, 7, 6, 20, 7, + 21, 8, 9, 8, 13, 9, 10, 9, 22, 10, 11, 10, 20, 11, 12, 13, 14, 14, 15, 14, 23, + 15, 16, 15, 17, 17, 18, 18, 19, 18, 24, 20, 24, 22, 23, 23, 24 +}; + +const igraph_real_t igraph_i_famous_zachary[] = { + 34, 78, 0, + 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, + 0, 10, 0, 11, 0, 12, 0, 13, 0, 17, 0, 19, 0, 21, 0, 31, + 1, 2, 1, 3, 1, 7, 1, 13, 1, 17, 1, 19, 1, 21, 1, 30, + 2, 3, 2, 7, 2, 27, 2, 28, 2, 32, 2, 9, 2, 8, 2, 13, + 3, 7, 3, 12, 3, 13, 4, 6, 4, 10, 5, 6, 5, 10, 5, 16, + 6, 16, 8, 30, 8, 32, 8, 33, 9, 33, 13, 33, 14, 32, 14, 33, + 15, 32, 15, 33, 18, 32, 18, 33, 19, 33, 20, 32, 20, 33, + 22, 32, 22, 33, 23, 25, 23, 27, 23, 32, 23, 33, 23, 29, + 24, 25, 24, 27, 24, 31, 25, 31, 26, 29, 26, 33, 27, 33, + 28, 31, 28, 33, 29, 32, 29, 33, 30, 32, 30, 33, 31, 32, 31, 33, + 32, 33 +}; + +static int igraph_i_famous(igraph_t *graph, const igraph_real_t *data) { + long int no_of_nodes = (long int) data[0]; + long int no_of_edges = (long int) data[1]; + igraph_bool_t directed = (igraph_bool_t) data[2]; + igraph_vector_t edges; + + igraph_vector_view(&edges, data + 3, 2 * no_of_edges); + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + directed)); + return 0; +} + +/** + * \function igraph_famous + * \brief Create a famous graph by simply providing its name. + * + * + * The name of the graph can be simply supplied as a string. + * Note that this function creates graphs which don't take any parameters, + * there are separate functions for graphs with parameters, e.g. \ref + * igraph_full() for creating a full graph. + * + * + * The following graphs are supported: + * \clist + * \cli Bull + * The bull graph, 5 vertices, 5 edges, resembles the + * head of a bull if drawn properly. + * \cli Chvatal + * This is the smallest triangle-free graph that is + * both 4-chromatic and 4-regular. According to the Grunbaum + * conjecture there exists an m-regular, m-chromatic graph + * with n vertices for every m>1 and n>2. The Chvatal graph + * is an example for m=4 and n=12. It has 24 edges. + * \cli Coxeter + * A non-Hamiltonian cubic symmetric graph with 28 + * vertices and 42 edges. + * \cli Cubical + * The Platonic graph of the cube. A convex regular + * polyhedron with 8 vertices and 12 edges. + * \cli Diamond + * A graph with 4 vertices and 5 edges, resembles a + * schematic diamond if drawn properly. + * \cli Dodecahedral, Dodecahedron + * Another Platonic solid + * with 20 vertices and 30 edges. + * \cli Folkman + * The semisymmetric graph with minimum number of + * vertices, 20 and 40 edges. A semisymmetric graph is + * regular, edge transitive and not vertex transitive. + * \cli Franklin + * This is a graph whose embedding to the Klein + * bottle can be colored with six colors, it is a + * counterexample to the necessity of the Heawood + * conjecture on a Klein bottle. It has 12 vertices and 18 + * edges. + * \cli Frucht + * The Frucht Graph is the smallest cubical graph + * whose automorphism group consists only of the identity + * element. It has 12 vertices and 18 edges. + * \cli Grotzsch + * The Grötzsch graph is a triangle-free graph with + * 11 vertices, 20 edges, and chromatic number 4. It is named after + * German mathematician Herbert Grötzsch, and its existence + * demonstrates that the assumption of planarity is necessary in + * Grötzsch's theorem that every triangle-free planar + * graph is 3-colorable. + * \cli Heawood + * The Heawood graph is an undirected graph with 14 + * vertices and 21 edges. The graph is cubic, and all cycles in the + * graph have six or more edges. Every smaller cubic graph has shorter + * cycles, so this graph is the 6-cage, the smallest cubic graph of + * girth 6. + * \cli Herschel + * The Herschel graph is the smallest + * nonhamiltonian polyhedral graph. It is the + * unique such graph on 11 nodes, and has 18 edges. + * \cli House + * The house graph is a 5-vertex, 6-edge graph, the + * schematic draw of a house if drawn properly, basically a + * triangle on top of a square. + * \cli HouseX + * The same as the house graph with an X in the square. 5 + * vertices and 8 edges. + * \cli Icosahedral, Icosahedron + * A Platonic solid with 12 + * vertices and 30 edges. + * \cli Krackhardt_Kite + * A social network with 10 vertices and 18 edges. + * Krackhardt, D. Assessing the Political Landscape: + * Structure, Cognition, and Power in Organizations. + * Admin. Sci. Quart. 35, 342-369, 1990. + * \cli Levi + * The graph is a 4-arc transitive cubic graph, it has + * 30 vertices and 45 edges. + * \cli McGee + * The McGee graph is the unique 3-regular 7-cage + * graph, it has 24 vertices and 36 edges. + * \cli Meredith + * The Meredith graph is a quartic graph on 70 + * nodes and 140 edges that is a counterexample to the conjecture that + * every 4-regular 4-connected graph is Hamiltonian. + * \cli Noperfectmatching + * A connected graph with 16 vertices and + * 27 edges containing no perfect matching. A matching in a graph + * is a set of pairwise non-incident edges; that is, no two edges + * share a common vertex. A perfect matching is a matching + * which covers all vertices of the graph. + * \cli Nonline + * A graph whose connected components are the 9 + * graphs whose presence as a vertex-induced subgraph in a + * graph makes a nonline graph. It has 50 vertices and 72 edges. + * \cli Octahedral, Octahedron + * Platonic solid with 6 + * vertices and 12 edges. + * \cli Petersen + * A 3-regular graph with 10 vertices and 15 edges. It is + * the smallest hypohamiltonian graph, i.e. it is + * non-hamiltonian but removing any single vertex from it makes it + * Hamiltonian. + * \cli Robertson + * The unique (4,5)-cage graph, i.e. a 4-regular + * graph of girth 5. It has 19 vertices and 38 edges. + * \cli Smallestcyclicgroup + * A smallest nontrivial graph + * whose automorphism group is cyclic. It has 9 vertices and + * 15 edges. + * \cli Tetrahedral, Tetrahedron + * Platonic solid with 4 + * vertices and 6 edges. + * \cli Thomassen + * The smallest hypotraceable graph, + * on 34 vertices and 52 edges. A hypotracable graph does + * not contain a Hamiltonian path but after removing any + * single vertex from it the remainder always contains a + * Hamiltonian path. A graph containing a Hamiltonian path + * is called traceable. + * \cli Tutte + * Tait's Hamiltonian graph conjecture states that + * every 3-connected 3-regular planar graph is Hamiltonian. + * This graph is a counterexample. It has 46 vertices and 69 + * edges. + * \cli Uniquely3colorable + * Returns a 12-vertex, triangle-free + * graph with chromatic number 3 that is uniquely + * 3-colorable. + * \cli Walther + * An identity graph with 25 vertices and 31 + * edges. An identity graph has a single graph automorphism, + * the trivial one. + * \cli Zachary + * Social network of friendships between 34 members of a + * karate club at a US university in the 1970s. See + * W. W. Zachary, An information flow model for conflict and + * fission in small groups, Journal of Anthropological + * Research 33, 452-473 (1977). + * \endclist + * + * \param graph Pointer to an uninitialized graph object. + * \param name Character constant, the name of the graph to be + * created, it is case insensitive. + * \return Error code, \c IGRAPH_EINVAL if there is no graph with the + * given name. + * + * \sa Other functions for creating graph structures: + * \ref igraph_ring(), \ref igraph_tree(), \ref igraph_lattice(), \ref + * igraph_full(). + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number + * of edges in the graph. + */ + +int igraph_famous(igraph_t *graph, const char *name) { + + if (!strcasecmp(name, "bull")) { + return igraph_i_famous(graph, igraph_i_famous_bull); + } else if (!strcasecmp(name, "chvatal")) { + return igraph_i_famous(graph, igraph_i_famous_chvatal); + } else if (!strcasecmp(name, "coxeter")) { + return igraph_i_famous(graph, igraph_i_famous_coxeter); + } else if (!strcasecmp(name, "cubical")) { + return igraph_i_famous(graph, igraph_i_famous_cubical); + } else if (!strcasecmp(name, "diamond")) { + return igraph_i_famous(graph, igraph_i_famous_diamond); + } else if (!strcasecmp(name, "dodecahedral") || + !strcasecmp(name, "dodecahedron")) { + return igraph_i_famous(graph, igraph_i_famous_dodecahedron); + } else if (!strcasecmp(name, "folkman")) { + return igraph_i_famous(graph, igraph_i_famous_folkman); + } else if (!strcasecmp(name, "franklin")) { + return igraph_i_famous(graph, igraph_i_famous_franklin); + } else if (!strcasecmp(name, "frucht")) { + return igraph_i_famous(graph, igraph_i_famous_frucht); + } else if (!strcasecmp(name, "grotzsch")) { + return igraph_i_famous(graph, igraph_i_famous_grotzsch); + } else if (!strcasecmp(name, "heawood")) { + return igraph_i_famous(graph, igraph_i_famous_heawood); + } else if (!strcasecmp(name, "herschel")) { + return igraph_i_famous(graph, igraph_i_famous_herschel); + } else if (!strcasecmp(name, "house")) { + return igraph_i_famous(graph, igraph_i_famous_house); + } else if (!strcasecmp(name, "housex")) { + return igraph_i_famous(graph, igraph_i_famous_housex); + } else if (!strcasecmp(name, "icosahedral") || + !strcasecmp(name, "icosahedron")) { + return igraph_i_famous(graph, igraph_i_famous_icosahedron); + } else if (!strcasecmp(name, "krackhardt_kite")) { + return igraph_i_famous(graph, igraph_i_famous_krackhardt_kite); + } else if (!strcasecmp(name, "levi")) { + return igraph_i_famous(graph, igraph_i_famous_levi); + } else if (!strcasecmp(name, "mcgee")) { + return igraph_i_famous(graph, igraph_i_famous_mcgee); + } else if (!strcasecmp(name, "meredith")) { + return igraph_i_famous(graph, igraph_i_famous_meredith); + } else if (!strcasecmp(name, "noperfectmatching")) { + return igraph_i_famous(graph, igraph_i_famous_noperfectmatching); + } else if (!strcasecmp(name, "nonline")) { + return igraph_i_famous(graph, igraph_i_famous_nonline); + } else if (!strcasecmp(name, "octahedral") || + !strcasecmp(name, "octahedron")) { + return igraph_i_famous(graph, igraph_i_famous_octahedron); + } else if (!strcasecmp(name, "petersen")) { + return igraph_i_famous(graph, igraph_i_famous_petersen); + } else if (!strcasecmp(name, "robertson")) { + return igraph_i_famous(graph, igraph_i_famous_robertson); + } else if (!strcasecmp(name, "smallestcyclicgroup")) { + return igraph_i_famous(graph, igraph_i_famous_smallestcyclicgroup); + } else if (!strcasecmp(name, "tetrahedral") || + !strcasecmp(name, "tetrahedron")) { + return igraph_i_famous(graph, igraph_i_famous_tetrahedron); + } else if (!strcasecmp(name, "thomassen")) { + return igraph_i_famous(graph, igraph_i_famous_thomassen); + } else if (!strcasecmp(name, "tutte")) { + return igraph_i_famous(graph, igraph_i_famous_tutte); + } else if (!strcasecmp(name, "uniquely3colorable")) { + return igraph_i_famous(graph, igraph_i_famous_uniquely3colorable); + } else if (!strcasecmp(name, "walther")) { + return igraph_i_famous(graph, igraph_i_famous_walther); + } else if (!strcasecmp(name, "zachary")) { + return igraph_i_famous(graph, igraph_i_famous_zachary); + } + + IGRAPH_ERRORF("%s is not a known graph. See the documentation for valid graph names.", IGRAPH_EINVAL, name); +} diff --git a/src/rigraph/core/constructors/full.c b/src/rigraph/core/constructors/full.c new file mode 100644 index 0000000..bddcfb5 --- /dev/null +++ b/src/rigraph/core/constructors/full.c @@ -0,0 +1,154 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +/** + * \ingroup generators + * \function igraph_full + * \brief Creates a full graph (directed or undirected, with or without loops). + * + * + * In a full graph every possible edge is present, every vertex is + * connected to every other vertex. A full graph in \c igraph should be + * distinguished from the concept of complete graphs as used in graph theory. + * If n is a positive integer, then the complete graph K_n on n vertices is + * the undirected simple graph with the following property. For any distinct + * pair (u,v) of vertices in K_n, uv (or equivalently vu) is an edge of K_n. + * In \c igraph, a full graph on n vertices can be K_n, a directed version of + * K_n, or K_n with at least one loop edge. In any case, if F is a full graph + * on n vertices as generated by \c igraph, then K_n is a subgraph of the + * undirected version of F. + * + * \param graph Pointer to an uninitialized graph object. + * \param n Integer, the number of vertices in the graph. + * \param directed Logical, whether to create a directed graph. + * \param loops Logical, whether to include self-edges (loops). + * \return Error code: + * \c IGRAPH_EINVAL: invalid number of vertices. + * + * Time complexity: O(|V|+|E|), + * |V| is the number of vertices, + * |E| the number of edges in the + * graph. Of course this is the same as + * O(|E|)=O(|V||V|) + * here. + * + * \sa \ref igraph_lattice(), \ref igraph_star(), \ref igraph_tree() + * for creating other regular structures. + * + * \example examples/simple/igraph_full.c + */ +int igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + igraph_bool_t loops) { + + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + long int i, j; + + if (n < 0) { + IGRAPH_ERROR("invalid number of vertices", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + if (directed && loops) { + IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * n * n)); + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, j); /* reserved */ + } + } + } else if (directed && !loops) { + IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * n * (n - 1))); + for (i = 0; i < n; i++) { + for (j = 0; j < i; j++) { + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, j); /* reserved */ + } + for (j = i + 1; j < n; j++) { + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, j); /* reserved */ + } + } + } else if (!directed && loops) { + IGRAPH_CHECK(igraph_vector_reserve(&edges, n * (n + 1))); + for (i = 0; i < n; i++) { + for (j = i; j < n; j++) { + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, j); /* reserved */ + } + } + } else { + IGRAPH_CHECK(igraph_vector_reserve(&edges, n * (n - 1))); + for (i = 0; i < n; i++) { + for (j = i + 1; j < n; j++) { + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, j); /* reserved */ + } + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_full_citation + * Creates a full citation graph + * + * This is a directed graph, where every i->j edge is + * present if and only if j<i. + * If the \c directed argument is zero then an undirected graph is + * created, and it is just a full graph. + * \param graph Pointer to an uninitialized graph object, the result + * is stored here. + * \param n The number of vertices. + * \param directed Whether to created a directed graph. If zero an + * undirected graph is created. + * \return Error code. + * + * Time complexity: O(|V|^2), as we have many edges. + */ +int igraph_full_citation(igraph_t *graph, igraph_integer_t n, + igraph_bool_t directed) { + igraph_vector_t edges; + long int i, j, ptr = 0; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, n * (n - 1)); + for (i = 1; i < n; i++) { + for (j = 0; j < i; j++) { + VECTOR(edges)[ptr++] = i; + VECTOR(edges)[ptr++] = j; + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/constructors/kautz.c b/src/rigraph/core/constructors/kautz.c new file mode 100644 index 0000000..017d05c --- /dev/null +++ b/src/rigraph/core/constructors/kautz.c @@ -0,0 +1,186 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +/** + * \function igraph_kautz + * \brief Generate a Kautz graph. + * + * A Kautz graph is a labeled graph, vertices are labeled by strings + * of length \c n+1 above an alphabet with \c m+1 letters, with + * the restriction that every two consecutive letters in the string + * must be different. There is a directed edge from a vertex \c v to + * another vertex \c w if it is possible to transform the string of + * \c v into the string of \c w by removing the first letter and + * appending a letter to it. For string length 1 the new letter + * cannot equal the old letter, so there are no loops. + * + * + * Kautz graphs have some interesting properties, see e.g. Wikipedia + * for details. + * + * + * Vincent Matossian wrote the first version of this function in R, + * thanks. + * \param graph Pointer to an uninitialized graph object, the result + * will be stored here. + * \param m Integer, \c m+1 is the number of letters in the alphabet. + * \param n Integer, \c n+1 is the length of the strings. + * \return Error code. + * + * \sa \ref igraph_de_bruijn(). + * + * Time complexity: O(|V|* [(m+1)/m]^n +|E|), in practice it is more + * like O(|V|+|E|). |V| is the number of vertices, |E| is the number + * of edges and \c m and \c n are the corresponding arguments. + */ +int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { + + /* m+1 - number of symbols */ + /* n+1 - length of strings */ + + long int mm = m; + long int no_of_nodes, no_of_edges; + long int allstrings; + long int i, j, idx = 0; + igraph_vector_t edges; + igraph_vector_long_t digits, table; + igraph_vector_long_t index1, index2; + long int actb = 0; + long int actvalue = 0; + + if (m < 0 || n < 0) { + IGRAPH_ERROR("`m' and `n' should be non-negative in a Kautz graph", + IGRAPH_EINVAL); + } + + if (n == 0) { + return igraph_full(graph, m + 1, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + } + if (m == 0) { + return igraph_empty(graph, 0, IGRAPH_DIRECTED); + } + + no_of_nodes = (long int) ((m + 1) * pow(m, n)); + no_of_edges = no_of_nodes * m; + allstrings = (long int) pow(m + 1, n + 1); + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + IGRAPH_CHECK(igraph_vector_long_init(&table, n + 1)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &table); + j = 1; + for (i = n; i >= 0; i--) { + VECTOR(table)[i] = j; + j *= (m + 1); + } + + IGRAPH_CHECK(igraph_vector_long_init(&digits, n + 1)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &digits); + IGRAPH_CHECK(igraph_vector_long_init(&index1, (long int) pow(m + 1, n + 1))); + IGRAPH_FINALLY(igraph_vector_long_destroy, &index1); + IGRAPH_CHECK(igraph_vector_long_init(&index2, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &index2); + + /* Fill the index tables*/ + while (1) { + /* at the beginning of the loop, 0:actb contain the valid prefix */ + /* we might need to fill it to get a valid string */ + long int z = 0; + if (VECTOR(digits)[actb] == 0) { + z = 1; + } + for (actb++; actb <= n; actb++) { + VECTOR(digits)[actb] = z; + actvalue += z * VECTOR(table)[actb]; + z = 1 - z; + } + actb = n; + + /* ok, we have a valid string now */ + VECTOR(index1)[actvalue] = idx + 1; + VECTOR(index2)[idx] = actvalue; + idx++; + + /* finished? */ + if (idx >= no_of_nodes) { + break; + } + + /* not yet, we need a valid prefix now */ + while (1) { + /* try to increase digits at position actb */ + long int next = VECTOR(digits)[actb] + 1; + if (actb != 0 && VECTOR(digits)[actb - 1] == next) { + next++; + } + if (next <= m) { + /* ok, no problem */ + actvalue += (next - VECTOR(digits)[actb]) * VECTOR(table)[actb]; + VECTOR(digits)[actb] = next; + break; + } else { + /* bad luck, try the previous digit */ + actvalue -= VECTOR(digits)[actb] * VECTOR(table)[actb]; + actb--; + } + } + } + + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + + /* Now come the edges at last */ + for (i = 0; i < no_of_nodes; i++) { + long int fromvalue = VECTOR(index2)[i]; + long int lastdigit = fromvalue % (mm + 1); + long int basis = (fromvalue * (mm + 1)) % allstrings; + for (j = 0; j <= m; j++) { + long int tovalue, to; + if (j == lastdigit) { + continue; + } + tovalue = basis + j; + to = VECTOR(index1)[tovalue] - 1; + if (to < 0) { + continue; + } + igraph_vector_push_back(&edges, i); + igraph_vector_push_back(&edges, to); + } + } + + igraph_vector_long_destroy(&index2); + igraph_vector_long_destroy(&index1); + igraph_vector_long_destroy(&digits); + igraph_vector_long_destroy(&table); + IGRAPH_FINALLY_CLEAN(4); + + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + IGRAPH_DIRECTED)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} diff --git a/src/rigraph/core/constructors/lcf.c b/src/rigraph/core/constructors/lcf.c new file mode 100644 index 0000000..585c673 --- /dev/null +++ b/src/rigraph/core/constructors/lcf.c @@ -0,0 +1,141 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_operators.h" + +/** + * \function igraph_lcf_vector + * \brief Creates a graph from LCF notation. + * + * This function is essentially the same as \ref igraph_lcf(), only + * the way for giving the arguments is different. See \ref + * igraph_lcf() for details. + * \param graph Pointer to an uninitialized graph object. + * \param n Integer constant giving the number of vertices. + * \param shifts A vector giving the shifts. + * \param repeats An integer constant giving the number of repeats + * for the shifts. + * \return Error code. + * + * \sa \ref igraph_lcf(), \ref igraph_extended_chordal_ring() + * + * Time complexity: O(|V|+|E|), linear in the number of vertices plus + * the number of edges. + */ +int igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, + const igraph_vector_t *shifts, + igraph_integer_t repeats) { + + igraph_vector_t edges; + long int no_of_shifts = igraph_vector_size(shifts); + long int ptr = 0, i, sptr = 0; + long int no_of_nodes = n; + long int no_of_edges = n + no_of_shifts * repeats; + + if (repeats < 0) { + IGRAPH_ERROR("number of repeats must be positive", IGRAPH_EINVAL); + } + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * no_of_edges); + + if (no_of_nodes > 0) { + /* Create a ring first */ + for (i = 0; i < no_of_nodes; i++) { + VECTOR(edges)[ptr++] = i; + VECTOR(edges)[ptr++] = i + 1; + } + VECTOR(edges)[ptr - 1] = 0; + } + + /* Then add the rest */ + while (ptr < 2 * no_of_edges) { + long int sh = (long int) VECTOR(*shifts)[sptr % no_of_shifts]; + long int from = sptr % no_of_nodes; + long int to = (no_of_nodes + sptr + sh) % no_of_nodes; + VECTOR(edges)[ptr++] = from; + VECTOR(edges)[ptr++] = to; + sptr++; + } + + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + IGRAPH_UNDIRECTED)); + IGRAPH_CHECK(igraph_simplify(graph, 1 /* true */, 1 /* true */, NULL)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_lcf + * \brief Creates a graph from LCF notation. + * + * + * LCF is short for Lederberg-Coxeter-Frucht, it is a concise notation for + * 3-regular Hamiltonian graphs. It consists of three parameters: the + * number of vertices in the graph, a list of shifts giving additional + * edges to a cycle backbone, and another integer giving how many times + * the shifts should be performed. See + * http://mathworld.wolfram.com/LCFNotation.html for details. + * + * \param graph Pointer to an uninitialized graph object. + * \param n Integer, the number of vertices in the graph. + * \param ... The shifts and the number of repeats for the shifts, + * plus an additional 0 to mark the end of the arguments. + * \return Error code. + * + * \sa See \ref igraph_lcf_vector() for a similar function using a + * vector_t instead of the variable length argument list. + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number + * of edges. + * + * \example examples/simple/igraph_lcf.c + */ +int igraph_lcf(igraph_t *graph, igraph_integer_t n, ...) { + igraph_vector_t shifts; + igraph_integer_t repeats; + va_list ap; + + IGRAPH_VECTOR_INIT_FINALLY(&shifts, 0); + + va_start(ap, n); + while (1) { + int num = va_arg(ap, int); + if (num == 0) { + break; + } + IGRAPH_CHECK(igraph_vector_push_back(&shifts, num)); + } + if (igraph_vector_size(&shifts) == 0) { + repeats = 0; + } else { + repeats = (igraph_integer_t) igraph_vector_pop_back(&shifts); + } + + IGRAPH_CHECK(igraph_lcf_vector(graph, n, &shifts, repeats)); + igraph_vector_destroy(&shifts); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} diff --git a/src/rigraph/core/constructors/linegraph.c b/src/rigraph/core/constructors/linegraph.c new file mode 100644 index 0000000..5387540 --- /dev/null +++ b/src/rigraph/core/constructors/linegraph.c @@ -0,0 +1,164 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +#include "../core/interruption.h" + +/* Note to self: tried using adjacency lists instead of igraph_incident queries, + * with minimal performance improvements on a graph with 70K vertices and 360K + * edges. (1.09s instead of 1.10s). I think it's not worth the fuss. */ +static int igraph_i_linegraph_undirected(const igraph_t *graph, igraph_t *linegraph) { + long int no_of_edges = igraph_ecount(graph); + long int i, j, n; + igraph_vector_t adjedges, adjedges2; + igraph_vector_t edges; + long int prev = -1; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&adjedges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&adjedges2, 0); + + for (i = 0; i < no_of_edges; i++) { + long int from = IGRAPH_FROM(graph, i); + long int to = IGRAPH_TO(graph, i); + + IGRAPH_ALLOW_INTERRUPTION(); + + if (from != prev) { + IGRAPH_CHECK(igraph_incident(graph, &adjedges, (igraph_integer_t) from, + IGRAPH_ALL)); + } + n = igraph_vector_size(&adjedges); + for (j = 0; j < n; j++) { + long int e = (long int) VECTOR(adjedges)[j]; + if (e < i) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, e)); + } + } + + IGRAPH_CHECK(igraph_incident(graph, &adjedges2, (igraph_integer_t) to, + IGRAPH_ALL)); + n = igraph_vector_size(&adjedges2); + for (j = 0; j < n; j++) { + long int e = (long int) VECTOR(adjedges2)[j]; + if (e < i) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, e)); + } + } + + prev = from; + } + + igraph_vector_destroy(&adjedges); + igraph_vector_destroy(&adjedges2); + IGRAPH_FINALLY_CLEAN(2); + + igraph_create(linegraph, &edges, (igraph_integer_t) no_of_edges, + igraph_is_directed(graph)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_linegraph_directed(const igraph_t *graph, igraph_t *linegraph) { + long int no_of_edges = igraph_ecount(graph); + long int i, j, n; + igraph_vector_t adjedges; + igraph_vector_t edges; + long int prev = -1; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&adjedges, 0); + + for (i = 0; i < no_of_edges; i++) { + long int from = IGRAPH_FROM(graph, i); + + IGRAPH_ALLOW_INTERRUPTION(); + + if (from != prev) { + IGRAPH_CHECK(igraph_incident(graph, &adjedges, (igraph_integer_t) from, + IGRAPH_IN)); + } + n = igraph_vector_size(&adjedges); + for (j = 0; j < n; j++) { + long int e = (long int) VECTOR(adjedges)[j]; + IGRAPH_CHECK(igraph_vector_push_back(&edges, e)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + } + + prev = from; + } + + igraph_vector_destroy(&adjedges); + IGRAPH_FINALLY_CLEAN(1); + igraph_create(linegraph, &edges, (igraph_integer_t) no_of_edges, igraph_is_directed(graph)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_linegraph + * \brief Create the line graph of a graph. + * + * The line graph L(G) of a G undirected graph is defined as follows. + * L(G) has one vertex for each edge in G and two different vertices in L(G) + * are connected by an edge if their corresponding edges share an end point. + * In a multigraph, if two end points are shared, two edges are created. + * The vertex of a loop is counted as two end points. + * + * + * The line graph L(G) of a G directed graph is slightly different, + * L(G) has one vertex for each edge in G and two vertices in L(G) are connected + * by a directed edge if the target of the first vertex's corresponding edge + * is the same as the source of the second vertex's corresponding edge. + * + * + * Edge \em i in the original graph will correspond to vertex \em i + * in the line graph. + * + * + * The first version of this function was contributed by Vincent Matossian, + * thanks. + * \param graph The input graph, may be directed or undirected. + * \param linegraph Pointer to an uninitialized graph object, the + * result is stored here. + * \return Error code. + * + * Time complexity: O(|V|+|E|), the number of edges plus the number of vertices. + */ + +int igraph_linegraph(const igraph_t *graph, igraph_t *linegraph) { + + if (igraph_is_directed(graph)) { + return igraph_i_linegraph_directed(graph, linegraph); + } else { + return igraph_i_linegraph_undirected(graph, linegraph); + } +} diff --git a/src/rigraph/core/constructors/prufer.c b/src/rigraph/core/constructors/prufer.c new file mode 100644 index 0000000..40fa3ea --- /dev/null +++ b/src/rigraph/core/constructors/prufer.c @@ -0,0 +1,119 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +/** + * \ingroup generators + * \function igraph_from_prufer + * \brief Generates a tree from a Prüfer sequence. + * + * A Prüfer sequence is a unique sequence of integers associated + * with a labelled tree. A tree on n vertices can be represented by a + * sequence of n-2 integers, each between 0 and n-1 (inclusive). + * + * The algorithm used by this function is based on + * Paulius Micikevičius, Saverio Caminiti, Narsingh Deo: + * Linear-time Algorithms for Encoding Trees as Sequences of Node Labels + * + * \param graph Pointer to an uninitialized graph object. + * \param prufer The Prüfer sequence + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * there is not enough memory to perform the operation. + * \cli IGRAPH_EINVAL + * invalid Prüfer sequence given + * \endclist + * + * \sa \ref igraph_to_prufer(), \ref igraph_tree(), \ref igraph_tree_game() + * + */ +int igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer) { + igraph_vector_int_t degree; + igraph_vector_t edges; + long n; + long i, k; + long u, v; /* vertices */ + long ec; + + n = igraph_vector_int_size(prufer) + 2; + + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, n); /* initializes vector to zeros */ + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * (n - 1)); + + /* build out-degree vector (i.e. number of child vertices) and verify Prufer sequence */ + for (i = 0; i < n - 2; ++i) { + long u = VECTOR(*prufer)[i]; + if (u >= n || u < 0) { + IGRAPH_ERROR("Invalid Prufer sequence", IGRAPH_EINVAL); + } + VECTOR(degree)[u] += 1; + } + + v = 0; /* initialize v now, in case Prufer sequence is empty */ + k = 0; /* index into the Prufer vector */ + ec = 0; /* index into the edges vector */ + for (i = 0; i < n; ++i) { + u = i; + + while (k < n - 2 && u <= i && (VECTOR(degree)[u] == 0)) { + /* u is a leaf here */ + + v = VECTOR(*prufer)[k]; /* parent of u */ + + /* add edge */ + VECTOR(edges)[ec++] = v; + VECTOR(edges)[ec++] = u; + + k += 1; + + VECTOR(degree)[v] -= 1; + + u = v; + } + + if (k == n - 2) { + break; + } + } + + /* find u for last edge, v is already set */ + for (u = i + 1; u < n; ++u) + if ((VECTOR(degree)[u] == 0) && u != v) { + break; + } + + /* add last edge */ + VECTOR(edges)[ec++] = v; + VECTOR(edges)[ec++] = u; + + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) n, /* directed = */ 0)); + + igraph_vector_destroy(&edges); + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/constructors/regular.c b/src/rigraph/core/constructors/regular.c new file mode 100644 index 0000000..89d763c --- /dev/null +++ b/src/rigraph/core/constructors/regular.c @@ -0,0 +1,511 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" + +#include "../core/interruption.h" + +/** + * \ingroup generators + * \function igraph_star + * \brief Creates a \em star graph, every vertex connects only to the center. + * + * \param graph Pointer to an uninitialized graph object, this will + * be the result. + * \param n Integer constant, the number of vertices in the graph. + * \param mode Constant, gives the type of the star graph to + * create. Possible values: + * \clist + * \cli IGRAPH_STAR_OUT + * directed star graph, edges point + * \em from the center to the other vertices. + * \cli IGRAPH_STAR_IN + * directed star graph, edges point + * \em to the center from the other vertices. + * \cli IGRAPH_STAR_MUTUAL + * directed star graph with mutual edges. + * \cli IGRAPH_STAR_UNDIRECTED + * an undirected star graph is + * created. + * \endclist + * \param center Id of the vertex which will be the center of the + * graph. + * \return Error code: + * \clist + * \cli IGRAPH_EINVVID + * invalid number of vertices. + * \cli IGRAPH_EINVAL + * invalid center vertex. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(|V|), the + * number of vertices in the graph. + * + * \sa \ref igraph_lattice(), \ref igraph_ring(), \ref igraph_tree() + * for creating other regular structures. + * + * \example examples/simple/igraph_star.c + */ +int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, + igraph_integer_t center) { + + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + long int i; + + if (n < 0) { + IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVVID); + } + if (center < 0 || center > n - 1) { + IGRAPH_ERROR("Invalid center vertex", IGRAPH_EINVAL); + } + if (mode != IGRAPH_STAR_OUT && mode != IGRAPH_STAR_IN && + mode != IGRAPH_STAR_MUTUAL && mode != IGRAPH_STAR_UNDIRECTED) { + IGRAPH_ERROR("invalid mode", IGRAPH_EINVMODE); + } + + if (mode != IGRAPH_STAR_MUTUAL) { + IGRAPH_VECTOR_INIT_FINALLY(&edges, (n - 1) * 2); + } else { + IGRAPH_VECTOR_INIT_FINALLY(&edges, (n - 1) * 2 * 2); + } + + if (mode == IGRAPH_STAR_OUT) { + for (i = 0; i < center; i++) { + VECTOR(edges)[2 * i] = center; + VECTOR(edges)[2 * i + 1] = i; + } + for (i = center + 1; i < n; i++) { + VECTOR(edges)[2 * (i - 1)] = center; + VECTOR(edges)[2 * (i - 1) + 1] = i; + } + } else if (mode == IGRAPH_STAR_MUTUAL) { + for (i = 0; i < center; i++) { + VECTOR(edges)[4 * i] = center; + VECTOR(edges)[4 * i + 1] = i; + VECTOR(edges)[4 * i + 2] = i; + VECTOR(edges)[4 * i + 3] = center; + } + for (i = center + 1; i < n; i++) { + VECTOR(edges)[4 * i - 4] = center; + VECTOR(edges)[4 * i - 3] = i; + VECTOR(edges)[4 * i - 2] = i; + VECTOR(edges)[4 * i - 1] = center; + } + } else { + for (i = 0; i < center; i++) { + VECTOR(edges)[2 * i + 1] = center; + VECTOR(edges)[2 * i] = i; + } + for (i = center + 1; i < n; i++) { + VECTOR(edges)[2 * (i - 1) + 1] = center; + VECTOR(edges)[2 * (i - 1)] = i; + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, 0, + (mode != IGRAPH_STAR_UNDIRECTED))); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \ingroup generators + * \function igraph_lattice + * \brief Arbitrary dimensional square lattices. + * + * Creates d-dimensional square lattices of the given size. Optionally, + * the lattice can be made periodic, and the neighbors within a given + * graph distance can be connected. + * + * + * In the zero-dimensional case, the singleton graph is returned. + * + * + * The vertices of the resulting graph are ordered such that the + * index of the vertex at position (i_0, i_1, i_2, ..., i_d) + * in a lattice of size (n_0, n_1, ..., n_d) will be + * i_0 + n_0 * i_1 + n_0 * n_1 * i_2 + .... + * + * \param graph An uninitialized graph object. + * \param dimvector Vector giving the sizes of the lattice in each of + * its dimensions. The dimension of the lattice will be the + * same as the length of this vector. + * \param nei Integer value giving the distance (number of steps) + * within which two vertices will be connected. + * \param directed Boolean, whether to create a directed graph. + * If the \c mutual and \c circular arguments are not set to true, + * edges will be directed from lower-index vertices towards + * higher-index ones. + * \param mutual Boolean, if the graph is directed this gives whether + * to create all connections as mutual. + * \param circular Boolean, defines whether the generated lattice is + * periodic. + * \return Error code: + * \c IGRAPH_EINVAL: invalid (negative) + * dimension vector. + * + * Time complexity: If \p nei is less than two then it is O(|V|+|E|) (as + * far as I remember), |V| and |E| are the number of vertices + * and edges in the generated graph. Otherwise it is O(|V|*d^k+|E|), d + * is the average degree of the graph, k is the \p nei argument. + */ +int igraph_lattice(igraph_t *graph, const igraph_vector_t *dimvector, + igraph_integer_t nei, igraph_bool_t directed, igraph_bool_t mutual, + igraph_bool_t circular) { + + long int dims = igraph_vector_size(dimvector); + long int no_of_nodes = (long int) igraph_vector_prod(dimvector); + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + long int *coords, *weights; + long int i, j; + int carry, pos; + + if (igraph_vector_any_smaller(dimvector, 0)) { + IGRAPH_ERROR("Invalid dimension vector", IGRAPH_EINVAL); + } + + /* init coords & weights */ + + coords = IGRAPH_CALLOC(dims, long int); + if (coords == 0) { + IGRAPH_ERROR("Lattice creation failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, coords); + weights = IGRAPH_CALLOC(dims, long int); + if (weights == 0) { + IGRAPH_ERROR("Lattice creation failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, weights); + if (dims > 0) { + weights[0] = 1; + for (i = 1; i < dims; i++) { + weights[i] = weights[i - 1] * (long int) VECTOR(*dimvector)[i - 1]; + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_nodes * dims + + mutual * directed * no_of_nodes * dims)); + + for (i = 0; i < no_of_nodes; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + for (j = 0; j < dims; j++) { + if (circular || coords[j] != VECTOR(*dimvector)[j] - 1) { + long int new_nei; + if (coords[j] != VECTOR(*dimvector)[j] - 1) { + new_nei = i + weights[j] + 1; + } else { + new_nei = i - (long int) (VECTOR(*dimvector)[j] - 1) * weights[j] + 1; + } + if (new_nei != i + 1 && + (VECTOR(*dimvector)[j] != 2 || coords[j] != 1 || directed)) { + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, new_nei - 1); /* reserved */ + } + } /* if circular || coords[j] */ + if (mutual && directed && (circular || coords[j] != 0)) { + long int new_nei; + if (coords[j] != 0) { + new_nei = i - weights[j] + 1; + } else { + new_nei = i + (long int) (VECTOR(*dimvector)[j] - 1) * weights[j] + 1; + } + if (new_nei != i + 1 && + (VECTOR(*dimvector)[j] != 2 || !circular)) { + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, new_nei - 1); /* reserved */ + } + } /* if circular || coords[0] */ + } /* for j= 2) { + IGRAPH_CHECK(igraph_connect_neighborhood(graph, nei, IGRAPH_ALL)); + } + + /* clean up */ + IGRAPH_FREE(coords); + IGRAPH_FREE(weights); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \ingroup generators + * \function igraph_ring + * \brief Creates a \em cycle graph or a \em path graph. + * + * A circular ring on \c n vertices is commonly known in graph + * theory as the cycle graph, and often denoted by C_n. + * Removing a single edge from the cycle graph C_n results + * in the path graph P_n. This function can generate both. + * + * + * This function is a convenience wrapper for the one-dimensional case of + * \ref igraph_lattice(). + * + * \param graph Pointer to an uninitialized graph object. + * \param n The number of vertices in the graph. + * \param directed Logical, whether to create a directed graph. + * All edges will be oriented in the same direction along + * the cycle or path. + * \param mutual Logical, whether to create mutual edges in directed + * graphs. It is ignored for undirected graphs. + * \param circular Logical, whether to create a closed ring (a cycle) + * or an open path. + * \return Error code: + * \c IGRAPH_EINVAL: invalid number of vertices. + * + * Time complexity: O(|V|), the number of vertices in the graph. + * + * \sa \ref igraph_lattice() for generating more general lattices. + * + * \example examples/simple/igraph_ring.c + */ +int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + igraph_bool_t mutual, igraph_bool_t circular) { + + igraph_vector_t v = IGRAPH_VECTOR_NULL; + + if (n < 0) { + IGRAPH_ERRORF("The number of vertices must be non-negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, n); + } + + IGRAPH_VECTOR_INIT_FINALLY(&v, 1); + VECTOR(v)[0] = n; + + IGRAPH_CHECK(igraph_lattice(graph, &v, 1, directed, mutual, circular)); + + igraph_vector_destroy(&v); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_tree + * \brief Creates a tree in which almost all vertices have the same number of children. + * + * To obtain a completely symmetric tree with \c l layers, where each + * vertex has precisely \p children descendants, use + * n = (children^(l+1) - 1) / (children - 1). + * Such trees are often called k-ary trees, where \c k refers + * to the number of children. + * + * + * Note that for n=0, the null graph is returned, + * which is not considered to be a tree by \ref igraph_is_tree(). + * + * \param graph Pointer to an uninitialized graph object. + * \param n Integer, the number of vertices in the graph. + * \param children Integer, the number of children of a vertex in the + * tree. + * \param type Constant, gives whether to create a directed tree, and + * if this is the case, also its orientation. Possible values: + * \clist + * \cli IGRAPH_TREE_OUT + * directed tree, the edges point + * from the parents to their children, + * \cli IGRAPH_TREE_IN + * directed tree, the edges point from + * the children to their parents. + * \cli IGRAPH_TREE_UNDIRECTED + * undirected tree. + * \endclist + * \return Error code: + * \c IGRAPH_EINVAL: invalid number of vertices. + * \c IGRAPH_INVMODE: invalid mode argument. + * + * Time complexity: O(|V|+|E|), the + * number of vertices plus the number of edges in the graph. + * + * \sa \ref igraph_lattice(), \ref igraph_star() for creating other regular + * structures; \ref igraph_from_prufer() for creating arbitrary trees; + * \ref igraph_tree_game() for uniform random sampling of trees. + * + * \example examples/simple/igraph_tree.c + */ +int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, + igraph_tree_mode_t type) { + + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + long int i, j; + long int idx = 0; + long int to = 1; + + if (n < 0) { + IGRAPH_ERROR("Number of vertices cannot be negative.", IGRAPH_EINVAL); + } + if (children <= 0) { + IGRAPH_ERROR("Number of children must be positive.", IGRAPH_EINVAL); + } + if (type != IGRAPH_TREE_OUT && type != IGRAPH_TREE_IN && + type != IGRAPH_TREE_UNDIRECTED) { + IGRAPH_ERROR("Invalid tree orientation type.", IGRAPH_EINVMODE); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, n > 0 ? 2 * (n - 1) : 0); + + i = 0; + if (type == IGRAPH_TREE_OUT) { + while (idx < 2 * (n - 1)) { + for (j = 0; j < children && idx < 2 * (n - 1); j++) { + VECTOR(edges)[idx++] = i; + VECTOR(edges)[idx++] = to++; + } + i++; + } + } else { + while (idx < 2 * (n - 1)) { + for (j = 0; j < children && idx < 2 * (n - 1); j++) { + VECTOR(edges)[idx++] = to++; + VECTOR(edges)[idx++] = i; + } + i++; + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, n, type != IGRAPH_TREE_UNDIRECTED)); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \function igraph_extended_chordal_ring + * \brief Create an extended chordal ring. + * + * An extended chordal ring is a cycle graph with additional chords + * connecting its vertices. + * + * Each row \c L of the matrix \p W specifies a set of chords to be + * inserted, in the following way: vertex \c i will connect to a vertex + * L[(i mod p)] steps ahead of it along the cycle, where + * \c p is the length of \c L. + * In other words, vertex \c i will be connected to vertex + * (i + L[(i mod p)]) mod nodes. If multiple edges are + * defined in this way, this will output a non-simple graph. The result + * can be simplified using \ref igraph_simplify(). + * + * + * See also Kotsis, G: Interconnection Topologies for Parallel Processing + * Systems, PARS Mitteilungen 11, 1-6, 1993. The igraph extended chordal + * rings are not identical to the ones in the paper. In igraph + * the matrix specifies which edges to add. In the paper, a condition is + * specified which should simultaneously hold between two endpoints and + * the reverse endpoints. + * + * \param graph Pointer to an uninitialized graph object, the result + * will be stored here. + * \param nodes Integer constant, the number of vertices in the + * graph. It must be at least 3. + * \param W The matrix specifying the extra edges. The number of + * columns should divide the number of total vertices. The elements + * are allowed to be negative. + * \param directed Whether the graph should be directed. + * \return Error code. + * + * \sa \ref igraph_ring(), \ref igraph_lcf(), \ref igraph_lcf_vector(). + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number + * of edges. + */ +int igraph_extended_chordal_ring( + igraph_t *graph, igraph_integer_t nodes, const igraph_matrix_t *W, + igraph_bool_t directed) { + igraph_vector_t edges; + long int period = igraph_matrix_ncol(W); + long int nrow = igraph_matrix_nrow(W); + long int i, j, mpos = 0, epos = 0; + + if (nodes < 3) { + IGRAPH_ERROR("An extended chordal ring has at least 3 nodes", IGRAPH_EINVAL); + } + + if ((long int)nodes % period != 0) { + IGRAPH_ERROR("The period (number of columns in W) should divide the " + "number of nodes", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * (nodes + nodes * nrow)); + + for (i = 0; i < nodes - 1; i++) { + VECTOR(edges)[epos++] = i; + VECTOR(edges)[epos++] = i + 1; + } + VECTOR(edges)[epos++] = nodes - 1; + VECTOR(edges)[epos++] = 0; + + if (nrow > 0) { + for (i = 0; i < nodes; i++) { + for (j = 0; j < nrow; j++) { + long int offset = (long int) MATRIX(*W, j, mpos); + long int v = (i + offset) % nodes; + + if (v < 0) { + v += nodes; /* handle negative offsets */ + } + + VECTOR(edges)[epos++] = i; + VECTOR(edges)[epos++] = v; + + } + mpos++; if (mpos == period) { + mpos = 0; + } + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/core/array.c b/src/rigraph/core/core/array.c new file mode 100644 index 0000000..4d9904a --- /dev/null +++ b/src/rigraph/core/core/array.c @@ -0,0 +1,50 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_array.h" + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "array.pmt" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#define BASE_LONG +#include "igraph_pmt.h" +#include "array.pmt" +#include "igraph_pmt_off.h" +#undef BASE_LONG + +#define BASE_CHAR +#include "igraph_pmt.h" +#include "array.pmt" +#include "igraph_pmt_off.h" +#undef BASE_CHAR + +#define BASE_BOOL +#include "igraph_pmt.h" +#include "array.pmt" +#include "igraph_pmt_off.h" +#undef BASE_BOOL diff --git a/src/rigraph/core/core/array.pmt b/src/rigraph/core/core/array.pmt new file mode 100644 index 0000000..6d5c735 --- /dev/null +++ b/src/rigraph/core/core/array.pmt @@ -0,0 +1,90 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" + +int FUNCTION(igraph_array3, init)(TYPE(igraph_array3) *a, long int n1, long int n2, + long int n3) { + int ret; + ret = FUNCTION(igraph_vector, init)(&a->data, n1 * n2 * n3); + a->n1 = n1; + a->n2 = n2; + a->n3 = n3; + a->n1n2 = n1 * n2; + + return ret; +} + +void FUNCTION(igraph_array3, destroy)(TYPE(igraph_array3) *a) { + FUNCTION(igraph_vector, destroy)(&a->data); +} + +long int FUNCTION(igraph_array3, size)(const TYPE(igraph_array3) *a) { + return (a->n1n2) * (a->n3); +} + +long int FUNCTION(igraph_array3, n)(const TYPE(igraph_array3) *a, long int idx) { + switch (idx) { + case 1: return a->n1; + break; + case 2: return a->n2; + break; + case 3: return a->n3; + break; + } + return 0; +} + +int FUNCTION(igraph_array3, resize)(TYPE(igraph_array3) *a, long int n1, long int n2, + long int n3) { + int ret = FUNCTION(igraph_vector, resize)(&a->data, n1 * n2 * n3); + a->n1 = n1; + a->n2 = n2; + a->n3 = n3; + a->n1n2 = n1 * n2; + + return ret; +} + +void FUNCTION(igraph_array3, null)(TYPE(igraph_array3) *a) { + FUNCTION(igraph_vector, null)(&a->data); +} + +BASE FUNCTION(igraph_array3, sum)(const TYPE(igraph_array3) *a) { + return FUNCTION(igraph_vector, sum)(&a->data); +} + +void FUNCTION(igraph_array3, scale)(TYPE(igraph_array3) *a, BASE by) { + FUNCTION(igraph_vector, scale)(&a->data, by); +} + +void FUNCTION(igraph_array3, fill)(TYPE(igraph_array3) *a, BASE e) { + FUNCTION(igraph_vector, fill)(&a->data, e); +} + +int FUNCTION(igraph_array3, update)(TYPE(igraph_array3) *to, + const TYPE(igraph_array3) *from) { + IGRAPH_CHECK(FUNCTION(igraph_array3, resize)(to, from->n1, from->n2, from->n3)); + FUNCTION(igraph_vector, update)(&to->data, &from->data); + return 0; +} diff --git a/src/rigraph/core/core/buckets.c b/src/rigraph/core/core/buckets.c new file mode 100644 index 0000000..343780d --- /dev/null +++ b/src/rigraph/core/core/buckets.c @@ -0,0 +1,196 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" + +#include "buckets.h" + +/* The igraph_buckets_t data structure can store at most 'size' + * unique integers in 'bsize' buckets. It has the following simple + * operations (in addition to _init() and _destroy(): + * - _add() adding an element to the given bucket. + * - _popmax() removing an element from the bucket with the highest + * id. + * Currently buckets work as stacks, last-in-first-out mode. + * - _empty() queries whether the buckets is empty. + * + * Internal representation: we use a vector to create single linked + * lists, and another vector that points to the starting element of + * each bucket. Zero means the end of the chain. So bucket i contains + * elements bptr[i], buckets[bptr[i]], buckets[buckets[bptr[i]]], + * etc., until a zero is found. + * + * We also keep the total number of elements in the buckets and the + * id of the non-empty bucket with the highest id, to facilitate the + * _empty() and _popmax() operations. + */ + +int igraph_buckets_init(igraph_buckets_t *b, long int bsize, long int size) { + IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->bptr, bsize); + IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->buckets, size); + b->max = -1; b->no = 0; + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +void igraph_buckets_destroy(igraph_buckets_t *b) { + igraph_vector_long_destroy(&b->bptr); + igraph_vector_long_destroy(&b->buckets); +} + +long int igraph_buckets_popmax(igraph_buckets_t *b) { + /* Precondition: there is at least a non-empty bucket */ + /* Search for the highest bucket first */ + long int max; + while ( (max = (long int) VECTOR(b->bptr)[(long int) b->max]) == 0) { + b->max --; + } + VECTOR(b->bptr)[(long int) b->max] = VECTOR(b->buckets)[max - 1]; + b->no--; + + return max - 1; +} + +long int igraph_buckets_pop(igraph_buckets_t *b, long int bucket) { + long int ret = VECTOR(b->bptr)[bucket] - 1; + VECTOR(b->bptr)[bucket] = VECTOR(b->buckets)[ret]; + b->no--; + return ret; +} + +igraph_bool_t igraph_buckets_empty(const igraph_buckets_t *b) { + return (b->no == 0); +} + +igraph_bool_t igraph_buckets_empty_bucket(const igraph_buckets_t *b, + long int bucket) { + return VECTOR(b->bptr)[bucket] == 0; +} + +void igraph_buckets_add(igraph_buckets_t *b, long int bucket, + long int elem) { + + VECTOR(b->buckets)[(long int) elem] = VECTOR(b->bptr)[(long int) bucket]; + VECTOR(b->bptr)[(long int) bucket] = elem + 1; + if (bucket > b->max) { + b->max = (int) bucket; + } + b->no++; +} + +void igraph_buckets_clear(igraph_buckets_t *b) { + igraph_vector_long_null(&b->bptr); + igraph_vector_long_null(&b->buckets); + b->max = -1; + b->no = 0; +} + +int igraph_dbuckets_init(igraph_dbuckets_t *b, long int bsize, long int size) { + IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->bptr, bsize); + IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->next, size); + IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->prev, size); + b->max = -1; b->no = 0; + IGRAPH_FINALLY_CLEAN(3); + return 0; +} + +void igraph_dbuckets_destroy(igraph_dbuckets_t *b) { + igraph_vector_long_destroy(&b->bptr); + igraph_vector_long_destroy(&b->next); + igraph_vector_long_destroy(&b->prev); +} + +void igraph_dbuckets_clear(igraph_dbuckets_t *b) { + igraph_vector_long_null(&b->bptr); + igraph_vector_long_null(&b->next); + igraph_vector_long_null(&b->prev); + b->max = -1; + b->no = 0; +} + +long int igraph_dbuckets_popmax(igraph_dbuckets_t *b) { + long int max; + while ( (max = (long int) VECTOR(b->bptr)[(long int) b->max]) == 0) { + b->max --; + } + return igraph_dbuckets_pop(b, b->max); +} + +long int igraph_dbuckets_pop(igraph_dbuckets_t *b, long int bucket) { + long int ret = VECTOR(b->bptr)[bucket] - 1; + long int next = VECTOR(b->next)[ret]; + VECTOR(b->bptr)[bucket] = next; + if (next != 0) { + VECTOR(b->prev)[next - 1] = 0; + } + + b->no--; + return ret; +} + +igraph_bool_t igraph_dbuckets_empty(const igraph_dbuckets_t *b) { + return (b->no == 0); +} + +igraph_bool_t igraph_dbuckets_empty_bucket(const igraph_dbuckets_t *b, + long int bucket) { + return VECTOR(b->bptr)[bucket] == 0; +} + +void igraph_dbuckets_add(igraph_dbuckets_t *b, long int bucket, + long int elem) { + long int oldfirst = VECTOR(b->bptr)[bucket]; + VECTOR(b->bptr)[bucket] = elem + 1; + VECTOR(b->next)[elem] = oldfirst; + if (oldfirst != 0) { + VECTOR(b->prev)[oldfirst - 1] = elem + 1; + } + if (bucket > b->max) { + b->max = (int) bucket; + } + b->no++; +} + +/* Remove an arbitrary element */ + +void igraph_dbuckets_delete(igraph_dbuckets_t *b, long int bucket, + long int elem) { + if (VECTOR(b->bptr)[bucket] == elem + 1) { + /* First element in bucket */ + long int next = VECTOR(b->next)[elem]; + if (next != 0) { + VECTOR(b->prev)[next - 1] = 0; + } + VECTOR(b->bptr)[bucket] = next; + } else { + long int next = VECTOR(b->next)[elem]; + long int prev = VECTOR(b->prev)[elem]; + if (next != 0) { + VECTOR(b->prev)[next - 1] = prev; + } + if (prev != 0) { + VECTOR(b->next)[prev - 1] = next; + } + } + b->no--; +} diff --git a/src/rigraph/core/core/buckets.h b/src/rigraph/core/core/buckets.h new file mode 100644 index 0000000..31635f1 --- /dev/null +++ b/src/rigraph/core/core/buckets.h @@ -0,0 +1,81 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_CORE_BUCKETS_H +#define IGRAPH_CORE_BUCKETS_H + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus + #define __BEGIN_DECLS extern "C" { + #define __END_DECLS } +#else + #define __BEGIN_DECLS /* empty */ + #define __END_DECLS /* empty */ +#endif + +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* Buckets, needed for the maximum flow algorithm */ + +typedef struct igraph_buckets_t { + igraph_vector_long_t bptr; + igraph_vector_long_t buckets; + igraph_integer_t max, no; +} igraph_buckets_t; + +int igraph_buckets_init(igraph_buckets_t *b, long int bsize, long int size); +void igraph_buckets_destroy(igraph_buckets_t *b); +void igraph_buckets_clear(igraph_buckets_t *b); +long int igraph_buckets_popmax(igraph_buckets_t *b); +long int igraph_buckets_pop(igraph_buckets_t *b, long int bucket); +igraph_bool_t igraph_buckets_empty(const igraph_buckets_t *b); +igraph_bool_t igraph_buckets_empty_bucket(const igraph_buckets_t *b, + long int bucket); +void igraph_buckets_add(igraph_buckets_t *b, long int bucket, + long int elem); + +typedef struct igraph_dbuckets_t { + igraph_vector_long_t bptr; + igraph_vector_long_t next, prev; + igraph_integer_t max, no; +} igraph_dbuckets_t; + +int igraph_dbuckets_init(igraph_dbuckets_t *b, long int bsize, long int size); +void igraph_dbuckets_destroy(igraph_dbuckets_t *b); +void igraph_dbuckets_clear(igraph_dbuckets_t *b); +long int igraph_dbuckets_popmax(igraph_dbuckets_t *b); +long int igraph_dbuckets_pop(igraph_dbuckets_t *b, long int bucket); +igraph_bool_t igraph_dbuckets_empty(const igraph_dbuckets_t *b); +igraph_bool_t igraph_dbuckets_empty_bucket(const igraph_dbuckets_t *b, + long int bucket); +void igraph_dbuckets_add(igraph_dbuckets_t *b, long int bucket, + long int elem); +void igraph_dbuckets_delete(igraph_dbuckets_t *b, long int bucket, + long int elem); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/cutheap.c b/src/rigraph/core/core/cutheap.c new file mode 100644 index 0000000..86bb4c3 --- /dev/null +++ b/src/rigraph/core/core/cutheap.c @@ -0,0 +1,170 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" + +#include "cutheap.h" + +#define PARENT(x) ((x)/2) +#define LEFTCHILD(x) ((x)*2+1) +#define RIGHTCHILD(x) ((x)*2) +#define INACTIVE IGRAPH_INFINITY +#define UNDEFINED 0.0 +#define INDEXINC 1 + +static void igraph_i_cutheap_switch(igraph_i_cutheap_t *ch, + long int hidx1, long int hidx2) { + if (hidx1 != hidx2) { + long int idx1 = (long int) VECTOR(ch->index)[hidx1]; + long int idx2 = (long int) VECTOR(ch->index)[hidx2]; + + igraph_real_t tmp = VECTOR(ch->heap)[hidx1]; + VECTOR(ch->heap)[hidx1] = VECTOR(ch->heap)[hidx2]; + VECTOR(ch->heap)[hidx2] = tmp; + + VECTOR(ch->index)[hidx1] = idx2; + VECTOR(ch->index)[hidx2] = idx1; + + VECTOR(ch->hptr)[idx1] = hidx2 + INDEXINC; + VECTOR(ch->hptr)[idx2] = hidx1 + INDEXINC; + } +} + +static void igraph_i_cutheap_sink(igraph_i_cutheap_t *ch, long int hidx) { + long int size = igraph_vector_size(&ch->heap); + if (LEFTCHILD(hidx) >= size) { + /* leaf node */ + } else if (RIGHTCHILD(hidx) == size || + VECTOR(ch->heap)[LEFTCHILD(hidx)] >= + VECTOR(ch->heap)[RIGHTCHILD(hidx)]) { + /* sink to the left if needed */ + if (VECTOR(ch->heap)[hidx] < VECTOR(ch->heap)[LEFTCHILD(hidx)]) { + igraph_i_cutheap_switch(ch, hidx, LEFTCHILD(hidx)); + igraph_i_cutheap_sink(ch, LEFTCHILD(hidx)); + } + } else { + /* sink to the right */ + if (VECTOR(ch->heap)[hidx] < VECTOR(ch->heap)[RIGHTCHILD(hidx)]) { + igraph_i_cutheap_switch(ch, hidx, RIGHTCHILD(hidx)); + igraph_i_cutheap_sink(ch, RIGHTCHILD(hidx)); + } + } +} + +static void igraph_i_cutheap_shift_up(igraph_i_cutheap_t *ch, long int hidx) { + if (hidx == 0 || VECTOR(ch->heap)[hidx] < VECTOR(ch->heap)[PARENT(hidx)]) { + /* at the top */ + } else { + igraph_i_cutheap_switch(ch, hidx, PARENT(hidx)); + igraph_i_cutheap_shift_up(ch, PARENT(hidx)); + } +} + +int igraph_i_cutheap_init(igraph_i_cutheap_t *ch, igraph_integer_t nodes) { + ch->dnodes = nodes; + IGRAPH_VECTOR_INIT_FINALLY(&ch->heap, nodes); /* all zero */ + IGRAPH_CHECK(igraph_vector_init_seq(&ch->index, 0, nodes - 1)); + IGRAPH_FINALLY(igraph_vector_destroy, &ch->index); + IGRAPH_CHECK(igraph_vector_init_seq(&ch->hptr, INDEXINC, nodes + INDEXINC - 1)); + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +void igraph_i_cutheap_destroy(igraph_i_cutheap_t *ch) { + igraph_vector_destroy(&ch->hptr); + igraph_vector_destroy(&ch->index); + igraph_vector_destroy(&ch->heap); +} + +igraph_bool_t igraph_i_cutheap_empty(igraph_i_cutheap_t *ch) { + return igraph_vector_empty(&ch->heap); +} + +/* Number of active vertices */ + +igraph_integer_t igraph_i_cutheap_active_size(igraph_i_cutheap_t *ch) { + return (igraph_integer_t) igraph_vector_size(&ch->heap); +} + +/* Number of all (defined) vertices */ + +igraph_integer_t igraph_i_cutheap_size(igraph_i_cutheap_t *ch) { + return (igraph_integer_t) (ch->dnodes); +} + +igraph_real_t igraph_i_cutheap_maxvalue(igraph_i_cutheap_t *ch) { + return VECTOR(ch->heap)[0]; +} + +igraph_integer_t igraph_i_cutheap_popmax(igraph_i_cutheap_t *ch) { + long int size = igraph_vector_size(&ch->heap); + igraph_integer_t maxindex = (igraph_integer_t) VECTOR(ch->index)[0]; + /* put the last element to the top */ + igraph_i_cutheap_switch(ch, 0, size - 1); + /* remove the last element */ + VECTOR(ch->hptr)[(long int) igraph_vector_tail(&ch->index)] = INACTIVE; + igraph_vector_pop_back(&ch->heap); + igraph_vector_pop_back(&ch->index); + igraph_i_cutheap_sink(ch, 0); + + return maxindex; +} + +/* Update the value of an active vertex, if not active it will be ignored */ + +int igraph_i_cutheap_update(igraph_i_cutheap_t *ch, igraph_integer_t index, + igraph_real_t add) { + igraph_real_t hidx = VECTOR(ch->hptr)[(long int)index]; + if (hidx != INACTIVE && hidx != UNDEFINED) { + long int hidx2 = (long int) (hidx - INDEXINC); + /* printf("updating vertex %li, heap index %li\n", (long int) index, hidx2); */ + VECTOR(ch->heap)[hidx2] += add; + igraph_i_cutheap_sink(ch, hidx2); + igraph_i_cutheap_shift_up(ch, hidx2); + } + return 0; +} + +/* Reset the value of all vertices to zero and make them active */ + +int igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, long int vertex) { + long int i, j, n = igraph_vector_size(&ch->hptr); + /* undefine */ + VECTOR(ch->hptr)[vertex] = UNDEFINED; + ch->dnodes -= 1; + + IGRAPH_CHECK(igraph_vector_resize(&ch->heap, ch->dnodes)); + igraph_vector_null(&ch->heap); + + IGRAPH_CHECK(igraph_vector_resize(&ch->index, ch->dnodes)); + + j = 0; + for (i = 0; i < n; i++) { + if (VECTOR(ch->hptr)[i] != UNDEFINED) { + VECTOR(ch->index)[j] = i; + VECTOR(ch->hptr)[i] = j + INDEXINC; + j++; + } + } + + return 0; +} diff --git a/src/rigraph/core/core/cutheap.h b/src/rigraph/core/core/cutheap.h new file mode 100644 index 0000000..d7cb4c7 --- /dev/null +++ b/src/rigraph/core/core/cutheap.h @@ -0,0 +1,63 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_CORE_CUTHEAP_H +#define IGRAPH_CORE_CUTHEAP_H + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus + #define __BEGIN_DECLS extern "C" { + #define __END_DECLS } +#else + #define __BEGIN_DECLS /* empty */ + #define __END_DECLS /* empty */ +#endif + +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* Special maximum heap, needed for the minimum cut algorithm */ + +typedef struct igraph_i_cutheap_t { + igraph_vector_t heap; + igraph_vector_t index; + igraph_vector_t hptr; + long int dnodes; +} igraph_i_cutheap_t; + +IGRAPH_PRIVATE_EXPORT int igraph_i_cutheap_init(igraph_i_cutheap_t *ch, igraph_integer_t nodes); +IGRAPH_PRIVATE_EXPORT void igraph_i_cutheap_destroy(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_i_cutheap_empty(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_i_cutheap_active_size(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_i_cutheap_size(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_i_cutheap_maxvalue(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_i_cutheap_popmax(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT int igraph_i_cutheap_update(igraph_i_cutheap_t *ch, igraph_integer_t index, + igraph_real_t add); +IGRAPH_PRIVATE_EXPORT int igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, long int vertex); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/dqueue.c b/src/rigraph/core/core/dqueue.c new file mode 100644 index 0000000..212b242 --- /dev/null +++ b/src/rigraph/core/core/dqueue.c @@ -0,0 +1,55 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" +#include "igraph_dqueue.h" + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "dqueue.pmt" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#define BASE_LONG +#include "igraph_pmt.h" +#include "dqueue.pmt" +#include "igraph_pmt_off.h" +#undef BASE_LONG + +#define BASE_CHAR +#include "igraph_pmt.h" +#include "dqueue.pmt" +#include "igraph_pmt_off.h" +#undef BASE_CHAR + +#define BASE_BOOL +#include "igraph_pmt.h" +#include "dqueue.pmt" +#include "igraph_pmt_off.h" +#undef BASE_BOOL + +#define BASE_INT +#include "igraph_pmt.h" +#include "dqueue.pmt" +#include "igraph_pmt_off.h" +#undef BASE_INT diff --git a/src/rigraph/core/core/dqueue.pmt b/src/rigraph/core/core/dqueue.pmt new file mode 100644 index 0000000..6ef7b67 --- /dev/null +++ b/src/rigraph/core/core/dqueue.pmt @@ -0,0 +1,383 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_memory.h" +#include "igraph_error.h" +#include "config.h" + +#include /* memcpy & co. */ +#include + +/** + * \section igraph_dqueue + * + * This is the classic data type of the double ended queue. Most of + * the time it is used if a First-In-First-Out (FIFO) behavior is + * needed. See the operations below. + * + * + * + * \example examples/simple/dqueue.c + * + */ + +/** + * \ingroup dqueue + * \function igraph_dqueue_init + * \brief Initialize a double ended queue (deque). + * + * The queue will be always empty. + * \param q Pointer to an uninitialized deque. + * \param size How many elements to allocate memory for. + * \return Error code. + * + * Time complexity: O(\p size). + */ + +int FUNCTION(igraph_dqueue, init) (TYPE(igraph_dqueue)* q, long int size) { + IGRAPH_ASSERT(q != 0); + if (size <= 0 ) { + size = 1; + } + q->stor_begin = IGRAPH_CALLOC(size, BASE); + if (q->stor_begin == 0) { + IGRAPH_ERROR("dqueue init failed", IGRAPH_ENOMEM); + } + q->stor_end = q->stor_begin + size; + q->begin = q->stor_begin; + q->end = NULL; + + return 0; +} + +/** + * \ingroup dqueue + * \function igraph_dqueue_destroy + * \brief Destroy a double ended queue. + * + * \param q The queue to destroy + * + * Time complexity: O(1). + */ + +void FUNCTION(igraph_dqueue, destroy) (TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != 0); + if (q->stor_begin != 0) { + IGRAPH_FREE(q->stor_begin); + q->stor_begin = 0; + } +} + +/** + * \ingroup dqueue + * \function igraph_dqueue_empty + * \brief Decide whether the queue is empty. + * + * \param q The queue. + * \return Boolean, \c TRUE if \p q contains at least one element, \c + * FALSE otherwise. + * + * Time complexity: O(1). + */ + +igraph_bool_t FUNCTION(igraph_dqueue, empty) (const TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != 0); + IGRAPH_ASSERT(q->stor_begin != 0); + return q->end == NULL; +} + +/** + * \ingroup dqueue + * \function igraph_dqueue_clear + * \brief Remove all elements from the queue. + * + * \param q The queue + * + * Time complexity: O(1). + */ + +void FUNCTION(igraph_dqueue, clear) (TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != 0); + IGRAPH_ASSERT(q->stor_begin != 0); + q->begin = q->stor_begin; + q->end = NULL; +} + +/** + * \ingroup dqueue + * \function igraph_dqueue_full + * \brief Check whether the queue is full. + * + * If a queue is full the next igraph_dqueue_push() operation will allocate + * more memory. + * \param q The queue. + * \return \c TRUE if \p q is full, \c FALSE otherwise. + * + * Time complecity: O(1). + */ + +igraph_bool_t FUNCTION(igraph_dqueue, full) (TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != 0); + IGRAPH_ASSERT(q->stor_begin != 0); + return q->begin == q->end; +} + +/** + * \ingroup dqueue + * \function igraph_dqueue_size + * \brief Number of elements in the queue. + * + * \param q The queue. + * \return Integer, the number of elements currently in the queue. + * + * Time complexity: O(1). + */ + +long int FUNCTION(igraph_dqueue, size) (const TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != 0); + IGRAPH_ASSERT(q->stor_begin != 0); + if (q->end == NULL) { + return 0; + } else if (q->begin < q->end) { + return q->end - q->begin; + } else { + return q->stor_end - q->begin + q->end - q->stor_begin; + } +} + +/** + * \ingroup dqueue + * \function igraph_dqueue_head + * \brief Head of the queue. + * + * The queue must contain at least one element. + * \param q The queue. + * \return The first element in the queue. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_dqueue, head) (const TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != 0); + IGRAPH_ASSERT(q->stor_begin != 0); + return *(q->begin); +} + +/** + * \ingroup dqueue + * \function igraph_dqueue_back + * \brief Tail of the queue. + * + * The queue must contain at least one element. + * \param q The queue. + * \return The last element in the queue. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_dqueue, back) (const TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != 0); + IGRAPH_ASSERT(q->stor_begin != 0); + if (q->end == q->stor_begin) { + return *(q->stor_end - 1); + } + return *(q->end - 1); +} + +/** + * \ingroup dqueue + * \function igraph_dqueue_pop + * \brief Remove the head. + * + * Removes and returns the first element in the queue. The queue must + * be non-empty. + * \param q The input queue. + * \return The first element in the queue. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_dqueue, pop) (TYPE(igraph_dqueue)* q) { + BASE tmp = *(q->begin); + IGRAPH_ASSERT(q != 0); + IGRAPH_ASSERT(q->stor_begin != 0); + (q->begin)++; + if (q->begin == q->stor_end) { + q->begin = q->stor_begin; + } + if (q->begin == q->end) { + q->end = NULL; + } + + return tmp; +} + +/** + * \ingroup dqueue + * \function igraph_dqueue_pop_back + * \brief Remove the tail + * + * Removes and returns the last element in the queue. The queue must + * be non-empty. + * \param q The queue. + * \return The last element in the queue. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_dqueue, pop_back) (TYPE(igraph_dqueue)* q) { + BASE tmp; + IGRAPH_ASSERT(q != 0); + IGRAPH_ASSERT(q->stor_begin != 0); + if (q->end != q->stor_begin) { + tmp = *((q->end) - 1); + q->end = (q->end) - 1; + } else { + tmp = *((q->stor_end) - 1); + q->end = (q->stor_end) - 1; + } + if (q->begin == q->end) { + q->end = NULL; + } + + return tmp; +} + +/** + * \ingroup dqueue + * \function igraph_dqueue_push + * \brief Appends an element. + * + * Append an element to the end of the queue. + * \param q The queue. + * \param elem The element to append. + * \return Error code. + * + * Time complexity: O(1) if no memory allocation is needed, O(n), the + * number of elements in the queue otherwise. But not that by + * allocating always twice as much memory as the current size of the + * queue we ensure that n push operations can always be done in at + * most O(n) time. (Assuming memory allocation is at most linear.) + */ + +int FUNCTION(igraph_dqueue, push) (TYPE(igraph_dqueue)* q, BASE elem) { + IGRAPH_ASSERT(q != 0); + IGRAPH_ASSERT(q->stor_begin != 0); + if (q->begin != q->end) { + /* not full */ + if (q->end == NULL) { + q->end = q->begin; + } + *(q->end) = elem; + (q->end)++; + if (q->end == q->stor_end) { + q->end = q->stor_begin; + } + } else { + /* full, allocate more storage */ + + BASE *bigger = NULL, *old = q->stor_begin; + + bigger = IGRAPH_CALLOC( 2 * (q->stor_end - q->stor_begin) + 1, BASE ); + if (bigger == 0) { + IGRAPH_ERROR("dqueue push failed", IGRAPH_ENOMEM); + } + + if (q->stor_end - q->begin) { + memcpy(bigger, q->begin, + (size_t)(q->stor_end - q->begin) * sizeof(BASE)); + } + if (q->end - q->stor_begin > 0) { + memcpy(bigger + (q->stor_end - q->begin), q->stor_begin, + (size_t)(q->end - q->stor_begin) * sizeof(BASE)); + } + + q->end = bigger + (q->stor_end - q->stor_begin); + q->stor_end = bigger + 2 * (q->stor_end - q->stor_begin) + 1; + q->stor_begin = bigger; + q->begin = bigger; + + *(q->end) = elem; + (q->end)++; + if (q->end == q->stor_end) { + q->end = q->stor_begin; + } + + IGRAPH_FREE(old); + } + + return 0; +} + +#if defined (OUT_FORMAT) + +#ifndef USING_R +int FUNCTION(igraph_dqueue, print)(const TYPE(igraph_dqueue)* q) { + return FUNCTION(igraph_dqueue, fprint)(q, stdout); +} +#endif + +int FUNCTION(igraph_dqueue, fprint)(const TYPE(igraph_dqueue)* q, FILE *file) { + if (q->end != NULL) { + /* There is one element at least */ + BASE *p = q->begin; + fprintf(file, OUT_FORMAT, *p); + p++; + if (q->end > q->begin) { + /* Q is in one piece */ + while (p != q->end) { + fprintf(file, " " OUT_FORMAT, *p); + p++; + } + } else { + /* Q is in two pieces */ + while (p != q->stor_end) { + fprintf(file, " " OUT_FORMAT, *p); + p++; + } + p = q->stor_begin; + while (p != q->end) { + fprintf(file, " " OUT_FORMAT, *p); + p++; + } + } + } + + fprintf(file, "\n"); + + return 0; +} + +#endif + +BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, long int idx) { + if ((q->begin + idx < q->end) || + (q->begin >= q->end && q->begin + idx < q->stor_end)) { + return q->begin[idx]; + } else if (q->begin >= q->end && q->stor_begin + idx < q->end) { + idx = idx - (q->stor_end - q->begin); + return q->stor_begin[idx]; + } else { + return 0; /* Error */ + } +} diff --git a/src/rigraph/core/core/error.c b/src/rigraph/core/core/error.c new file mode 100644 index 0000000..d7b65f7 --- /dev/null +++ b/src/rigraph/core/core/error.c @@ -0,0 +1,385 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "config.h" +#include "igraph_error.h" +#include "igraph_types.h" + +#include +#include +#include + +/* Detecting ASan with GCC: + * https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html + * Detecting ASan with Clang: + * https://clang.llvm.org/docs/AddressSanitizer.html#conditional-compilation-with-has-feature-address-sanitizer + */ +#if defined(__SANITIZE_ADDRESS__) +# define IGRAPH_SANITIZER_AVAILABLE 1 +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +# define IGRAPH_SANITIZER_AVAILABLE 1 +# endif +#endif + +#ifdef IGRAPH_SANITIZER_AVAILABLE +#include +#endif + +#ifdef USING_R +#include +#endif + +/***** Helper functions *****/ + +/* All calls to abort() in this compilation unit must go through igraph_abort(), + * in order to make it easy for igraph's R interface to not have any reference to abort(), + * which is disallowed by CRAN. + * + * Since the R interface sets its own error / fatal error handlers, this function + * is never actually called by it. + * + * Note that some of the other #ifndef USING_R's in this file are still needed + * to avoid references to fprintf and stderr. + */ +static IGRAPH_NORETURN void igraph_abort() { +#ifndef USING_R +#ifdef IGRAPH_SANITIZER_AVAILABLE + fprintf(stderr, "\nStack trace:\n"); + __sanitizer_print_stack_trace(); +#endif + abort(); +#else + /* R's error() function is declared 'noreturn'. We use it here to satisfy the compiler that igraph_abort() does indeed not return. */ + error("igraph_abort() was called. This should never happen. Please report this as an igraph bug, along with steps to reproduce it."); +#endif +} + + +/***** Handling errors *****/ + +static igraph_error_handler_t *igraph_i_error_handler = 0; +static char igraph_i_errormsg_buffer[500]; +static char igraph_i_warningmsg_buffer[500]; +static char igraph_i_fatalmsg_buffer[500]; + +/* Error strings corresponding to each igraph_error_type_t enum value. */ +static const char *igraph_i_error_strings[] = { + /* 0 */ "No error", + /* 1 */ "Failed", + /* 2 */ "Out of memory", + /* 3 */ "Parse error", + /* 4 */ "Invalid value", + /* 5 */ "Already exists", + /* 6 */ "Invalid edge vector", + /* 7 */ "Invalid vertex id", + /* 8 */ "Non-square matrix", + /* 9 */ "Invalid mode", + /* 10 */ "File operation error", + /* 11 */ "Unfold infinite iterator", + /* 12 */ "Unimplemented function call", + /* 13 */ "Interrupted", + /* 14 */ "Numeric procedure did not converge", + /* 15 */ "Matrix-vector product failed", + /* 16 */ "N must be positive", + /* 17 */ "NEV must be positive", + /* 18 */ "NCV must be greater than NEV and less than or equal to N " + "(and for the non-symmetric solver NCV-NEV >=2 must also hold)", + /* 19 */ "Maximum number of iterations should be positive", + /* 20 */ "Invalid WHICH parameter", + /* 21 */ "Invalid BMAT parameter", + /* 22 */ "WORKL is too small", + /* 23 */ "LAPACK error in tridiagonal eigenvalue calculation", + /* 24 */ "Starting vector is zero", + /* 25 */ "MODE is invalid", + /* 26 */ "MODE and BMAT are not compatible", + /* 27 */ "ISHIFT must be 0 or 1", + /* 28 */ "NEV and WHICH='BE' are incompatible", + /* 29 */ "Could not build an Arnoldi factorization", + /* 30 */ "No eigenvalues to sufficient accuracy", + /* 31 */ "HOWMNY is invalid", + /* 32 */ "HOWMNY='S' is not implemented", + /* 33 */ "Different number of converged Ritz values", + /* 34 */ "Error from calculation of a real Schur form", + /* 35 */ "LAPACK (dtrevc) error for calculating eigenvectors", + /* 36 */ "Unknown ARPACK error", + /* 37 */ "Negative loop detected while calculating shortest paths", + /* 38 */ "Internal error, likely a bug in igraph", + /* 39 */ "Maximum number of iterations reached", + /* 40 */ "No shifts could be applied during a cycle of the " + "Implicitly restarted Arnoldi iteration. One possibility " + "is to increase the size of NCV relative to NEV", + /* 41 */ "The Schur form computed by LAPACK routine dlahqr " + "could not be reordered by LAPACK routine dtrsen.", + /* 42 */ "Big integer division by zero", + /* 43 */ "GLPK Error, GLP_EBOUND", + /* 44 */ "GLPK Error, GLP_EROOT", + /* 45 */ "GLPK Error, GLP_ENOPFS", + /* 46 */ "GLPK Error, GLP_ENODFS", + /* 47 */ "GLPK Error, GLP_EFAIL", + /* 48 */ "GLPK Error, GLP_EMIPGAP", + /* 49 */ "GLPK Error, GLP_ETMLIM", + /* 50 */ "GLPK Error, GLP_STOP", + /* 51 */ "Internal attribute handler error", + /* 52 */ "Unimplemented attribute combination for this type", + /* 53 */ "LAPACK call resulted in an error", + /* 54 */ "Internal DrL error", + /* 55 */ "Integer or double overflow", + /* 56 */ "Internal GPLK error", + /* 57 */ "CPU time exceeded", + /* 58 */ "Integer or double underflow", + /* 59 */ "Random walk got stuck", + /* 60 */ "Search stopped; this error should never be visible to the user, " + "please report this error along with the steps to reproduce it." +}; + +const char* igraph_strerror(const int igraph_errno) { + if (igraph_errno < 0 || + ((unsigned long)igraph_errno) >= sizeof(igraph_i_error_strings) / sizeof(char *)) { + return "Invalid error code; no error string available."; + } + return igraph_i_error_strings[igraph_errno]; +} + +int igraph_error(const char *reason, const char *file, int line, + int igraph_errno) { + + if (igraph_i_error_handler) { + igraph_i_error_handler(reason, file, line, igraph_errno); +#ifndef USING_R + } else { + igraph_error_handler_abort(reason, file, line, igraph_errno); +#endif + } + return igraph_errno; +} + +int igraph_errorf(const char *reason, const char *file, int line, + int igraph_errno, ...) { + va_list ap; + va_start(ap, igraph_errno); + vsnprintf(igraph_i_errormsg_buffer, + sizeof(igraph_i_errormsg_buffer) / sizeof(char), reason, ap); + return igraph_error(igraph_i_errormsg_buffer, file, line, igraph_errno); +} + +int igraph_errorvf(const char *reason, const char *file, int line, + int igraph_errno, va_list ap) { + vsnprintf(igraph_i_errormsg_buffer, + sizeof(igraph_i_errormsg_buffer) / sizeof(char), reason, ap); + return igraph_error(igraph_i_errormsg_buffer, file, line, igraph_errno); +} + +#ifndef USING_R +void igraph_error_handler_abort(const char *reason, const char *file, + int line, int igraph_errno) { + fprintf(stderr, "Error at %s:%i : %s - %s.\n", + file, line, reason, igraph_strerror(igraph_errno)); + igraph_abort(); +} +#endif + +void igraph_error_handler_ignore(const char *reason, const char *file, + int line, int igraph_errno) { + IGRAPH_UNUSED(reason); + IGRAPH_UNUSED(file); + IGRAPH_UNUSED(line); + IGRAPH_UNUSED(igraph_errno); + + IGRAPH_FINALLY_FREE(); +} + +#ifndef USING_R +void igraph_error_handler_printignore(const char *reason, const char *file, + int line, int igraph_errno) { + fprintf(stderr, "Error at %s:%i : %s - %s.\n", + file, line, reason, igraph_strerror(igraph_errno)); + IGRAPH_FINALLY_FREE(); +} +#endif + +igraph_error_handler_t *igraph_set_error_handler(igraph_error_handler_t *new_handler) { + igraph_error_handler_t *previous_handler = igraph_i_error_handler; + igraph_i_error_handler = new_handler; + return previous_handler; +} + + +/***** "Finally" stack *****/ + +struct igraph_i_protectedPtr igraph_i_finally_stack[100]; + +/* + * Adds another element to the free list + */ + +void IGRAPH_FINALLY_REAL(void (*func)(void*), void* ptr) { + int no = igraph_i_finally_stack[0].all; + IGRAPH_ASSERT(no < 100); + IGRAPH_ASSERT(no >= 0); + igraph_i_finally_stack[no].ptr = ptr; + igraph_i_finally_stack[no].func = func; + igraph_i_finally_stack[0].all ++; + /* printf("--> Finally stack contains now %d elements\n", igraph_i_finally_stack[0].all); */ +} + +void IGRAPH_FINALLY_CLEAN(int minus) { + igraph_i_finally_stack[0].all -= minus; + if (igraph_i_finally_stack[0].all < 0) { + int left = igraph_i_finally_stack[0].all + minus; + /* Set to zero in case fatal error handler does a longjmp instead of terminating the process: */ + igraph_i_finally_stack[0].all = 0; + IGRAPH_FATALF("Corrupt finally stack: trying to pop %d element(s) when only %d left.", minus, left); + } + /* printf("<-- Finally stack contains now %d elements\n", igraph_i_finally_stack[0].all); */ +} + +void IGRAPH_FINALLY_FREE(void) { + int p; + /* printf("[X] Finally stack will be cleaned (contained %d elements)\n", igraph_i_finally_stack[0].all); */ + for (p = igraph_i_finally_stack[0].all - 1; p >= 0; p--) { + igraph_i_finally_stack[p].func(igraph_i_finally_stack[p].ptr); + } + igraph_i_finally_stack[0].all = 0; +} + +int IGRAPH_FINALLY_STACK_SIZE(void) { + return igraph_i_finally_stack[0].all; +} + + +/***** Handling warnings *****/ + +static igraph_warning_handler_t *igraph_i_warning_handler = 0; + +/** + * \function igraph_warning_handler_ignore + * \brief Ignores all warnings. + * + * This warning handler function simply ignores all warnings. + * \param reason Textual description of the warning. + * \param file The source file in which the warning was noticed. + * \param line The number of line in the source file which triggered the + * warning.. + * \param igraph_errno Warnings could have potentially error codes as well, + * but this is currently not used in igraph. + */ + +void igraph_warning_handler_ignore(const char *reason, const char *file, + int line, int igraph_errno) { + IGRAPH_UNUSED(reason); + IGRAPH_UNUSED(file); + IGRAPH_UNUSED(line); + IGRAPH_UNUSED(igraph_errno); +} + +#ifndef USING_R + +/** + * \function igraph_warning_handler_print + * \brief Prints all warnings to the standard error. + * + * This warning handler function simply prints all warnings to the + * standard error. + * \param reason Textual description of the warning. + * \param file The source file in which the warning was noticed. + * \param line The number of line in the source file which triggered the + * warning.. + * \param igraph_errno Warnings could have potentially error codes as well, + * but this is currently not used in igraph. + */ + +void igraph_warning_handler_print(const char *reason, const char *file, + int line, int igraph_errno) { + IGRAPH_UNUSED(igraph_errno); + fprintf(stderr, "Warning at %s:%i : %s\n", file, line, reason); +} +#endif + +int igraph_warning(const char *reason, const char *file, int line, + int igraph_errno) { + + if (igraph_i_warning_handler) { + igraph_i_warning_handler(reason, file, line, igraph_errno); +#ifndef USING_R + } else { + igraph_warning_handler_print(reason, file, line, igraph_errno); +#endif + } + return igraph_errno; +} + +int igraph_warningf(const char *reason, const char *file, int line, + int igraph_errno, ...) { + va_list ap; + va_start(ap, igraph_errno); + vsnprintf(igraph_i_warningmsg_buffer, + sizeof(igraph_i_warningmsg_buffer) / sizeof(char), reason, ap); + return igraph_warning(igraph_i_warningmsg_buffer, file, line, + igraph_errno); +} + +igraph_warning_handler_t *igraph_set_warning_handler(igraph_warning_handler_t *new_handler) { + igraph_warning_handler_t *previous_handler = igraph_i_warning_handler; + igraph_i_warning_handler = new_handler; + return previous_handler; +} + + +/***** Handling fatal errors *****/ + +static igraph_fatal_handler_t *igraph_i_fatal_handler = NULL; + +igraph_fatal_handler_t *igraph_set_fatal_handler(igraph_fatal_handler_t *new_handler) { + igraph_fatal_handler_t *previous_handler = igraph_i_fatal_handler; + igraph_i_fatal_handler = new_handler; + return previous_handler; +} + +#ifndef USING_R +void igraph_fatal_handler_abort(const char *reason, const char *file, int line) { + fprintf(stderr, "Fatal error at %s:%i : %s\n", file, line, reason); + igraph_abort(); +} +#endif + +void igraph_fatal(const char *reason, const char *file, int line) { + if (igraph_i_fatal_handler) { + igraph_i_fatal_handler(reason, file, line); +#ifndef USING_R + } else { + igraph_fatal_handler_abort(reason, file, line); +#endif + } + /* The following line should never be reached, as fatal error handlers are not supposed to return. + It is here to satisfy the compiler that this function indeed does not return. */ + igraph_abort(); +} + +void igraph_fatalf(const char *reason, const char *file, int line, ...) { + va_list ap; + va_start(ap, line); + vsnprintf(igraph_i_fatalmsg_buffer, sizeof(igraph_i_fatalmsg_buffer) / sizeof(char), reason, ap); + va_end(ap); + igraph_fatal(igraph_i_fatalmsg_buffer, file, line); +} diff --git a/src/rigraph/core/core/estack.c b/src/rigraph/core/core/estack.c new file mode 100644 index 0000000..b5a4955 --- /dev/null +++ b/src/rigraph/core/core/estack.c @@ -0,0 +1,67 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "estack.h" + +int igraph_estack_init(igraph_estack_t *s, long int setsize, + long int stacksize) { + IGRAPH_CHECK(igraph_vector_bool_init(&s->isin, setsize)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &s->isin); + IGRAPH_CHECK(igraph_stack_long_init(&s->stack, stacksize)); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +void igraph_estack_destroy(igraph_estack_t *s) { + igraph_stack_long_destroy(&s->stack); + igraph_vector_bool_destroy(&s->isin); +} + +int igraph_estack_push(igraph_estack_t *s, long int elem) { + if ( !VECTOR(s->isin)[elem] ) { + IGRAPH_CHECK(igraph_stack_long_push(&s->stack, elem)); + VECTOR(s->isin)[elem] = 1; + } + return 0; +} + +long int igraph_estack_pop(igraph_estack_t *s) { + long int elem = igraph_stack_long_pop(&s->stack); + VECTOR(s->isin)[elem] = 0; + return elem; +} + +igraph_bool_t igraph_estack_iselement(const igraph_estack_t *s, + long int elem) { + return VECTOR(s->isin)[elem]; +} + +long int igraph_estack_size(const igraph_estack_t *s) { + return igraph_stack_long_size(&s->stack); +} + +#ifndef USING_R +int igraph_estack_print(const igraph_estack_t *s) { + return igraph_stack_long_print(&s->stack); +} +#endif diff --git a/src/rigraph/core/core/estack.h b/src/rigraph/core/core/estack.h new file mode 100644 index 0000000..65178ba --- /dev/null +++ b/src/rigraph/core/core/estack.h @@ -0,0 +1,47 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_ESTACK_H +#define IGRAPH_ESTACK_H + +#include "igraph_stack.h" +#include "igraph_vector.h" + +typedef struct igraph_estack_t { + igraph_stack_long_t stack; + igraph_vector_bool_t isin; +} igraph_estack_t; + +IGRAPH_PRIVATE_EXPORT int igraph_estack_init(igraph_estack_t *s, long int setsize, + long int stacksize); +IGRAPH_PRIVATE_EXPORT void igraph_estack_destroy(igraph_estack_t *s); + +IGRAPH_PRIVATE_EXPORT int igraph_estack_push(igraph_estack_t *s, long int elem); +IGRAPH_PRIVATE_EXPORT long int igraph_estack_pop(igraph_estack_t *s); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_estack_iselement(const igraph_estack_t *s, + long int elem); +IGRAPH_PRIVATE_EXPORT long int igraph_estack_size(const igraph_estack_t *s); + +IGRAPH_PRIVATE_EXPORT int igraph_estack_print(const igraph_estack_t *s); + +#endif diff --git a/src/rigraph/core/core/exceptions.h b/src/rigraph/core/core/exceptions.h new file mode 100644 index 0000000..f1cb6ec --- /dev/null +++ b/src/rigraph/core/core/exceptions.h @@ -0,0 +1,19 @@ +#ifndef IGRAPH_HANDLE_EXCEPTIONS_H +#define IGRAPH_HANDLE_EXCEPTIONS_H + +#include +#include + +/* igraph functions which may be called from C code must not throw C++ exceptions. + * This includes all public functions. This macro is meant to handle exceptions thrown + * by C++ libraries used by igraph (such as bliss). Wrap the entire body + * of public functions implemented in C++ in IGRAPH_HANDLE_EXCEPTIONS(). + */ +#define IGRAPH_HANDLE_EXCEPTIONS(code) \ + try { code; } \ + catch (const std::bad_alloc &e) { IGRAPH_ERROR(e.what(), IGRAPH_ENOMEM); } \ + catch (const std::exception &e) { IGRAPH_ERROR(e.what(), IGRAPH_FAILURE); } \ + catch (...) { IGRAPH_ERROR("Unknown exception caught.", IGRAPH_FAILURE); } + + +#endif // IGRAPH_HANDLE_EXCEPTIONS_H diff --git a/src/rigraph/core/core/fixed_vectorlist.c b/src/rigraph/core/core/fixed_vectorlist.c new file mode 100644 index 0000000..56e138f --- /dev/null +++ b/src/rigraph/core/core/fixed_vectorlist.c @@ -0,0 +1,81 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_memory.h" + +#include "fixed_vectorlist.h" + +void igraph_fixed_vectorlist_destroy(igraph_fixed_vectorlist_t *l) { + long int i, n = igraph_vector_ptr_size(&l->v); + for (i = 0; i < n; i++) { + igraph_vector_t *v = VECTOR(l->v)[i]; + if (v) { + igraph_vector_destroy(v); + } + } + igraph_vector_ptr_destroy(&l->v); + igraph_free(l->vecs); +} + +int igraph_fixed_vectorlist_convert(igraph_fixed_vectorlist_t *l, + const igraph_vector_t *from, + long int size) { + + igraph_vector_t sizes; + long int i, no = igraph_vector_size(from); + + l->vecs = IGRAPH_CALLOC(size, igraph_vector_t); + if (!l->vecs) { + IGRAPH_ERROR("Cannot merge attributes for simplify", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, l->vecs); + IGRAPH_CHECK(igraph_vector_ptr_init(&l->v, size)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &l->v); + IGRAPH_VECTOR_INIT_FINALLY(&sizes, size); + + for (i = 0; i < no; i++) { + long int to = (long int) VECTOR(*from)[i]; + if (to >= 0) { + VECTOR(sizes)[to] += 1; + } + } + for (i = 0; i < size; i++) { + igraph_vector_t *v = &(l->vecs[i]); + IGRAPH_CHECK(igraph_vector_init(v, (long int) VECTOR(sizes)[i])); + igraph_vector_clear(v); + VECTOR(l->v)[i] = v; + } + for (i = 0; i < no; i++) { + long int to = (long int) VECTOR(*from)[i]; + if (to >= 0) { + igraph_vector_t *v = &(l->vecs[to]); + igraph_vector_push_back(v, i); + } + } + + igraph_vector_destroy(&sizes); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} diff --git a/src/rigraph/core/core/fixed_vectorlist.h b/src/rigraph/core/core/fixed_vectorlist.h new file mode 100644 index 0000000..4a0907f --- /dev/null +++ b/src/rigraph/core/core/fixed_vectorlist.h @@ -0,0 +1,51 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_TYPES_INTERNAL_H +#define IGRAPH_TYPES_INTERNAL_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Vectorlist, fixed length */ +/* -------------------------------------------------- */ + +typedef struct igraph_fixed_vectorlist_t { + igraph_vector_t *vecs; + igraph_vector_ptr_t v; + long int length; +} igraph_fixed_vectorlist_t; + +void igraph_fixed_vectorlist_destroy(igraph_fixed_vectorlist_t *l); +int igraph_fixed_vectorlist_convert(igraph_fixed_vectorlist_t *l, + const igraph_vector_t *from, + long int size); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/grid.c b/src/rigraph/core/core/grid.c new file mode 100644 index 0000000..b27eb36 --- /dev/null +++ b/src/rigraph/core/core/grid.c @@ -0,0 +1,348 @@ +/* -*- mode: C -*- */ +/* + IGraph R package. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" + +#include "grid.h" + +#include + +/* internal function */ + +int igraph_2dgrid_which(igraph_2dgrid_t *grid, igraph_real_t xc, igraph_real_t yc, + long int *x, long int *y) { + + if (xc <= grid->minx) { + *x = 0; + } else if (xc >= grid->maxx) { + *x = grid->stepsx - 1; + } else { + *x = (long int) floor((xc - (grid->minx)) / (grid->deltax)); + } + + if (yc <= grid->miny) { + *y = 0; + } else if (yc >= grid->maxy) { + *y = grid->stepsy - 1; + } else { + *y = (long int) floor((yc - (grid->miny)) / (grid->deltay)); + } + + return 0; +} + +int igraph_2dgrid_init(igraph_2dgrid_t *grid, igraph_matrix_t *coords, + igraph_real_t minx, igraph_real_t maxx, igraph_real_t deltax, + igraph_real_t miny, igraph_real_t maxy, igraph_real_t deltay) { + long int i; + + grid->coords = coords; + grid->minx = minx; + grid->maxx = maxx; + grid->deltax = deltax; + grid->miny = miny; + grid->maxy = maxy; + grid->deltay = deltay; + + grid->stepsx = (long int) ceil((maxx - minx) / deltax); + grid->stepsy = (long int) ceil((maxy - miny) / deltay); + + IGRAPH_CHECK(igraph_matrix_init(&grid->startidx, + grid->stepsx, grid->stepsy)); + IGRAPH_FINALLY(igraph_matrix_destroy, &grid->startidx); + IGRAPH_VECTOR_INIT_FINALLY(&grid->next, igraph_matrix_nrow(coords)); + IGRAPH_VECTOR_INIT_FINALLY(&grid->prev, igraph_matrix_nrow(coords)); + + for (i = 0; i < igraph_vector_size(&grid->next); i++) { + VECTOR(grid->next)[i] = -1; + } + + grid->massx = 0; + grid->massy = 0; + grid->vertices = 0; + + IGRAPH_FINALLY_CLEAN(3); + return 0; +} + +void igraph_2dgrid_destroy(igraph_2dgrid_t *grid) { + igraph_matrix_destroy(&grid->startidx); + igraph_vector_destroy(&grid->next); + igraph_vector_destroy(&grid->prev); +} + +void igraph_2dgrid_add(igraph_2dgrid_t *grid, long int elem, + igraph_real_t xc, igraph_real_t yc) { + long int x, y; + long int first; + + MATRIX(*grid->coords, elem, 0) = xc; + MATRIX(*grid->coords, elem, 1) = yc; + + /* add to cell */ + igraph_2dgrid_which(grid, xc, yc, &x, &y); + first = (long int) MATRIX(grid->startidx, x, y); + VECTOR(grid->prev)[elem] = 0; + VECTOR(grid->next)[elem] = first; + if (first != 0) { + VECTOR(grid->prev)[first - 1] = elem + 1; + } + MATRIX(grid->startidx, x, y) = elem + 1; + + grid->massx += xc; + grid->massy += yc; + grid->vertices += 1; +} + +void igraph_2dgrid_add2(igraph_2dgrid_t *grid, long int elem) { + long int x, y; + long int first; + igraph_real_t xc, yc; + + xc = MATRIX(*grid->coords, elem, 0); + yc = MATRIX(*grid->coords, elem, 1); + + /* add to cell */ + igraph_2dgrid_which(grid, xc, yc, &x, &y); + first = (long int) MATRIX(grid->startidx, x, y); + VECTOR(grid->prev)[elem] = 0; + VECTOR(grid->next)[elem] = first; + if (first != 0) { + VECTOR(grid->prev)[first - 1] = elem + 1; + } + MATRIX(grid->startidx, x, y) = elem + 1; + + grid->massx += xc; + grid->massy += yc; + grid->vertices += 1; +} + +void igraph_2dgrid_move(igraph_2dgrid_t *grid, long int elem, + igraph_real_t xc, igraph_real_t yc) { + long int oldx, oldy; + long int newx, newy; + igraph_real_t oldxc = MATRIX(*grid->coords, elem, 0); + igraph_real_t oldyc = MATRIX(*grid->coords, elem, 1); + long int first; + + xc = oldxc + xc; yc = oldyc + yc; + + igraph_2dgrid_which(grid, oldxc, oldyc, &oldx, &oldy); + igraph_2dgrid_which(grid, xc, yc, &newx, &newy); + if (oldx != newx || oldy != newy) { + /* remove from this cell */ + if (VECTOR(grid->prev)[elem] != 0) { + VECTOR(grid->next) [ (long int) VECTOR(grid->prev)[elem] - 1 ] = + VECTOR(grid->next)[elem]; + } else { + MATRIX(grid->startidx, oldx, oldy) = VECTOR(grid->next)[elem]; + } + if (VECTOR(grid->next)[elem] != 0) { + VECTOR(grid->prev)[ (long int) VECTOR(grid->next)[elem] - 1 ] = + VECTOR(grid->prev)[elem]; + } + + /* add to this cell */ + first = (long int) MATRIX(grid->startidx, newx, newy); + VECTOR(grid->prev)[elem] = 0; + VECTOR(grid->next)[elem] = first; + if (first != 0) { + VECTOR(grid->prev)[first - 1] = elem + 1; + } + MATRIX(grid->startidx, newx, newy) = elem + 1; + } + + grid->massx += -oldxc + xc; + grid->massy += -oldyc + yc; + + MATRIX(*grid->coords, elem, 0) = xc; + MATRIX(*grid->coords, elem, 1) = yc; + +} + +void igraph_2dgrid_getcenter(const igraph_2dgrid_t *grid, + igraph_real_t *massx, igraph_real_t *massy) { + *massx = (grid->massx) / (grid->vertices); + *massy = (grid->massy) / (grid->vertices); +} + +igraph_bool_t igraph_2dgrid_in(const igraph_2dgrid_t *grid, long int elem) { + return VECTOR(grid->next)[elem] != -1; +} + +igraph_real_t igraph_2dgrid_dist(const igraph_2dgrid_t *grid, + long int e1, long int e2) { + igraph_real_t x = MATRIX(*grid->coords, e1, 0) - MATRIX(*grid->coords, e2, 0); + igraph_real_t y = MATRIX(*grid->coords, e1, 1) - MATRIX(*grid->coords, e2, 1); + + return sqrt(x * x + y * y); +} + +igraph_real_t igraph_2dgrid_dist2(const igraph_2dgrid_t *grid, + long int e1, long int e2) { + igraph_real_t x = MATRIX(*grid->coords, e1, 0) - MATRIX(*grid->coords, e2, 0); + igraph_real_t y = MATRIX(*grid->coords, e1, 1) - MATRIX(*grid->coords, e2, 1); + + return x * x + y * y; +} + +static int igraph_i_2dgrid_addvertices(igraph_2dgrid_t *grid, igraph_vector_t *eids, + igraph_integer_t vid, igraph_real_t r, + long int x, long int y) { + long int act; + igraph_real_t *v = VECTOR(grid->next); + + r = r * r; + act = (long int) MATRIX(grid->startidx, x, y); + while (act != 0) { + if (igraph_2dgrid_dist2(grid, vid, act - 1) < r) { + IGRAPH_CHECK(igraph_vector_push_back(eids, act - 1)); + } + act = (long int) v[act - 1]; + } + return 0; +} + +int igraph_2dgrid_neighbors(igraph_2dgrid_t *grid, igraph_vector_t *eids, + igraph_integer_t vid, igraph_real_t r) { + igraph_real_t xc = MATRIX(*grid->coords, (long int)vid, 0); + igraph_real_t yc = MATRIX(*grid->coords, (long int)vid, 1); + long int x, y; + igraph_vector_clear(eids); + + igraph_2dgrid_which(grid, xc, yc, &x, &y); + + /* this cell */ + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y); + + /* left */ + if (x != 0) { + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y); + } + /* right */ + if (x != grid->stepsx - 1) { + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x + 1, y); + } + /* up */ + if (y != 0) { + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y - 1); + } + /* down */ + if (y != grid->stepsy - 1) { + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y + 1); + } + /* up & left */ + if (x != 0 && y != 0) { + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y - 1); + } + /* up & right */ + if (x != grid->stepsx - 1 && y != 0) { + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x + 1, y - 1); + } + /* down & left */ + if (x != 0 && y != grid->stepsy - 1) { + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y + 1); + } + /* down & right */ + if (x != grid->stepsx - 1 && y != grid->stepsy - 1) { + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y + 1); + } + + return 0; +} + +void igraph_2dgrid_reset(igraph_2dgrid_t *grid, igraph_2dgrid_iterator_t *it) { + /* Search for the first cell containing a vertex */ + it->x = 0; it->y = 0; it->vid = (long int) MATRIX(grid->startidx, 0, 0); + while ( it->vid == 0 && (it->x < grid->stepsx - 1 || it->y < grid->stepsy - 1)) { + it->x += 1; + if (it->x == grid->stepsx) { + it->x = 0; it->y += 1; + } + it->vid = (long int) MATRIX(grid->startidx, it->x, it->y); + } +} + +igraph_integer_t igraph_2dgrid_next(igraph_2dgrid_t *grid, + igraph_2dgrid_iterator_t *it) { + long int ret = it->vid; + + if (ret == 0) { + return 0; + } + + /* First neighbor */ + it->ncells = -1; + if (it->x != grid->stepsx - 1) { + it->ncells += 1; + it->nx[it->ncells] = it->x + 1; + it->ny[it->ncells] = it->y; + } + if (it->y != grid->stepsy - 1) { + it->ncells += 1; + it->nx[it->ncells] = it->x; + it->ny[it->ncells] = it->y + 1; + } + if (it->ncells == 1) { + it->ncells += 1; + it->nx[it->ncells] = it->x + 1; + it->ny[it->ncells] = it->y + 1; + } + it->ncells += 1; + it->nx[it->ncells] = it->x; + it->ny[it->ncells] = it->y; + + it->nei = (long int) VECTOR(grid->next) [ ret - 1 ]; + while (it->ncells > 0 && it->nei == 0 ) { + it->ncells -= 1; + it->nei = (long int) MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]); + } + + /* Next vertex */ + it->vid = (long int) VECTOR(grid->next)[ it->vid - 1 ]; + while ( (it->x < grid->stepsx - 1 || it->y < grid->stepsy - 1) && + it->vid == 0) { + it->x += 1; + if (it->x == grid->stepsx) { + it->x = 0; it->y += 1; + } + it->vid = (long int) MATRIX(grid->startidx, it->x, it->y); + } + + return (igraph_integer_t) ret; +} + +igraph_integer_t igraph_2dgrid_next_nei(igraph_2dgrid_t *grid, + igraph_2dgrid_iterator_t *it) { + long int ret = it->nei; + + if (it->nei != 0) { + it->nei = (long int) VECTOR(grid->next) [ ret - 1 ]; + } + while (it->ncells > 0 && it->nei == 0 ) { + it->ncells -= 1; + it->nei = (long int) MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]); + } + + return (igraph_integer_t) ret; +} diff --git a/src/rigraph/core/core/grid.h b/src/rigraph/core/core/grid.h new file mode 100644 index 0000000..d362ab7 --- /dev/null +++ b/src/rigraph/core/core/grid.h @@ -0,0 +1,80 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_CORE_GRID_H +#define IGRAPH_CORE_GRID_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_matrix.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/** + * 2d grid containing points + */ + +typedef struct igraph_2dgrid_t { + igraph_matrix_t *coords; + igraph_real_t minx, maxx, deltax; + igraph_real_t miny, maxy, deltay; + long int stepsx, stepsy; + igraph_matrix_t startidx; + igraph_vector_t next; + igraph_vector_t prev; + igraph_real_t massx, massy; /* The sum of the coordinates */ + long int vertices; /* Number of active vertices */ +} igraph_2dgrid_t; + +int igraph_2dgrid_init(igraph_2dgrid_t *grid, igraph_matrix_t *coords, + igraph_real_t minx, igraph_real_t maxx, igraph_real_t deltax, + igraph_real_t miny, igraph_real_t maxy, igraph_real_t deltay); +void igraph_2dgrid_destroy(igraph_2dgrid_t *grid); +void igraph_2dgrid_add(igraph_2dgrid_t *grid, long int elem, + igraph_real_t xc, igraph_real_t yc); +void igraph_2dgrid_add2(igraph_2dgrid_t *grid, long int elem); +void igraph_2dgrid_move(igraph_2dgrid_t *grid, long int elem, + igraph_real_t xc, igraph_real_t yc); +void igraph_2dgrid_getcenter(const igraph_2dgrid_t *grid, + igraph_real_t *massx, igraph_real_t *massy); +igraph_bool_t igraph_2dgrid_in(const igraph_2dgrid_t *grid, long int elem); +igraph_real_t igraph_2dgrid_dist(const igraph_2dgrid_t *grid, + long int e1, long int e2); +int igraph_2dgrid_neighbors(igraph_2dgrid_t *grid, igraph_vector_t *eids, + igraph_integer_t vid, igraph_real_t r); + +typedef struct igraph_2dgrid_iterator_t { + long int vid, x, y; + long int nei; + long int nx[4], ny[4], ncells; +} igraph_2dgrid_iterator_t; + +void igraph_2dgrid_reset(igraph_2dgrid_t *grid, igraph_2dgrid_iterator_t *it); +igraph_integer_t igraph_2dgrid_next(igraph_2dgrid_t *grid, + igraph_2dgrid_iterator_t *it); +igraph_integer_t igraph_2dgrid_next_nei(igraph_2dgrid_t *grid, + igraph_2dgrid_iterator_t *it); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/heap.c b/src/rigraph/core/core/heap.c new file mode 100644 index 0000000..b94a8d1 --- /dev/null +++ b/src/rigraph/core/core/heap.c @@ -0,0 +1,64 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" +#include "igraph_heap.h" + +#define BASE_IGRAPH_REAL +#define HEAP_TYPE_MAX +#include "igraph_pmt.h" +#include "heap.pmt" +#include "igraph_pmt_off.h" +#undef HEAP_TYPE_MAX +#define HEAP_TYPE_MIN +#include "igraph_pmt.h" +#include "heap.pmt" +#include "igraph_pmt_off.h" +#undef HEAP_TYPE_MIN +#undef BASE_IGRAPH_REAL + +#define BASE_LONG +#define HEAP_TYPE_MAX +#include "igraph_pmt.h" +#include "heap.pmt" +#include "igraph_pmt_off.h" +#undef HEAP_TYPE_MAX +#define HEAP_TYPE_MIN +#include "igraph_pmt.h" +#include "heap.pmt" +#include "igraph_pmt_off.h" +#undef HEAP_TYPE_MIN +#undef BASE_LONG + +#define BASE_CHAR +#define HEAP_TYPE_MAX +#include "igraph_pmt.h" +#include "heap.pmt" +#include "igraph_pmt_off.h" +#undef HEAP_TYPE_MAX +#define HEAP_TYPE_MIN +#include "igraph_pmt.h" +#include "heap.pmt" +#include "igraph_pmt_off.h" +#undef HEAP_TYPE_MIN +#undef BASE_CHAR diff --git a/src/rigraph/core/core/heap.pmt b/src/rigraph/core/core/heap.pmt new file mode 100644 index 0000000..2501c8b --- /dev/null +++ b/src/rigraph/core/core/heap.pmt @@ -0,0 +1,355 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_memory.h" +#include "igraph_error.h" +#include "config.h" + +#include /* memcpy & co. */ +#include + +#define PARENT(x) (((x)+1)/2-1) +#define LEFTCHILD(x) (((x)+1)*2-1) +#define RIGHTCHILD(x) (((x)+1)*2) + +/* Declare internal functions */ +static void FUNCTION(igraph_heap, i_build)(BASE* arr, long int size, long int head); +static void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, long int size, long int elem); +static void FUNCTION(igraph_heap, i_sink)(BASE* arr, long int size, long int head); +static void FUNCTION(igraph_heap, i_switch)(BASE* arr, long int e1, long int e2); + +/** + * \ingroup heap + * \function igraph_heap_init + * \brief Initializes an empty heap object. + * + * Creates an empty heap, but allocates size for some elements. + * \param h Pointer to an uninitialized heap object. + * \param alloc_size Number of elements to allocate memory for. + * \return Error code. + * + * Time complexity: O(\p alloc_size), assuming memory allocation is a + * linear operation. + */ + +int FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, long int alloc_size) { + if (alloc_size <= 0 ) { + alloc_size = 1; + } + h->stor_begin = IGRAPH_CALLOC(alloc_size, BASE); + if (h->stor_begin == 0) { + IGRAPH_ERROR("heap init failed", IGRAPH_ENOMEM); + } + h->stor_end = h->stor_begin + alloc_size; + h->end = h->stor_begin; + h->destroy = 1; + + return 0; +} + +/** + * \ingroup heap + * \function igraph_heap_init_array + * \brief Build a heap from an array. + * + * Initializes a heap object from an array, the heap is also + * built of course (constructor). + * \param h Pointer to an uninitialized heap object. + * \param data Pointer to an array of base data type. + * \param len The length of the array at \p data. + * \return Error code. + * + * Time complexity: O(n), the number of elements in the heap. + */ + +int FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *h, BASE* data, long int len) { + h->stor_begin = IGRAPH_CALLOC(len, BASE); + if (h->stor_begin == 0) { + IGRAPH_ERROR("heap init from array failed", IGRAPH_ENOMEM); + } + h->stor_end = h->stor_begin + len; + h->end = h->stor_end; + h->destroy = 1; + + memcpy(h->stor_begin, data, (size_t) len * sizeof(igraph_real_t)); + + FUNCTION(igraph_heap, i_build) (h->stor_begin, h->end - h->stor_begin, 0); + + return 0; +} + +/** + * \ingroup heap + * \function igraph_heap_destroy + * \brief Destroys an initialized heap object. + * + * \param h The heap object. + * + * Time complexity: O(1). + */ + +void FUNCTION(igraph_heap, destroy)(TYPE(igraph_heap)* h) { + if (h->destroy) { + if (h->stor_begin != 0) { + IGRAPH_FREE(h->stor_begin); + h->stor_begin = 0; + } + } +} + +/** + * \ingroup heap + * \function igraph_heap_empty + * \brief Decides whether a heap object is empty. + * + * \param h The heap object. + * \return \c TRUE if the heap is empty, \c FALSE otherwise. + * + * TIme complexity: O(1). + */ + +igraph_bool_t FUNCTION(igraph_heap, empty)(TYPE(igraph_heap)* h) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + return h->stor_begin == h->end; +} + +/** + * \ingroup heap + * \function igraph_heap_push + * \brief Add an element. + * + * Adds an element to the heap. + * \param h The heap object. + * \param elem The element to add. + * \return Error code. + * + * Time complexity: O(log n), n is the number of elements in the + * heap if no reallocation is needed, O(n) otherwise. It is ensured + * that n push operations are performed in O(n log n) time. + */ + +int FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + + /* full, allocate more storage */ + if (h->stor_end == h->end) { + long int new_size = FUNCTION(igraph_heap, size)(h) * 2; + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(FUNCTION(igraph_heap, reserve)(h, new_size)); + } + + *(h->end) = elem; + h->end += 1; + + /* maintain heap */ + FUNCTION(igraph_heap, i_shift_up)(h->stor_begin, FUNCTION(igraph_heap, size)(h), + FUNCTION(igraph_heap, size)(h) - 1); + + return 0; +} + +/** + * \ingroup heap + * \function igraph_heap_top + * \brief Top element. + * + * For maximum heaps this is the largest, for minimum heaps the + * smallest element of the heap. + * \param h The heap object. + * \return The top element. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_heap, top)(TYPE(igraph_heap)* h) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + IGRAPH_ASSERT(h->stor_begin != h->end); + + return h->stor_begin[0]; +} + +/** + * \ingroup heap + * \function igraph_heap_delete_top + * \brief Return and removes the top element + * + * Removes and returns the top element of the heap. For maximum heaps + * this is the largest, for minimum heaps the smallest element. + * \param h The heap object. + * \return The top element. + * + * Time complexity: O(log n), n is the number of elements in the + * heap. + */ + +BASE FUNCTION(igraph_heap, delete_top)(TYPE(igraph_heap)* h) { + BASE tmp; + + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + + tmp = h->stor_begin[0]; + FUNCTION(igraph_heap, i_switch)(h->stor_begin, 0, FUNCTION(igraph_heap, size)(h) - 1); + h->end -= 1; + FUNCTION(igraph_heap, i_sink)(h->stor_begin, h->end - h->stor_begin, 0); + + return tmp; +} + +/** + * \ingroup heap + * \function igraph_heap_size + * \brief Number of elements + * + * Gives the number of elements in a heap. + * \param h The heap object. + * \return The number of elements in the heap. + * + * Time complexity: O(1). + */ + +long int FUNCTION(igraph_heap, size)(TYPE(igraph_heap)* h) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + return h->end - h->stor_begin; +} + +/** + * \ingroup heap + * \function igraph_heap_reserve + * \brief Allocate more memory + * + * Allocates memory for future use. The size of the heap is + * unchanged. If the heap is larger than the \p size parameter then + * nothing happens. + * \param h The heap object. + * \param size The number of elements to allocate memory for. + * \return Error code. + * + * Time complexity: O(\p size) if \p size is larger than the current + * number of elements. O(1) otherwise. + */ + +int FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, long int size) { + long int actual_size = FUNCTION(igraph_heap, size)(h); + BASE *tmp; + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + + if (size <= actual_size) { + return 0; + } + + tmp = IGRAPH_REALLOC(h->stor_begin, (size_t) size, BASE); + if (tmp == 0) { + IGRAPH_ERROR("heap reserve failed", IGRAPH_ENOMEM); + } + h->stor_begin = tmp; + h->stor_end = h->stor_begin + size; + h->end = h->stor_begin + actual_size; + + return 0; +} + +/** + * \ingroup heap + * \brief Build a heap, this should not be called directly. + */ + +void FUNCTION(igraph_heap, i_build)(BASE* arr, + long int size, long int head) { + + if (RIGHTCHILD(head) < size) { + /* both subtrees */ + FUNCTION(igraph_heap, i_build)(arr, size, LEFTCHILD(head) ); + FUNCTION(igraph_heap, i_build)(arr, size, RIGHTCHILD(head)); + FUNCTION(igraph_heap, i_sink)(arr, size, head); + } else if (LEFTCHILD(head) < size) { + /* only left */ + FUNCTION(igraph_heap, i_build)(arr, size, LEFTCHILD(head)); + FUNCTION(igraph_heap, i_sink)(arr, size, head); + } else { + /* none */ + } +} + +/** + * \ingroup heap + * \brief Shift an element upwards in a heap, this should not be + * called directly. + */ + +void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, long int size, long int elem) { + + if (elem == 0 || arr[elem] HEAPLESS arr[PARENT(elem)]) { + /* at the top */ + } else { + FUNCTION(igraph_heap, i_switch)(arr, elem, PARENT(elem)); + FUNCTION(igraph_heap, i_shift_up)(arr, size, PARENT(elem)); + } +} + +/** + * \ingroup heap + * \brief Moves an element down in a heap, this function should not be + * called directly. + */ + +void FUNCTION(igraph_heap, i_sink)(BASE* arr, long int size, long int head) { + + if (LEFTCHILD(head) >= size) { + /* no subtrees */ + } else if (RIGHTCHILD(head) == size || + arr[LEFTCHILD(head)] HEAPMOREEQ arr[RIGHTCHILD(head)]) { + /* sink to the left if needed */ + if (arr[head] HEAPLESS arr[LEFTCHILD(head)]) { + FUNCTION(igraph_heap, i_switch)(arr, head, LEFTCHILD(head)); + FUNCTION(igraph_heap, i_sink)(arr, size, LEFTCHILD(head)); + } + } else { + /* sink to the right */ + if (arr[head] HEAPLESS arr[RIGHTCHILD(head)]) { + FUNCTION(igraph_heap, i_switch)(arr, head, RIGHTCHILD(head)); + FUNCTION(igraph_heap, i_sink)(arr, size, RIGHTCHILD(head)); + } + } +} + +/** + * \ingroup heap + * \brief Switches two elements in a heap, this function should not be + * called directly. + */ + +void FUNCTION(igraph_heap, i_switch)(BASE* arr, long int e1, long int e2) { + if (e1 != e2) { + BASE tmp = arr[e1]; + arr[e1] = arr[e2]; + arr[e2] = tmp; + } +} diff --git a/src/rigraph/core/core/indheap.c b/src/rigraph/core/core/indheap.c new file mode 100644 index 0000000..7d2a108 --- /dev/null +++ b/src/rigraph/core/core/indheap.c @@ -0,0 +1,953 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" + +#include "indheap.h" + +#include /* memcpy & co. */ +#include + +/* -------------------------------------------------- */ +/* Indexed heap */ +/* -------------------------------------------------- */ + +#define PARENT(x) (((x)+1)/2-1) +#define LEFTCHILD(x) (((x)+1)*2-1) +#define RIGHTCHILD(x) (((x)+1)*2) + +static void igraph_indheap_i_build(igraph_indheap_t* h, long int head); +static void igraph_indheap_i_shift_up(igraph_indheap_t* h, long int elem); +static void igraph_indheap_i_sink(igraph_indheap_t* h, long int head); +static void igraph_indheap_i_switch(igraph_indheap_t* h, long int e1, long int e2); + +/** + * \ingroup indheap + * \brief Initializes an indexed heap (constructor). + * + * @return Error code: + * - IGRAPH_ENOMEM: out of memory + */ + +int igraph_indheap_init(igraph_indheap_t* h, long int alloc_size) { + if (alloc_size <= 0 ) { + alloc_size = 1; + } + h->stor_begin = IGRAPH_CALLOC(alloc_size, igraph_real_t); + if (h->stor_begin == 0) { + h->index_begin = 0; + IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM); + } + h->index_begin = IGRAPH_CALLOC(alloc_size, long int); + if (h->index_begin == 0) { + IGRAPH_FREE(h->stor_begin); + h->stor_begin = 0; + IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM); + } + + h->stor_end = h->stor_begin + alloc_size; + h->end = h->stor_begin; + h->destroy = 1; + + return 0; +} + +int igraph_indheap_clear(igraph_indheap_t *h) { + h->end = h->stor_begin; + return 0; +} + +/** + * \ingroup indheap + * \brief Initializes and build an indexed heap from a C array (constructor). + * + * @return Error code: + * - IGRAPH_ENOMEM: out of memory + */ + +int igraph_indheap_init_array (igraph_indheap_t *h, igraph_real_t* data, long int len) { + long int i; + + h->stor_begin = IGRAPH_CALLOC(len, igraph_real_t); + if (h->stor_begin == 0) { + h->index_begin = 0; + IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM); + } + h->index_begin = IGRAPH_CALLOC(len, long int); + if (h->index_begin == 0) { + IGRAPH_FREE(h->stor_begin); + h->stor_begin = 0; + IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM); + } + h->stor_end = h->stor_begin + len; + h->end = h->stor_end; + h->destroy = 1; + + memcpy(h->stor_begin, data, (size_t) len * sizeof(igraph_real_t)); + for (i = 0; i < len; i++) { + h->index_begin[i] = i + 1; + } + + igraph_indheap_i_build (h, 0); + + return 0; +} + +/** + * \ingroup indheap + * \brief Destroys an initialized indexed heap. + */ + +void igraph_indheap_destroy (igraph_indheap_t* h) { + IGRAPH_ASSERT(h != 0); + if (h->destroy) { + if (h->stor_begin != 0) { + IGRAPH_FREE(h->stor_begin); + h->stor_begin = 0; + } + if (h->index_begin != 0) { + IGRAPH_FREE(h->index_begin); + h->index_begin = 0; + } + } +} + +/** + * \ingroup indheap + * \brief Checks whether a heap is empty. + */ + +igraph_bool_t igraph_indheap_empty (igraph_indheap_t* h) { + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + return h->stor_begin == h->end; +} + +/** + * \ingroup indheap + * \brief Adds an element to an indexed heap. + */ + +int igraph_indheap_push (igraph_indheap_t* h, igraph_real_t elem) { + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + + /* full, allocate more storage */ + if (h->stor_end == h->end) { + long int new_size = igraph_indheap_size(h) * 2; + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(igraph_indheap_reserve(h, new_size)); + } + + *(h->end) = elem; + h->end += 1; + *(h->index_begin + igraph_indheap_size(h) - 1) = igraph_indheap_size(h) - 1; + + /* maintain indheap */ + igraph_indheap_i_shift_up(h, igraph_indheap_size(h) - 1); + + return 0; +} + +/** + * \ingroup indheap + * \brief Adds an element to an indexed heap with a given index. + */ + +int igraph_indheap_push_with_index(igraph_indheap_t* h, long int idx, igraph_real_t elem) { + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + + /* full, allocate more storage */ + if (h->stor_end == h->end) { + long int new_size = igraph_indheap_size(h) * 2; + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(igraph_indheap_reserve(h, new_size)); + } + + *(h->end) = elem; + h->end += 1; + *(h->index_begin + igraph_indheap_size(h) - 1) = idx; + + /* maintain indheap */ + igraph_indheap_i_shift_up(h, igraph_indheap_size(h) - 1); + + return 0; +} + +/** + * \ingroup indheap + * \brief Modifies an element in an indexed heap. + */ + +int igraph_indheap_modify(igraph_indheap_t* h, long int idx, igraph_real_t elem) { + long int i, n; + + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + + n = igraph_indheap_size(h); + for (i = 0; i < n; i++) + if (h->index_begin[i] == idx) { + h->stor_begin[i] = elem; + break; + } + + if (i == n) { + return 0; + } + + /* maintain indheap */ + igraph_indheap_i_build(h, 0); + + return 0; +} + +/** + * \ingroup indheap + * \brief Returns the largest element in an indexed heap. + */ + +igraph_real_t igraph_indheap_max (igraph_indheap_t* h) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + IGRAPH_ASSERT(h->stor_begin != h->end); + + return h->stor_begin[0]; +} + +/** + * \ingroup indheap + * \brief Removes the largest element from an indexed heap. + */ + +igraph_real_t igraph_indheap_delete_max(igraph_indheap_t* h) { + igraph_real_t tmp; + + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + + tmp = h->stor_begin[0]; + igraph_indheap_i_switch(h, 0, igraph_indheap_size(h) - 1); + h->end -= 1; + igraph_indheap_i_sink(h, 0); + + return tmp; +} + +/** + * \ingroup indheap + * \brief Gives the number of elements in an indexed heap. + */ + +long int igraph_indheap_size (igraph_indheap_t* h) { + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + return h->end - h->stor_begin; +} + +/** + * \ingroup indheap + * \brief Reserves more memory for an indexed heap. + * + * @return Error code: + * - IGRAPH_ENOMEM: out of memory + */ + +int igraph_indheap_reserve (igraph_indheap_t* h, long int size) { + long int actual_size = igraph_indheap_size(h); + igraph_real_t *tmp1; + long int *tmp2; + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + + if (size <= actual_size) { + return 0; + } + + tmp1 = IGRAPH_CALLOC(size, igraph_real_t); + if (tmp1 == 0) { + IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, tmp1); + tmp2 = IGRAPH_CALLOC(size, long int); + if (tmp2 == 0) { + IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, tmp2); + memcpy(tmp1, h->stor_begin, (size_t) actual_size * sizeof(igraph_real_t)); + memcpy(tmp2, h->index_begin, (size_t) actual_size * sizeof(long int)); + IGRAPH_FREE(h->stor_begin); + IGRAPH_FREE(h->index_begin); + + h->stor_begin = tmp1; + h->index_begin = tmp2; + h->stor_end = h->stor_begin + size; + h->end = h->stor_begin + actual_size; + + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +/** + * \ingroup indheap + * \brief Returns the index of the largest element in an indexed heap. + */ + +long int igraph_indheap_max_index(igraph_indheap_t *h) { + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + return h->index_begin[0]; +} + +/** + * \ingroup indheap + * \brief Builds an indexed heap, this function should not be called + * directly. + */ + +static void igraph_indheap_i_build(igraph_indheap_t* h, long int head) { + + long int size = igraph_indheap_size(h); + if (RIGHTCHILD(head) < size) { + /* both subtrees */ + igraph_indheap_i_build(h, LEFTCHILD(head) ); + igraph_indheap_i_build(h, RIGHTCHILD(head)); + igraph_indheap_i_sink(h, head); + } else if (LEFTCHILD(head) < size) { + /* only left */ + igraph_indheap_i_build(h, LEFTCHILD(head)); + igraph_indheap_i_sink(h, head); + } else { + /* none */ + } +} + +/** + * \ingroup indheap + * \brief Moves an element up in the heap, don't call this function + * directly. + */ + +static void igraph_indheap_i_shift_up(igraph_indheap_t *h, long int elem) { + + if (elem == 0 || h->stor_begin[elem] < h->stor_begin[PARENT(elem)]) { + /* at the top */ + } else { + igraph_indheap_i_switch(h, elem, PARENT(elem)); + igraph_indheap_i_shift_up(h, PARENT(elem)); + } +} + +/** + * \ingroup indheap + * \brief Moves an element down in the heap, don't call this function + * directly. + */ + +static void igraph_indheap_i_sink(igraph_indheap_t* h, long int head) { + + long int size = igraph_indheap_size(h); + if (LEFTCHILD(head) >= size) { + /* no subtrees */ + } else if (RIGHTCHILD(head) == size || + h->stor_begin[LEFTCHILD(head)] >= h->stor_begin[RIGHTCHILD(head)]) { + /* sink to the left if needed */ + if (h->stor_begin[head] < h->stor_begin[LEFTCHILD(head)]) { + igraph_indheap_i_switch(h, head, LEFTCHILD(head)); + igraph_indheap_i_sink(h, LEFTCHILD(head)); + } + } else { + /* sink to the right */ + if (h->stor_begin[head] < h->stor_begin[RIGHTCHILD(head)]) { + igraph_indheap_i_switch(h, head, RIGHTCHILD(head)); + igraph_indheap_i_sink(h, RIGHTCHILD(head)); + } + } +} + +/** + * \ingroup indheap + * \brief Switches two elements in a heap, don't call this function + * directly. + */ + +static void igraph_indheap_i_switch(igraph_indheap_t* h, long int e1, long int e2) { + if (e1 != e2) { + igraph_real_t tmp = h->stor_begin[e1]; + h->stor_begin[e1] = h->stor_begin[e2]; + h->stor_begin[e2] = tmp; + + tmp = h->index_begin[e1]; + h->index_begin[e1] = h->index_begin[e2]; + h->index_begin[e2] = (long int) tmp; + } +} + +/*************************************************/ + +/* -------------------------------------------------- */ +/* Doubly indexed heap */ +/* -------------------------------------------------- */ + +/* static void igraph_d_indheap_i_build(igraph_d_indheap_t* h, long int head); */ /* Unused function */ +static void igraph_d_indheap_i_shift_up(igraph_d_indheap_t* h, long int elem); +static void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, long int head); +static void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, long int e1, long int e2); + +/** + * \ingroup doubleindheap + * \brief Initializes an empty doubly indexed heap object (constructor). + * + * @return Error code: + * - IGRAPH_ENOMEM: out of memory + */ + +int igraph_d_indheap_init (igraph_d_indheap_t* h, long int alloc_size) { + if (alloc_size <= 0 ) { + alloc_size = 1; + } + h->stor_begin = IGRAPH_CALLOC(alloc_size, igraph_real_t); + if (h->stor_begin == 0) { + h->index_begin = 0; + h->index2_begin = 0; + IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM); + } + h->stor_end = h->stor_begin + alloc_size; + h->end = h->stor_begin; + h->destroy = 1; + h->index_begin = IGRAPH_CALLOC(alloc_size, long int); + if (h->index_begin == 0) { + IGRAPH_FREE(h->stor_begin); + h->stor_begin = 0; + h->index2_begin = 0; + IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM); + } + h->index2_begin = IGRAPH_CALLOC(alloc_size, long int); + if (h->index2_begin == 0) { + IGRAPH_FREE(h->stor_begin); + IGRAPH_FREE(h->index_begin); + h->stor_begin = 0; + h->index_begin = 0; + IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM); + } + + return 0; +} + +/** + * \ingroup doubleindheap + * \brief Destroys an initialized doubly indexed heap object. + */ + +void igraph_d_indheap_destroy (igraph_d_indheap_t* h) { + IGRAPH_ASSERT(h != 0); + if (h->destroy) { + if (h->stor_begin != 0) { + IGRAPH_FREE(h->stor_begin); + h->stor_begin = 0; + } + if (h->index_begin != 0) { + IGRAPH_FREE(h->index_begin); + h->index_begin = 0; + } + if (h->index2_begin != 0) { + IGRAPH_FREE(h->index2_begin); + h->index2_begin = 0; + } + } +} + +/** + * \ingroup doubleindheap + * \brief Decides whether a heap is empty. + */ + +igraph_bool_t igraph_d_indheap_empty (igraph_d_indheap_t* h) { + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + return h->stor_begin == h->end; +} + +/** + * \ingroup doubleindheap + * \brief Adds an element to the heap. + */ + +int igraph_d_indheap_push (igraph_d_indheap_t* h, igraph_real_t elem, + long int idx, long int idx2) { + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + + /* full, allocate more storage */ + if (h->stor_end == h->end) { + long int new_size = igraph_d_indheap_size(h) * 2; + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(igraph_d_indheap_reserve(h, new_size)); + } + + *(h->end) = elem; + h->end += 1; + *(h->index_begin + igraph_d_indheap_size(h) - 1) = idx ; + *(h->index2_begin + igraph_d_indheap_size(h) - 1) = idx2 ; + + /* maintain d_indheap */ + igraph_d_indheap_i_shift_up(h, igraph_d_indheap_size(h) - 1); + + return 0; +} + +/** + * \ingroup doubleindheap + * \brief Returns the largest element in the heap. + */ + +igraph_real_t igraph_d_indheap_max (igraph_d_indheap_t* h) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + IGRAPH_ASSERT(h->stor_begin != h->end); + + return h->stor_begin[0]; +} + +/** + * \ingroup doubleindheap + * \brief Removes the largest element from the heap. + */ + +igraph_real_t igraph_d_indheap_delete_max(igraph_d_indheap_t* h) { + igraph_real_t tmp; + + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + + tmp = h->stor_begin[0]; + igraph_d_indheap_i_switch(h, 0, igraph_d_indheap_size(h) - 1); + h->end -= 1; + igraph_d_indheap_i_sink(h, 0); + + return tmp; +} + +/** + * \ingroup doubleindheap + * \brief Gives the number of elements in the heap. + */ + +long int igraph_d_indheap_size (igraph_d_indheap_t* h) { + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + return h->end - h->stor_begin; +} + +/** + * \ingroup doubleindheap + * \brief Allocates memory for a heap. + * + * @return Error code: + * - IGRAPH_ENOMEM: out of memory + */ + +int igraph_d_indheap_reserve (igraph_d_indheap_t* h, long int size) { + long int actual_size = igraph_d_indheap_size(h); + igraph_real_t *tmp1; + long int *tmp2, *tmp3; + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + + if (size <= actual_size) { + return 0; + } + + tmp1 = IGRAPH_CALLOC(size, igraph_real_t); + if (tmp1 == 0) { + IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, tmp1); + tmp2 = IGRAPH_CALLOC(size, long int); + if (tmp2 == 0) { + IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, tmp2); + tmp3 = IGRAPH_CALLOC(size, long int); + if (tmp3 == 0) { + IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, tmp3); + + memcpy(tmp1, h->stor_begin, (size_t) actual_size * sizeof(igraph_real_t)); + memcpy(tmp2, h->index_begin, (size_t) actual_size * sizeof(long int)); + memcpy(tmp3, h->index2_begin, (size_t) actual_size * sizeof(long int)); + IGRAPH_FREE(h->stor_begin); + IGRAPH_FREE(h->index_begin); + IGRAPH_FREE(h->index2_begin); + + h->stor_begin = tmp1; + h->stor_end = h->stor_begin + size; + h->end = h->stor_begin + actual_size; + h->index_begin = tmp2; + h->index2_begin = tmp3; + + IGRAPH_FINALLY_CLEAN(3); + return 0; +} + +/** + * \ingroup doubleindheap + * \brief Gives the indices of the maximal element in the heap. + */ + +void igraph_d_indheap_max_index(igraph_d_indheap_t *h, long int *idx, long int *idx2) { + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + (*idx) = h->index_begin[0]; + (*idx2) = h->index2_begin[0]; +} + +/** + * \ingroup doubleindheap + * \brief Builds the heap, don't call it directly. + */ + +/* Unused function, temporarily disabled */ +#if 0 +static void igraph_d_indheap_i_build(igraph_d_indheap_t* h, long int head) { + + long int size = igraph_d_indheap_size(h); + if (RIGHTCHILD(head) < size) { + /* both subtrees */ + igraph_d_indheap_i_build(h, LEFTCHILD(head) ); + igraph_d_indheap_i_build(h, RIGHTCHILD(head)); + igraph_d_indheap_i_sink(h, head); + } else if (LEFTCHILD(head) < size) { + /* only left */ + igraph_d_indheap_i_build(h, LEFTCHILD(head)); + igraph_d_indheap_i_sink(h, head); + } else { + /* none */ + } +} +#endif + +/** + * \ingroup doubleindheap + * \brief Moves an element up in the heap, don't call it directly. + */ + +static void igraph_d_indheap_i_shift_up(igraph_d_indheap_t *h, long int elem) { + + if (elem == 0 || h->stor_begin[elem] < h->stor_begin[PARENT(elem)]) { + /* at the top */ + } else { + igraph_d_indheap_i_switch(h, elem, PARENT(elem)); + igraph_d_indheap_i_shift_up(h, PARENT(elem)); + } +} + +/** + * \ingroup doubleindheap + * \brief Moves an element down in the heap, don't call it directly. + */ + +static void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, long int head) { + + long int size = igraph_d_indheap_size(h); + if (LEFTCHILD(head) >= size) { + /* no subtrees */ + } else if (RIGHTCHILD(head) == size || + h->stor_begin[LEFTCHILD(head)] >= h->stor_begin[RIGHTCHILD(head)]) { + /* sink to the left if needed */ + if (h->stor_begin[head] < h->stor_begin[LEFTCHILD(head)]) { + igraph_d_indheap_i_switch(h, head, LEFTCHILD(head)); + igraph_d_indheap_i_sink(h, LEFTCHILD(head)); + } + } else { + /* sink to the right */ + if (h->stor_begin[head] < h->stor_begin[RIGHTCHILD(head)]) { + igraph_d_indheap_i_switch(h, head, RIGHTCHILD(head)); + igraph_d_indheap_i_sink(h, RIGHTCHILD(head)); + } + } +} + +/** + * \ingroup doubleindheap + * \brief Switches two elements in the heap, don't call it directly. + */ + +static void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, long int e1, long int e2) { + if (e1 != e2) { + long int tmpi; + igraph_real_t tmp = h->stor_begin[e1]; + h->stor_begin[e1] = h->stor_begin[e2]; + h->stor_begin[e2] = tmp; + + tmpi = h->index_begin[e1]; + h->index_begin[e1] = h->index_begin[e2]; + h->index_begin[e2] = tmpi; + + tmpi = h->index2_begin[e1]; + h->index2_begin[e1] = h->index2_begin[e2]; + h->index2_begin[e2] = tmpi; + } +} + +/*************************************************/ + +/* -------------------------------------------------- */ +/* Two-way indexed heap */ +/* -------------------------------------------------- */ + +#undef PARENT +#undef LEFTCHILD +#undef RIGHTCHILD +#define PARENT(x) (((x)+1)/2-1) +#define LEFTCHILD(x) (((x)+1)*2-1) +#define RIGHTCHILD(x) (((x)+1)*2) + +/* This is a smart indexed heap. In addition to the "normal" indexed heap + it allows to access every element through its index in O(1) time. + In other words, for this heap the indexing operation is O(1), the + normal heap does this in O(n) time.... */ + +static void igraph_i_2wheap_switch(igraph_2wheap_t *h, + long int e1, long int e2) { + if (e1 != e2) { + long int tmp1, tmp2; + igraph_real_t tmp3 = VECTOR(h->data)[e1]; + VECTOR(h->data)[e1] = VECTOR(h->data)[e2]; + VECTOR(h->data)[e2] = tmp3; + + tmp1 = VECTOR(h->index)[e1]; + tmp2 = VECTOR(h->index)[e2]; + + VECTOR(h->index2)[tmp1] = e2 + 2; + VECTOR(h->index2)[tmp2] = e1 + 2; + + VECTOR(h->index)[e1] = tmp2; + VECTOR(h->index)[e2] = tmp1; + } +} + +static void igraph_i_2wheap_shift_up(igraph_2wheap_t *h, + long int elem) { + if (elem == 0 || VECTOR(h->data)[elem] < VECTOR(h->data)[PARENT(elem)]) { + /* at the top */ + } else { + igraph_i_2wheap_switch(h, elem, PARENT(elem)); + igraph_i_2wheap_shift_up(h, PARENT(elem)); + } +} + +static void igraph_i_2wheap_sink(igraph_2wheap_t *h, + long int head) { + long int size = igraph_2wheap_size(h); + if (LEFTCHILD(head) >= size) { + /* no subtrees */ + } else if (RIGHTCHILD(head) == size || + VECTOR(h->data)[LEFTCHILD(head)] >= VECTOR(h->data)[RIGHTCHILD(head)]) { + /* sink to the left if needed */ + if (VECTOR(h->data)[head] < VECTOR(h->data)[LEFTCHILD(head)]) { + igraph_i_2wheap_switch(h, head, LEFTCHILD(head)); + igraph_i_2wheap_sink(h, LEFTCHILD(head)); + } + } else { + /* sink to the right */ + if (VECTOR(h->data)[head] < VECTOR(h->data)[RIGHTCHILD(head)]) { + igraph_i_2wheap_switch(h, head, RIGHTCHILD(head)); + igraph_i_2wheap_sink(h, RIGHTCHILD(head)); + } + } +} + +/* ------------------ */ +/* These are public */ +/* ------------------ */ + +int igraph_2wheap_init(igraph_2wheap_t *h, long int size) { + h->size = size; + /* We start with the biggest */ + IGRAPH_CHECK(igraph_vector_long_init(&h->index2, size)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &h->index2); + IGRAPH_VECTOR_INIT_FINALLY(&h->data, 0); + IGRAPH_CHECK(igraph_vector_long_init(&h->index, 0)); + /* IGRAPH_FINALLY(igraph_vector_long_destroy, &h->index); */ + + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +void igraph_2wheap_destroy(igraph_2wheap_t *h) { + igraph_vector_destroy(&h->data); + igraph_vector_long_destroy(&h->index); + igraph_vector_long_destroy(&h->index2); +} + +int igraph_2wheap_clear(igraph_2wheap_t *h) { + igraph_vector_clear(&h->data); + igraph_vector_long_clear(&h->index); + igraph_vector_long_null(&h->index2); + return 0; +} + +igraph_bool_t igraph_2wheap_empty(const igraph_2wheap_t *h) { + return igraph_vector_empty(&h->data); +} + +int igraph_2wheap_push_with_index(igraph_2wheap_t *h, + long int idx, igraph_real_t elem) { + + /* printf("-> %.2g [%li]\n", elem, idx); */ + + long int size = igraph_vector_size(&h->data); + IGRAPH_CHECK(igraph_vector_push_back(&h->data, elem)); + IGRAPH_CHECK(igraph_vector_long_push_back(&h->index, idx)); + VECTOR(h->index2)[idx] = size + 2; + + /* maintain heap */ + igraph_i_2wheap_shift_up(h, size); + return 0; +} + +long int igraph_2wheap_size(const igraph_2wheap_t *h) { + return igraph_vector_size(&h->data); +} + +long int igraph_2wheap_max_size(const igraph_2wheap_t *h) { + return h->size; +} + +igraph_real_t igraph_2wheap_max(const igraph_2wheap_t *h) { + return VECTOR(h->data)[0]; +} + +long int igraph_2wheap_max_index(const igraph_2wheap_t *h) { + return VECTOR(h->index)[0]; +} + +igraph_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, long int idx) { + return VECTOR(h->index2)[idx] != 0; +} + +igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, long int idx) { + return VECTOR(h->index2)[idx] > 1; +} + +igraph_real_t igraph_2wheap_get(const igraph_2wheap_t *h, long int idx) { + long int i = VECTOR(h->index2)[idx] - 2; + return VECTOR(h->data)[i]; +} + +igraph_real_t igraph_2wheap_delete_max(igraph_2wheap_t *h) { + + igraph_real_t tmp = VECTOR(h->data)[0]; + long int tmpidx = VECTOR(h->index)[0]; + igraph_i_2wheap_switch(h, 0, igraph_2wheap_size(h) - 1); + igraph_vector_pop_back(&h->data); + igraph_vector_long_pop_back(&h->index); + VECTOR(h->index2)[tmpidx] = 0; + igraph_i_2wheap_sink(h, 0); + + /* printf("<-max %.2g\n", tmp); */ + + return tmp; +} + +igraph_real_t igraph_2wheap_deactivate_max(igraph_2wheap_t *h) { + + igraph_real_t tmp = VECTOR(h->data)[0]; + long int tmpidx = VECTOR(h->index)[0]; + igraph_i_2wheap_switch(h, 0, igraph_2wheap_size(h) - 1); + igraph_vector_pop_back(&h->data); + igraph_vector_long_pop_back(&h->index); + VECTOR(h->index2)[tmpidx] = 1; + igraph_i_2wheap_sink(h, 0); + + return tmp; +} + +igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, long int *idx) { + + igraph_real_t tmp = VECTOR(h->data)[0]; + long int tmpidx = VECTOR(h->index)[0]; + igraph_i_2wheap_switch(h, 0, igraph_2wheap_size(h) - 1); + igraph_vector_pop_back(&h->data); + igraph_vector_long_pop_back(&h->index); + VECTOR(h->index2)[tmpidx] = 0; + igraph_i_2wheap_sink(h, 0); + + if (idx) { + *idx = tmpidx; + } + return tmp; +} + +int igraph_2wheap_modify(igraph_2wheap_t *h, long int idx, igraph_real_t elem) { + + long int pos = VECTOR(h->index2)[idx] - 2; + + /* printf("-- %.2g -> %.2g\n", VECTOR(h->data)[pos], elem); */ + + VECTOR(h->data)[pos] = elem; + igraph_i_2wheap_sink(h, pos); + igraph_i_2wheap_shift_up(h, pos); + + return 0; +} + +/* Check that the heap is in a consistent state */ + +int igraph_2wheap_check(igraph_2wheap_t *h) { + long int size = igraph_2wheap_size(h); + long int i; + igraph_bool_t error = 0; + + /* Check the heap property */ + for (i = 0; i < size; i++) { + if (LEFTCHILD(i) >= size) { + break; + } + if (VECTOR(h->data)[LEFTCHILD(i)] > VECTOR(h->data)[i]) { + error = 1; break; + } + if (RIGHTCHILD(i) >= size) { + break; + } + if (VECTOR(h->data)[RIGHTCHILD(i)] > VECTOR(h->data)[i]) { + error = 1; break; + } + } + + if (error) { + IGRAPH_ERROR("Inconsistent heap", IGRAPH_EINTERNAL); + } + + return 0; +} diff --git a/src/rigraph/core/core/indheap.h b/src/rigraph/core/core/indheap.h new file mode 100644 index 0000000..16977f9 --- /dev/null +++ b/src/rigraph/core/core/indheap.h @@ -0,0 +1,140 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_CORE_INDHEAP_H +#define IGRAPH_CORE_INDHEAP_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Indexed heap */ +/* -------------------------------------------------- */ + +/** + * Indexed heap data type. + * \ingroup internal + */ + +typedef struct s_indheap { + igraph_real_t* stor_begin; + igraph_real_t* stor_end; + igraph_real_t* end; + int destroy; + long int* index_begin; +} igraph_indheap_t; + +#define IGRAPH_INDHEAP_NULL { 0,0,0,0,0 } + +int igraph_indheap_init (igraph_indheap_t* h, long int size); +int igraph_indheap_init_array (igraph_indheap_t *t, igraph_real_t* data, long int len); +void igraph_indheap_destroy (igraph_indheap_t* h); +int igraph_indheap_clear(igraph_indheap_t *h); +igraph_bool_t igraph_indheap_empty (igraph_indheap_t* h); +int igraph_indheap_push (igraph_indheap_t* h, igraph_real_t elem); +int igraph_indheap_push_with_index(igraph_indheap_t* h, long int idx, igraph_real_t elem); +int igraph_indheap_modify(igraph_indheap_t* h, long int idx, igraph_real_t elem); +igraph_real_t igraph_indheap_max (igraph_indheap_t* h); +igraph_real_t igraph_indheap_delete_max(igraph_indheap_t* h); +long int igraph_indheap_size (igraph_indheap_t* h); +int igraph_indheap_reserve (igraph_indheap_t* h, long int size); +long int igraph_indheap_max_index(igraph_indheap_t *h); + + +/* -------------------------------------------------- */ +/* Doubly indexed heap */ +/* -------------------------------------------------- */ + +/* This is a heap containing double elements and + two indices, its intended usage is the storage of + weighted edges. +*/ + +/** + * Doubly indexed heap data type. + * \ingroup internal + */ + +typedef struct s_indheap_d { + igraph_real_t* stor_begin; + igraph_real_t* stor_end; + igraph_real_t* end; + int destroy; + long int* index_begin; + long int* index2_begin; +} igraph_d_indheap_t; + + +#define IGRAPH_D_INDHEAP_NULL { 0,0,0,0,0,0 } + +IGRAPH_PRIVATE_EXPORT int igraph_d_indheap_init(igraph_d_indheap_t *h, long int size); +IGRAPH_PRIVATE_EXPORT void igraph_d_indheap_destroy(igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_d_indheap_empty(igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT int igraph_d_indheap_push(igraph_d_indheap_t *h, igraph_real_t elem, + long int idx, long int idx2); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_d_indheap_max(igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_d_indheap_delete_max(igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT long int igraph_d_indheap_size(igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT int igraph_d_indheap_reserve(igraph_d_indheap_t *h, long int size); +IGRAPH_PRIVATE_EXPORT void igraph_d_indheap_max_index(igraph_d_indheap_t *h, long int *idx, long int *idx2); + +/* -------------------------------------------------- */ +/* Two-way indexed heap */ +/* -------------------------------------------------- */ + +/* This is a smart indexed heap. In addition to the "normal" indexed heap + it allows to access every element through its index in O(1) time. + In other words, for this heap the _modify operation is O(1), the + normal heap does this in O(n) time.... */ + +typedef struct igraph_2wheap_t { + long int size; + igraph_vector_t data; + igraph_vector_long_t index; + igraph_vector_long_t index2; +} igraph_2wheap_t; + +IGRAPH_PRIVATE_EXPORT int igraph_2wheap_init(igraph_2wheap_t *h, long int size); +IGRAPH_PRIVATE_EXPORT void igraph_2wheap_destroy(igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT int igraph_2wheap_clear(igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT int igraph_2wheap_push_with_index(igraph_2wheap_t *h, + long int idx, igraph_real_t elem); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_2wheap_empty(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT long int igraph_2wheap_size(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT long int igraph_2wheap_max_size(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_2wheap_max(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT long int igraph_2wheap_max_index(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_2wheap_deactivate_max(igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, long int idx); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, long int idx); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_2wheap_get(const igraph_2wheap_t *h, long int idx); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_2wheap_delete_max(igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, long int *idx); +IGRAPH_PRIVATE_EXPORT int igraph_2wheap_modify(igraph_2wheap_t *h, long int idx, igraph_real_t elem); +IGRAPH_PRIVATE_EXPORT int igraph_2wheap_check(igraph_2wheap_t *h); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/interruption.c b/src/rigraph/core/core/interruption.c new file mode 100644 index 0000000..b9a88ea --- /dev/null +++ b/src/rigraph/core/core/interruption.c @@ -0,0 +1,42 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_interrupt.h" +#include "config.h" + +igraph_interruption_handler_t +*igraph_i_interruption_handler = 0; + +int igraph_allow_interruption(void* data) { + if (igraph_i_interruption_handler) { + return igraph_i_interruption_handler(data); + } + return IGRAPH_SUCCESS; +} + +igraph_interruption_handler_t * +igraph_set_interruption_handler (igraph_interruption_handler_t * new_handler) { + igraph_interruption_handler_t * previous_handler = igraph_i_interruption_handler; + igraph_i_interruption_handler = new_handler; + return previous_handler; +} diff --git a/src/rigraph/core/core/interruption.h b/src/rigraph/core/core/interruption.h new file mode 100644 index 0000000..b9bcc34 --- /dev/null +++ b/src/rigraph/core/core/interruption.h @@ -0,0 +1,59 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_INTERRUPT_INTERNAL_H +#define IGRAPH_INTERRUPT_INTERNAL_H + +#include "igraph_decls.h" +#include "igraph_interrupt.h" +#include "config.h" + + +__BEGIN_DECLS + +extern igraph_interruption_handler_t *igraph_i_interruption_handler; + +/** + * \define IGRAPH_ALLOW_INTERRUPTION + * \brief + * + * This macro should be called when interruption is allowed. It calls + * \ref igraph_allow_interruption() with the proper parameters and if that returns + * anything but \c IGRAPH_SUCCESS then + * the macro returns the "calling" function as well, with the proper + * error code (\c IGRAPH_INTERRUPTED). + */ + +#define IGRAPH_ALLOW_INTERRUPTION() \ + do { \ + if (igraph_i_interruption_handler) { if (igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) return IGRAPH_INTERRUPTED; \ + } } while (0) + +#define IGRAPH_ALLOW_INTERRUPTION_NORETURN() \ + do { \ + if (igraph_i_interruption_handler) { igraph_allow_interruption(NULL); } \ + } while (0) + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/marked_queue.c b/src/rigraph/core/core/marked_queue.c new file mode 100644 index 0000000..c75491f --- /dev/null +++ b/src/rigraph/core/core/marked_queue.c @@ -0,0 +1,115 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "marked_queue.h" + +#define BATCH_MARKER -1 + +int igraph_marked_queue_init(igraph_marked_queue_t *q, + long int size) { + IGRAPH_CHECK(igraph_dqueue_init(&q->Q, 0)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &q->Q); + IGRAPH_CHECK(igraph_vector_long_init(&q->set, size)); + q->mark = 1; + q->size = 0; + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +void igraph_marked_queue_destroy(igraph_marked_queue_t *q) { + igraph_vector_long_destroy(&q->set); + igraph_dqueue_destroy(&q->Q); +} + +void igraph_marked_queue_reset(igraph_marked_queue_t *q) { + igraph_dqueue_clear(&q->Q); + q->size = 0; + q->mark += 1; + if (q->mark == 0) { + igraph_vector_long_null(&q->set); + q->mark += 1; + } +} + +igraph_bool_t igraph_marked_queue_empty(const igraph_marked_queue_t *q) { + return q->size == 0; +} + +long int igraph_marked_queue_size(const igraph_marked_queue_t *q) { + return q->size; +} + +igraph_bool_t igraph_marked_queue_iselement(const igraph_marked_queue_t *q, + long int elem) { + return (VECTOR(q->set)[elem] == q->mark); +} + +int igraph_marked_queue_push(igraph_marked_queue_t *q, long int elem) { + if (VECTOR(q->set)[elem] != q->mark) { + IGRAPH_CHECK(igraph_dqueue_push(&q->Q, elem)); + VECTOR(q->set)[elem] = q->mark; + q->size += 1; + } + return 0; +} + +int igraph_marked_queue_start_batch(igraph_marked_queue_t *q) { + IGRAPH_CHECK(igraph_dqueue_push(&q->Q, BATCH_MARKER)); + return 0; +} + +void igraph_marked_queue_pop_back_batch(igraph_marked_queue_t *q) { + long int size = igraph_dqueue_size(&q->Q); + long int elem; + while (size > 0 && + (elem = (long int) igraph_dqueue_pop_back(&q->Q)) != BATCH_MARKER) { + VECTOR(q->set)[elem] = 0; + size--; + q->size--; + } +} + +#ifndef USING_R +int igraph_marked_queue_print(const igraph_marked_queue_t *q) { + IGRAPH_CHECK(igraph_dqueue_print(&q->Q)); + return 0; +} +#endif + +int igraph_marked_queue_fprint(const igraph_marked_queue_t *q, FILE *file) { + IGRAPH_CHECK(igraph_dqueue_fprint(&q->Q, file)); + return 0; +} + +int igraph_marked_queue_as_vector(const igraph_marked_queue_t *q, + igraph_vector_t *vec) { + long int i, p, n = igraph_dqueue_size(&q->Q); + IGRAPH_CHECK(igraph_vector_resize(vec, q->size)); + for (i = 0, p = 0; i < n; i++) { + igraph_real_t e = igraph_dqueue_e(&q->Q, i); + if (e != BATCH_MARKER) { + VECTOR(*vec)[p++] = e; + } + } + return 0; +} diff --git a/src/rigraph/core/core/marked_queue.h b/src/rigraph/core/core/marked_queue.h new file mode 100644 index 0000000..be36e28 --- /dev/null +++ b/src/rigraph/core/core/marked_queue.h @@ -0,0 +1,70 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_MARKED_QUEUE_H +#define IGRAPH_MARKED_QUEUE_H + +#include "igraph_vector.h" +#include "igraph_dqueue.h" + +#include + +/* This is essentially a double ended queue, with some extra features: + (1) The is-element? operation is fast, O(1). This requires that we + know a limit for the number of elements in the queue. + (2) We can insert elements in batches, and the whole batch can be + removed at once. + + Currently only the top-end operations are implemented, so the queue + is essentially a stack. +*/ + +typedef struct igraph_marked_queue_t { + igraph_dqueue_t Q; + igraph_vector_long_t set; + long int mark; + long int size; +} igraph_marked_queue_t; + +IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_init(igraph_marked_queue_t *q, + long int size); +IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_destroy(igraph_marked_queue_t *q); +IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_reset(igraph_marked_queue_t *q); + +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_marked_queue_empty(const igraph_marked_queue_t *q); +IGRAPH_PRIVATE_EXPORT long int igraph_marked_queue_size(const igraph_marked_queue_t *q); + +IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_print(const igraph_marked_queue_t *q); +IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_fprint(const igraph_marked_queue_t *q, FILE *file); + +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_marked_queue_iselement(const igraph_marked_queue_t *q, + long int elem); +IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_push(igraph_marked_queue_t *q, long int elem); + +IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_start_batch(igraph_marked_queue_t *q); +IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_pop_back_batch(igraph_marked_queue_t *q); + +IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_as_vector(const igraph_marked_queue_t *q, + igraph_vector_t *vec); + +#endif diff --git a/src/rigraph/core/core/math.h b/src/rigraph/core/core/math.h new file mode 100644 index 0000000..c50bd13 --- /dev/null +++ b/src/rigraph/core/core/math.h @@ -0,0 +1,88 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2008-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_MATH_H +#define IGRAPH_MATH_H + +#include "igraph_decls.h" + +#include "config.h" + +#include +#include + +__BEGIN_DECLS + +/** + * \def IGRAPH_SHORTEST_PATH_EPSILON + * + * Relative error threshold used in weighted shortest path calculations + * to decide whether two shortest paths are of equal length. + */ +#define IGRAPH_SHORTEST_PATH_EPSILON 1e-10 + +/* + * Compiler-related hacks, mostly because of Microsoft Visual C++ + */ +double igraph_i_round(double X); +int igraph_i_snprintf(char *buffer, size_t count, const char *format, ...); + +double igraph_log2(const double a); +double igraph_log1p(double a); +double igraph_fmin(double a, double b); +#ifndef HAVE_LOG2 + #define log2(a) igraph_log2(a) +#endif +#ifndef HAVE_LOG1P + #define log1p(a) igraph_log1p(a) +#endif +#ifndef HAVE_FMIN + #define fmin(a,b) igraph_fmin((a),(b)) +#endif +#ifndef HAVE_ROUND + #define round igraph_i_round +#endif + +#ifndef M_PI + #define M_PI 3.14159265358979323846 +#endif +#ifndef M_PI_2 + #define M_PI_2 1.57079632679489661923 +#endif +#ifndef M_LN2 + #define M_LN2 0.69314718055994530942 +#endif +#ifndef M_SQRT2 + #define M_SQRT2 1.4142135623730950488016887 +#endif +#ifndef M_LN_SQRT_2PI + #define M_LN_SQRT_2PI 0.918938533204672741780329736406 /* log(sqrt(2*pi)) + == log(2*pi)/2 */ +#endif + +IGRAPH_PRIVATE_EXPORT int igraph_almost_equals(double a, double b, double eps); +IGRAPH_PRIVATE_EXPORT int igraph_cmp_epsilon(double a, double b, double eps); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/matrix.c b/src/rigraph/core/core/matrix.c new file mode 100644 index 0000000..80bd31e --- /dev/null +++ b/src/rigraph/core/core/matrix.c @@ -0,0 +1,158 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" +#include "igraph_matrix.h" + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "matrix.pmt" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#define BASE_INT +#include "igraph_pmt.h" +#include "matrix.pmt" +#include "igraph_pmt_off.h" +#undef BASE_INT + +#define BASE_LONG +#include "igraph_pmt.h" +#include "matrix.pmt" +#include "igraph_pmt_off.h" +#undef BASE_LONG + +#define BASE_CHAR +#include "igraph_pmt.h" +#include "matrix.pmt" +#include "igraph_pmt_off.h" +#undef BASE_CHAR + +#define BASE_BOOL +#include "igraph_pmt.h" +#include "matrix.pmt" +#include "igraph_pmt_off.h" +#undef BASE_BOOL + +#define BASE_COMPLEX +#include "igraph_pmt.h" +#include "matrix.pmt" +#include "igraph_pmt_off.h" +#undef BASE_COMPLEX + +#ifndef USING_R +int igraph_matrix_complex_print(const igraph_matrix_complex_t *m) { + + long int nr = igraph_matrix_complex_nrow(m); + long int nc = igraph_matrix_complex_ncol(m); + long int i, j; + for (i = 0; i < nr; i++) { + for (j = 0; j < nc; j++) { + igraph_complex_t z = MATRIX(*m, i, j); + if (j != 0) { + putchar(' '); + } + printf("%g%+gi", IGRAPH_REAL(z), IGRAPH_IMAG(z)); + } + printf("\n"); + } + + return 0; +} +#endif + +int igraph_matrix_complex_fprint(const igraph_matrix_complex_t *m, + FILE *file) { + + long int nr = igraph_matrix_complex_nrow(m); + long int nc = igraph_matrix_complex_ncol(m); + long int i, j; + for (i = 0; i < nr; i++) { + for (j = 0; j < nc; j++) { + igraph_complex_t z = MATRIX(*m, i, j); + if (j != 0) { + fputc(' ', file); + } + fprintf(file, "%g%+gi", IGRAPH_REAL(z), IGRAPH_IMAG(z)); + } + fprintf(file, "\n"); + } + + return 0; +} + +int igraph_matrix_complex_real(const igraph_matrix_complex_t *v, + igraph_matrix_t *real) { + long int nrow = igraph_matrix_complex_nrow(v); + long int ncol = igraph_matrix_complex_ncol(v); + IGRAPH_CHECK(igraph_matrix_resize(real, nrow, ncol)); + IGRAPH_CHECK(igraph_vector_complex_real(&v->data, &real->data)); + return 0; +} + +int igraph_matrix_complex_imag(const igraph_matrix_complex_t *v, + igraph_matrix_t *imag) { + long int nrow = igraph_matrix_complex_nrow(v); + long int ncol = igraph_matrix_complex_ncol(v); + IGRAPH_CHECK(igraph_matrix_resize(imag, nrow, ncol)); + IGRAPH_CHECK(igraph_vector_complex_imag(&v->data, &imag->data)); + return 0; +} + +int igraph_matrix_complex_realimag(const igraph_matrix_complex_t *v, + igraph_matrix_t *real, + igraph_matrix_t *imag) { + long int nrow = igraph_matrix_complex_nrow(v); + long int ncol = igraph_matrix_complex_ncol(v); + IGRAPH_CHECK(igraph_matrix_resize(real, nrow, ncol)); + IGRAPH_CHECK(igraph_matrix_resize(imag, nrow, ncol)); + IGRAPH_CHECK(igraph_vector_complex_realimag(&v->data, &real->data, + &imag->data)); + return 0; +} + +int igraph_matrix_complex_create(igraph_matrix_complex_t *v, + const igraph_matrix_t *real, + const igraph_matrix_t *imag) { + IGRAPH_CHECK(igraph_vector_complex_create(&v->data, &real->data, + &imag->data)); + return 0; +} + +int igraph_matrix_complex_create_polar(igraph_matrix_complex_t *v, + const igraph_matrix_t *r, + const igraph_matrix_t *theta) { + IGRAPH_CHECK(igraph_vector_complex_create_polar(&v->data, &r->data, + &theta->data)); + return 0; +} + +igraph_bool_t igraph_matrix_all_e_tol(const igraph_matrix_t *lhs, + const igraph_matrix_t *rhs, + igraph_real_t tol) { + return igraph_vector_e_tol(&lhs->data, &rhs->data, tol); +} + +int igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol) { + return igraph_vector_zapsmall(&m->data, tol); +} diff --git a/src/rigraph/core/core/matrix.pmt b/src/rigraph/core/core/matrix.pmt new file mode 100644 index 0000000..27071ac --- /dev/null +++ b/src/rigraph/core/core/matrix.pmt @@ -0,0 +1,1641 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_memory.h" +#include "igraph_random.h" +#include "igraph_error.h" + +#include /* memcpy & co. */ +#include + +/** + * \section about_igraph_matrix_t_objects About \type igraph_matrix_t objects + * + * This type is just an interface to \type igraph_vector_t. + * + * The \type igraph_matrix_t type usually stores n + * elements in O(n) space, but not always. See the documentation of + * the vector type. + */ + +/** + * \section igraph_matrix_constructor_and_destructor Matrix constructors and + * destructors + */ + +/** + * \ingroup matrix + * \function igraph_matrix_init + * \brief Initializes a matrix. + * + * + * Every matrix needs to be initialized before using it. This is done + * by calling this function. A matrix has to be destroyed if it is not + * needed any more; see \ref igraph_matrix_destroy(). + * \param m Pointer to a not yet initialized matrix object to be + * initialized. + * \param nrow The number of rows in the matrix. + * \param ncol The number of columns in the matrix. + * \return Error code. + * + * Time complexity: usually O(n), + * n is the + * number of elements in the matrix. + */ + +int FUNCTION(igraph_matrix, init)(TYPE(igraph_matrix) *m, long int nrow, long int ncol) { + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&m->data, nrow * ncol)); + m->nrow = nrow; + m->ncol = ncol; + return IGRAPH_SUCCESS; +} + +const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view)(const TYPE(igraph_matrix) *m, + const BASE *data, + long int nrow, + long int ncol) { + TYPE(igraph_matrix) *m2 = (TYPE(igraph_matrix)*)m; + FUNCTION(igraph_vector, view)(&m2->data, data, nrow * ncol); + m2->nrow = nrow; + m2->ncol = ncol; + return m; +} + +/** + * \ingroup matrix + * \function igraph_matrix_destroy + * \brief Destroys a matrix object. + * + * + * This function frees all the memory allocated for a matrix + * object. The destroyed object needs to be reinitialized before using + * it again. + * \param m The matrix to destroy. + * + * Time complexity: operating system dependent. + */ + +void FUNCTION(igraph_matrix, destroy)(TYPE(igraph_matrix) *m) { + FUNCTION(igraph_vector, destroy)(&m->data); +} + +/** + * \ingroup matrix + * \function igraph_matrix_capacity + * \brief Returns the number of elements allocated for a matrix. + * + * Note that this might be different from the size of the matrix (as + * queried by \ref igraph_matrix_size(), and specifies how many elements + * the matrix can hold, without reallocation. + * \param v Pointer to the (previously initialized) matrix object + * to query. + * \return The allocated capacity. + * + * \sa \ref igraph_matrix_size(), \ref igraph_matrix_nrow(), + * \ref igraph_matrix_ncol(). + * + * Time complexity: O(1). + */ + +long int FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m) { + return FUNCTION(igraph_vector, capacity)(&m->data); +} + + +/** + * \section igraph_matrix_accessing_elements Accessing elements of a matrix + */ + +/** + * \ingroup matrix + * \function igraph_matrix_resize + * \brief Resizes a matrix. + * + * + * This function resizes a matrix by adding more elements to it. + * The matrix contains arbitrary data after resizing it. + * That is, after calling this function you cannot expect that element + * (i,j) in the matrix remains the + * same as before. + * \param m Pointer to an already initialized matrix object. + * \param nrow The number of rows in the resized matrix. + * \param ncol The number of columns in the resized matrix. + * \return Error code. + * + * Time complexity: O(1) if the + * matrix gets smaller, usually O(n) + * if it gets larger, n is the + * number of elements in the resized matrix. + */ + +int FUNCTION(igraph_matrix, resize)(TYPE(igraph_matrix) *m, long int nrow, long int ncol) { + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&m->data, nrow * ncol)); + m->nrow = nrow; + m->ncol = ncol; + return 0; +} + +/** + * \ingroup matrix + * \function igraph_matrix_resize_min + * \brief Deallocates unused memory for a matrix. + * + * + * Note that this function might fail if there is not enough memory + * available. + * + * + * Also note, that this function leaves the matrix intact, i.e. + * it does not destroy any of the elements. However, usually it involves + * copying the matrix in memory. + * \param m Pointer to an initialized matrix. + * \return Error code. + * + * \sa \ref igraph_matrix_resize(). + * + * Time complexity: operating system dependent. + */ + +int FUNCTION(igraph_matrix, resize_min)(TYPE(igraph_matrix) *m) { + TYPE(igraph_vector) tmp; + long int size = FUNCTION(igraph_matrix, size)(m); + long int capacity = FUNCTION(igraph_matrix, capacity)(m); + if (size == capacity) { + return 0; + } + + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&tmp, size)); + FUNCTION(igraph_vector, update)(&tmp, &m->data); + FUNCTION(igraph_vector, destroy)(&m->data); + m->data = tmp; + + return 0; +} + + +/** + * \ingroup matrix + * \function igraph_matrix_size + * \brief The number of elements in a matrix. + * + * \param m Pointer to an initialized matrix object. + * \return The size of the matrix. + * + * Time complexity: O(1). + */ + +long int FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m) { + return (m->nrow) * (m->ncol); +} + +/** + * \ingroup matrix + * \function igraph_matrix_nrow + * \brief The number of rows in a matrix. + * + * \param m Pointer to an initialized matrix object. + * \return The number of rows in the matrix. + * + * Time complexity: O(1). + */ + +long int FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m) { + return m->nrow; +} + +/** + * \ingroup matrix + * \function igraph_matrix_ncol + * \brief The number of columns in a matrix. + * + * \param m Pointer to an initialized matrix object. + * \return The number of columns in the matrix. + * + * Time complexity: O(1). + */ + +long int FUNCTION(igraph_matrix, ncol)(const TYPE(igraph_matrix) *m) { + return m->ncol; +} + +/** + * \ingroup matrix + * \function igraph_matrix_copy_to + * \brief Copies a matrix to a regular C array. + * + * + * The matrix is copied columnwise, as this is the format most + * programs and languages use. + * The C array should be of sufficient size; there are (of course) no + * range checks. + * \param m Pointer to an initialized matrix object. + * \param to Pointer to a C array; the place to copy the data to. + * \return Error code. + * + * Time complexity: O(n), + * n is the number of + * elements in the matrix. + */ + +void FUNCTION(igraph_matrix, copy_to)(const TYPE(igraph_matrix) *m, BASE *to) { + FUNCTION(igraph_vector, copy_to)(&m->data, to); +} + +/** + * \ingroup matrix + * \function igraph_matrix_null + * \brief Sets all elements in a matrix to zero. + * + * \param m Pointer to an initialized matrix object. + * + * Time complexity: O(n), + * n is the number of elements in + * the matrix. + */ + +void FUNCTION(igraph_matrix, null)(TYPE(igraph_matrix) *m) { + FUNCTION(igraph_vector, null)(&m->data); +} + +/** + * \ingroup matrix + * \function igraph_matrix_add_cols + * \brief Adds columns to a matrix. + * \param m The matrix object. + * \param n The number of columns to add. + * \return Error code, \c IGRAPH_ENOMEM if there is + * not enough memory to perform the operation. + * + * Time complexity: linear with the number of elements of the new, + * resized matrix. + */ + +int FUNCTION(igraph_matrix, add_cols)(TYPE(igraph_matrix) *m, long int n) { + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(m, m->nrow, m->ncol + n)); + return 0; +} + +/** + * \ingroup matrix + * \function igraph_matrix_add_rows + * \brief Adds rows to a matrix. + * \param m The matrix object. + * \param n The number of rows to add. + * \return Error code, \c IGRAPH_ENOMEM if there + * isn't enough memory for the operation. + * + * Time complexity: linear with the number of elements of the new, + * resized matrix. + */ + +int FUNCTION(igraph_matrix, add_rows)(TYPE(igraph_matrix) *m, long int n) { + long int i; + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&m->data, (m->ncol) * (m->nrow + n))); + for (i = m->ncol - 1; i >= 0; i--) { + FUNCTION(igraph_vector, move_interval2)(&m->data, (m->nrow)*i, (m->nrow) * (i + 1), + (m->nrow + n)*i); + } + m->nrow += n; + return 0; +} + +/** + * \ingroup matrix + * \function igraph_matrix_remove_col + * \brief Removes a column from a matrix. + * + * \param m The matrix object. + * \param col The column to remove. + * \return Error code, always returns with success. + * + * Time complexity: linear with the number of elements of the new, + * resized matrix. + */ + +int FUNCTION(igraph_matrix, remove_col)(TYPE(igraph_matrix) *m, long int col) { + FUNCTION(igraph_vector, remove_section)(&m->data, (m->nrow)*col, (m->nrow) * (col + 1)); + m->ncol--; + return 0; +} + +/** + * \ingroup matrix + * \function igraph_matrix_permdelete_rows + * \brief Removes rows from a matrix (for internal use). + * + * Time complexity: linear with the number of elements of the original + * matrix. + */ + +int FUNCTION(igraph_matrix, permdelete_rows)(TYPE(igraph_matrix) *m, long int *index, long int nremove) { + long int i, j; + for (j = 0; j < m->nrow; j++) { + if (index[j] != 0) { + for (i = 0; i < m->ncol; i++) { + MATRIX(*m, index[j] - 1, i) = MATRIX(*m, j, i); + } + } + } + /* Remove unnecessary elements from the end of each column */ + for (i = 0; i < m->ncol; i++) + FUNCTION(igraph_vector, remove_section)(&m->data, + (i + 1) * (m->nrow - nremove), (i + 1) * (m->nrow - nremove) + nremove); + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(m, m->nrow - nremove, m->ncol)); + + return 0; +} + +/** + * \ingroup matrix + * \function igraph_matrix_delete_rows_neg + * \brief Removes columns from a matrix (for internal use). + * + * Time complexity: linear with the number of elements of the original + * matrix. + */ + +int FUNCTION(igraph_matrix, delete_rows_neg)(TYPE(igraph_matrix) *m, + const igraph_vector_t *neg, long int nremove) { + long int i, j, idx = 0; + for (i = 0; i < m->ncol; i++) { + for (j = 0; j < m->nrow; j++) { + if (VECTOR(*neg)[j] >= 0) { + MATRIX(*m, idx++, i) = MATRIX(*m, j, i); + } + } + idx = 0; + } + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(m, m->nrow - nremove, m->ncol)); + + return 0; +} + +/** + * \ingroup matrix + * \function igraph_matrix_copy + * \brief Copies a matrix. + * + * + * Creates a matrix object by copying from an existing matrix. + * \param to Pointer to an uninitialized matrix object. + * \param from The initialized matrix object to copy. + * \return Error code, \c IGRAPH_ENOMEM if there + * isn't enough memory to allocate the new matrix. + * + * Time complexity: O(n), the number + * of elements in the matrix. + */ + +int FUNCTION(igraph_matrix, copy)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) { + IGRAPH_CHECK(FUNCTION(igraph_vector, copy)(&to->data, &from->data)); + to->nrow = from->nrow; + to->ncol = from->ncol; + return IGRAPH_SUCCESS; +} + +#ifndef NOTORDERED + +/** + * \function igraph_matrix_max + * \brief Largest element of a matrix. + * + * + * If the matrix is empty, an arbitrary number is returned. + * \param m The matrix object. + * \return The maximum element of \p m, or NaN if any element is NaN. + * + * Added in version 0.2. + * + * Time complexity: O(mn), the number of elements in the matrix. + */ + +igraph_real_t FUNCTION(igraph_matrix, max)(const TYPE(igraph_matrix) *m) { + return FUNCTION(igraph_vector, max)(&m->data); +} + +#endif + +/** + * \function igraph_matrix_scale + * + * Multiplies each element of the matrix by a constant. + * \param m The matrix. + * \param by The constant. + * + * Added in version 0.2. + * + * Time complexity: O(n), the number of elements in the matrix. + */ + +void FUNCTION(igraph_matrix, scale)(TYPE(igraph_matrix) *m, BASE by) { + FUNCTION(igraph_vector, scale)(&m->data, by); +} + +/** + * \function igraph_matrix_select_rows + * \brief Select some rows of a matrix. + * + * This function selects some rows of a matrix and returns them in a + * new matrix. The result matrix should be initialized before calling + * the function. + * \param m The input matrix. + * \param res The result matrix. It should be initialized and will be + * resized as needed. + * \param rows Vector; it contains the row indices (starting with + * zero) to extract. Note that no range checking is performed. + * \return Error code. + * + * Time complexity: O(nm), n is the number of rows, m the number of + * columns of the result matrix. + */ + +int FUNCTION(igraph_matrix, select_rows)(const TYPE(igraph_matrix) *m, + TYPE(igraph_matrix) *res, + const igraph_vector_t *rows) { + long int norows = igraph_vector_size(rows); + long int i, j, ncols = FUNCTION(igraph_matrix, ncol)(m); + + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(res, norows, ncols)); + for (i = 0; i < norows; i++) { + for (j = 0; j < ncols; j++) { + MATRIX(*res, i, j) = MATRIX(*m, (long int)VECTOR(*rows)[i], j); + } + } + + return 0; +} + +/** + * \function igraph_matrix_select_rows_cols + * \brief Select some rows and columns of a matrix. + * + * This function selects some rows and columns of a matrix and returns + * them in a new matrix. The result matrix should be initialized before + * calling the function. + * \param m The input matrix. + * \param res The result matrix. It should be initialized and will be + * resized as needed. + * \param rows Vector; it contains the row indices (starting with + * zero) to extract. Note that no range checking is performed. + * \param cols Vector; it contains the column indices (starting with + * zero) to extract. Note that no range checking is performed. + * \return Error code. + * + * Time complexity: O(nm), n is the number of rows, m the number of + * columns of the result matrix. + */ + +int FUNCTION(igraph_matrix, select_rows_cols)(const TYPE(igraph_matrix) *m, + TYPE(igraph_matrix) *res, + const igraph_vector_t *rows, + const igraph_vector_t *cols) { + long int nrows = igraph_vector_size(rows); + long int ncols = igraph_vector_size(cols); + long int i, j; + + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(res, nrows, ncols)); + for (i = 0; i < nrows; i++) { + for (j = 0; j < ncols; j++) { + MATRIX(*res, i, j) = MATRIX(*m, (long int)VECTOR(*rows)[i], + (long int)VECTOR(*cols)[j]); + } + } + + return 0; +} + +/** + * \function igraph_matrix_get_col + * \brief Select a column. + * + * Extract a column of a matrix and return it as a vector. + * \param m The input matrix. + * \param res The result will we stored in this vector. It should be + * initialized and will be resized as needed. + * \param index The index of the column to select. + * \return Error code. + * + * Time complexity: O(n), the number of rows in the matrix. + */ + +int FUNCTION(igraph_matrix, get_col)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res, + long int index) { + long int nrow = FUNCTION(igraph_matrix, nrow)(m); + + if (index >= m->ncol) { + IGRAPH_ERROR("Index out of range for selecting matrix column", IGRAPH_EINVAL); + } + IGRAPH_CHECK(FUNCTION(igraph_vector, get_interval)(&m->data, res, + nrow * index, nrow * (index + 1))); + return 0; +} + +/** + * \function igraph_matrix_sum + * \brief Sum of elements. + * + * Returns the sum of the elements of a matrix. + * \param m The input matrix. + * \return The sum of the elements. + * + * Time complexity: O(mn), the number of elements in the matrix. + */ + +BASE FUNCTION(igraph_matrix, sum)(const TYPE(igraph_matrix) *m) { + return FUNCTION(igraph_vector, sum)(&m->data); +} + +/** + * \function igraph_matrix_all_e + * \brief Are all elements equal? + * + * \param lhs The first matrix. + * \param rhs The second matrix. + * \return Positive integer (=true) if the elements in the \p lhs are all + * equal to the corresponding elements in \p rhs. Returns \c 0 + * (=false) if the dimensions of the matrices don't match. + * + * Time complexity: O(nm), the size of the matrices. + */ + +igraph_bool_t FUNCTION(igraph_matrix, all_e)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs) { + return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && + FUNCTION(igraph_vector, all_e)(&lhs->data, &rhs->data); +} + +igraph_bool_t +FUNCTION(igraph_matrix, is_equal)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs) { + return FUNCTION(igraph_matrix, all_e)(lhs, rhs); +} + +#ifndef NOTORDERED + +/** + * \function igraph_matrix_all_l + * \brief Are all elements less? + * + * \param lhs The first matrix. + * \param rhs The second matrix. + * \return Positive integer (=true) if the elements in the \p lhs are all + * less than the corresponding elements in \p rhs. Returns \c 0 + * (=false) if the dimensions of the matrices don't match. + * + * Time complexity: O(nm), the size of the matrices. + */ + +igraph_bool_t FUNCTION(igraph_matrix, all_l)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs) { + return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && + FUNCTION(igraph_vector, all_l)(&lhs->data, &rhs->data); +} + +/** + * \function igraph_matrix_all_g + * \brief Are all elements greater? + * + * \param lhs The first matrix. + * \param rhs The second matrix. + * \return Positive integer (=true) if the elements in the \p lhs are all + * greater than the corresponding elements in \p rhs. Returns \c 0 + * (=false) if the dimensions of the matrices don't match. + * + * Time complexity: O(nm), the size of the matrices. + */ + +igraph_bool_t FUNCTION(igraph_matrix, all_g)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs) { + return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && + FUNCTION(igraph_vector, all_g)(&lhs->data, &rhs->data); +} + +/** + * \function igraph_matrix_all_le + * \brief Are all elements less or equal? + * + * \param lhs The first matrix. + * \param rhs The second matrix. + * \return Positive integer (=true) if the elements in the \p lhs are all + * less than or equal to the corresponding elements in \p + * rhs. Returns \c 0 (=false) if the dimensions of the matrices + * don't match. + * + * Time complexity: O(nm), the size of the matrices. + */ + +igraph_bool_t +FUNCTION(igraph_matrix, all_le)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs) { + return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && + FUNCTION(igraph_vector, all_le)(&lhs->data, &rhs->data); +} + +/** + * \function igraph_matrix_all_ge + * \brief Are all elements greater or equal? + * + * \param lhs The first matrix. + * \param rhs The second matrix. + * \return Positive integer (=true) if the elements in the \p lhs are all + * greater than or equal to the corresponding elements in \p + * rhs. Returns \c 0 (=false) if the dimensions of the matrices + * don't match. + * + * Time complexity: O(nm), the size of the matrices. + */ + +igraph_bool_t +FUNCTION(igraph_matrix, all_ge)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs) { + return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && + FUNCTION(igraph_vector, all_ge)(&lhs->data, &rhs->data); +} + +#endif + +#ifndef NOTORDERED + +/** + * \function igraph_matrix_maxdifference + * \brief Maximum absolute difference between two matrices. + * + * Calculate the maximum absolute difference of two matrices. Both matrices + * must be non-empty. If their dimensions differ then a warning is given and + * the comparison is performed by vectors columnwise from both matrices. + * The remaining elements in the larger vector are ignored. + * \param m1 The first matrix. + * \param m2 The second matrix. + * \return The element with the largest absolute value in \c m1 - \c m2. + * + * Time complexity: O(mn), the elements in the smaller matrix. + */ + +igraph_real_t FUNCTION(igraph_matrix, maxdifference)(const TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2) { + long int col1 = FUNCTION(igraph_matrix, ncol)(m1); + long int col2 = FUNCTION(igraph_matrix, ncol)(m2); + long int row1 = FUNCTION(igraph_matrix, nrow)(m1); + long int row2 = FUNCTION(igraph_matrix, nrow)(m2); + if (col1 != col2 || row1 != row2) { + IGRAPH_WARNING("Comparing non-conformant matrices"); + } + return FUNCTION(igraph_vector, maxdifference)(&m1->data, &m2->data); +} + +#endif + +/** + * \function igraph_matrix_transpose + * \brief Transpose a matrix. + * + * Calculate the transpose of a matrix. Note that the function + * reallocates the memory used for the matrix. + * \param m The input (and output) matrix. + * \return Error code. + * + * Time complexity: O(mn), the number of elements in the matrix. + */ + +int FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m) { + long int nrow = m->nrow; + long int ncol = m->ncol; + if (nrow > 1 && ncol > 1) { + TYPE(igraph_vector) newdata; + long int i, size = nrow * ncol, mod = size - 1; + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&newdata, size)); + IGRAPH_FINALLY(FUNCTION(igraph_vector, destroy), &newdata); + for (i = 0; i < size; i++) { + VECTOR(newdata)[i] = VECTOR(m->data)[ (i * nrow) % mod ]; + } + VECTOR(newdata)[size - 1] = VECTOR(m->data)[size - 1]; + FUNCTION(igraph_vector, destroy)(&m->data); + IGRAPH_FINALLY_CLEAN(1); + m->data = newdata; + } + m->nrow = ncol; + m->ncol = nrow; + + return 0; +} + +/** + * \function igraph_matrix_e + * Extract an element from a matrix. + * + * Use this if you need a function for some reason and cannot use the + * \ref MATRIX macro. Note that no range checking is performed. + * \param m The input matrix. + * \param row The row index. + * \param col The column index. + * \return The element in the given row and column. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_matrix, e)(const TYPE(igraph_matrix) *m, + long int row, long int col) { + return MATRIX(*m, row, col); +} + +/** + * \function igraph_matrix_e_ptr + * Pointer to an element of a matrix. + * + * The function returns a pointer to an element. No range checking is + * performed. + * \param m The input matrix. + * \param row The row index. + * \param col The column index. + * \return Pointer to the element in the given row and column. + * + * Time complexity: O(1). + */ + +BASE* FUNCTION(igraph_matrix, e_ptr)(const TYPE(igraph_matrix) *m, + long int row, long int col) { + return &MATRIX(*m, row, col); +} + +/** + * \function igraph_matrix_set + * Set an element. + * + * Set an element of a matrix. No range checking is performed. + * \param m The input matrix. + * \param row The row index. + * \param col The column index. + * \param value The new value of the element. + * + * Time complexity: O(1). + */ + +void FUNCTION(igraph_matrix, set)(TYPE(igraph_matrix)* m, long int row, long int col, + BASE value) { + MATRIX(*m, row, col) = value; +} + +/** + * \function igraph_matrix_fill + * Fill with an element. + * + * Set the matrix to a constant matrix. + * \param m The input matrix. + * \param e The element to set. + * + * Time complexity: O(mn), the number of elements. + */ + +void FUNCTION(igraph_matrix, fill)(TYPE(igraph_matrix) *m, BASE e) { + FUNCTION(igraph_vector, fill)(&m->data, e); +} + +/** + * \function igraph_matrix_update + * Update from another matrix. + * + * This function replicates \p from in the matrix \p to. + * Note that \p to must be already initialized. + * \param to The result matrix. + * \param from The matrix to replicate; it is left unchanged. + * \return Error code. + * + * Time complexity: O(mn), the number of elements. + */ + +int FUNCTION(igraph_matrix, update)(TYPE(igraph_matrix) *to, + const TYPE(igraph_matrix) *from) { + + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(to, from->nrow, from->ncol)); + FUNCTION(igraph_vector, update)(&to->data, &from->data); + return 0; +} + +/** + * \function igraph_matrix_rbind + * Combine two matrices rowwise. + * + * This function places the rows of \p from below the rows of \c to + * and stores the result in \p to. The number of columns in the two + * matrices must match. + * \param to The upper matrix; the result is also stored here. + * \param from The lower matrix. It is left unchanged. + * \return Error code. + * + * Time complexity: O(mn), the number of elements in the newly created + * matrix. + */ + +int FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, + const TYPE(igraph_matrix) *from) { + long int tocols = to->ncol, fromcols = from->ncol; + long int torows = to->nrow, fromrows = from->nrow; + long int offset, c, r, index, offset2; + if (tocols != fromcols) { + IGRAPH_ERROR("Cannot do rbind, number of columns do not match", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&to->data, + tocols * (fromrows + torows))); + to->nrow += fromrows; + + offset = (tocols - 1) * fromrows; + index = tocols * torows - 1; + for (c = tocols - 1; c > 0; c--) { + for (r = 0; r < torows; r++, index--) { + VECTOR(to->data)[index + offset] = VECTOR(to->data)[index]; + } + offset -= fromrows; + } + + offset = torows; offset2 = 0; + for (c = 0; c < tocols; c++) { + memcpy(VECTOR(to->data) + offset, VECTOR(from->data) + offset2, + sizeof(BASE) * (size_t) fromrows); + offset += fromrows + torows; + offset2 += fromrows; + } + return 0; +} + +/** + * \function igraph_matrix_cbind + * Combine matrices columnwise. + * + * This function places the columns of \p from on the right of \p to, + * and stores the result in \p to. + * \param to The left matrix; the result is stored here too. + * \param from The right matrix. It is left unchanged. + * \return Error code. + * + * Time complexity: O(mn), the number of elements on the new matrix. + */ + +int FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to, + const TYPE(igraph_matrix) *from) { + + long int tocols = to->ncol, fromcols = from->ncol; + long int torows = to->nrow, fromrows = from->nrow; + if (torows != fromrows) { + IGRAPH_ERROR("Cannot do rbind, number of rows do not match", IGRAPH_EINVAL); + } + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(to, torows, tocols + fromcols)); + FUNCTION(igraph_vector, copy_to)(&from->data, VECTOR(to->data) + tocols * torows); + return 0; +} + +/** + * \function igraph_matrix_swap + * Swap two matrices. + * + * The contents of the two matrices will be swapped. + * \param m1 The first matrix. + * \param m2 The second matrix. + * \return Error code. + * + * Time complexity: O(1). + */ + +int FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2) { + return FUNCTION(igraph_vector, swap)(&m1->data, &m2->data); +} + +/** + * \function igraph_matrix_get_row + * Extract a row. + * + * Extract a row from a matrix and return it as a vector. + * \param m The input matrix. + * \param res Pointer to an initialized vector; it will be resized if + * needed. + * \param index The index of the row to select. + * \return Error code. + * + * Time complexity: O(n), the number of columns in the matrix. + */ + +int FUNCTION(igraph_matrix, get_row)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res, long int index) { + long int rows = m->nrow, cols = m->ncol; + long int i, j; + + if (index >= rows) { + IGRAPH_ERROR("Index out of range for selecting matrix row", IGRAPH_EINVAL); + } + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(res, cols)); + + for (i = index, j = 0; j < cols; i += rows, j++) { + VECTOR(*res)[j] = VECTOR(m->data)[i]; + } + return 0; +} + +/** + * \function igraph_matrix_set_row + * Set a row from a vector. + * + * Sets the elements of a row with the given vector. This has the effect of + * setting row \c index to have the elements in the vector \c v. The length of + * the vector and the number of columns in the matrix must match, + * otherwise an error is triggered. + * \param m The input matrix. + * \param v The vector containing the new elements of the row. + * \param index Index of the row to set. + * \return Error code. + * + * Time complexity: O(n), the number of columns in the matrix. + */ + +int FUNCTION(igraph_matrix, set_row)(TYPE(igraph_matrix) *m, + const TYPE(igraph_vector) *v, long int index) { + long int rows = m->nrow, cols = m->ncol; + long int i, j; + + if (index >= rows) { + IGRAPH_ERROR("Index out of range for selecting matrix row", IGRAPH_EINVAL); + } + if (FUNCTION(igraph_vector, size)(v) != cols) { + IGRAPH_ERROR("Cannot set matrix row, invalid vector length", IGRAPH_EINVAL); + } + for (i = index, j = 0; j < cols; i += rows, j++) { + VECTOR(m->data)[i] = VECTOR(*v)[j]; + } + return 0; +} + +/** + * \function igraph_matrix_set_col + * Set a column from a vector. + * + * Sets the elements of a column with the given vector. In effect, column + * \c index will be set with elements from the vector \c v. The length of + * the vector and the number of rows in the matrix must match, + * otherwise an error is triggered. + * \param m The input matrix. + * \param v The vector containing the new elements of the column. + * \param index Index of the column to set. + * \return Error code. + * + * Time complexity: O(m), the number of rows in the matrix. + */ + +int FUNCTION(igraph_matrix, set_col)(TYPE(igraph_matrix) *m, + const TYPE(igraph_vector) *v, long int index) { + long int rows = m->nrow, cols = m->ncol; + long int i, j; + + if (index >= cols) { + IGRAPH_ERROR("Index out of range for setting matrix column", IGRAPH_EINVAL); + } + if (FUNCTION(igraph_vector, size)(v) != rows) { + IGRAPH_ERROR("Cannot set matrix column, invalid vector length", IGRAPH_EINVAL); + } + for (i = index * rows, j = 0; j < rows; i++, j++) { + VECTOR(m->data)[i] = VECTOR(*v)[j]; + } + return 0; +} + +/** + * \function igraph_matrix_swap_rows + * Swap two rows. + * + * Swap two rows in the matrix. + * \param m The input matrix. + * \param i The index of the first row. + * \param j The index of the second row. + * \return Error code. + * + * Time complexity: O(n), the number of columns. + */ + +int FUNCTION(igraph_matrix, swap_rows)(TYPE(igraph_matrix) *m, + long int i, long int j) { + long int ncol = m->ncol, nrow = m->nrow; + long int n = nrow * ncol; + long int index1, index2; + if (i >= nrow || j >= nrow) { + IGRAPH_ERROR("Cannot swap rows, index out of range", IGRAPH_EINVAL); + } + if (i == j) { + return 0; + } + for (index1 = i, index2 = j; index1 < n; index1 += nrow, index2 += nrow) { + BASE tmp; + tmp = VECTOR(m->data)[index1]; + VECTOR(m->data)[index1] = VECTOR(m->data)[index2]; + VECTOR(m->data)[index2] = tmp; + } + return 0; +} + +/** + * \function igraph_matrix_swap_cols + * Swap two columns. + * + * Swap two columns in the matrix. + * \param m The input matrix. + * \param i The index of the first column. + * \param j The index of the second column. + * \return Error code. + * + * Time complexity: O(m), the number of rows. + */ + +int FUNCTION(igraph_matrix, swap_cols)(TYPE(igraph_matrix) *m, + long int i, long int j) { + long int ncol = m->ncol, nrow = m->nrow; + long int k, index1, index2; + if (i >= ncol || j >= ncol) { + IGRAPH_ERROR("Cannot swap columns, index out of range", IGRAPH_EINVAL); + } + if (i == j) { + return 0; + } + for (index1 = i * nrow, index2 = j * nrow, k = 0; k < nrow; k++, index1++, index2++) { + BASE tmp = VECTOR(m->data)[index1]; + VECTOR(m->data)[index1] = VECTOR(m->data)[index2]; + VECTOR(m->data)[index2] = tmp; + } + return 0; +} + +/** + * \function igraph_matrix_add_constant + * Add a constant to every element. + * + * \param m The input matrix. + * \param plud The constant to add. + * + * Time complexity: O(mn), the number of elements. + */ + +void FUNCTION(igraph_matrix, add_constant)(TYPE(igraph_matrix) *m, BASE plus) { + FUNCTION(igraph_vector, add_constant)(&m->data, plus); +} + +/** + * \function igraph_matrix_add + * Add two matrices. + * + * Add \p m2 to \p m1, and store the result in \p m1. The dimensions of the + * matrices must match. + * \param m1 The first matrix; the result will be stored here. + * \param m2 The second matrix; it is left unchanged. + * \return Error code. + * + * Time complexity: O(mn), the number of elements. + */ + +int FUNCTION(igraph_matrix, add)(TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2) { + if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) { + IGRAPH_ERROR("Cannot add non-conformant matrices", IGRAPH_EINVAL); + } + return FUNCTION(igraph_vector, add)(&m1->data, &m2->data); +} + +/** + * \function igraph_matrix_sub + * Difference of two matrices. + * + * Subtract \p m2 from \p m1 and store the result in \p m1. + * The dimensions of the two matrices must match. + * \param m1 The first matrix; the result is stored here. + * \param m2 The second matrix; it is left unchanged. + * \return Error code. + * + * Time complexity: O(mn), the number of elements. + */ + +int FUNCTION(igraph_matrix, sub)(TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2) { + if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) { + IGRAPH_ERROR("Cannot subtract non-conformant matrices", IGRAPH_EINVAL); + } + return FUNCTION(igraph_vector, sub)(&m1->data, &m2->data); +} + +/** + * \function igraph_matrix_mul_elements + * Elementwise multiplication. + * + * Multiply \p m1 by \p m2 elementwise and store the result in \p m1. + * The dimensions of the two matrices must match. + * \param m1 The first matrix; the result is stored here. + * \param m2 The second matrix; it is left unchanged. + * \return Error code. + * + * Time complexity: O(mn), the number of elements. + */ + +int FUNCTION(igraph_matrix, mul_elements)(TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2) { + if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) { + IGRAPH_ERROR("Cannot multiply non-conformant matrices", IGRAPH_EINVAL); + } + return FUNCTION(igraph_vector, mul)(&m1->data, &m2->data); +} + +/** + * \function igraph_matrix_div_elements + * Elementwise division. + * + * Divide \p m1 by \p m2 elementwise and store the result in \p m1. + * The dimensions of the two matrices must match. + * \param m1 The dividend. The result is store here. + * \param m2 The divisor. It is left unchanged. + * \return Error code. + * + * Time complexity: O(mn), the number of elements. + */ + +int FUNCTION(igraph_matrix, div_elements)(TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2) { + if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) { + IGRAPH_ERROR("Cannot divide non-conformant matrices", IGRAPH_EINVAL); + } + return FUNCTION(igraph_vector, div)(&m1->data, &m2->data); +} + +#ifndef NOTORDERED + +/** + * \function igraph_matrix_min + * \brief Smallest element of a matrix. + * + * The matrix must be non-empty. + * \param m The input matrix. + * \return The smallest element of \p m, or NaN if any element is NaN. + * + * Time complexity: O(mn), the number of elements in the matrix. + */ + +igraph_real_t FUNCTION(igraph_matrix, min)(const TYPE(igraph_matrix) *m) { + return FUNCTION(igraph_vector, min)(&m->data); +} + +/** + * \function igraph_matrix_which_min + * \brief Indices of the smallest element. + * + * + * The matrix must be non-empty. If the smallest element is not unique, + * then the indices of the first such element are returned. If the matrix contains + * NaN values, the indices of the first NaN value are returned. + * \param m The matrix. + * \param i Pointer to a long int. The row index of the + * minimum is stored here. + * \param j Pointer to a long int. The column index of + * the minimum is stored here. + * \return Error code. + * + * Time complexity: O(mn), the number of elements. + */ + +int FUNCTION(igraph_matrix, which_min)(const TYPE(igraph_matrix) *m, + long int *i, long int *j) { + long int vmin = FUNCTION(igraph_vector, which_min)(&m->data); + *i = vmin % m->nrow; + *j = vmin / m->nrow; + return 0; +} + +/** + * \function igraph_matrix_which_max + * \brief Indices of the largest element. + * + * + * The matrix must be non-empty. If the largest element is not unique, + * then the indices of the first such element are returned. If the matrix contains + * NaN values, the indices of the first NaN value are returned. + * \param m The matrix. + * \param i Pointer to a long int. The row index of the + * maximum is stored here. + * \param j Pointer to a long int. The column index of + * the maximum is stored here. + * \return Error code. + * + * Time complexity: O(mn), the number of elements. + */ + +int FUNCTION(igraph_matrix, which_max)(const TYPE(igraph_matrix) *m, + long int *i, long int *j) { + long int vmax = FUNCTION(igraph_vector, which_max)(&m->data); + *i = vmax % m->nrow; + *j = vmax / m->nrow; + return 0; +} + +/** + * \function igraph_matrix_minmax + * \brief Minimum and maximum elements of a matrix. + * + * Handy if you want to have both the smallest and largest element of + * a matrix. The matrix is only traversed once. The matrix must be non-empty. + * If a matrix contains at least one NaN, both \c min and \c max will be NaN. + * \param m The input matrix. It must contain at least one element. + * \param min Pointer to a base type variable. The minimum is stored here. + * \param max Pointer to a base type variable. The maximum is stored here. + * \return Error code. + * + * Time complexity: O(mn), the number of elements. + */ + +int FUNCTION(igraph_matrix, minmax)(const TYPE(igraph_matrix) *m, + BASE *min, BASE *max) { + return FUNCTION(igraph_vector, minmax)(&m->data, min, max); +} + +/** + * \function igraph_matrix_which_minmax + * \brief Indices of the minimum and maximum elements + * + * + * Handy if you need the indices of the smallest and largest + * elements. The matrix is traversed only once. The matrix must be + * non-empty. If the minimum or maximum is not unique, the index + * of the first minimum or the first maximum is returned, respectively. + * If a matrix contains at least one NaN, both \c which_min and \c which_max + * will point to the first NaN value. + * \param m The input matrix. + * \param imin Pointer to a long int, the row index of + * the minimum is stored here. + * \param jmin Pointer to a long int, the column index of + * the minimum is stored here. + * \param imax Pointer to a long int, the row index of + * the maximum is stored here. + * \param jmax Pointer to a long int, the column index of + * the maximum is stored here. + * \return Error code. + * + * Time complexity: O(mn), the number of elements. + */ + +int FUNCTION(igraph_matrix, which_minmax)(const TYPE(igraph_matrix) *m, + long int *imin, long int *jmin, + long int *imax, long int *jmax) { + long int vmin, vmax; + FUNCTION(igraph_vector, which_minmax)(&m->data, &vmin, &vmax); + *imin = vmin % m->nrow; + *jmin = vmin / m->nrow; + *imax = vmax % m->nrow; + *jmax = vmax / m->nrow; + return 0; +} + +#endif + +/** + * \function igraph_matrix_isnull + * Check for a null matrix. + * + * Checks whether all elements are zero. + * \param m The input matrix. + * \return Boolean, \c TRUE is \p m contains only zeros and \c FALSE + * otherwise. + * + * Time complexity: O(mn), the number of elements. + */ + +igraph_bool_t FUNCTION(igraph_matrix, isnull)(const TYPE(igraph_matrix) *m) { + return FUNCTION(igraph_vector, isnull)(&m->data); +} + +/** + * \function igraph_matrix_empty + * Check for an empty matrix. + * + * It is possible to have a matrix with zero rows or zero columns, or + * even both. This functions checks for these. + * \param m The input matrix. + * \return Boolean, \c TRUE if the matrix contains zero elements, and + * \c FALSE otherwise. + * + * Time complexity: O(1). + */ + +igraph_bool_t FUNCTION(igraph_matrix, empty)(const TYPE(igraph_matrix) *m) { + return FUNCTION(igraph_vector, empty)(&m->data); +} + +/** + * \function igraph_matrix_is_symmetric + * Check for symmetric matrix. + * + * A non-square matrix is not symmetric by definition. + * \param m The input matrix. + * \return Boolean, \c TRUE if the matrix is square and symmetric, \c + * FALSE otherwise. + * + * Time complexity: O(mn), the number of elements. O(1) for non-square + * matrices. + */ + +igraph_bool_t FUNCTION(igraph_matrix, is_symmetric)(const TYPE(igraph_matrix) *m) { + + long int n = m->nrow; + long int r, c; + if (m->ncol != n) { + return 0; + } + for (r = 1; r < n; r++) { + for (c = 0; c < r; c++) { + BASE a1 = MATRIX(*m, r, c); + BASE a2 = MATRIX(*m, c, r); +#ifdef EQ + if (!EQ(a1, a2)) { + return 0; + } +#else + if (a1 != a2) { + return 0; + } +#endif + } + } + return 1; +} + +/** + * \function igraph_matrix_prod + * Product of the elements. + * + * Note this function can result in overflow easily, even for not too + * big matrices. + * \param m The input matrix. + * \return The product of the elements. + * + * Time complexity: O(mn), the number of elements. + */ + +BASE FUNCTION(igraph_matrix, prod)(const TYPE(igraph_matrix) *m) { + return FUNCTION(igraph_vector, prod)(&m->data); +} + +/** + * \function igraph_matrix_rowsum + * Rowwise sum. + * + * Calculate the sum of the elements in each row. + * \param m The input matrix. + * \param res Pointer to an initialized vector; the result is stored + * here. It will be resized if necessary. + * \return Error code. + * + * Time complexity: O(mn), the number of elements in the matrix. + */ + +int FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res) { + long int nrow = m->nrow, ncol = m->ncol; + long int r, c; + BASE sum; + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(res, nrow)); + for (r = 0; r < nrow; r++) { + sum = ZERO; + for (c = 0; c < ncol; c++) { +#ifdef SUM + SUM(sum, sum, MATRIX(*m, r, c)); +#else + sum += MATRIX(*m, r, c); +#endif + } + VECTOR(*res)[r] = sum; + } + return 0; +} + +/** + * \function igraph_matrix_colsum + * Columnwise sum. + * + * Calculate the sum of the elements in each column. + * \param m The input matrix. + * \param res Pointer to an initialized vector; the result is stored + * here. It will be resized if necessary. + * \return Error code. + * + * Time complexity: O(mn), the number of elements in the matrix. + */ + +int FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res) { + long int nrow = m->nrow, ncol = m->ncol; + long int r, c; + BASE sum; + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(res, ncol)); + for (c = 0; c < ncol; c++) { + sum = ZERO; + for (r = 0; r < nrow; r++) { +#ifdef SUM + SUM(sum, sum, MATRIX(*m, r, c)); +#else + sum += MATRIX(*m, r, c); +#endif + } + VECTOR(*res)[c] = sum; + } + return 0; +} + +/** + * \function igraph_matrix_contains + * Search for an element. + * + * Search for the given element in the matrix. + * \param m The input matrix. + * \param e The element to search for. + * \return Boolean, \c TRUE if the matrix contains \p e, \c FALSE + * otherwise. + * + * Time complexity: O(mn), the number of elements. + */ + +igraph_bool_t FUNCTION(igraph_matrix, contains)(const TYPE(igraph_matrix) *m, + BASE e) { + return FUNCTION(igraph_vector, contains)(&m->data, e); +} + +/** + * \function igraph_matrix_search + * Search from a given position. + * + * Search for an element in a matrix and start the search from the + * given position. The search is performed columnwise. + * \param m The input matrix. + * \param from The position to search from, the positions are + * enumerated columnwise. + * \param what The element to search for. + * \param pos Pointer to a long int. If the element is + * found, then this is set to the position of its first appearance. + * \param row Pointer to a long int. If the element is + * found, then this is set to its row index. + * \param col Pointer to a long int. If the element is + * found, then this is set to its column index. + * \return Boolean, \c TRUE if the element is found, \c FALSE + * otherwise. + * + * Time complexity: O(mn), the number of elements. + */ + +igraph_bool_t FUNCTION(igraph_matrix, search)(const TYPE(igraph_matrix) *m, + long int from, BASE what, + long int *pos, + long int *row, long int *col) { + igraph_bool_t find = FUNCTION(igraph_vector, search)(&m->data, from, what, pos); + if (find) { + *row = *pos % m->nrow; + *col = *pos / m->nrow; + } + return find; +} + +/** + * \function igraph_matrix_remove_row + * Remove a row. + * + * A row is removed from the matrix. + * \param m The input matrix. + * \param row The index of the row to remove. + * \return Error code. + * + * Time complexity: O(mn), the number of elements in the matrix. + */ + +int FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, long int row) { + + long int c, r, index = row + 1, leap = 1, n = m->nrow * m->ncol; + if (row >= m->nrow) { + IGRAPH_ERROR("Cannot remove row, index out of range", IGRAPH_EINVAL); + } + + for (c = 0; c < m->ncol; c++) { + for (r = 0; r < m->nrow - 1 && index < n; r++) { + VECTOR(m->data)[index - leap] = VECTOR(m->data)[index]; + index++; + } + leap++; + index++; + } + m->nrow--; + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&m->data, m->nrow * m->ncol)); + return 0; +} + +/** + * \function igraph_matrix_select_cols + * \brief Select some columns of a matrix. + * + * This function selects some columns of a matrix and returns them in a + * new matrix. The result matrix should be initialized before calling + * the function. + * \param m The input matrix. + * \param res The result matrix. It should be initialized and will be + * resized as needed. + * \param cols Vector; it contains the column indices (starting with + * zero) to extract. Note that no range checking is performed. + * \return Error code. + * + * Time complexity: O(nm), n is the number of rows, m the number of + * columns of the result matrix. + */ + +int FUNCTION(igraph_matrix, select_cols)(const TYPE(igraph_matrix) *m, + TYPE(igraph_matrix) *res, + const igraph_vector_t *cols) { + long int ncols = igraph_vector_size(cols); + long int nrows = m->nrow; + long int i, j; + + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(res, nrows, ncols)); + for (i = 0; i < nrows; i++) { + for (j = 0; j < ncols; j++) { + MATRIX(*res, i, j) = MATRIX(*m, i, (long int)VECTOR(*cols)[j]); + } + } + return 0; +} + +#ifdef OUT_FORMAT + +#ifndef USING_R +int FUNCTION(igraph_matrix, print)(const TYPE(igraph_matrix) *m) { + + long int nr = FUNCTION(igraph_matrix, nrow)(m); + long int nc = FUNCTION(igraph_matrix, ncol)(m); + long int i, j; + for (i = 0; i < nr; i++) { + for (j = 0; j < nc; j++) { + if (j != 0) { + putchar(' '); + } + printf(OUT_FORMAT, MATRIX(*m, i, j)); + } + printf("\n"); + } + + return 0; +} + +int FUNCTION(igraph_matrix, printf)(const TYPE(igraph_matrix) *m, + const char *format) { + long int nr = FUNCTION(igraph_matrix, nrow)(m); + long int nc = FUNCTION(igraph_matrix, ncol)(m); + long int i, j; + for (i = 0; i < nr; i++) { + for (j = 0; j < nc; j++) { + if (j != 0) { + putchar(' '); + } + printf(format, MATRIX(*m, i, j)); + } + printf("\n"); + } + + return 0; +} + +#endif + +int FUNCTION(igraph_matrix, fprint)(const TYPE(igraph_matrix) *m, + FILE *file) { + + long int nr = FUNCTION(igraph_matrix, nrow)(m); + long int nc = FUNCTION(igraph_matrix, ncol)(m); + long int i, j; + for (i = 0; i < nr; i++) { + for (j = 0; j < nc; j++) { + if (j != 0) { + fputc(' ', file); + } + fprintf(file, OUT_FORMAT, MATRIX(*m, i, j)); + } + fprintf(file, "\n"); + } + + return 0; +} + +#endif diff --git a/src/rigraph/core/core/memory.c b/src/rigraph/core/core/memory.c new file mode 100644 index 0000000..a59afe7 --- /dev/null +++ b/src/rigraph/core/core/memory.c @@ -0,0 +1,73 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_memory.h" + +/** + * \function igraph_free + * \brief Deallocate memory that was allocated by igraph functions. + * + * Some igraph functions return a pointer vector (igraph_vector_ptr_t) + * containing pointers to other igraph or other data types. These data + * types are dynamically allocated and have to be deallocated + * manually when the user does not need them any more. This can be done + * by calling igraph_free on them. + * + * + * Here is a complete example on how to use \c igraph_free properly. + * + * \example examples/simple/igraph_free.c + * + * \param p Pointer to the piece of memory to be deallocated. + * \return Error code, currently always zero, meaning success. + * + * Time complexity: platform dependent, ideally it should be O(1). + * + * \sa \ref igraph_malloc() + */ + +void igraph_free(void *p) { + IGRAPH_FREE(p); +} + + +/** + * \function igraph_malloc + * \brief Allocate memory that can be safely deallocated by igraph functions. + * + * Some igraph functions, such as \ref igraph_vector_ptr_free_all() and + * \ref igraph_vector_ptr_destroy_all() can free memory that may have been + * allocated by the user. \c igraph_malloc() works exactly like \c malloc() + * from the C standard library, but it is guaranteed that it can be safely + * paired with the \c free() function used by igraph internally (which is + * also user-accessible through \ref igraph_free()). + * + * \param n Number of bytes to be allocated. + * \return Pointer to the piece of allocated memory. + * + * \sa \ref igraph_free() + */ + +void *igraph_malloc(size_t n) { + return malloc(n); +} diff --git a/src/rigraph/core/core/printing.c b/src/rigraph/core/core/printing.c new file mode 100644 index 0000000..7399973 --- /dev/null +++ b/src/rigraph/core/core/printing.c @@ -0,0 +1,146 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" + +#include + +#ifdef _MSC_VER + #define snprintf _snprintf +#endif + +#ifdef DBL_DIG + /* Use DBL_DIG to determine the maximum precision used for %g */ + #define STRINGIFY_HELPER(x) #x + #define STRINGIFY(x) STRINGIFY_HELPER(x) + #define IGRAPH_REAL_PRINTF_PRECISE_FORMAT "%." STRINGIFY(DBL_DIG) "g" +#else + /* Assume a precision of 10 digits for %g */ + #define IGRAPH_REAL_PRINTF_PRECISE_FORMAT "%.10g" +#endif + +#ifndef USING_R +int igraph_real_printf(igraph_real_t val) { + if (igraph_finite(val)) { + return printf("%g", val); + } else if (igraph_is_nan(val)) { + return printf("NaN"); + } else if (igraph_is_inf(val)) { + if (val < 0) { + return printf("-Inf"); + } else { + return printf("Inf"); + } + } else { + /* fallback */ + return printf("%g", val); + } +} +#endif + +int igraph_real_fprintf(FILE *file, igraph_real_t val) { + if (igraph_finite(val)) { + return fprintf(file, "%g", val); + } else if (igraph_is_nan(val)) { + return fprintf(file, "NaN"); + } else if (igraph_is_inf(val)) { + if (val < 0) { + return fprintf(file, "-Inf"); + } else { + return fprintf(file, "Inf"); + } + } else { + /* fallback */ + return fprintf(file, "%g", val); + } +} + +int igraph_real_snprintf(char* str, size_t size, igraph_real_t val) { + if (igraph_finite(val)) { + return snprintf(str, size, "%g", val); + } else if (igraph_is_nan(val)) { + return snprintf(str, size, "NaN"); + } else if (igraph_is_inf(val)) { + if (val < 0) { + return snprintf(str, size, "-Inf"); + } else { + return snprintf(str, size, "Inf"); + } + } else { + /* fallback */ + return snprintf(str, size, "%g", val); + } +} + +#ifndef USING_R +int igraph_real_printf_precise(igraph_real_t val) { + if (igraph_finite(val)) { + return printf(IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); + } else if (igraph_is_nan(val)) { + return printf("NaN"); + } else if (igraph_is_inf(val)) { + if (val < 0) { + return printf("-Inf"); + } else { + return printf("Inf"); + } + } else { + /* fallback */ + return printf(IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); + } +} +#endif + +int igraph_real_fprintf_precise(FILE *file, igraph_real_t val) { + if (igraph_finite(val)) { + return fprintf(file, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); + } else if (igraph_is_nan(val)) { + return fprintf(file, "NaN"); + } else if (igraph_is_inf(val)) { + if (val < 0) { + return fprintf(file, "-Inf"); + } else { + return fprintf(file, "Inf"); + } + } else { + /* fallback */ + return fprintf(file, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); + } +} + +int igraph_real_snprintf_precise(char* str, size_t size, igraph_real_t val) { + if (igraph_finite(val)) { + return snprintf(str, size, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); + } else if (igraph_is_nan(val)) { + return snprintf(str, size, "NaN"); + } else if (igraph_is_inf(val)) { + if (val < 0) { + return snprintf(str, size, "-Inf"); + } else { + return snprintf(str, size, "Inf"); + } + } else { + /* fallback */ + return snprintf(str, size, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); + } +} diff --git a/src/rigraph/core/core/progress.c b/src/rigraph/core/core/progress.c new file mode 100644 index 0000000..77d0e57 --- /dev/null +++ b/src/rigraph/core/core/progress.c @@ -0,0 +1,154 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_progress.h" + +#include "config.h" + +static igraph_progress_handler_t *igraph_i_progress_handler = 0; +static char igraph_i_progressmsg_buffer[1000]; + +/** + * \function igraph_progress + * Report progress + * + * Note that the usual way to report progress is the \ref IGRAPH_PROGRESS + * macro, as that takes care of the return value of the progress + * handler. + * \param message A string describing the function or algorithm + * that is reporting the progress. Current igraph functions + * always use the name \p message argument if reporting from the + * same function. + * \param percent Numeric, the percentage that was completed by the + * algorithm or function. + * \param data User-defined data. Current igraph functions that + * report progress pass a null pointer here. Users can + * write their own progress handlers and functions with progress + * reporting, and then pass some meaningfull context here. + * \return If there is a progress handler installed and + * it does not return \c IGRAPH_SUCCESS, then \c IGRAPH_INTERRUPTED + * is returned. + * + * Time complexity: O(1). + */ + +int igraph_progress(const char *message, igraph_real_t percent, void *data) { + if (igraph_i_progress_handler) { + if (igraph_i_progress_handler(message, percent, data) != IGRAPH_SUCCESS) { + return IGRAPH_INTERRUPTED; + } + } + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_progressf + * Report progress, printf-like version + * + * This is a more flexible version of \ref igraph_progress(), with + * a printf-like template string. First the template string + * is filled with the additional arguments and then \ref + * igraph_progress() is called. + * + * Note that there is an upper limit for the length of + * the \p message string, currently 1000 characters. + * \param message A string describing the function or algorithm + * that is reporting the progress. For this function this is a + * template string, using the same syntax as the standard + * \c libc \c printf function. + * \param percent Numeric, the percentage that was completed by the + * algorithm or function. + * \param data User-defined data. Current igraph functions that + * report progress pass a null pointer here. Users can + * write their own progress handlers and functions with progress + * reporting, and then pass some meaningfull context here. + * \param ... Additional argument that were specified in the + * \p message argument. + * \return If there is a progress handler installed and + * it does not return \c IGRAPH_SUCCESS, then \c IGRAPH_INTERRUPTED + * is returned. + * \return + */ + +int igraph_progressf(const char *message, igraph_real_t percent, void *data, + ...) { + va_list ap; + va_start(ap, data); + vsnprintf(igraph_i_progressmsg_buffer, + sizeof(igraph_i_progressmsg_buffer) / sizeof(char), message, ap); + return igraph_progress(igraph_i_progressmsg_buffer, percent, data); +} + +#ifndef USING_R + +/** + * \function igraph_progress_handler_stderr + * \brief A simple predefined progress handler. + * + * This simple progress handler first prints \p message, and then + * the percentage complete value in a short message to standard error. + * \param message A string describing the function or algorithm + * that is reporting the progress. Current igraph functions + * always use the same \p message argument if reporting from the + * same function. + * \param percent Numeric, the percentage that was completed by the + * algorithm or function. + * \param data User-defined data. Current igraph functions that + * report progress pass a null pointer here. Users can + * write their own progress handlers and functions with progress + * reporting, and then pass some meaningfull context here. + * \return This function always returns with \c IGRAPH_SUCCESS. + * + * Time complexity: O(1). + */ + +int igraph_progress_handler_stderr(const char *message, igraph_real_t percent, + void* data) { + IGRAPH_UNUSED(data); + fputs(message, stderr); + fprintf(stderr, "%.1f percent ready.\n", percent); + return IGRAPH_SUCCESS; +} +#endif + +/** + * \function igraph_set_progress_handler + * \brief Install a progress handler, or remove the current handler. + * + * There is a single simple predefined progress handler: + * \ref igraph_progress_handler_stderr(). + * \param new_handler Pointer to a function of type + * \ref igraph_progress_handler_t, the progress handler function to + * install. To uninstall the current progress handler, this argument + * can be a null pointer. + * \return Pointer to the previously installed progress handler function. + * + * Time complexity: O(1). + */ + +igraph_progress_handler_t * +igraph_set_progress_handler(igraph_progress_handler_t new_handler) { + igraph_progress_handler_t *previous_handler = igraph_i_progress_handler; + igraph_i_progress_handler = new_handler; + return previous_handler; +} diff --git a/src/rigraph/core/core/psumtree.c b/src/rigraph/core/core/psumtree.c new file mode 100644 index 0000000..b61f0d6 --- /dev/null +++ b/src/rigraph/core/core/psumtree.c @@ -0,0 +1,253 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + Copyright (C) 2006 Elliot Paquette + Kalamazoo College, 1200 Academy st, Kalamazoo, MI + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" +#include "igraph_psumtree.h" +#include "igraph_error.h" + +#include + +static double igraph_i_log2(double f) { + return log(f) / log(2.0); +} + +/** + * \ingroup psumtree + * \section igraph_psumtree + * + * The \type igraph_psumtree_t data type represents a partial prefix sum + * tree. A partial prefix sum tree is a data structure that can be used to draw + * samples from a discrete probability distribution with dynamic probabilities + * that are updated frequently. This is achieved by creating a binary tree where + * the leaves are the items. Each leaf contains the probability corresponding to + * the items. Intermediate nodes of the tree always contain the sum of its two + * children. When the value of a leaf node is updated, the values of its + * ancestors are also updated accordingly. + * + * Samples can be drawn from the probability distribution represented by + * the tree by generating a uniform random number between 0 (inclusive) and the + * value of the root of the tree (exclusive), and then following the branches + * of the tree as follows. In each step, the value in the current node is + * compared with the generated number. If the value in the node is larger, + * the left branch of the tree is taken; otherwise the generated number is + * decreased by the value in the node and the right branch of the tree is + * taken, until a leaf node is reached. + * + * Note that the sampling process works only if all the values in the tree + * are non-negative. This is enforced by the object; in particular, trying to + * set a negative value for an item will produce an igraph error. + */ + +/* + * Internally, a partial prefix sum tree is stored in a contiguous chunk of + * memory which we treat as a vector v. The first part (0,...,offset - 1) of + * the vector v contains the prefixes of the values contained in the latter part + * (offset, offset + size - 1) of vector v. + * + * More precisely: the part between (offset, offset + size - 1) of vector v + * contains the values (not necessarily probabilities) corresponding to the + * individual items. For the part in front of it, it holds that the value at + * index i (zero-based) is the sum of values at index (2*i + 1) and index + * (2*i + 2). The item at index zero contains the sum of all values in the + * slice between (offset, offset + size - 1). + */ + +/** + * \ingroup psumtree + * \function igraph_psumtree_init + * \brief Initializes a partial prefix sum tree. + * + * + * The tree is initialized with a fixed number of elements. After initialization, + * the value corresponding to each element is zero. + * + * \param t The tree to initialize + * \param size The number of elements in the tree + * \return Error code, typically \c IGRAPH_ENOMEM if there is not enough memory + * + * Time complexity: O(n) for a tree containing n elements + */ +int igraph_psumtree_init(igraph_psumtree_t *t, long int size) { + t->size = size; + t->offset = (long int) (pow(2, ceil(igraph_i_log2(size))) - 1); + IGRAPH_CHECK(igraph_vector_init(&t->v, t->offset + t->size)); + return 0; +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_reset + * \brief Resets all the values in the tree to zero. + * + * \param t The tree to reset. + */ +void igraph_psumtree_reset(igraph_psumtree_t *t) { + igraph_vector_fill(&(t->v), 0); +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_destroy + * \brief Destroys a partial prefix sum tree. + * + * + * All partial prefix sum trees initialized by \ref igraph_psumtree_init() + * should be properly destroyed by this function. A destroyed tree needs to be + * reinitialized by \ref igraph_psumtree_init() if you want to use it again. + * + * \param t Pointer to the (previously initialized) tree to destroy. + * + * Time complexity: operating system dependent. + */ +void igraph_psumtree_destroy(igraph_psumtree_t *t) { + igraph_vector_destroy(&(t->v)); +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_get + * \brief Retrieves the value corresponding to an item in the tree. + * + * + * + * \param t The tree to query. + * \param idx The index of the item whose value is to be retrieved. + * \return The value corresponding to the item with the given index. + * + * Time complexity: O(1) + */ +igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, long int idx) { + const igraph_vector_t *tree = &t->v; + return VECTOR(*tree)[t->offset + idx]; +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_search + * \brief Finds an item in the tree, given a value. + * + * This function finds the item with the lowest index where it holds that the + * sum of all the items with a \em lower index is less than or equal to the given + * value and that the sum of all the items with a lower index plus the item + * itself is larger than the given value. + * + * + * If you think about the partial prefix sum tree as a tool to sample from a + * discrete probability distribution, then calling this function repeatedly + * with uniformly distributed random numbers in the range 0 (inclusive) to the + * sum of all values in the tree (exclusive) will sample the items in the tree + * with a probability that is proportional to their associated values. + * + * \param t The tree to query. + * \param idx The index of the item is returned here. + * \param search The value to use for the search. + * \return Error code; currently the search always succeeds. + * + * Time complexity: O(log n), where n is the number of items in the tree. + */ +int igraph_psumtree_search(const igraph_psumtree_t *t, long int *idx, + igraph_real_t search) { + const igraph_vector_t *tree = &t->v; + long int i = 1; + long int size = igraph_vector_size(tree); + + while ( 2 * i + 1 <= size) { + if ( search <= VECTOR(*tree)[i * 2 - 1] ) { + i <<= 1; + } else { + search -= VECTOR(*tree)[i * 2 - 1]; + i <<= 1; + i += 1; + } + } + if (2 * i <= size) { + i = 2 * i; + } + + *idx = i - t->offset - 1; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_update + * \brief Updates the value associated to an item in the tree. + * + * \param t The tree to query. + * \param idx The index of the item to update. + * \param new_value The new value of the item. + * \return Error code, \c IGRAPH_EINVAL if the new value is negative or NaN, + * \c IGRAPH_SUCCESS if the operation was successful. + * + * Time complexity: O(log n), where n is the number of items in the tree. + */ +int igraph_psumtree_update(igraph_psumtree_t *t, long int idx, + igraph_real_t new_value) { + const igraph_vector_t *tree = &t->v; + igraph_real_t difference; + + if (new_value >= 0) { + idx = idx + t->offset + 1; + difference = new_value - VECTOR(*tree)[idx - 1]; + + while ( idx >= 1 ) { + VECTOR(*tree)[idx - 1] += difference; + idx >>= 1; + } + + return IGRAPH_SUCCESS; + } else { + /* caters for negative values and NaN */ + return IGRAPH_EINVAL; + } +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_size + * \brief Returns the size of the tree. + * + * \param t The tree object + * \return The number of discrete items in the tree. + * + * Time complexity: O(1). + */ +long int igraph_psumtree_size(const igraph_psumtree_t *t) { + return t->size; +} + +/** + * \ingroup psumtree + * \function igraph_psumtree_sum + * \brief Returns the sum of the values of the leaves in the tree. + * + * \param t The tree object + * \return The sum of the values of the leaves in the tree. + * + * Time complexity: O(1). + */ +igraph_real_t igraph_psumtree_sum(const igraph_psumtree_t *t) { + return VECTOR(t->v)[0]; +} diff --git a/src/rigraph/core/core/set.c b/src/rigraph/core/core/set.c new file mode 100644 index 0000000..cb0a0c1 --- /dev/null +++ b/src/rigraph/core/core/set.c @@ -0,0 +1,320 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" + +#include "set.h" + +#include /* memmove */ + +#define SET(s) ((s).stor_begin) + +/** + * \ingroup set + * \function igraph_set_init + * \brief Initializes a set. + * + * \param set pointer to the set to be initialized + * \param size the expected number of elements in the set + * + * \return error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: operating system dependent, should be around + * O(n), n is the expected size of the set. + */ +int igraph_set_init(igraph_set_t *set, int long size) { + long int alloc_size; + + if (size < 0) { + size = 0; + } + alloc_size = size > 0 ? size : 1; + set->stor_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); + set->stor_end = set->stor_begin + alloc_size; + set->end = set->stor_begin; + + return 0; +} + +/** + * \ingroup set + * \function igraph_set_destroy + * \brief Destroys a set object. + * + * \param set pointer to the set to be destroyed + * + * Time complexity: operating system dependent. + */ +void igraph_set_destroy(igraph_set_t* set) { + IGRAPH_ASSERT(set != 0); + if (set->stor_begin != 0) { + IGRAPH_FREE(set->stor_begin); + set->stor_begin = NULL; + } +} + +/** + * \ingroup set + * \function igraph_set_inited + * \brief Determines whether a set is initialized or not. + * + * This function checks whether the internal storage for the members of the + * set has been allocated or not, and it assumes that the pointer for the + * internal storage area contains \c NULL if the area is not initialized yet. + * This only applies if you have allocated an array of sets with \c IGRAPH_CALLOC or + * if you used the \c IGRAPH_SET_NULL constant to initialize the set. + * + * \param set The set object. + * + * Time complexity: O(1) + */ +igraph_bool_t igraph_set_inited(igraph_set_t* set) { + return (set->stor_begin != 0); +} + +/** + * \ingroup set + * \function igraph_set_reserve + * \brief Reserve memory for a set. + * + * \param set The set object. + * \param size the new \em allocated size of the set. + * + * Time complexity: operating system dependent, should be around + * O(n), n is the new allocated size of the set. + */ +int igraph_set_reserve(igraph_set_t* set, long int size) { + long int actual_size = igraph_set_size(set); + igraph_integer_t *tmp; + IGRAPH_ASSERT(set != NULL); + IGRAPH_ASSERT(set->stor_begin != NULL); + if (size <= actual_size) { + return 0; + } + + tmp = IGRAPH_REALLOC(set->stor_begin, (size_t) size, igraph_integer_t); + if (tmp == 0) { + IGRAPH_ERROR("cannot reserve space for set", IGRAPH_ENOMEM); + } + set->stor_begin = tmp; + set->stor_end = set->stor_begin + size; + set->end = set->stor_begin + actual_size; + + return 0; +} + +/** + * \ingroup set + * \function igraph_set_empty + * \brief Decides whether the size of the set is zero. + * + * \param set The set object. + * \return Non-zero number if the size of the set is not zero and + * zero otherwise. + * + * Time complexity: O(1). + */ +igraph_bool_t igraph_set_empty(const igraph_set_t* set) { + IGRAPH_ASSERT(set != NULL); + IGRAPH_ASSERT(set->stor_begin != NULL); + return set->stor_begin == set->end; +} + +/** + * \ingroup set + * \function igraph_set_clear + * \brief Removes all elements from a set. + * + * + * This function simply sets the size of the set to zero, it does + * not free any allocated memory. For that you have to call + * \ref igraph_set_destroy(). + * \param v The set object. + * + * Time complexity: O(1). + */ +void igraph_set_clear(igraph_set_t* set) { + IGRAPH_ASSERT(set != NULL); + IGRAPH_ASSERT(set->stor_begin != NULL); + set->end = set->stor_begin; +} + + +/** + * \ingroup set + * \function igraph_set_size + * \brief Gives the size (=length) of the set. + * + * \param v The set object + * \return The size of the set. + * + * Time complexity: O(1). + */ + +long int igraph_set_size(const igraph_set_t* set) { + IGRAPH_ASSERT(set != NULL); + IGRAPH_ASSERT(set->stor_begin != NULL); + return set->end - set->stor_begin; +} + + +/** + * \ingroup set + * \function igraph_set_add + * \brief Adds an element to the set. + * + * \param set The set object. + * \param e The element to be added. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory. + * + * Time complexity: O(log(n)), n is the number of elements in \p set. + */ +int igraph_set_add(igraph_set_t* set, igraph_integer_t e) { + long int left, right, middle; + long int size; + IGRAPH_ASSERT(set != NULL); + IGRAPH_ASSERT(set->stor_begin != NULL); + + size = igraph_set_size(set); + + /* search where to insert the new element */ + left = 0; + right = size - 1; + while (left < right - 1) { + middle = (left + right) / 2; + if (SET(*set)[middle] > e) { + right = middle; + } else if (SET(*set)[middle] < e) { + left = middle; + } else { + left = middle; + break; + } + } + + if (right >= 0 && SET(*set)[left] != e && SET(*set)[right] == e) { + left = right; + } + + while (left < size && set->stor_begin[left] < e) { + left++; + } + if (left >= size || set->stor_begin[left] != e) { + /* full, allocate more storage */ + if (set->stor_end == set->end) { + long int new_size = size * 2; + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(igraph_set_reserve(set, new_size)); + } + + /* Element should be inserted at position 'left' */ + if (left < size) + memmove(set->stor_begin + left + 1, set->stor_begin + left, + (size_t) (size - left)*sizeof(set->stor_begin[0])); + + set->stor_begin[left] = e; + set->end += 1; + } + + return 0; +} + +/** + * \ingroup set + * \function igraph_set_contains + * \brief Checks whether a given element is in the set or not. + * + * \param set The set object. + * \param e The element being sought. + * \return Positive integer (true) if \p e is found, zero (false) otherwise. + * + * Time complexity: O(log(n)), n is the number of elements in \p set. + */ +igraph_bool_t igraph_set_contains(igraph_set_t* set, igraph_integer_t e) { + long int left, right, middle; + + IGRAPH_ASSERT(set != NULL); + IGRAPH_ASSERT(set->stor_begin != NULL); + + left = 0; + right = igraph_set_size(set) - 1; + + if (right == -1) { + return 0; /* the set is empty */ + } + + /* search for the new element */ + while (left < right - 1) { + middle = (left + right) / 2; + if (SET(*set)[middle] > e) { + right = middle; + } else if (SET(*set)[middle] < e) { + left = middle; + } else { + return 1; + } + } + + return SET(*set)[left] == e || SET(*set)[right] == e; +} + +/** + * \ingroup set + * \function igraph_set_iterate + * \brief Iterates through the element to the set. + * + * Elements are returned in an arbitrary order. + * + * \param set The set object. + * \param state Internal state of the iteration. + * This should be a pointer to a \c long variable + * which must be zero for the first invocation. + * The object should not be adjusted and its value should + * not be used for anything during the iteration. + * \param element The next element or \c NULL (if the iteration + * has ended) is returned here. + * + * \return Nonzero if there are more elements, zero otherwise. + */ +igraph_bool_t igraph_set_iterate(igraph_set_t* set, long int* state, + igraph_integer_t* element) { + IGRAPH_ASSERT(set != 0); + IGRAPH_ASSERT(set->stor_begin != 0); + IGRAPH_ASSERT(state != 0); + IGRAPH_ASSERT(element != 0); + + if (*state < igraph_set_size(set)) { + *element = set->stor_begin[*state]; + *state = *state + 1; + return 1; + } else { + *element = 0; + return 0; + } +} diff --git a/src/rigraph/core/core/set.h b/src/rigraph/core/core/set.h new file mode 100644 index 0000000..95697c0 --- /dev/null +++ b/src/rigraph/core/core/set.h @@ -0,0 +1,65 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_CORE_SET_H +#define IGRAPH_CORE_SET_H + +#include "igraph_decls.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Flexible set */ +/* -------------------------------------------------- */ + +/** + * Set containing integer numbers regardless of the order + * \ingroup types + */ + +typedef struct s_set { + igraph_integer_t* stor_begin; + igraph_integer_t* stor_end; + igraph_integer_t* end; +} igraph_set_t; + +#define IGRAPH_SET_NULL { 0,0,0 } +#define IGRAPH_SET_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_set_init(v, size)); \ + IGRAPH_FINALLY(igraph_set_destroy, v); } while (0) + +IGRAPH_PRIVATE_EXPORT int igraph_set_init(igraph_set_t* set, long int size); +IGRAPH_PRIVATE_EXPORT void igraph_set_destroy(igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_set_inited(igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT int igraph_set_reserve(igraph_set_t* set, long int size); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_set_empty(const igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT void igraph_set_clear(igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT long int igraph_set_size(const igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT int igraph_set_add(igraph_set_t* v, igraph_integer_t e); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_set_contains(igraph_set_t* set, igraph_integer_t e); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_set_iterate(igraph_set_t* set, long int* state, + igraph_integer_t* element); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/sparsemat.c b/src/rigraph/core/core/sparsemat.c new file mode 100644 index 0000000..fe5e34a --- /dev/null +++ b/src/rigraph/core/core/sparsemat.c @@ -0,0 +1,3273 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "../../vendor/cs/cs.h" + +#include "igraph_sparsemat.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_constructors.h" +#include "igraph_memory.h" +#include "igraph_vector_ptr.h" +#include "igraph_attributes.h" + +#include + +/** + * \section about_sparsemat About sparse matrices + * + * + * The igraph_sparsemat_t data type stores sparse matrices, + * i.e. matrices in which the majority of the elements are zero. + * + * + * The data type is essentially a wrapper to some of the + * functions in the CXSparse library, by Tim Davis, see + * http://faculty.cse.tamu.edu/davis/suitesparse.html + * + * + * + * Matrices can be stored in two formats: triplet and + * column-compressed. The triplet format is intended for sparse matrix + * initialization, as it is easy to add new (non-zero) elements to + * it. Most of the computations are done on sparse matrices in + * column-compressed format, after the user has converted the triplet + * matrix to column-compressed, via \ref igraph_sparsemat_compress(). + * + * + * + * Both formats are dynamic, in the sense that new elements can be + * added to them, possibly resulting the allocation of more memory. + * + * + * + * Row and column indices follow the C convention and are zero-based. + * + * + * + * \example examples/simple/igraph_sparsemat.c + * \example examples/simple/igraph_sparsemat3.c + * \example examples/simple/igraph_sparsemat4.c + * \example examples/simple/igraph_sparsemat6.c + * \example examples/simple/igraph_sparsemat7.c + * \example examples/simple/igraph_sparsemat8.c + * + */ + +/** + * \function igraph_sparsemat_init + * \brief Initializes a sparse matrix, in triplet format. + * + * This is the most common way to create a sparse matrix, together + * with the \ref igraph_sparsemat_entry() function, which can be used to + * add the non-zero elements one by one. Once done, the user can call + * \ref igraph_sparsemat_compress() to convert the matrix to + * column-compressed, to allow computations with it. + * + * The user must call \ref igraph_sparsemat_destroy() on + * the matrix to deallocate the memory, once the matrix is no more + * needed. + * \param A Pointer to a not yet initialized sparse matrix. + * \param rows The number of rows in the matrix. + * \param cols The number of columns. + * \param nzmax The maximum number of non-zero elements in the + * matrix. It is not compulsory to get this right, but it is + * useful for the allocation of the proper amount of memory. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_init(igraph_sparsemat_t *A, int rows, int cols, int nzmax) { + + if (rows < 0) { + IGRAPH_ERROR("Negative number of rows", IGRAPH_EINVAL); + } + if (cols < 0) { + IGRAPH_ERROR("Negative number of columns", IGRAPH_EINVAL); + } + + A->cs = cs_spalloc( rows, cols, nzmax, /*values=*/ 1, + /*triplet=*/ 1); + if (!A->cs) { + IGRAPH_ERROR("Cannot allocate memory for sparse matrix", IGRAPH_ENOMEM); + } + + return 0; +} + +/** + * \function igraph_sparsemat_copy + * \brief Copies a sparse matrix. + * + * Create a sparse matrix object, by copying another one. The source + * matrix can be either in triplet or column-compressed format. + * + * + * Exactly the same amount of memory will be allocated to the + * copy matrix, as it is currently for the original one. + * \param to Pointer to an uninitialized sparse matrix, the copy will + * be created here. + * \param from The sparse matrix to copy. + * \return Error code. + * + * Time complexity: O(n+nzmax), the number of columns plus the maximum + * number of non-zero elements. + */ + +int igraph_sparsemat_copy(igraph_sparsemat_t *to, + const igraph_sparsemat_t *from) { + + CS_INT ne = from->cs->nz == -1 ? from->cs->n + 1 : from->cs->nzmax; + + to->cs = cs_spalloc(from->cs->m, from->cs->n, from->cs->nzmax, + /*values=*/ 1, + /*triplet=*/ igraph_sparsemat_is_triplet(from)); + + to->cs->nzmax = from->cs->nzmax; + to->cs->m = from->cs->m; + to->cs->n = from->cs->n; + to->cs->nz = from->cs->nz; + + memcpy(to->cs->p, from->cs->p, sizeof(int) * (size_t) ne); + memcpy(to->cs->i, from->cs->i, sizeof(int) * (size_t) (from->cs->nzmax)); + memcpy(to->cs->x, from->cs->x, sizeof(double) * (size_t) (from->cs->nzmax)); + + return 0; +} + +/** + * \function igraph_sparsemat_destroy + * \brief Deallocates memory used by a sparse matrix. + * + * One destroyed, the sparse matrix must be initialized again, before + * calling any other operation on it. + * \param A The sparse matrix to destroy. + * + * Time complexity: O(1). + */ + +void igraph_sparsemat_destroy(igraph_sparsemat_t *A) { + cs_spfree(A->cs); +} + +/** + * \function igraph_sparsemat_realloc + * \brief Allocates more (or less) memory for a sparse matrix. + * + * Sparse matrices automatically allocate more memory, as needed. To + * control memory allocation, the user can call this function, to + * allocate memory for a given number of non-zero elements. + * + * \param A The sparse matrix, it can be in triplet or + * column-compressed format. + * \param nzmax The new maximum number of non-zero elements. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_realloc(igraph_sparsemat_t *A, int nzmax) { + if (!cs_sprealloc(A->cs, nzmax)) { + IGRAPH_ERROR("Could not allocate more memory for sparse matrix.", IGRAPH_ENOMEM); + } + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_nrow + * \brief Number of rows. + * + * \param A The input matrix, in triplet or column-compressed format. + * \return The number of rows in the \p A matrix. + * + * Time complexity: O(1). + */ + +long int igraph_sparsemat_nrow(const igraph_sparsemat_t *A) { + return A->cs->m; +} + +/** + * \function igraph_sparsemat_ncol + * \brief Number of columns. + * + * \param A The input matrix, in triplet or column-compressed format. + * \return The number of columns in the \p A matrix. + * + * Time complexity: O(1). + */ + +long int igraph_sparsemat_ncol(const igraph_sparsemat_t *A) { + return A->cs->n; +} + +/** + * \function igraph_sparsemat_type + * \brief Type of a sparse matrix (triplet or column-compressed). + * + * Gives whether a sparse matrix is stored in the triplet format or in + * column-compressed format. + * \param A The input matrix. + * \return Either \c IGRAPH_SPARSEMAT_CC or \c + * IGRAPH_SPARSEMAT_TRIPLET. + * + * Time complexity: O(1). + */ + +igraph_sparsemat_type_t igraph_sparsemat_type(const igraph_sparsemat_t *A) { + return A->cs->nz < 0 ? IGRAPH_SPARSEMAT_CC : IGRAPH_SPARSEMAT_TRIPLET; +} + +/** + * \function igraph_sparsemat_is_triplet + * \brief Is this sparse matrix in triplet format? + * + * Decides whether a sparse matrix is in triplet format. + * \param A The input matrix. + * \return One if the input matrix is in triplet format, zero + * otherwise. + * + * Time complexity: O(1). + */ + +igraph_bool_t igraph_sparsemat_is_triplet(const igraph_sparsemat_t *A) { + return A->cs->nz >= 0; +} + +/** + * \function igraph_sparsemat_is_cc + * \brief Is this sparse matrix in column-compressed format? + * + * Decides whether a sparse matrix is in column-compressed format. + * \param A The input matrix. + * \return One if the input matrix is in column-compressed format, zero + * otherwise. + * + * Time complexity: O(1). + */ + +igraph_bool_t igraph_sparsemat_is_cc(const igraph_sparsemat_t *A) { + return A->cs->nz < 0; +} + +/** + * \function igraph_sparsemat_permute + * \brief Permutes the rows and columns of a sparse matrix. + * + * \param A The input matrix, it must be in column-compressed format. + * \param p Integer vector, giving the permutation of the rows. + * \param q Integer vector, the permutation of the columns. + * \param res Pointer to an uninitialized sparse matrix, the result is + * stored here. + * \return Error code. + * + * Time complexity: O(m+n+nz), the number of rows plus the number of + * columns plus the number of non-zero elements in the matrix. + */ + +int igraph_sparsemat_permute(const igraph_sparsemat_t *A, + const igraph_vector_int_t *p, + const igraph_vector_int_t *q, + igraph_sparsemat_t *res) { + + CS_INT nrow = A->cs->m, ncol = A->cs->n; + igraph_vector_int_t pinv; + CS_INT i; + + if (nrow != igraph_vector_int_size(p)) { + IGRAPH_ERROR("Invalid row permutation length", IGRAPH_FAILURE); + } + if (ncol != igraph_vector_int_size(q)) { + IGRAPH_ERROR("Invalid column permutation length", IGRAPH_FAILURE); + } + + /* We invert the permutation by hand */ + IGRAPH_CHECK(igraph_vector_int_init(&pinv, nrow)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &pinv); + for (i = 0; i < nrow; i++) { + VECTOR(pinv)[ VECTOR(*p)[i] ] = (int) i; + } + + /* And call the permutation routine */ + res->cs = cs_permute(A->cs, VECTOR(pinv), VECTOR(*q), /*values=*/ 1); + if (!res->cs) { + IGRAPH_ERROR("Cannot index sparse matrix", IGRAPH_FAILURE); + } + + igraph_vector_int_destroy(&pinv); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_sparsemat_index_rows(const igraph_sparsemat_t *A, + const igraph_vector_int_t *p, + igraph_sparsemat_t *res, + igraph_real_t *constres) { + + igraph_sparsemat_t II, II2; + CS_INT nrow = A->cs->m; + long int idx_rows = igraph_vector_int_size(p); + long int k; + + /* Create index matrix */ + IGRAPH_CHECK(igraph_sparsemat_init(&II2, (int) idx_rows, (int) nrow, + (int) idx_rows)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &II2); + for (k = 0; k < idx_rows; k++) { + igraph_sparsemat_entry(&II2, (int) k, VECTOR(*p)[k], 1.0); + } + IGRAPH_CHECK(igraph_sparsemat_compress(&II2, &II)); + igraph_sparsemat_destroy(&II2); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &II); + + /* Multiply */ + IGRAPH_CHECK(igraph_sparsemat_multiply(&II, A, res)); + igraph_sparsemat_destroy(&II); + IGRAPH_FINALLY_CLEAN(1); + + if (constres) { + if (res->cs->p[1] != 0) { + *constres = res->cs->x[0]; + } else { + *constres = 0.0; + } + } + + return 0; +} + +static int igraph_i_sparsemat_index_cols(const igraph_sparsemat_t *A, + const igraph_vector_int_t *q, + igraph_sparsemat_t *res, + igraph_real_t *constres) { + + igraph_sparsemat_t JJ, JJ2; + CS_INT ncol = A->cs->n; + long int idx_cols = igraph_vector_int_size(q); + long int k; + + /* Create index matrix */ + IGRAPH_CHECK(igraph_sparsemat_init(&JJ2, (int) ncol, (int) idx_cols, + (int) idx_cols)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ2); + for (k = 0; k < idx_cols; k++) { + igraph_sparsemat_entry(&JJ2, VECTOR(*q)[k], (int) k, 1.0); + } + IGRAPH_CHECK(igraph_sparsemat_compress(&JJ2, &JJ)); + igraph_sparsemat_destroy(&JJ2); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ); + + /* Multiply */ + IGRAPH_CHECK(igraph_sparsemat_multiply(A, &JJ, res)); + igraph_sparsemat_destroy(&JJ); + IGRAPH_FINALLY_CLEAN(1); + + if (constres) { + if (res->cs->p [1] != 0) { + *constres = res->cs->x [0]; + } else { + *constres = 0.0; + } + } + + return 0; +} + +/** + * \function igraph_sparsemat_index + * \brief Extracts a submatrix or a single element. + * + * This function indexes into a sparse matrix. + * It serves two purposes. First, it can extract + * submatrices from a sparse matrix. Second, as a special case, it can + * extract a single element from a sparse matrix. + * + * \param A The input matrix, it must be in column-compressed format. + * \param p An integer vector, or a null pointer. The selected row + * index or indices. A null pointer selects all rows. + * \param q An integer vector, or a null pointer. The selected column + * index or indices. A null pointer selects all columns. + * \param res Pointer to an uninitialized sparse matrix, or a null + * pointer. If not a null pointer, then the selected submatrix is + * stored here. + * \param constres Pointer to a real variable or a null pointer. If + * not a null pointer, then the first non-zero element in the + * selected submatrix is stored here, if there is one. Otherwise + * zero is stored here. This behavior is handy if one + * wants to select a single entry from the matrix. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_index(const igraph_sparsemat_t *A, + const igraph_vector_int_t *p, + const igraph_vector_int_t *q, + igraph_sparsemat_t *res, + igraph_real_t *constres) { + + igraph_sparsemat_t II, JJ, II2, JJ2, tmp; + CS_INT nrow = A->cs->m; + CS_INT ncol = A->cs->n; + long int idx_rows = p ? igraph_vector_int_size(p) : -1; + long int idx_cols = q ? igraph_vector_int_size(q) : -1; + long int k; + + igraph_sparsemat_t *myres = res, mres; + + if (!p && !q) { + IGRAPH_ERROR("No index vectors", IGRAPH_EINVAL); + } + + if (!res && (idx_rows != 1 || idx_cols != 1)) { + IGRAPH_ERROR("Sparse matrix indexing: must give `res' if not a " + "single element is selected", IGRAPH_EINVAL); + } + + if (!q) { + return igraph_i_sparsemat_index_rows(A, p, res, constres); + } + if (!p) { + return igraph_i_sparsemat_index_cols(A, q, res, constres); + } + + if (!res) { + myres = &mres; + } + + /* Create first index matrix */ + IGRAPH_CHECK(igraph_sparsemat_init(&II2, (int) idx_rows, (int) nrow, + (int) idx_rows)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &II2); + for (k = 0; k < idx_rows; k++) { + igraph_sparsemat_entry(&II2, (int) k, VECTOR(*p)[k], 1.0); + } + IGRAPH_CHECK(igraph_sparsemat_compress(&II2, &II)); + igraph_sparsemat_destroy(&II2); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &II); + + /* Create second index matrix */ + IGRAPH_CHECK(igraph_sparsemat_init(&JJ2, (int) ncol, (int) idx_cols, + (int) idx_cols)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ2); + for (k = 0; k < idx_cols; k++) { + igraph_sparsemat_entry(&JJ2, VECTOR(*q)[k], (int) k, 1.0); + } + IGRAPH_CHECK(igraph_sparsemat_compress(&JJ2, &JJ)); + igraph_sparsemat_destroy(&JJ2); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ); + + /* Multiply */ + IGRAPH_CHECK(igraph_sparsemat_multiply(&II, A, &tmp)); + igraph_sparsemat_destroy(&II); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); + IGRAPH_CHECK(igraph_sparsemat_multiply(&tmp, &JJ, myres)); + igraph_sparsemat_destroy(&tmp); + igraph_sparsemat_destroy(&JJ); + IGRAPH_FINALLY_CLEAN(2); + + if (constres) { + if (myres->cs->p [1] != 0) { + *constres = myres->cs->x [0]; + } else { + *constres = 0.0; + } + } + + if (!res) { + igraph_sparsemat_destroy(myres); + } + + return 0; +} + +/** + * \function igraph_sparsemat_entry + * \brief Adds an element to a sparse matrix. + * + * This function can be used to add the entries to a sparse matrix, + * after initializing it with \ref igraph_sparsemat_init(). If you add + * multiple entries in the same position, they will all be saved, and + * the resulting value is the sum of all entries in that position. + * + * \param A The input matrix, it must be in triplet format. + * \param row The row index of the entry to add. + * \param col The column index of the entry to add. + * \param elem The value of the entry. + * \return Error code. + * + * Time complexity: O(1) on average. + */ + +int igraph_sparsemat_entry(igraph_sparsemat_t *A, int row, int col, + igraph_real_t elem) { + if (!igraph_sparsemat_is_triplet(A)) { + IGRAPH_ERROR("Entries can only be added to sparse matrices that are in triplet format.", + IGRAPH_EINVAL); + } + + if (!cs_entry(A->cs, row, col, elem)) { + IGRAPH_ERROR("Cannot add entry to sparse matrix.", + IGRAPH_FAILURE); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_compress + * \brief Converts a sparse matrix to column-compressed format. + * + * Converts a sparse matrix from triplet format to column-compressed format. + * Almost all sparse matrix operations require that the matrix is in + * column-compressed format. + * + * \param A The input matrix, it must be in triplet format. + * \param res Pointer to an uninitialized sparse matrix object, the + * compressed version of \p A is stored here. + * \return Error code. + * + * Time complexity: O(nz) where \c nz is the number of non-zero elements. + */ + +int igraph_sparsemat_compress(const igraph_sparsemat_t *A, + igraph_sparsemat_t *res) { + + if (! igraph_sparsemat_is_triplet(A)) { + IGRAPH_ERROR("Sparse matrix to compress is not in triplet format.", IGRAPH_EINVAL); + } + res->cs = cs_compress(A->cs); + if (!res->cs) { + IGRAPH_ERROR("Cannot compress sparse matrix", IGRAPH_FAILURE); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_transpose + * \brief Transposes a sparse matrix. + * + * \param A The input matrix, column-compressed or triple format. + * \param res Pointer to an uninitialized sparse matrix, the result is + * stored here. + * \param values If this is non-zero, the matrix transpose is + * calculated the normal way. If it is zero, then only the pattern + * of the input matrix is stored in the result, the values are not. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_transpose(const igraph_sparsemat_t *A, + igraph_sparsemat_t *res, + int values) { + + if (A->cs->nz < 0) { + /* column-compressed */ + res->cs = cs_transpose(A->cs, values); + if (!res->cs) { + IGRAPH_ERROR("Cannot transpose sparse matrix", IGRAPH_FAILURE); + } + } else { + /* triplets */ + CS_INT *tmp; + IGRAPH_CHECK(igraph_sparsemat_copy(res, A)); + tmp = res->cs->p; + res->cs->p = res->cs->i; + res->cs->i = tmp; + } + return 0; +} + +static int igraph_i_sparsemat_is_symmetric_cc(const igraph_sparsemat_t *A, igraph_bool_t *result) { + igraph_sparsemat_t t, tt; + igraph_bool_t res; + int nz; + + IGRAPH_CHECK(igraph_sparsemat_transpose(A, &t, /*values=*/ 1)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &t); + IGRAPH_CHECK(igraph_sparsemat_dupl(&t)); + IGRAPH_CHECK(igraph_sparsemat_transpose(&t, &tt, /*values=*/ 1)); + igraph_sparsemat_destroy(&t); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tt); + IGRAPH_CHECK(igraph_sparsemat_transpose(&tt, &t, /*values=*/ 1)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &t); + + nz = t.cs->p[t.cs->n]; + res = memcmp(t.cs->i, tt.cs->i, sizeof(int) * (size_t) nz) == 0; + res = res && memcmp(t.cs->p, tt.cs->p, sizeof(int) * + (size_t)(t.cs->n + 1)) == 0; + res = res && memcmp(t.cs->x, tt.cs->x, sizeof(igraph_real_t) * (size_t)nz) == 0; + + igraph_sparsemat_destroy(&t); + igraph_sparsemat_destroy(&tt); + IGRAPH_FINALLY_CLEAN(2); + + *result = res; + + return IGRAPH_SUCCESS; +} + +static int igraph_i_sparsemat_is_symmetric_triplet(const igraph_sparsemat_t *A, igraph_bool_t *result) { + igraph_sparsemat_t tmp; + + IGRAPH_CHECK(igraph_sparsemat_compress(A, &tmp)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); + IGRAPH_CHECK(igraph_i_sparsemat_is_symmetric_cc(&tmp, result)); + igraph_sparsemat_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +igraph_bool_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A) { + igraph_bool_t res = 0; + + if (A->cs->m != A->cs->n) { + return 0; + } + + /* TODO(ntamas): return values from igraph_i_sparsemat_is_symmetric_... are + * ignored here; this should be fixed. Right now these functions don't + * change 'res' if they fail so we will report matrices as not being + * symmetric if an error happens */ + if (A->cs->nz < 0) { + igraph_i_sparsemat_is_symmetric_cc(A, &res); + } else { + igraph_i_sparsemat_is_symmetric_triplet(A, &res); + } + + return res; +} + +/** + * \function igraph_sparsemat_dupl + * \brief Removes duplicate elements from a sparse matrix. + * + * It is possible that a column-compressed sparse matrix stores a + * single matrix entry in multiple pieces. The entry is then the sum + * of all its pieces. (Some functions create matrices like this.) This + * function eliminates the multiple pieces. + * \param A The input matrix, in column-compressed format. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_dupl(igraph_sparsemat_t *A) { + + if (!cs_dupl(A->cs)) { + IGRAPH_ERROR("Cannot remove duplicates from sparse matrix", + IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_fkeep + * \brief Filters the elements of a sparse matrix. + * + * This function can be used to filter the (non-zero) elements of a + * sparse matrix. For all entries, it calls the supplied function and + * depending on the return values either keeps, or deleted the element + * from the matrix. + * \param A The input matrix, in column-compressed format. + * \param fkeep The filter function. It must take four arguments: the + * first is an \c int, the row index of the entry, the second is + * another \c int, the column index. The third is \c igraph_real_t, + * the value of the entry. The fourth element is a \c void pointer, + * the \p other argument is passed here. The function must return + * an \c int. If this is zero, then the entry is deleted, otherwise + * it is kept. + * \param other A \c void pointer that is passed to the filtering + * function. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_fkeep( + igraph_sparsemat_t *A, + igraph_integer_t (*fkeep)(igraph_integer_t, igraph_integer_t, igraph_real_t, void*), + void *other +) { + + IGRAPH_ASSERT(A); + IGRAPH_ASSERT(fkeep); + if (!igraph_sparsemat_is_cc(A)) { + IGRAPH_ERROR("The sparse matrix is not in compressed format.", IGRAPH_EINVAL); + } + if (cs_fkeep(A->cs, fkeep, other) < 0) { + IGRAPH_ERROR("External function cs_keep has returned an unknown error while filtering the matrix.", IGRAPH_FAILURE); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_dropzeros + * \brief Drops the zero elements from a sparse matrix. + * + * As a result of matrix operations, some of the entries in a sparse + * matrix might be zero. This function removes these entries. + * \param A The input matrix, it must be in column-compressed format. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_dropzeros(igraph_sparsemat_t *A) { + + if (!cs_dropzeros(A->cs)) { + IGRAPH_ERROR("Cannot drop zeros from sparse matrix", IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_droptol + * \brief Drops the almost zero elements from a sparse matrix. + * + * This function is similar to \ref igraph_sparsemat_dropzeros(), but it + * also drops entries that are closer to zero than the given tolerance + * threshold. + * \param A The input matrix, it must be in column-compressed format. + * \param tol Real number, giving the tolerance threshold. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol) { + + IGRAPH_ASSERT(A); + if (!igraph_sparsemat_is_cc(A)) { + IGRAPH_ERROR("The sparse matrix is not in compressed format.", IGRAPH_EINVAL); + } + if (cs_droptol(A->cs, tol) < 0) { + IGRAPH_ERROR("External function cs_droptol has returned an unknown error.", IGRAPH_FAILURE); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_multiply + * \brief Matrix multiplication. + * + * Multiplies two sparse matrices. + * \param A The first input matrix (left hand side), in + * column-compressed format. + * \param B The second input matrix (right hand side), in + * column-compressed format. + * \param res Pointer to an uninitialized sparse matrix, the result is + * stored here. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_multiply(const igraph_sparsemat_t *A, + const igraph_sparsemat_t *B, + igraph_sparsemat_t *res) { + + res->cs = cs_multiply(A->cs, B->cs); + if (!res->cs) { + IGRAPH_ERROR("Cannot multiply matrices", IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_add + * \brief Sum of two sparse matrices. + * + * \param A The first input matrix, in column-compressed format. + * \param B The second input matrix, in column-compressed format. + * \param alpha Real scalar, \p A is multiplied by \p alpha before the + * addition. + * \param beta Real scalar, \p B is multiplied by \p beta before the + * addition. + * \param res Pointer to an uninitialized sparse matrix, the result + * is stored here. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_add(const igraph_sparsemat_t *A, + const igraph_sparsemat_t *B, + igraph_real_t alpha, + igraph_real_t beta, + igraph_sparsemat_t *res) { + + res->cs = cs_add(A->cs, B->cs, alpha, beta); + if (!res->cs) { + IGRAPH_ERROR("Cannot add matrices", IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_gaxpy + * \brief Matrix-vector product, added to another vector. + * + * \param A The input matrix, in column-compressed format. + * \param x The input vector, its size must match the number of + * columns in \p A. + * \param res This vector is added to the matrix-vector product + * and it is overwritten by the result. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, + const igraph_vector_t *x, + igraph_vector_t *res) { + + if (A->cs->n != igraph_vector_size(x) || + A->cs->m != igraph_vector_size(res)) { + IGRAPH_ERROR("Invalid matrix/vector size for multiplication", + IGRAPH_EINVAL); + } + + if (! (cs_gaxpy(A->cs, VECTOR(*x), VECTOR(*res)))) { + IGRAPH_ERROR("Cannot perform sparse matrix vector multiplication", + IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_lsolve + * \brief Solves a lower-triangular linear system. + * + * Solve the Lx=b linear equation system, where the L coefficient + * matrix is square and lower-triangular, with a zero-free diagonal. + * \param L The input matrix, in column-compressed format. + * \param b The right hand side of the linear system. + * \param res An initialized vector, the result is stored here. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_lsolve(const igraph_sparsemat_t *L, + const igraph_vector_t *b, + igraph_vector_t *res) { + + if (L->cs->m != L->cs->n) { + IGRAPH_ERROR("Cannot perform lower triangular solve", IGRAPH_NONSQUARE); + } + + if (res != b) { + IGRAPH_CHECK(igraph_vector_update(res, b)); + } + + if (! cs_lsolve(L->cs, VECTOR(*res))) { + IGRAPH_ERROR("Cannot perform lower triangular solve", IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_ltsolve + * \brief Solves an upper-triangular linear system. + * + * Solve the L'x=b linear equation system, where the L + * matrix is square and lower-triangular, with a zero-free diagonal. + * \param L The input matrix, in column-compressed format. + * \param b The right hand side of the linear system. + * \param res An initialized vector, the result is stored here. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_ltsolve(const igraph_sparsemat_t *L, + const igraph_vector_t *b, + igraph_vector_t *res) { + + if (L->cs->m != L->cs->n) { + IGRAPH_ERROR("Cannot perform transposed lower triangular solve", + IGRAPH_NONSQUARE); + } + + if (res != b) { + IGRAPH_CHECK(igraph_vector_update(res, b)); + } + + if (!cs_ltsolve(L->cs, VECTOR(*res))) { + IGRAPH_ERROR("Cannot perform lower triangular solve", IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_usolve + * \brief Solves an upper-triangular linear system. + * + * Solves the Ux=b upper triangular system. + * \param U The input matrix, in column-compressed format. + * \param b The right hand side of the linear system. + * \param res An initialized vector, the result is stored here. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_usolve(const igraph_sparsemat_t *U, + const igraph_vector_t *b, + igraph_vector_t *res) { + + if (U->cs->m != U->cs->n) { + IGRAPH_ERROR("Cannot perform upper triangular solve", IGRAPH_NONSQUARE); + } + + if (res != b) { + IGRAPH_CHECK(igraph_vector_update(res, b)); + } + + if (! cs_usolve(U->cs, VECTOR(*res))) { + IGRAPH_ERROR("Cannot perform upper triangular solve", IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_utsolve + * \brief Solves a lower-triangular linear system. + * + * This is the same as \ref igraph_sparsemat_usolve(), but U'x=b is + * solved, where the apostrophe denotes the transpose. + * \param U The input matrix, in column-compressed format. + * \param b The right hand side of the linear system. + * \param res An initialized vector, the result is stored here. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_utsolve(const igraph_sparsemat_t *U, + const igraph_vector_t *b, + igraph_vector_t *res) { + + if (U->cs->m != U->cs->n) { + IGRAPH_ERROR("Cannot perform transposed upper triangular solve", + IGRAPH_NONSQUARE); + } + + if (res != b) { + IGRAPH_CHECK(igraph_vector_update(res, b)); + } + + if (!cs_utsolve(U->cs, VECTOR(*res))) { + IGRAPH_ERROR("Cannot perform transposed upper triangular solve", + IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_cholsol + * \brief Solves a symmetric linear system via Cholesky decomposition. + * + * Solve Ax=b, where A is a symmetric positive definite matrix. + * \param A The input matrix, in column-compressed format. + * \param v The right hand side. + * \param res An initialized vector, the result is stored here. + * \param order An integer giving the ordering method to use for the + * factorization. Zero is the natural ordering; if it is one, then + * the fill-reducing minimum-degree ordering of A+A' is used. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res, + int order) { + + if (A->cs->m != A->cs->n) { + IGRAPH_ERROR("Cannot perform sparse symmetric solve", + IGRAPH_NONSQUARE); + } + + if (res != b) { + IGRAPH_CHECK(igraph_vector_update(res, b)); + } + + if (! cs_cholsol(order, A->cs, VECTOR(*res))) { + IGRAPH_ERROR("Cannot perform sparse symmetric solve", IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_lusol + * \brief Solves a linear system via LU decomposition. + * + * Solve Ax=b, via LU factorization of A. + * \param A The input matrix, in column-compressed format. + * \param b The right hand side of the equation. + * \param res An initialized vector, the result is stored here. + * \param order The ordering method to use, zero means the natural + * ordering, one means the fill-reducing minimum-degree ordering of + * A+A', two means the ordering of A'*A, after removing the dense + * rows from A. Three means the ordering of A'*A. + * \param tol Real number, the tolerance limit to use for the numeric + * LU factorization. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_lusol(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res, + int order, + igraph_real_t tol) { + + if (A->cs->m != A->cs->n) { + IGRAPH_ERROR("Cannot perform LU solve", + IGRAPH_NONSQUARE); + } + + if (res != b) { + IGRAPH_CHECK(igraph_vector_update(res, b)); + } + + if (! cs_lusol(order, A->cs, VECTOR(*res), tol)) { + IGRAPH_ERROR("Cannot perform LU solve", IGRAPH_FAILURE); + } + + return 0; +} + +static int igraph_i_sparsemat_cc(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed) { + + igraph_vector_t edges; + CS_INT no_of_nodes = A->cs->m; + CS_INT no_of_edges = A->cs->p[A->cs->n]; + CS_INT *p = A->cs->p; + CS_INT *i = A->cs->i; + long int from = 0; + long int to = 0; + long int e = 0; + + if (no_of_nodes != A->cs->n) { + IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + + while (*p < no_of_edges) { + while (to < * (p + 1)) { + if (directed || from >= *i) { + VECTOR(edges)[e++] = from; + VECTOR(edges)[e++] = (*i); + } + to++; + i++; + } + from++; + p++; + } + igraph_vector_resize(&edges, e); + + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed) { + + igraph_vector_t edges; + CS_INT no_of_nodes = A->cs->m; + CS_INT no_of_edges = A->cs->nz; + CS_INT *i = A->cs->p; + CS_INT *j = A->cs->i; + long int e; + + if (no_of_nodes != A->cs->n) { + IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + + for (e = 0; e < 2 * no_of_edges; i++, j++) { + if (directed || *i >= *j) { + VECTOR(edges)[e++] = (*i); + VECTOR(edges)[e++] = (*j); + } + } + igraph_vector_resize(&edges, e); + + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_sparsemat + * \brief Creates an igraph graph from a sparse matrix. + * + * One edge is created for each non-zero entry in the matrix. If you + * have a symmetric matrix, and want to create an undirected graph, + * then delete the entries in the upper diagonal first, or call \ref + * igraph_simplify() on the result graph to eliminate the multiple + * edges. + * \param graph Pointer to an uninitialized igraph_t object, the + * graphs is stored here. + * \param A The input matrix, in triplet or column-compressed format. + * \param directed Boolean scalar, whether to create a directed + * graph. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed) { + + if (A->cs->nz < 0) { + return (igraph_i_sparsemat_cc(graph, A, directed)); + } else { + return (igraph_i_sparsemat_triplet(graph, A, directed)); + } +} + +static int igraph_i_weighted_sparsemat_cc(const igraph_sparsemat_t *A, + igraph_bool_t directed, const char *attr, + igraph_bool_t loops, + igraph_vector_t *edges, + igraph_vector_t *weights) { + + CS_INT no_of_edges = A->cs->p[A->cs->n]; + CS_INT *p = A->cs->p; + CS_INT *i = A->cs->i; + CS_ENTRY *x = A->cs->x; + long int from = 0; + long int to = 0; + long int e = 0, w = 0; + + IGRAPH_UNUSED(attr); + + igraph_vector_resize(edges, no_of_edges * 2); + igraph_vector_resize(weights, no_of_edges); + + while (*p < no_of_edges) { + while (to < * (p + 1)) { + if ( (loops || from != *i) && (directed || from >= *i) && *x != 0) { + VECTOR(*edges)[e++] = (*i); + VECTOR(*edges)[e++] = from; + VECTOR(*weights)[w++] = (*x); + } + to++; + i++; + x++; + } + from++; + p++; + } + + igraph_vector_resize(edges, e); + igraph_vector_resize(weights, w); + + return 0; +} + +static int igraph_i_weighted_sparsemat_triplet(const igraph_sparsemat_t *A, + igraph_bool_t directed, + const char *attr, + igraph_bool_t loops, + igraph_vector_t *edges, + igraph_vector_t *weights) { + + IGRAPH_UNUSED(A); IGRAPH_UNUSED(directed); IGRAPH_UNUSED(attr); + IGRAPH_UNUSED(loops); IGRAPH_UNUSED(edges); IGRAPH_UNUSED(weights); + + /* TODO */ + IGRAPH_ERROR("Triplet matrices are not implemented", + IGRAPH_UNIMPLEMENTED); +} + +int igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed, const char *attr, + igraph_bool_t loops) { + + igraph_vector_t edges, weights; + CS_INT pot_edges = A->cs->nz < 0 ? A->cs->p[A->cs->n] : A->cs->nz; + const char* default_attr = "weight"; + igraph_vector_ptr_t attr_vec; + igraph_attribute_record_t attr_rec; + CS_INT no_of_nodes = A->cs->m; + + if (no_of_nodes != A->cs->n) { + IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, pot_edges * 2); + IGRAPH_VECTOR_INIT_FINALLY(&weights, pot_edges); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&attr_vec, 1); + + if (A->cs->nz < 0) { + IGRAPH_CHECK(igraph_i_weighted_sparsemat_cc(A, directed, attr, loops, + &edges, &weights)); + } else { + IGRAPH_CHECK(igraph_i_weighted_sparsemat_triplet(A, directed, attr, + loops, &edges, + &weights)); + } + + /* Prepare attribute record */ + attr_rec.name = attr ? attr : default_attr; + attr_rec.type = IGRAPH_ATTRIBUTE_NUMERIC; + attr_rec.value = &weights; + VECTOR(attr_vec)[0] = &attr_rec; + + /* Create graph */ + IGRAPH_CHECK(igraph_empty(graph, (igraph_integer_t) no_of_nodes, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + if (igraph_vector_size(&edges) > 0) { + IGRAPH_CHECK(igraph_add_edges(graph, &edges, &attr_vec)); + } + IGRAPH_FINALLY_CLEAN(1); + + /* Cleanup */ + igraph_vector_destroy(&edges); + igraph_vector_destroy(&weights); + igraph_vector_ptr_destroy(&attr_vec); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \function igraph_get_sparsemat + * \brief Converts an igraph graph to a sparse matrix. + * + * If the graph is undirected, then a symmetric matrix is created. + * \param graph The input graph. + * \param res Pointer to an uninitialized sparse matrix. The result + * will be stored here. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + long int nzmax = directed ? no_of_edges : no_of_edges * 2; + long int i; + + IGRAPH_CHECK(igraph_sparsemat_init(res, (igraph_integer_t) no_of_nodes, + (igraph_integer_t) no_of_nodes, + (igraph_integer_t) nzmax)); + + for (i = 0; i < no_of_edges; i++) { + long int from = IGRAPH_FROM(graph, i); + long int to = IGRAPH_TO(graph, i); + IGRAPH_CHECK(igraph_sparsemat_entry(res, (int) from, (int) to, 1.0)); + if (!directed && from != to) { + IGRAPH_CHECK(igraph_sparsemat_entry(res, (int) to, (int) from, 1.0)); + } + } + + return 0; +} + +#define CHECK(x) if ((x)<0) { IGRAPH_ERROR("Cannot write to file", IGRAPH_EFILE); } + +/** + * \function igraph_sparsemat_print + * \brief Prints a sparse matrix to a file. + * + * Only the non-zero entries are printed. This function serves more as + * a debugging utility, as currently there is no function that could + * read back the printed matrix from the file. + * \param A The input matrix, triplet or column-compressed format. + * \param outstream The stream to print it to. + * \return Error code. + * + * Time complexity: O(nz) for triplet matrices, O(n+nz) for + * column-compressed matrices. nz is the number of non-zero elements, + * n is the number columns in the matrix. + */ + +int igraph_sparsemat_print(const igraph_sparsemat_t *A, + FILE *outstream) { + + if (A->cs->nz < 0) { + /* CC */ + CS_INT j, p; + for (j = 0; j < A->cs->n; j++) { + CHECK(fprintf(outstream, "col " CS_ID ": locations " CS_ID " to " CS_ID "\n", + j, A->cs->p[j], A->cs->p[j + 1] - 1)); + for (p = A->cs->p[j]; p < A->cs->p[j + 1]; p++) { + CHECK(fprintf(outstream, CS_ID " : %g\n", A->cs->i[p], A->cs->x[p])); + } + } + } else { + /* Triplet */ + CS_INT p; + for (p = 0; p < A->cs->nz; p++) { + CHECK(fprintf(outstream, CS_ID " " CS_ID " : %g\n", + A->cs->i[p], A->cs->p[p], A->cs->x[p])); + } + } + + return 0; +} + +#undef CHECK + +static int igraph_i_sparsemat_eye_triplet(igraph_sparsemat_t *A, int n, int nzmax, + igraph_real_t value) { + long int i; + + IGRAPH_CHECK(igraph_sparsemat_init(A, n, n, nzmax)); + + for (i = 0; i < n; i++) { + igraph_sparsemat_entry(A, (int) i, (int) i, value); + } + + return 0; +} + +static int igraph_i_sparsemat_eye_cc(igraph_sparsemat_t *A, int n, + igraph_real_t value) { + CS_INT i; + + A->cs = cs_spalloc(n, n, n, /*values=*/ 1, /*triplet=*/ 0); + if (!A->cs) { + IGRAPH_ERROR("Cannot create eye sparse matrix", IGRAPH_FAILURE); + } + + for (i = 0; i < n; i++) { + A->cs->p [i] = i; + A->cs->i [i] = i; + A->cs->x [i] = value; + } + A->cs->p [n] = n; + + return 0; +} + +/** + * \function igraph_sparsemat_eye + * \brief Creates a sparse identity matrix. + * + * \param A An uninitialized sparse matrix, the result is stored + * here. + * \param n The number of rows and number of columns in the matrix. + * \param nzmax The maximum number of non-zero elements, this + * essentially gives the amount of memory that will be allocated for + * matrix elements. + * \param value The value to store in the diagonal. + * \param compress Whether to create a column-compressed matrix. If + * false, then a triplet matrix is created. + * \return Error code. + * + * Time complexity: O(n). + */ + +int igraph_sparsemat_eye(igraph_sparsemat_t *A, int n, int nzmax, + igraph_real_t value, + igraph_bool_t compress) { + if (compress) { + return (igraph_i_sparsemat_eye_cc(A, n, value)); + } else { + return (igraph_i_sparsemat_eye_triplet(A, n, nzmax, value)); + } +} + +static int igraph_i_sparsemat_diag_triplet(igraph_sparsemat_t *A, int nzmax, + const igraph_vector_t *values) { + + int i, n = (int) igraph_vector_size(values); + + IGRAPH_CHECK(igraph_sparsemat_init(A, n, n, nzmax)); + + for (i = 0; i < n; i++) { + igraph_sparsemat_entry(A, i, i, VECTOR(*values)[i]); + } + + return 0; + +} + +static int igraph_i_sparsemat_diag_cc(igraph_sparsemat_t *A, + const igraph_vector_t *values) { + + CS_INT i, n = igraph_vector_size(values); + + A->cs = cs_spalloc(n, n, n, /*values=*/ 1, /*triplet=*/ 0); + if (!A->cs) { + IGRAPH_ERROR("Cannot create eye sparse matrix", IGRAPH_FAILURE); + } + + for (i = 0; i < n; i++) { + A->cs->p [i] = i; + A->cs->i [i] = i; + A->cs->x [i] = VECTOR(*values)[i]; + } + A->cs->p [n] = n; + + return 0; + +} + +/** + * \function igraph_sparsemat_diag + * \brief Creates a sparse diagonal matrix. + * + * \param A An uninitialized sparse matrix, the result is stored + * here. + * \param nzmax The maximum number of non-zero elements, this + * essentially gives the amount of memory that will be allocated for + * matrix elements. + * \param values The values to store in the diagonal, the size of the + * matrix defined by the length of this vector. + * \param compress Whether to create a column-compressed matrix. If + * false, then a triplet matrix is created. + * \return Error code. + * + * Time complexity: O(n), the length of the diagonal vector. + */ + +int igraph_sparsemat_diag(igraph_sparsemat_t *A, int nzmax, + const igraph_vector_t *values, + igraph_bool_t compress) { + + if (compress) { + return (igraph_i_sparsemat_diag_cc(A, values)); + } else { + return (igraph_i_sparsemat_diag_triplet(A, nzmax, values)); + } +} + +static int igraph_i_sparsemat_arpack_multiply(igraph_real_t *to, + const igraph_real_t *from, + int n, + void *extra) { + igraph_sparsemat_t *A = extra; + igraph_vector_t vto, vfrom; + igraph_vector_view(&vto, to, n); + igraph_vector_view(&vfrom, from, n); + igraph_vector_null(&vto); + IGRAPH_CHECK(igraph_sparsemat_gaxpy(A, &vfrom, &vto)); + return 0; +} + +typedef struct igraph_i_sparsemat_arpack_rssolve_data_t { + igraph_sparsemat_symbolic_t *dis; + igraph_sparsemat_numeric_t *din; + igraph_real_t tol; + igraph_sparsemat_solve_t method; +} igraph_i_sparsemat_arpack_rssolve_data_t; + +static int igraph_i_sparsemat_arpack_solve(igraph_real_t *to, + const igraph_real_t *from, + int n, + void *extra) { + + igraph_i_sparsemat_arpack_rssolve_data_t *data = extra; + igraph_vector_t vfrom, vto; + + igraph_vector_view(&vfrom, from, n); + igraph_vector_view(&vto, to, n); + + if (data->method == IGRAPH_SPARSEMAT_SOLVE_LU) { + IGRAPH_CHECK(igraph_sparsemat_luresol(data->dis, data->din, &vfrom, + &vto)); + } else if (data->method == IGRAPH_SPARSEMAT_SOLVE_QR) { + IGRAPH_CHECK(igraph_sparsemat_qrresol(data->dis, data->din, &vfrom, + &vto)); + + } + + return 0; +} + +/** + * \function igraph_sparsemat_arpack_rssolve + * \brief Eigenvalues and eigenvectors of a symmetric sparse matrix via ARPACK. + * + * \param The input matrix, must be column-compressed. + * \param options It is passed to \ref igraph_arpack_rssolve(). See + * \ref igraph_arpack_options_t for the details. If \c mode is 1, + * then ARPACK uses regular mode, if \c mode is 3, then shift and + * invert mode is used and the \c sigma structure member defines + * the shift. + * \param storage Storage for ARPACK. See \ref + * igraph_arpack_rssolve() and \ref igraph_arpack_storage_t for + * details. + * \param values An initialized vector or a null pointer, the + * eigenvalues are stored here. + * \param vectors An initialised matrix, or a null pointer, the + * eigenvectors are stored here, in the columns. + * \param solvemethod The method to solve the linear system, if \c + * mode is 3, i.e. the shift and invert mode is used. + * Possible values: + * \clist + * \cli IGRAPH_SPARSEMAT_SOLVE_LU + * The linear system is solved using LU decomposition. + * \cli IGRAPH_SPARSEMAT_SOLVE_QR + * The linear system is solved using QR decomposition. + * \endclist + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, + igraph_arpack_options_t *options, + igraph_arpack_storage_t *storage, + igraph_vector_t *values, + igraph_matrix_t *vectors, + igraph_sparsemat_solve_t solvemethod) { + + int n = (int) igraph_sparsemat_nrow(A); + + if (n != igraph_sparsemat_ncol(A)) { + IGRAPH_ERROR("Non-square matrix for ARPACK", IGRAPH_NONSQUARE); + } + + options->n = n; + + if (options->mode == 1) { + IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_sparsemat_arpack_multiply, + (void*) A, options, storage, + values, vectors)); + } else if (options->mode == 3) { + igraph_real_t sigma = options->sigma; + igraph_sparsemat_t OP, eye; + igraph_sparsemat_symbolic_t symb; + igraph_sparsemat_numeric_t num; + igraph_i_sparsemat_arpack_rssolve_data_t data; + /*-----------------------------------*/ + /* We need to factor the (A-sigma*I) */ + /*-----------------------------------*/ + + /* Create (A-sigma*I) */ + IGRAPH_CHECK(igraph_sparsemat_eye(&eye, /*n=*/ n, /*nzmax=*/ n, + /*value=*/ -sigma, /*compress=*/ 1)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &eye); + IGRAPH_CHECK(igraph_sparsemat_add(/*A=*/ A, /*B=*/ &eye, /*alpha=*/ 1.0, + /*beta=*/ 1.0, /*res=*/ &OP)); + igraph_sparsemat_destroy(&eye); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &OP); + + if (solvemethod == IGRAPH_SPARSEMAT_SOLVE_LU) { + /* Symbolic analysis */ + IGRAPH_CHECK(igraph_sparsemat_symblu(/*order=*/ 0, &OP, &symb)); + IGRAPH_FINALLY(igraph_sparsemat_symbolic_destroy, &symb); + /* Numeric LU factorization */ + IGRAPH_CHECK(igraph_sparsemat_lu(&OP, &symb, &num, /*tol=*/ 0)); + IGRAPH_FINALLY(igraph_sparsemat_numeric_destroy, &num); + } else if (solvemethod == IGRAPH_SPARSEMAT_SOLVE_QR) { + /* Symbolic analysis */ + IGRAPH_CHECK(igraph_sparsemat_symbqr(/*order=*/ 0, &OP, &symb)); + IGRAPH_FINALLY(igraph_sparsemat_symbolic_destroy, &symb); + /* Numeric QR factorization */ + IGRAPH_CHECK(igraph_sparsemat_qr(&OP, &symb, &num)); + IGRAPH_FINALLY(igraph_sparsemat_numeric_destroy, &num); + } + + data.dis = &symb; + data.din = # + data.tol = options->tol; + data.method = solvemethod; + IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_sparsemat_arpack_solve, + (void*) &data, options, storage, + values, vectors)); + + igraph_sparsemat_numeric_destroy(&num); + igraph_sparsemat_symbolic_destroy(&symb); + igraph_sparsemat_destroy(&OP); + IGRAPH_FINALLY_CLEAN(3); + } + + return 0; +} + +/** + * \function igraph_sparsemat_arpack_rnsolve + * \brief Eigenvalues and eigenvectors of a nonsymmetric sparse matrix via ARPACK. + * + * Eigenvalues and/or eigenvectors of a nonsymmetric sparse matrix. + * \param A The input matrix, in column-compressed mode. + * \param options ARPACK options, it is passed to \ref + * igraph_arpack_rnsolve(). See also \ref igraph_arpack_options_t + * for details. + * \param storage Storage for ARPACK, this is passed to \ref + * igraph_arpack_rnsolve(). See \ref igraph_arpack_storage_t for + * details. + * \param values An initialized matrix, or a null pointer. If not a + * null pointer, then the eigenvalues are stored here, the first + * column is the real part, the second column is the imaginary + * part. + * \param vectors An initialized matrix, or a null pointer. If not a + * null pointer, then the eigenvectors are stored here, please see + * \ref igraph_arpack_rnsolve() for the format. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_arpack_rnsolve(const igraph_sparsemat_t *A, + igraph_arpack_options_t *options, + igraph_arpack_storage_t *storage, + igraph_matrix_t *values, + igraph_matrix_t *vectors) { + + int n = (int) igraph_sparsemat_nrow(A); + + if (n != igraph_sparsemat_ncol(A)) { + IGRAPH_ERROR("Non-square matrix for ARPACK", IGRAPH_NONSQUARE); + } + + options->n = n; + + return igraph_arpack_rnsolve(igraph_i_sparsemat_arpack_multiply, + (void*) A, options, storage, + values, vectors); +} + +/** + * \function igraph_sparsemat_symbqr + * \brief Symbolic QR decomposition. + * + * QR decomposition of sparse matrices involves two steps, the first + * is calling this function, and then \ref + * igraph_sparsemat_qr(). + * \param order The ordering to use: 0 means natural ordering, 1 means + * minimum degree ordering of A+A', 2 is minimum degree ordering of + * A'A after removing the dense rows from A, and 3 is the minimum + * degree ordering of A'A. + * \param A The input matrix, in column-compressed format. + * \param dis The result of the symbolic analysis is stored here. Once + * not needed anymore, it must be destroyed by calling \ref + * igraph_sparsemat_symbolic_destroy(). + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_symbqr(long int order, const igraph_sparsemat_t *A, + igraph_sparsemat_symbolic_t *dis) { + + dis->symbolic = cs_sqr((int) order, A->cs, /*qr=*/ 1); + if (!dis->symbolic) { + IGRAPH_ERROR("Cannot do symbolic QR decomposition", IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_symblu + * \brief Symbolic LU decomposition. + * + * LU decomposition of sparse matrices involves two steps, the first + * is calling this function, and then \ref igraph_sparsemat_lu(). + * \param order The ordering to use: 0 means natural ordering, 1 means + * minimum degree ordering of A+A', 2 is minimum degree ordering of + * A'A after removing the dense rows from A, and 3 is the minimum + * degree ordering of A'A. + * \param A The input matrix, in column-compressed format. + * \param dis The result of the symbolic analysis is stored here. Once + * not needed anymore, it must be destroyed by calling \ref + * igraph_sparsemat_symbolic_destroy(). + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_symblu(long int order, const igraph_sparsemat_t *A, + igraph_sparsemat_symbolic_t *dis) { + + dis->symbolic = cs_sqr((int) order, A->cs, /*qr=*/ 0); + if (!dis->symbolic) { + IGRAPH_ERROR("Cannot do symbolic LU decomposition", IGRAPH_FAILURE); + } + + return 0; +} + +/** + * \function igraph_sparsemat_lu + * \brief LU decomposition of a sparse matrix. + * + * Performs numeric sparse LU decomposition of a matrix. + * \param A The input matrix, in column-compressed format. + * \param dis The symbolic analysis for LU decomposition, coming from + * a call to the \ref igraph_sparsemat_symblu() function. + * \param din The numeric decomposition, the result is stored here. It + * can be used to solve linear systems with changing right hand + * side vectors, by calling \ref igraph_sparsemat_luresol(). Once + * not needed any more, it must be destroyed by calling \ref + * igraph_sparsemat_symbolic_destroy() on it. + * \param tol The tolerance for the numeric LU decomposition. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_lu(const igraph_sparsemat_t *A, + const igraph_sparsemat_symbolic_t *dis, + igraph_sparsemat_numeric_t *din, double tol) { + din->numeric = cs_lu(A->cs, dis->symbolic, tol); + if (!din->numeric) { + IGRAPH_ERROR("Cannot do LU decomposition", IGRAPH_FAILURE); + } + return 0; +} + +/** + * \function igraph_sparsemat_qr + * \brief QR decomposition of a sparse matrix. + * + * Numeric QR decomposition of a sparse matrix. + * \param A The input matrix, in column-compressed format. + * \param dis The result of the symbolic QR analysis, from the + * function \ref igraph_sparsemat_symbqr(). + * \param din The result of the decomposition is stored here, it can + * be used to solve many linear systems with the same coefficient + * matrix and changing right hand sides, using the \ref + * igraph_sparsemat_qrresol() function. Once not needed any more, + * one should call \ref igraph_sparsemat_numeric_destroy() on it to + * free the allocated memory. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_qr(const igraph_sparsemat_t *A, + const igraph_sparsemat_symbolic_t *dis, + igraph_sparsemat_numeric_t *din) { + din->numeric = cs_qr(A->cs, dis->symbolic); + if (!din->numeric) { + IGRAPH_ERROR("Cannot do QR decomposition", IGRAPH_FAILURE); + } + return 0; +} + +/** + * \function igraph_sparsemat_luresol + * \brief Solves a linear system using a precomputed LU decomposition. + * + * Uses the LU decomposition of a matrix to solve linear systems. + * \param dis The symbolic analysis of the coefficient matrix, the + * result of \ref igraph_sparsemat_symblu(). + * \param din The LU decomposition, the result of a call to \ref + * igraph_sparsemat_lu(). + * \param b A vector that defines the right hand side of the linear + * equation system. + * \param res An initialized vector, the solution of the linear system + * is stored here. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, + const igraph_sparsemat_numeric_t *din, + const igraph_vector_t *b, + igraph_vector_t *res) { + int n = din->numeric->L->n; + igraph_real_t *workspace; + + if (res != b) { + IGRAPH_CHECK(igraph_vector_update(res, b)); + } + + workspace = IGRAPH_CALLOC(n, igraph_real_t); + if (!workspace) { + IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, workspace); + + if (!cs_ipvec(din->numeric->pinv, VECTOR(*res), workspace, n)) { + IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE); + } + if (!cs_lsolve(din->numeric->L, workspace)) { + IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE); + } + if (!cs_usolve(din->numeric->U, workspace)) { + IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE); + } + if (!cs_ipvec(dis->symbolic->q, workspace, VECTOR(*res), n)) { + IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE); + } + + IGRAPH_FREE(workspace); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_sparsemat_qrresol + * \brief Solves a linear system using a precomputed QR decomposition. + * + * Solves a linear system using a QR decomposition of its coefficient + * matrix. + * \param dis Symbolic analysis of the coefficient matrix, the result + * of \ref igraph_sparsemat_symbqr(). + * \param din The QR decomposition of the coefficient matrix, the + * result of \ref igraph_sparsemat_qr(). + * \param b Vector, giving the right hand side of the linear equation + * system. + * \param res An initialized vector, the solution is stored here. It + * is resized as needed. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis, + const igraph_sparsemat_numeric_t *din, + const igraph_vector_t *b, + igraph_vector_t *res) { + int n = din->numeric->L->n; + igraph_real_t *workspace; + int k; + + if (res != b) { + IGRAPH_CHECK(igraph_vector_update(res, b)); + } + + workspace = IGRAPH_CALLOC(dis->symbolic ? dis->symbolic->m2 : 1, + igraph_real_t); + if (!workspace) { + IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE); + } + IGRAPH_FINALLY(igraph_free, workspace); + + if (!cs_ipvec(dis->symbolic->pinv, VECTOR(*res), workspace, n)) { + IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE); + } + for (k = 0; k < n; k++) { + if (!cs_happly(din->numeric->L, k, din->numeric->B[k], workspace)) { + IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE); + } + } + if (!cs_usolve(din->numeric->U, workspace)) { + IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE); + } + if (!cs_ipvec(dis->symbolic->q, workspace, VECTOR(*res), n)) { + IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE); + } + + IGRAPH_FREE(workspace); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_sparsemat_symbolic_destroy + * \brief Deallocates memory after a symbolic decomposition. + * + * Frees the memory allocated by \ref igraph_sparsemat_symbqr() or + * \ref igraph_sparsemat_symblu(). + * \param dis The symbolic analysis. + * + * Time complexity: O(1). + */ + +void igraph_sparsemat_symbolic_destroy(igraph_sparsemat_symbolic_t *dis) { + cs_sfree(dis->symbolic); + dis->symbolic = 0; +} + +/** + * \function igraph_sparsemat_numeric_destroy + * \brief Deallocates memory after a numeric decomposition. + * + * Frees the memoty allocated by \ref igraph_sparsemat_qr() or \ref + * igraph_sparsemat_lu(). + * \param din The LU or QR decomposition. + * + * Time complexity: O(1). + */ + +void igraph_sparsemat_numeric_destroy(igraph_sparsemat_numeric_t *din) { + cs_nfree(din->numeric); + din->numeric = 0; +} + +/** + * \function igraph_matrix_as_sparsemat + * \brief Converts a dense matrix to a sparse matrix. + * + * \param res An uninitialized sparse matrix, the result is stored + * here. + * \param mat The dense input matrix. + * \param tol Real scalar, the tolerance. Values closer than \p tol to + * zero are considered as zero, and will not be included in the + * sparse matrix. + * \return Error code. + * + * Time complexity: O(mn), the number of elements in the dense + * matrix. + */ + +int igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, + const igraph_matrix_t *mat, + igraph_real_t tol) { + int nrow = (int) igraph_matrix_nrow(mat); + int ncol = (int) igraph_matrix_ncol(mat); + int i, j, nzmax = 0; + + for (i = 0; i < nrow; i++) { + for (j = 0; j < ncol; j++) { + if (fabs(MATRIX(*mat, i, j)) > tol) { + nzmax++; + } + } + } + + IGRAPH_CHECK(igraph_sparsemat_init(res, nrow, ncol, nzmax)); + + for (i = 0; i < nrow; i++) { + for (j = 0; j < ncol; j++) { + if (fabs(MATRIX(*mat, i, j)) > tol) { + IGRAPH_CHECK(igraph_sparsemat_entry(res, i, j, MATRIX(*mat, i, j))); + } + } + } + + return 0; +} + +static int igraph_i_sparsemat_as_matrix_cc(igraph_matrix_t *res, + const igraph_sparsemat_t *spmat) { + + long int nrow = igraph_sparsemat_nrow(spmat); + long int ncol = igraph_sparsemat_ncol(spmat); + CS_INT from = 0, to = 0; + CS_INT *p = spmat->cs->p; + CS_INT *i = spmat->cs->i; + CS_ENTRY *x = spmat->cs->x; + CS_INT nzmax = spmat->cs->nzmax; + + IGRAPH_CHECK(igraph_matrix_resize(res, nrow, ncol)); + igraph_matrix_null(res); + + while (*p < nzmax) { + while (to < * (p + 1)) { + MATRIX(*res, *i, from) += *x; + to++; + i++; + x++; + } + from++; + p++; + } + + return 0; +} + +static int igraph_i_sparsemat_as_matrix_triplet(igraph_matrix_t *res, + const igraph_sparsemat_t *spmat) { + long int nrow = igraph_sparsemat_nrow(spmat); + long int ncol = igraph_sparsemat_ncol(spmat); + CS_INT *i = spmat->cs->p; + CS_INT *j = spmat->cs->i; + CS_ENTRY *x = spmat->cs->x; + CS_INT nz = spmat->cs->nz; + CS_INT e; + + IGRAPH_CHECK(igraph_matrix_resize(res, nrow, ncol)); + igraph_matrix_null(res); + + for (e = 0; e < nz; e++, i++, j++, x++) { + MATRIX(*res, *j, *i) += *x; + } + + return 0; +} + +/** + * \function igraph_sparsemat_as_matrix + * \brief Converts a sparse matrix to a dense matrix. + * + * \param res Pointer to an initialized matrix, the result is stored + * here. It will be resized to the required size. + * \param spmat The input sparse matrix, in triplet or + * column-compressed format. + * \return Error code. + * + * Time complexity: O(mn), the number of elements in the dense + * matrix. + */ + +int igraph_sparsemat_as_matrix(igraph_matrix_t *res, + const igraph_sparsemat_t *spmat) { + if (spmat->cs->nz < 0) { + return (igraph_i_sparsemat_as_matrix_cc(res, spmat)); + } else { + return (igraph_i_sparsemat_as_matrix_triplet(res, spmat)); + } +} + +/** + * \function igraph_sparsemat_max + * \brief Maximum of a sparse matrix. + * + * \param A The input matrix, column-compressed. + * \return The maximum in the input matrix, or \c IGRAPH_NEGINFINITY + * if the matrix has zero elements. + * + * Time complexity: TODO. + */ + +igraph_real_t igraph_sparsemat_max(igraph_sparsemat_t *A) { + CS_INT i, n; + CS_ENTRY *ptr; + igraph_real_t res; + + IGRAPH_CHECK(igraph_sparsemat_dupl(A)); + + ptr = A->cs->x; + n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; + if (n == 0) { + return IGRAPH_NEGINFINITY; + } + res = *ptr; + for (i = 1; i < n; i++, ptr++) { + if (*ptr > res) { + res = *ptr; + } + } + return res; +} + +/* TODO: CC matrix don't actually need _dupl, + because the elements are right beside each other. + Same for max and minmax. */ + +/** + * \function igraph_sparsemat_min + * \brief Minimum of a sparse matrix. + * + * \param A The input matrix, column-compressed. + * \return The minimum in the input matrix, or \c IGRAPH_POSINFINITY + * if the matrix has zero elements. + * + * Time complexity: TODO. + */ + +igraph_real_t igraph_sparsemat_min(igraph_sparsemat_t *A) { + CS_INT i, n; + CS_ENTRY *ptr; + igraph_real_t res; + + IGRAPH_CHECK(igraph_sparsemat_dupl(A)); + + ptr = A->cs->x; + n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; + if (n == 0) { + return IGRAPH_POSINFINITY; + } + res = *ptr; + for (i = 1; i < n; i++, ptr++) { + if (*ptr < res) { + res = *ptr; + } + } + return res; +} + +/** + * \function igraph_sparsemat_minmax + * \brief Minimum and maximum of a sparse matrix. + * + * \param A The input matrix, column-compressed. + * \param min The minimum in the input matrix is stored here, or \c + * IGRAPH_POSINFINITY if the matrix has zero elements. + * \param max The maximum in the input matrix is stored here, or \c + * IGRAPH_NEGINFINITY if the matrix has zero elements. + * \return Error code. + * + * Time complexity: TODO. + */ + + +int igraph_sparsemat_minmax(igraph_sparsemat_t *A, + igraph_real_t *min, igraph_real_t *max) { + CS_INT i, n; + CS_ENTRY *ptr; + + IGRAPH_CHECK(igraph_sparsemat_dupl(A)); + + ptr = A->cs->x; + n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; + if (n == 0) { + *min = IGRAPH_POSINFINITY; + *max = IGRAPH_NEGINFINITY; + return 0; + } + *min = *max = *ptr; + for (i = 1; i < n; i++, ptr++) { + if (*ptr > *max) { + *max = *ptr; + } else if (*ptr < *min) { + *min = *ptr; + } + } + return 0; +} + +/** + * \function igraph_sparsemat_count_nonzero + * \brief Counts nonzero elements of a sparse matrix. + * + * \param A The input matrix, column-compressed. + * \return Error code. + * + * Time complexity: TODO. + */ + +long int igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A) { + CS_INT i, n; + CS_ENTRY *ptr; + int res = 0; + + IGRAPH_CHECK(igraph_sparsemat_dupl(A)); + + ptr = A->cs->x; + n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; + if (n == 0) { + return 0; + } + for (i = 0; i < n; i++, ptr++) { + if (*ptr) { + res++; + } + } + return res; +} + +/** + * \function igraph_sparsemat_count_nonzerotol + * \brief Counts nonzero elements of a sparse matrix, ignoring elements close to zero. + * + * Count the number of matrix entries that are closer to zero than \p + * tol. + * \param The input matrix, column-compressed. + * \param Real scalar, the tolerance. + * \return Error code. + * + * Time complexity: TODO. + */ + +long int igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, + igraph_real_t tol) { + CS_INT i, n; + CS_ENTRY *ptr; + int res = 0; + + IGRAPH_CHECK(igraph_sparsemat_dupl(A)); + + ptr = A->cs->x; + n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; + if (n == 0) { + return 0; + } + for (i = 0; i < n; i++, ptr++) { + if (*ptr < - tol || *ptr > tol) { + res++; + } + } + return res; +} + +static int igraph_i_sparsemat_rowsums_triplet(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT i; + CS_INT *pi = A->cs->i; + CS_ENTRY *px = A->cs->x; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); + igraph_vector_null(res); + + for (i = 0; i < A->cs->nz; i++, pi++, px++) { + VECTOR(*res)[ *pi ] += *px; + } + + return 0; +} + +static int igraph_i_sparsemat_rowsums_cc(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT ne = A->cs->p[A->cs->n]; + CS_ENTRY *px = A->cs->x; + CS_INT *pi = A->cs->i; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); + igraph_vector_null(res); + + for (; pi < A->cs->i + ne; pi++, px++) { + VECTOR(*res)[ *pi ] += *px; + } + + return 0; +} + +/** + * \function igraph_sparsemat_rowsums + * \brief Row-wise sums. + * + * \param A The input matrix, in triplet or column-compressed format. + * \param res An initialized vector, the result is stored here. It + * will be resized as needed. + * \return Error code. + * + * Time complexity: O(nz), the number of non-zero elements. + */ + +int igraph_sparsemat_rowsums(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + if (igraph_sparsemat_is_triplet(A)) { + return igraph_i_sparsemat_rowsums_triplet(A, res); + } else { + return igraph_i_sparsemat_rowsums_cc(A, res); + } +} + +static int igraph_i_sparsemat_rowmins_triplet(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT i; + CS_INT *pi = A->cs->i; + CS_ENTRY *px = A->cs->x; + double inf = IGRAPH_INFINITY; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); + igraph_vector_fill(res, inf); + + for (i = 0; i < A->cs->nz; i++, pi++, px++) { + if (*px < VECTOR(*res)[ *pi ]) { + VECTOR(*res)[ *pi ] = *px; + } + } + + return 0; +} + +static int igraph_i_sparsemat_rowmins_cc(igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT ne; + CS_ENTRY *px; + CS_INT *pi; + double inf = IGRAPH_INFINITY; + + IGRAPH_CHECK(igraph_sparsemat_dupl(A)); + + ne = A->cs->p[A->cs->n]; + px = A->cs->x; + pi = A->cs->i; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); + igraph_vector_fill(res, inf); + + for (; pi < A->cs->i + ne; pi++, px++) { + if (*px < VECTOR(*res)[ *pi ]) { + VECTOR(*res)[ *pi ] = *px; + } + } + + return 0; +} + +int igraph_sparsemat_rowmins(igraph_sparsemat_t *A, + igraph_vector_t *res) { + if (igraph_sparsemat_is_triplet(A)) { + return igraph_i_sparsemat_rowmins_triplet(A, res); + } else { + return igraph_i_sparsemat_rowmins_cc(A, res); + } +} + + +static int igraph_i_sparsemat_rowmaxs_triplet(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT i; + CS_INT *pi = A->cs->i; + CS_ENTRY *px = A->cs->x; + double inf = IGRAPH_NEGINFINITY; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); + igraph_vector_fill(res, inf); + + for (i = 0; i < A->cs->nz; i++, pi++, px++) { + if (*px > VECTOR(*res)[ *pi ]) { + VECTOR(*res)[ *pi ] = *px; + } + } + + return 0; +} + +static int igraph_i_sparsemat_rowmaxs_cc(igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT ne; + CS_ENTRY *px; + CS_INT *pi; + double inf = IGRAPH_NEGINFINITY; + + IGRAPH_CHECK(igraph_sparsemat_dupl(A)); + + ne = A->cs->p[A->cs->n]; + px = A->cs->x; + pi = A->cs->i; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); + igraph_vector_fill(res, inf); + + for (; pi < A->cs->i + ne; pi++, px++) { + if (*px > VECTOR(*res)[ *pi ]) { + VECTOR(*res)[ *pi ] = *px; + } + } + + return 0; +} + +int igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A, + igraph_vector_t *res) { + if (igraph_sparsemat_is_triplet(A)) { + return igraph_i_sparsemat_rowmaxs_triplet(A, res); + } else { + return igraph_i_sparsemat_rowmaxs_cc(A, res); + } +} + +static int igraph_i_sparsemat_colmins_triplet(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT i; + CS_INT *pp = A->cs->p; + CS_ENTRY *px = A->cs->x; + double inf = IGRAPH_INFINITY; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n)); + igraph_vector_fill(res, inf); + + for (i = 0; i < A->cs->nz; i++, pp++, px++) { + if (*px < VECTOR(*res)[ *pp ]) { + VECTOR(*res)[ *pp ] = *px; + } + } + + return 0; +} + +static int igraph_i_sparsemat_colmins_cc(igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT n; + CS_ENTRY *px; + CS_INT *pp; + CS_INT *pi; + double *pr; + double inf = IGRAPH_INFINITY; + + IGRAPH_CHECK(igraph_sparsemat_dupl(A)); + + n = A->cs->n; + px = A->cs->x; + pp = A->cs->p; + pi = A->cs->i; + + IGRAPH_CHECK(igraph_vector_resize(res, n)); + igraph_vector_fill(res, inf); + pr = VECTOR(*res); + + for (; pp < A->cs->p + n; pp++, pr++) { + for (; pi < A->cs->i + * (pp + 1); pi++, px++) { + if (*px < *pr) { + *pr = *px; + } + } + } + return 0; +} + +int igraph_sparsemat_colmins(igraph_sparsemat_t *A, + igraph_vector_t *res) { + if (igraph_sparsemat_is_triplet(A)) { + return igraph_i_sparsemat_colmins_triplet(A, res); + } else { + return igraph_i_sparsemat_colmins_cc(A, res); + } +} + +static int igraph_i_sparsemat_colmaxs_triplet(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT i; + CS_INT *pp = A->cs->p; + CS_ENTRY *px = A->cs->x; + double inf = IGRAPH_NEGINFINITY; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n)); + igraph_vector_fill(res, inf); + + for (i = 0; i < A->cs->nz; i++, pp++, px++) { + if (*px > VECTOR(*res)[ *pp ]) { + VECTOR(*res)[ *pp ] = *px; + } + } + + return 0; +} + +static int igraph_i_sparsemat_colmaxs_cc(igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT n; + CS_ENTRY *px; + CS_INT *pp; + CS_INT *pi; + double *pr; + double inf = IGRAPH_NEGINFINITY; + + IGRAPH_CHECK(igraph_sparsemat_dupl(A)); + + n = A->cs->n; + px = A->cs->x; + pp = A->cs->p; + pi = A->cs->i; + + IGRAPH_CHECK(igraph_vector_resize(res, n)); + igraph_vector_fill(res, inf); + pr = VECTOR(*res); + + for (; pp < A->cs->p + n; pp++, pr++) { + for (; pi < A->cs->i + * (pp + 1); pi++, px++) { + if (*px > *pr) { + *pr = *px; + } + } + } + return 0; +} + +int igraph_sparsemat_colmaxs(igraph_sparsemat_t *A, + igraph_vector_t *res) { + if (igraph_sparsemat_is_triplet(A)) { + return igraph_i_sparsemat_colmaxs_triplet(A, res); + } else { + return igraph_i_sparsemat_colmaxs_cc(A, res); + } +} + +static int igraph_i_sparsemat_which_min_rows_triplet(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos) { + CS_INT i; + CS_INT *pi = A->cs->i; + CS_INT *pp = A->cs->p; + CS_ENTRY *px = A->cs->x; + double inf = IGRAPH_INFINITY; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); + IGRAPH_CHECK(igraph_vector_int_resize(pos, A->cs->m)); + igraph_vector_fill(res, inf); + igraph_vector_int_null(pos); + + for (i = 0; i < A->cs->nz; i++, pi++, px++, pp++) { + if (*px < VECTOR(*res)[ *pi ]) { + VECTOR(*res)[ *pi ] = *px; + VECTOR(*pos)[ *pi ] = *pp; + } + } + + return 0; +} + +static int igraph_i_sparsemat_which_min_rows_cc(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos) { + CS_INT n; + CS_ENTRY *px; + CS_INT *pp; + CS_INT *pi; + double inf = IGRAPH_INFINITY; + int j; + + IGRAPH_CHECK(igraph_sparsemat_dupl(A)); + + n = A->cs->n; + px = A->cs->x; + pp = A->cs->p; + pi = A->cs->i; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); + IGRAPH_CHECK(igraph_vector_int_resize(pos, A->cs->m)); + igraph_vector_fill(res, inf); + igraph_vector_int_null(pos); + + for (j = 0; pp < A->cs->p + n; pp++, j++) { + for (; pi < A->cs->i + * (pp + 1); pi++, px++) { + if (*px < VECTOR(*res)[ *pi ]) { + VECTOR(*res)[ *pi ] = *px; + VECTOR(*pos)[ *pi ] = j; + } + } + } + + return 0; +} + +int igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos) { + if (igraph_sparsemat_is_triplet(A)) { + return igraph_i_sparsemat_which_min_rows_triplet(A, res, pos); + } else { + return igraph_i_sparsemat_which_min_rows_cc(A, res, pos); + } +} + +static int igraph_i_sparsemat_which_min_cols_triplet(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos) { + + CS_INT i; + CS_INT *pi = A->cs->i; + CS_INT *pp = A->cs->p; + CS_ENTRY *px = A->cs->x; + double inf = IGRAPH_INFINITY; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n)); + IGRAPH_CHECK(igraph_vector_int_resize(pos, A->cs->n)); + igraph_vector_fill(res, inf); + igraph_vector_int_null(pos); + + for (i = 0; i < A->cs->nz; i++, pi++, pp++, px++) { + if (*px < VECTOR(*res)[ *pp ]) { + VECTOR(*res)[ *pp ] = *px; + VECTOR(*pos)[ *pp ] = *pi; + } + } + + return 0; +} + +static int igraph_i_sparsemat_which_min_cols_cc(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos) { + CS_INT n, j, p; + CS_ENTRY *px; + double *pr; + igraph_integer_t *ppos; + double inf = IGRAPH_INFINITY; + + IGRAPH_CHECK(igraph_sparsemat_dupl(A)); + + n = A->cs->n; + px = A->cs->x; + + IGRAPH_CHECK(igraph_vector_resize(res, n)); + igraph_vector_fill(res, inf); + pr = VECTOR(*res); + IGRAPH_CHECK(igraph_vector_int_resize(pos, n)); + igraph_vector_int_null(pos); + ppos = VECTOR(*pos); + + for (j = 0; j < A->cs->n; j++, pr++, ppos++) { + for (p = A->cs->p[j]; p < A->cs->p[j + 1]; p++, px++) { + if (*px < *pr) { + *pr = *px; + *ppos = A->cs->i[p]; + } + } + } + return 0; +} + +int igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos) { + if (igraph_sparsemat_is_triplet(A)) { + return igraph_i_sparsemat_which_min_cols_triplet(A, res, pos); + } else { + return igraph_i_sparsemat_which_min_cols_cc(A, res, pos); + } +} + +static int igraph_i_sparsemat_colsums_triplet(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT i; + CS_INT *pp = A->cs->p; + CS_ENTRY *px = A->cs->x; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n)); + igraph_vector_null(res); + + for (i = 0; i < A->cs->nz; i++, pp++, px++) { + VECTOR(*res)[ *pp ] += *px; + } + + return 0; +} + +static int igraph_i_sparsemat_colsums_cc(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT n = A->cs->n; + CS_ENTRY *px = A->cs->x; + CS_INT *pp = A->cs->p; + CS_INT *pi = A->cs->i; + double *pr; + + IGRAPH_CHECK(igraph_vector_resize(res, n)); + igraph_vector_null(res); + pr = VECTOR(*res); + + for (; pp < A->cs->p + n; pp++, pr++) { + for (; pi < A->cs->i + * (pp + 1); pi++, px++) { + *pr += *px; + } + } + return 0; +} + +/** + * \function igraph_sparsemat_colsums + * \brief Column-wise sums. + * + * \param A The input matrix, in triplet or column-compressed format. + * \param res An initialized vector, the result is stored here. It + * will be resized as needed. + * \return Error code. + * + * Time complexity: O(nz) for triplet matrices, O(nz+n) for + * column-compressed ones, nz is the number of non-zero elements, n is + * the number of columns. + */ + +int igraph_sparsemat_colsums(const igraph_sparsemat_t *A, + igraph_vector_t *res) { + if (igraph_sparsemat_is_triplet(A)) { + return igraph_i_sparsemat_colsums_triplet(A, res); + } else { + return igraph_i_sparsemat_colsums_cc(A, res); + } +} + +/** + * \function igraph_sparsemat_scale + * \brief Scales a sparse matrix. + * + * Multiplies all elements of a sparse matrix, by the given scalar. + * \param A The input matrix. + * \param by The scaling factor. + * \return Error code. + * + * Time complexity: O(nz), the number of non-zero elements in the + * matrix. + */ + +int igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by) { + + CS_ENTRY *px = A->cs->x; + CS_INT n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; + CS_ENTRY *stop = px + n; + + for (; px < stop; px++) { + *px *= by; + } + + return 0; +} + +/** + * \function igraph_sparsemat_add_rows + * \brief Adds rows to a sparse matrix. + * + * The current matrix elements are retained and all elements in the + * new rows are zero. + * \param A The input matrix, in triplet or column-compressed format. + * \param n The number of rows to add. + * \return Error code. + * + * Time complexity: O(1). + */ + +int igraph_sparsemat_add_rows(igraph_sparsemat_t *A, long int n) { + A->cs->m += n; + return 0; +} + +/** + * \function igraph_sparsemat_add_cols + * \brief Adds columns to a sparse matrix. + * + * The current matrix elements are retained, and all elements in the + * new columns are zero. + * \param A The input matrix, in triplet or column-compressed format. + * \param n The number of columns to add. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_sparsemat_add_cols(igraph_sparsemat_t *A, long int n) { + if (igraph_sparsemat_is_triplet(A)) { + A->cs->n += n; + } else { + CS_INT realloc_ok = 0, i; + CS_INT *newp = cs_realloc(A->cs->p, (A->cs->n + n + 1), sizeof(int), &realloc_ok); + if (!realloc_ok) { + IGRAPH_ERROR("Cannot add columns to sparse matrix", IGRAPH_ENOMEM); + } + if (newp != A->cs->p) { + A->cs->p = newp; + } + for (i = A->cs->n + 1; i < A->cs->n + n + 1; i++) { + A->cs->p[i] = A->cs->p[i - 1]; + } + A->cs->n += n; + } + return 0; +} + +/** + * \function igraph_sparsemat_resize + * \brief Resizes a sparse matrix. + * + * This function resizes a sparse matrix. The resized sparse matrix + * will be empty. + * + * \param A The initialized sparse matrix to resize. + * \param nrow The new number of rows. + * \param ncol The new number of columns. + * \param nzmax The new maximum number of elements. + * \return Error code. + * + * Time complexity: O(nzmax), the maximum number of non-zero elements. + */ + +int igraph_sparsemat_resize(igraph_sparsemat_t *A, long int nrow, + long int ncol, int nzmax) { + + if (A->cs->nz < 0) { + igraph_sparsemat_t tmp; + IGRAPH_CHECK(igraph_sparsemat_init(&tmp, (int) nrow, (int) ncol, nzmax)); + igraph_sparsemat_destroy(A); + *A = tmp; + } else { + IGRAPH_CHECK(igraph_sparsemat_realloc(A, nzmax)); + A->cs->m = (int) nrow; + A->cs->n = (int) ncol; + A->cs->nz = 0; + } + return 0; +} + +/** + * \function igraph_sparsemat_nonzero_storage + * \brief Returns number of stored entries of a sparse matrix. + * + * This function will return the number of stored entries of a sparse + * matrix. These entries can be zero, and multiple entries can be + * at the same position. Use \ref igraph_sparsemat_dupl() to sum + * duplicate entries, and \ref igraph_sparsemat_dropzeros() to remove + * zeros. + * + * \param A A sparse matrix in either triplet or compressed form. + * \return Number of stored entries. + * + * Time complexity: O(1). + */ + +int igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A) { + if (A->cs->nz < 0) { + return A->cs->p[A->cs->n]; + } else { + return A->cs->nz; + } +} + +int igraph_sparsemat_getelements(const igraph_sparsemat_t *A, + igraph_vector_int_t *i, + igraph_vector_int_t *j, + igraph_vector_t *x) { + CS_INT nz = A->cs->nz; + if (nz < 0) { + nz = A->cs->p[A->cs->n]; + IGRAPH_CHECK(igraph_vector_int_resize(i, nz)); + IGRAPH_CHECK(igraph_vector_int_resize(j, A->cs->n + 1)); + IGRAPH_CHECK(igraph_vector_resize(x, nz)); + memcpy(VECTOR(*i), A->cs->i, (size_t) nz * sizeof(int)); + memcpy(VECTOR(*j), A->cs->p, (size_t) (A->cs->n + 1) * sizeof(int)); + memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(igraph_real_t)); + } else { + IGRAPH_CHECK(igraph_vector_int_resize(i, nz)); + IGRAPH_CHECK(igraph_vector_int_resize(j, nz)); + IGRAPH_CHECK(igraph_vector_resize(x, nz)); + memcpy(VECTOR(*i), A->cs->i, (size_t) nz * sizeof(int)); + memcpy(VECTOR(*j), A->cs->p, (size_t) nz * sizeof(int)); + memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(igraph_real_t)); + } + return 0; +} + +int igraph_sparsemat_scale_rows(igraph_sparsemat_t *A, + const igraph_vector_t *fact) { + CS_INT *i = A->cs->i; + CS_ENTRY *x = A->cs->x; + CS_INT no_of_edges = A->cs->nz < 0 ? A->cs->p[A->cs->n] : A->cs->nz; + CS_INT e; + + for (e = 0; e < no_of_edges; e++, x++, i++) { + igraph_real_t f = VECTOR(*fact)[*i]; + (*x) *= f; + } + + return 0; +} + +static int igraph_i_sparsemat_scale_cols_cc(igraph_sparsemat_t *A, + const igraph_vector_t *fact) { + CS_INT *i = A->cs->i; + CS_ENTRY *x = A->cs->x; + CS_INT no_of_edges = A->cs->p[A->cs->n]; + CS_INT e; + CS_INT c = 0; /* actual column */ + + for (e = 0; e < no_of_edges; e++, x++, i++) { + igraph_real_t f; + while (c < A->cs->n && A->cs->p[c + 1] == e) { + c++; + } + f = VECTOR(*fact)[c]; + (*x) *= f; + } + + return 0; +} + +static int igraph_i_sparsemat_scale_cols_triplet(igraph_sparsemat_t *A, + const igraph_vector_t *fact) { + CS_INT *j = A->cs->p; + CS_ENTRY *x = A->cs->x; + CS_INT no_of_edges = A->cs->nz; + CS_INT e; + + for (e = 0; e < no_of_edges; e++, x++, j++) { + igraph_real_t f = VECTOR(*fact)[*j]; + (*x) *= f; + } + + return 0; +} + +int igraph_sparsemat_scale_cols(igraph_sparsemat_t *A, + const igraph_vector_t *fact) { + if (A->cs->nz < 0) { + return igraph_i_sparsemat_scale_cols_cc(A, fact); + } else { + return igraph_i_sparsemat_scale_cols_triplet(A, fact); + } +} + +int igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, + const igraph_matrix_t *B, + igraph_matrix_t *res) { + + int m = (int) igraph_sparsemat_nrow(A); + int n = (int) igraph_sparsemat_ncol(A); + int p = (int) igraph_matrix_ncol(B); + int i; + + if (igraph_matrix_nrow(B) != n) { + IGRAPH_ERROR("Invalid dimensions in sparse-dense matrix product", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_matrix_resize(res, m, p)); + igraph_matrix_null(res); + + for (i = 0; i < p; i++) { + if (!(cs_gaxpy(A->cs, &MATRIX(*B, 0, i), &MATRIX(*res, 0, i)))) { + IGRAPH_ERROR("Cannot perform sparse-dense matrix multiplication", + IGRAPH_FAILURE); + } + } + + return 0; +} + +int igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, + const igraph_sparsemat_t *B, + igraph_matrix_t *res) { + int m = (int) igraph_matrix_nrow(A); + int n = (int) igraph_matrix_ncol(A); + int p = (int) igraph_sparsemat_ncol(B); + int r, c; + CS_INT *Bp = B->cs->p; + + if (igraph_sparsemat_nrow(B) != n) { + IGRAPH_ERROR("Invalid dimensions in dense-sparse matrix product", + IGRAPH_EINVAL); + } + + if (!igraph_sparsemat_is_cc(B)) { + IGRAPH_ERROR("Dense-sparse product is only implemented for " + "column-compressed sparse matrices", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_matrix_resize(res, m, p)); + igraph_matrix_null(res); + + for (c = 0; c < p; c++) { + for (r = 0; r < m; r++) { + int idx = *Bp; + while (idx < * (Bp + 1)) { + MATRIX(*res, r, c) += MATRIX(*A, r, B->cs->i[idx]) * B->cs->x[idx]; + idx++; + } + } + Bp++; + } + + return 0; +} + +/** + * \function igraph_sparsemat_view + * \brief Initialize a sparse matrix and set all parameters. + * + * This function can be used to temporarily handle existing sparse matrix data, + * usually created by another software library, as an \c igraph_sparsemat_t object, + * and thus avoid unnecessary copying. It supports data stored in either the + * compressed sparse column format, or the (i, j, x) triplet format + * where \c i and \c j are the matrix indices of a non-zero element, and \c x + * is its value. + * + * + * The compressed sparse column (or row) format is commonly used to represent + * sparse matrix data. It consists of three vectors, the \p p column pointers, the + * \p i row indices, and the \p x values. p[k] is the number + * of non-zero entires in matrix columns k-1 and lower. + * p[0] is always zero and p[n] is always the total + * number of non-zero entires in the matrix. i[l] is the row index + * of the \c l-th stored element, while x[l] is its value. + * If a matrix element with indices (j, k) is explicitly stored, + * it must be located between positions p[k] and p[k+1] - 1 + * (inclusive) in the \p i and \p x vectors. + * + * + * Do not call \ref igraph_sparsemat_destroy() on a sparse matrix created with + * this function. Instead, \ref igraph_free() must be called on the \c cs + * field of \p A to free the storage allocated by this function. + * + * + * Warning: Matrices created with this function must not be used with functions + * that may reallocate the underlying storage, such as \ref igraph_sparsemat_entry(). + * + * \param A The non-initialized sparse matrix. + * \param nzmax The maximum number of entries, typically the actual number of entries. + * \param m The number of matrix rows. + * \param n The number of matrix columns. + * \param p For a compressed matrix, this is the column pointer vector, and + * must be of size n+1. For a triplet format matrix, it + * is a vector of column indices and must be of size \p nzmax. + * \param i The row vector. This should contain the row indices of the + * elements in \p x. It must be of size \p nzmax. + * \param x The values of the non-zero elements of the sparse matrix. + * It must be of size \p nzmax. + * \param nz For a compressed matrix, is must be -1. For a triplet format + * matrix, is must contain the number of entries. + * \return Error code. + * + * Time complexity: O(1). + */ + +int igraph_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n, + int *p, int *i, double *x, int nz) { + + A->cs = IGRAPH_CALLOC(1, cs_di); + A->cs->nzmax = nzmax; + A->cs->m = m; + A->cs->n = n; + A->cs->p = p; + A->cs->i = i; + A->cs->x = x; + A->cs->nz = nz; + + return IGRAPH_SUCCESS; +} + +int igraph_i_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n, + int *p, int *i, double *x, int nz) { + IGRAPH_WARNING("igraph_i_sparsemat_view() is deprecated, use igraph_sparsemat_view()"); + return igraph_sparsemat_view(A, nzmax, m, n, p, i, x, nz); +} + +int igraph_sparsemat_sort(const igraph_sparsemat_t *A, + igraph_sparsemat_t *sorted) { + + igraph_sparsemat_t tmp; + + IGRAPH_CHECK(igraph_sparsemat_transpose(A, &tmp, /*values=*/ 1)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); + IGRAPH_CHECK(igraph_sparsemat_transpose(&tmp, sorted, /*values=*/ 1)); + igraph_sparsemat_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_sparsemat_getelements_sorted + * \brief Returns the sorted elements of a sparse matrix. + * + * This function will sort a sparse matrix and return the elements in + * 3 vectors. Two vectors will indicate where the elements are located, + * and one will give the elements. + * + * \param A A sparse matrix in either triplet or compressed form. + * \param i An initialized int vector. This will store the rows of the + * returned elements. + * \param j An initialized int vector. For a triplet matrix this will + * store the columns of the returned elements. For a compressed + * matrix, if the column index is \c k, then j[k] + * is the index in \p x of the start of the \c k-th column, and + * the last element of \c j is the total number of elements. + * The total number of elements in the \c k-th column is + * therefore j[k+1] - j[k]. For example, if there + * is one element in the first column, and five in the second, + * \c j will be set to {0, 1, 6}. + * \param x An initialized vector. The elements will be placed here. + * \return Error code. + * + * Time complexity: O(n), the number of stored elements in the sparse matrix. + */ + +int igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A, + igraph_vector_int_t *i, + igraph_vector_int_t *j, + igraph_vector_t *x) { + if (A->cs->nz < 0) { + igraph_sparsemat_t tmp; + IGRAPH_CHECK(igraph_sparsemat_sort(A, &tmp)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); + IGRAPH_CHECK(igraph_sparsemat_getelements(&tmp, i, j, x)); + igraph_sparsemat_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_CHECK(igraph_sparsemat_getelements(A, i, j, x)); + } + + return IGRAPH_SUCCESS; +} + +int igraph_sparsemat_nzmax(const igraph_sparsemat_t *A) { + return A->cs->nzmax; +} + +int igraph_sparsemat_neg(igraph_sparsemat_t *A) { + CS_INT i, nz = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; + CS_ENTRY *px = A->cs->x; + + for (i = 0; i < nz; i++, px++) { + *px = - (*px); + } + + return 0; +} + +/** + * \function igraph_sparsemat_iterator_init + * \brief Initialize a sparse matrix iterator. + * + * \param it A pointer to an uninitialized sparse matrix iterator. + * \param sparsemat Pointer to the sparse matrix. + * \return Error code. This will always return \c IGRAPH_SUCCESS + * + * Time complexity: O(n), the number of columns of the sparse matrix. + */ + +int igraph_sparsemat_iterator_init(igraph_sparsemat_iterator_t *it, + igraph_sparsemat_t *sparsemat) { + + it->mat = sparsemat; + igraph_sparsemat_iterator_reset(it); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_iterator_reset + * \brief Reset a sparse matrix iterator to the first element. + * + * \param it A pointer to the sparse matrix iterator. + * \return Error code. This will always return \c IGRAPH_SUCCESS + * + * Time complexity: O(n), the number of columns of the sparse matrix. + */ + +int igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it) { + it->pos = 0; + it->col = 0; + if (!igraph_sparsemat_is_triplet(it->mat)) { + while (it->col < it->mat->cs->n && + it->mat->cs->p[it->col + 1] == it->pos) { + it->col ++; + } + } + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_iterator_end + * \brief Query if the iterator is past the last element. + * + * \param it A pointer to the sparse matrix iterator. + * \return true if the iterator is past the last element, false if it + * points to an element in a sparse matrix. + * + * Time complexity: O(1). + */ + +igraph_bool_t +igraph_sparsemat_iterator_end(const igraph_sparsemat_iterator_t *it) { + CS_INT nz = it->mat->cs->nz == -1 ? it->mat->cs->p[it->mat->cs->n] : + it->mat->cs->nz; + return it->pos >= nz; +} + +/** + * \function igraph_sparsemat_iterator_row + * \brief Return the row of the iterator. + * + * \param it A pointer to the sparse matrix iterator. + * \return The row of the element at the current iterator position. + * + * Time complexity: O(1). + */ + +int igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it) { + return it->mat->cs->i[it->pos]; +} + +/** + * \function igraph_sparsemat_iterator_col + * \brief Return the column of the iterator. + * + * \param it A pointer to the sparse matrix iterator. + * \return The column of the element at the current iterator position. + * + * Time complexity: O(1). + */ + +int igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it) { + if (igraph_sparsemat_is_triplet(it->mat)) { + return it->mat->cs->p[it->pos]; + } else { + return it->col; + } +} + +/** + * \function igraph_sparsemat_iterator_get + * \brief Return the element at the current iterator position. + * + * \param it A pointer to the sparse matrix iterator. + * \return The value of the element at the current iterator position. + * + * Time complexity: O(1). + */ + +igraph_real_t +igraph_sparsemat_iterator_get(const igraph_sparsemat_iterator_t *it) { + return it->mat->cs->x[it->pos]; +} + +/** + * \function igraph_sparsemat_iterator_next + * \brief Let a sparse matrix iterator go to the next element. + * + * \param it A pointer to the sparse matrix iterator. + * \return The position of the iterator in the element vector. + * + * Time complexity: O(n), the number of columns of the sparse matrix. + */ + +int igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it) { + it->pos += 1; + while (it->col < it->mat->cs->n && + it->mat->cs->p[it->col + 1] == it->pos) { + it->col++; + } + return it->pos; +} + +/** + * \function igraph_sparsemat_iterator_idx + * \brief Returns the element vector index of a sparse matrix iterator. + * + * \param it A pointer to the sparse matrix iterator. + * \return The position of the iterator in the element vector. + * + * Time complexity: O(1). + */ + +int igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it) { + return it->pos; +} diff --git a/src/rigraph/core/core/spmatrix.c b/src/rigraph/core/core/spmatrix.c new file mode 100644 index 0000000..7d2a7ec --- /dev/null +++ b/src/rigraph/core/core/spmatrix.c @@ -0,0 +1,1066 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" +#include "igraph_spmatrix.h" +#include "igraph_error.h" + +#include /* memcpy & co. */ + +/** + * \section igraph_spmatrix_constructor_and_destructor Sparse matrix constructors + * and destructors. + */ + +/** + * \ingroup matrix + * \function igraph_spmatrix_init + * \brief Initializes a sparse matrix. + * + * + * Every sparse matrix needs to be initialized before using it, this is done + * by calling this function. A matrix has to be destroyed if it is not + * needed any more, see \ref igraph_spmatrix_destroy(). + * \param m Pointer to a not yet initialized sparse matrix object to be + * initialized. + * \param nrow The number of rows in the matrix. + * \param ncol The number of columns in the matrix. + * \return Error code. + * + * Time complexity: operating system dependent. + */ + +int igraph_spmatrix_init(igraph_spmatrix_t *m, long int nrow, long int ncol) { + IGRAPH_ASSERT(m != NULL); + IGRAPH_VECTOR_INIT_FINALLY(&m->ridx, 0); + IGRAPH_VECTOR_INIT_FINALLY(&m->cidx, ncol + 1); + IGRAPH_VECTOR_INIT_FINALLY(&m->data, 0); + IGRAPH_FINALLY_CLEAN(3); + m->nrow = nrow; + m->ncol = ncol; + return 0; +} + +/** + * \ingroup matrix + * \function igraph_spmatrix_destroy + * \brief Destroys a sparse matrix object. + * + * + * This function frees all the memory allocated for a sparse matrix + * object. The destroyed object needs to be reinitialized before using + * it again. + * \param m The matrix to destroy. + * + * Time complexity: operating system dependent. + */ + +void igraph_spmatrix_destroy(igraph_spmatrix_t *m) { + IGRAPH_ASSERT(m != NULL); + igraph_vector_destroy(&m->ridx); + igraph_vector_destroy(&m->cidx); + igraph_vector_destroy(&m->data); +} + +/** + * \ingroup matrix + * \function igraph_spmatrix_copy + * \brief Copies a sparse matrix. + * + * + * Creates a sparse matrix object by copying another one. + * \param to Pointer to an uninitialized sparse matrix object. + * \param from The initialized sparse matrix object to copy. + * \return Error code, \c IGRAPH_ENOMEM if there + * isn't enough memory to allocate the new sparse matrix. + * + * Time complexity: O(n), the number + * of elements in the matrix. + */ + +int igraph_spmatrix_copy(igraph_spmatrix_t *to, const igraph_spmatrix_t *from) { + IGRAPH_ASSERT(from != NULL); + IGRAPH_ASSERT(to != NULL); + to->nrow = from->nrow; + to->ncol = from->ncol; + IGRAPH_CHECK(igraph_vector_copy(&to->ridx, &from->ridx)); + IGRAPH_CHECK(igraph_vector_copy(&to->cidx, &from->cidx)); + IGRAPH_CHECK(igraph_vector_copy(&to->data, &from->data)); + return 0; +} + +/** + * \section igraph_spmatrix_accessing_elements Accessing elements of a sparse matrix + */ + +/** + * \ingroup matrix + * \function igraph_spmatrix_e + * \brief Accessing an element of a sparse matrix. + * + * Note that there are no range checks right now. + * \param m The matrix object. + * \param row The index of the row, starting with zero. + * \param col The index of the column, starting with zero. + * + * Time complexity: O(log n), where n is the number of nonzero elements in + * the requested column. + */ +igraph_real_t igraph_spmatrix_e(const igraph_spmatrix_t *m, + long int row, long int col) { + long int start, end; + + IGRAPH_ASSERT(m != NULL); + start = (long) VECTOR(m->cidx)[col]; + end = (long) VECTOR(m->cidx)[col + 1] - 1; + + if (end < start) { + return 0; + } + /* Elements residing in column col are between m->data[start] and + * m->data[end], inclusive, ordered by row index */ + while (start < end - 1) { + long int mid = (start + end) / 2; + if (VECTOR(m->ridx)[mid] > row) { + end = mid; + } else if (VECTOR(m->ridx)[mid] < row) { + start = mid; + } else { + start = mid; + break; + } + } + + if (VECTOR(m->ridx)[start] == row) { + return VECTOR(m->data)[start]; + } + if (VECTOR(m->ridx)[start] != row && VECTOR(m->ridx)[end] == row) { + return VECTOR(m->data)[end]; + } + return 0; +} + + +/** + * \ingroup matrix + * \function igraph_spmatrix_set + * \brief Setting an element of a sparse matrix. + * + * Note that there are no range checks right now. + * \param m The matrix object. + * \param row The index of the row, starting with zero. + * \param col The index of the column, starting with zero. + * \param value The new value. + * + * Time complexity: O(log n), where n is the number of nonzero elements in + * the requested column. + */ +int igraph_spmatrix_set(igraph_spmatrix_t *m, long int row, long int col, + igraph_real_t value) { + long int start, end; + + IGRAPH_ASSERT(m != NULL); + start = (long) VECTOR(m->cidx)[col]; + end = (long) VECTOR(m->cidx)[col + 1] - 1; + + if (end < start) { + /* First element in the column */ + if (value == 0.0) { + return 0; + } + IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row)); + IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value)); + for (start = col + 1; start < m->ncol + 1; start++) { + VECTOR(m->cidx)[start]++; + } + return 0; + } + + /* Elements residing in column col are between m->data[start] and + * m->data[end], inclusive, ordered by row index */ + while (start < end - 1) { + long int mid = (start + end) / 2; + if (VECTOR(m->ridx)[mid] > row) { + end = mid; + } else if (VECTOR(m->ridx)[mid] < row) { + start = mid; + } else { + start = mid; + break; + } + } + + if (VECTOR(m->ridx)[start] == row) { + /* Overwriting a value - or deleting it if it has been overwritten by zero */ + if (value == 0) { + igraph_vector_remove(&m->ridx, start); + igraph_vector_remove(&m->data, start); + for (start = col + 1; start < m->ncol + 1; start++) { + VECTOR(m->cidx)[start]--; + } + } else { + VECTOR(m->data)[start] = value; + } + return 0; + } else if (VECTOR(m->ridx)[end] == row) { + /* Overwriting a value - or deleting it if it has been overwritten by zero */ + if (value == 0) { + igraph_vector_remove(&m->ridx, end); + igraph_vector_remove(&m->data, end); + for (start = col + 1; start < m->ncol + 1; start++) { + VECTOR(m->cidx)[start]--; + } + } else { + VECTOR(m->data)[end] = value; + } + return 0; + } + + /* New element has to be inserted, but only if not a zero is + * being written into the matrix */ + if (value != 0.0) { + if (VECTOR(m->ridx)[end] < row) { + IGRAPH_CHECK(igraph_vector_insert(&m->ridx, end + 1, row)); + IGRAPH_CHECK(igraph_vector_insert(&m->data, end + 1, value)); + } else if (VECTOR(m->ridx)[start] < row) { + IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start + 1, row)); + IGRAPH_CHECK(igraph_vector_insert(&m->data, start + 1, value)); + } else { + IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row)); + IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value)); + } + for (start = col + 1; start < m->ncol + 1; start++) { + VECTOR(m->cidx)[start]++; + } + } + return 0; +} + + +/** + * \ingroup matrix + * \function igraph_spmatrix_add_e + * \brief Adding a real value to an element of a sparse matrix. + * + * Note that there are no range checks right now. This is implemented to avoid + * double lookup of a given element in the matrix by using \ref igraph_spmatrix_e() + * and \ref igraph_spmatrix_set() consecutively. + * + * \param m The matrix object. + * \param row The index of the row, starting with zero. + * \param col The index of the column, starting with zero. + * \param value The value to add. + * + * Time complexity: O(log n), where n is the number of nonzero elements in + * the requested column. + */ +int igraph_spmatrix_add_e(igraph_spmatrix_t *m, long int row, long int col, + igraph_real_t value) { + long int start, end; + + IGRAPH_ASSERT(m != NULL); + start = (long) VECTOR(m->cidx)[col]; + end = (long) VECTOR(m->cidx)[col + 1] - 1; + + if (end < start) { + /* First element in the column */ + if (value == 0.0) { + return 0; + } + IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row)); + IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value)); + for (start = col + 1; start < m->ncol + 1; start++) { + VECTOR(m->cidx)[start]++; + } + return 0; + } + + /* Elements residing in column col are between m->data[start] and + * m->data[end], inclusive, ordered by row index */ + while (start < end - 1) { + long int mid = (start + end) / 2; + if (VECTOR(m->ridx)[mid] > row) { + end = mid; + } else if (VECTOR(m->ridx)[mid] < row) { + start = mid; + } else { + start = mid; + break; + } + } + + if (VECTOR(m->ridx)[start] == row) { + /* Overwriting a value */ + if (VECTOR(m->data)[start] == -1) { + igraph_vector_remove(&m->ridx, start); + igraph_vector_remove(&m->data, start); + for (start = col + 1; start < m->ncol + 1; start++) { + VECTOR(m->cidx)[start]--; + } + } else { + VECTOR(m->data)[start] += value; + } + return 0; + } else if (VECTOR(m->ridx)[end] == row) { + /* Overwriting a value */ + if (VECTOR(m->data)[end] == -1) { + igraph_vector_remove(&m->ridx, end); + igraph_vector_remove(&m->data, end); + for (start = col + 1; start < m->ncol + 1; start++) { + VECTOR(m->cidx)[start]--; + } + } else { + VECTOR(m->data)[end] += value; + } + return 0; + } + + /* New element has to be inserted, but only if not a zero is + * being added to a zero element of the matrix */ + if (value != 0.0) { + if (VECTOR(m->ridx)[end] < row) { + IGRAPH_CHECK(igraph_vector_insert(&m->ridx, end + 1, row)); + IGRAPH_CHECK(igraph_vector_insert(&m->data, end + 1, value)); + } else if (VECTOR(m->ridx)[start] < row) { + IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start + 1, row)); + IGRAPH_CHECK(igraph_vector_insert(&m->data, start + 1, value)); + } else { + IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row)); + IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value)); + } + for (start = col + 1; start < m->ncol + 1; start++) { + VECTOR(m->cidx)[start]++; + } + } + return 0; +} + +/** + * \function igraph_spmatrix_add_col_values + * \brief Adds the values of a column to another column. + * + * \param to The index of the column to be added to. + * \param from The index of the column to be added. + * \return Error code. + */ +int igraph_spmatrix_add_col_values(igraph_spmatrix_t *m, long int to, long int from) { + long int i; + if (to < 0 || to >= m->ncol) { + IGRAPH_ERROR("The 'to' column does not exist.", IGRAPH_EINVAL); + } + if (from < 0 || from >= m->ncol) { + IGRAPH_ERROR("The 'from' column does not exist.", IGRAPH_EINVAL); + } + /* TODO: I think this implementation could be speeded up if I don't use + * igraph_spmatrix_add_e directly -- but maybe it's not worth the fuss */ + for (i = (long int) VECTOR(m->cidx)[from]; i < VECTOR(m->cidx)[from + 1]; i++) { + IGRAPH_CHECK(igraph_spmatrix_add_e(m, (long int) VECTOR(m->ridx)[i], + to, VECTOR(m->data)[i])); + } + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup matrix + * \function igraph_spmatrix_resize + * \brief Resizes a sparse matrix. + * + * + * This function resizes a sparse matrix by adding more elements to it. + * The matrix retains its data even after resizing it, except for the data + * which lies outside the new boundaries (if the new size is smaller). + * \param m Pointer to an already initialized sparse matrix object. + * \param nrow The number of rows in the resized matrix. + * \param ncol The number of columns in the resized matrix. + * \return Error code. + * + * Time complexity: O(n). + * n is the number of elements in the old matrix. + */ + +int igraph_spmatrix_resize(igraph_spmatrix_t *m, long int nrow, long int ncol) { + long int i, j, ci, ei, mincol; + IGRAPH_ASSERT(m != NULL); + /* Iterating through the matrix data and deleting unnecessary data. */ + /* At the same time, we create the new indices as well */ + if (nrow < m->nrow) { + ei = j = 0; + mincol = (m->ncol < ncol) ? m->ncol : ncol; + for (ci = 0; ci < mincol; ci++) { + for (; ei < VECTOR(m->cidx)[ci + 1]; ei++) { + if (VECTOR(m->ridx)[ei] < nrow) { + VECTOR(m->ridx)[j] = VECTOR(m->ridx)[ei]; + VECTOR(m->data)[j] = VECTOR(m->data)[ei]; + j++; + } + } + VECTOR(m->cidx)[ci] = j; + } + /* Contract the row index and the data vector */ + IGRAPH_CHECK(igraph_vector_resize(&m->ridx, j)); + IGRAPH_CHECK(igraph_vector_resize(&m->cidx, j)); + } + /* Updating cidx */ + IGRAPH_CHECK(igraph_vector_resize(&m->cidx, ncol + 1)); + for (i = m->ncol + 1; i < ncol + 1; i++) { + VECTOR(m->cidx)[i] = VECTOR(m->cidx)[m->ncol]; + } + m->nrow = nrow; + m->ncol = ncol; + return 0; +} + +/** + * \ingroup matrix + * \function igraph_spmatrix_count_nonzero + * \brief The number of non-zero elements in a sparse matrix. + * + * \param m Pointer to an initialized sparse matrix object. + * \return The size of the matrix. + * + * Time complexity: O(1). + */ + +long int igraph_spmatrix_count_nonzero(const igraph_spmatrix_t *m) { + IGRAPH_ASSERT(m != NULL); + return igraph_vector_size(&m->data); +} + + +/** + * \ingroup matrix + * \function igraph_spmatrix_size + * \brief The number of elements in a sparse matrix. + * + * \param m Pointer to an initialized sparse matrix object. + * \return The size of the matrix. + * + * Time complexity: O(1). + */ + +long int igraph_spmatrix_size(const igraph_spmatrix_t *m) { + IGRAPH_ASSERT(m != NULL); + return (m->nrow) * (m->ncol); +} + +/** + * \ingroup matrix + * \function igraph_spmatrix_nrow + * \brief The number of rows in a sparse matrix. + * + * \param m Pointer to an initialized sparse matrix object. + * \return The number of rows in the matrix. + * + * Time complexity: O(1). + */ + +long int igraph_spmatrix_nrow(const igraph_spmatrix_t *m) { + IGRAPH_ASSERT(m != NULL); + return m->nrow; +} + +/** + * \ingroup matrix + * \function igraph_spmatrix_ncol + * \brief The number of columns in a sparse matrix. + * + * \param m Pointer to an initialized sparse matrix object. + * \return The number of columns in the sparse matrix. + * + * Time complexity: O(1). + */ + +long int igraph_spmatrix_ncol(const igraph_spmatrix_t *m) { + IGRAPH_ASSERT(m != NULL); + return m->ncol; +} + +/** + * \ingroup matrix + * \brief Copies a sparse matrix to a regular C array. + * + * + * The matrix is copied columnwise, as this is the format most + * programs and languages use. + * The C array should be of sufficient size, there are (of course) no + * range checks done. + * \param m Pointer to an initialized sparse matrix object. + * \param to Pointer to a C array, the place to copy the data to. + * \return Error code. + * + * Time complexity: O(n), + * n is the number of + * elements in the matrix. + */ + +int igraph_spmatrix_copy_to(const igraph_spmatrix_t *m, igraph_real_t *to) { + long int c, dest_idx, idx; + + memset(to, 0, sizeof(igraph_real_t) * (size_t) igraph_spmatrix_size(m)); + for (c = 0, dest_idx = 0; c < m->ncol; c++, dest_idx += m->nrow) { + for (idx = (long int) VECTOR(m->cidx)[c]; idx < VECTOR(m->cidx)[c + 1]; idx++) { + to[dest_idx + (long)VECTOR(m->ridx)[idx]] = VECTOR(m->data)[idx]; + } + } + return 0; +} + +/** + * \ingroup matrix + * \brief Sets all element in a sparse matrix to zero. + * + * \param m Pointer to an initialized matrix object. + * \return Error code, always returns with success. + * + * Time complexity: O(n), + * n is the number of columns in the matrix + */ + +int igraph_spmatrix_null(igraph_spmatrix_t *m) { + IGRAPH_ASSERT(m != NULL); + igraph_vector_clear(&m->data); + igraph_vector_clear(&m->ridx); + igraph_vector_null(&m->cidx); + return 0; +} + +/** + * \ingroup matrix + * \function igraph_spmatrix_add_cols + * \brief Adds columns to a sparse matrix. + * \param m The sparse matrix object. + * \param n The number of columns to add. + * \return Error code. + * + * Time complexity: O(1). + */ + +int igraph_spmatrix_add_cols(igraph_spmatrix_t *m, long int n) { + igraph_spmatrix_resize(m, m->nrow, m->ncol + n); + return 0; +} + +/** + * \ingroup matrix + * \function igraph_spmatrix_add_rows + * \brief Adds rows to a sparse matrix. + * \param m The sparse matrix object. + * \param n The number of rows to add. + * \return Error code. + * + * Time complexity: O(1). + */ + +int igraph_spmatrix_add_rows(igraph_spmatrix_t *m, long int n) { + igraph_spmatrix_resize(m, m->nrow + n, m->ncol); + return 0; +} + +/** + * \function igraph_spmatrix_clear_row + * \brief Clears a row in the matrix (sets all of its elements to zero). + * \param m The matrix. + * \param row The index of the row to be cleared. + * \return Error code. + * + * Time complexity: O(n), the number of nonzero elements in the matrix. + */ + +int igraph_spmatrix_clear_row(igraph_spmatrix_t *m, long int row) { + if (row < 0 || row >= m->nrow) { + IGRAPH_ERROR("The row does not exist.", IGRAPH_EINVAL); + } + long int ci, ei, i, j, nremove = 0, nremove_old = 0; + igraph_vector_t permvec; + + IGRAPH_ASSERT(m != NULL); + IGRAPH_VECTOR_INIT_FINALLY(&permvec, igraph_vector_size(&m->data)); + for (ci = 0, i = 0, j = 1; ci < m->ncol; ci++) { + for (ei = (long int) VECTOR(m->cidx)[ci]; ei < VECTOR(m->cidx)[ci + 1]; ei++) { + if (VECTOR(m->ridx)[ei] == row) { + /* this element will be deleted, so all elements in cidx from the + * column index of this element will have to be decreased by one */ + nremove++; + } else { + /* this element will be kept */ + VECTOR(permvec)[i] = j; + j++; + } + i++; + } + if (ci > 0) { + VECTOR(m->cidx)[ci] -= nremove_old; + } + nremove_old = nremove; + } + VECTOR(m->cidx)[m->ncol] -= nremove; + igraph_vector_permdelete(&m->ridx, &permvec, nremove); + igraph_vector_permdelete(&m->data, &permvec, nremove); + igraph_vector_destroy(&permvec); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/* Unused local functions---temporarily disabled */ +#if 0 +static int igraph_i_spmatrix_clear_row_fast(igraph_spmatrix_t *m, long int row) { + long int ei, n; + + IGRAPH_ASSERT(m != NULL); + n = igraph_vector_size(&m->data); + for (ei = 0; ei < n; ei++) { + if (VECTOR(m->ridx)[ei] == row) { + VECTOR(m->data)[ei] = 0.0; + } + } + return 0; +} + +static int igraph_i_spmatrix_cleanup(igraph_spmatrix_t *m) { + long int ci, ei, i, j, nremove = 0, nremove_old = 0; + igraph_vector_t permvec; + + IGRAPH_ASSERT(m != NULL); + IGRAPH_VECTOR_INIT_FINALLY(&permvec, igraph_vector_size(&m->data)); + for (ci = 0, i = 0, j = 1; ci < m->ncol; ci++) { + for (ei = (long int) VECTOR(m->cidx)[ci]; ei < VECTOR(m->cidx)[ci + 1]; ei++) { + if (VECTOR(m->data)[ei] == 0.0) { + /* this element will be deleted, so all elements in cidx from the + * column index of this element will have to be decreased by one */ + nremove++; + } else { + /* this element will be kept */ + VECTOR(permvec)[i] = j; + j++; + } + i++; + } + if (ci > 0) { + VECTOR(m->cidx)[ci] -= nremove_old; + } + nremove_old = nremove; + } + VECTOR(m->cidx)[m->ncol] -= nremove; + igraph_vector_permdelete(&m->ridx, &permvec, nremove); + igraph_vector_permdelete(&m->data, &permvec, nremove); + igraph_vector_destroy(&permvec); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} +#endif + +/** + * \function igraph_spmatrix_clear_col + * \brief Clears a column in the matrix (sets all of its elements to zero). + * \param m The matrix. + * \param col The index of the column to be cleared. + * \return Error code. + * + * Time complexity: TODO + */ + +int igraph_spmatrix_clear_col(igraph_spmatrix_t *m, long int col) { + if (col < 0 || col >= m->ncol) { + IGRAPH_ERROR("The column does not exist.", IGRAPH_EINVAL); + } + long int i, n; + IGRAPH_ASSERT(m != NULL); + n = (long)VECTOR(m->cidx)[col + 1] - (long)VECTOR(m->cidx)[col]; + if (n == 0) { + return 0; + } + igraph_vector_remove_section(&m->ridx, (long int) VECTOR(m->cidx)[col], + (long int) VECTOR(m->cidx)[col + 1]); + igraph_vector_remove_section(&m->data, (long int) VECTOR(m->cidx)[col], + (long int) VECTOR(m->cidx)[col + 1]); + for (i = col + 1; i <= m->ncol; i++) { + VECTOR(m->cidx)[i] -= n; + } + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_spmatrix_scale + * \brief Multiplies each element of the sparse matrix by a constant. + * \param m The matrix. + * \param by The constant. + * + * Time complexity: O(n), the number of elements in the matrix. + */ + +void igraph_spmatrix_scale(igraph_spmatrix_t *m, igraph_real_t by) { + IGRAPH_ASSERT(m != NULL); + igraph_vector_scale(&m->data, by); +} + +/** + * \function igraph_spmatrix_colsums + * \brief Calculates the column sums of the matrix. + * \param m The matrix. + * \param res An initialized \c igraph_vector_t, the result will be stored here. + * The vector will be resized as needed. + * + * Time complexity: O(n), the number of nonzero elements in the matrix. + */ + +int igraph_spmatrix_colsums(const igraph_spmatrix_t *m, igraph_vector_t *res) { + long int i, c; + IGRAPH_ASSERT(m != NULL); + IGRAPH_CHECK(igraph_vector_resize(res, m->ncol)); + igraph_vector_null(res); + for (c = 0; c < m->ncol; c++) { + for (i = (long int) VECTOR(m->cidx)[c]; i < VECTOR(m->cidx)[c + 1]; i++) { + VECTOR(*res)[c] += VECTOR(m->data)[i]; + } + } + return 0; +} + +/** + * \function igraph_spmatrix_rowsums + * \brief Calculates the row sums of the matrix. + * \param m The matrix. + * \param res An initialized \c igraph_vector_t, the result will be stored here. + * The vector will be resized as needed. + * + * Time complexity: O(n), the number of nonzero elements in the matrix. + */ + +int igraph_spmatrix_rowsums(const igraph_spmatrix_t *m, igraph_vector_t *res) { + long int i, n; + IGRAPH_ASSERT(m != NULL); + + IGRAPH_CHECK(igraph_vector_resize(res, m->nrow)); + n = igraph_vector_size(&m->data); + igraph_vector_null(res); + for (i = 0; i < n; i++) { + VECTOR(*res)[(long int)VECTOR(m->ridx)[i]] += VECTOR(m->data)[i]; + } + return 0; +} + +/** + * \function igraph_spmatrix_max_nonzero + * \brief Returns the maximum nonzero element of a matrix. + * If the matrix is empty, zero is returned. + * + * \param m the matrix object. + * \param ridx the row index of the maximum element if not \c NULL. + * \param cidx the column index of the maximum element if not \c NULL. + * + * Time complexity: O(n), the number of nonzero elements in the matrix. + */ +igraph_real_t igraph_spmatrix_max_nonzero(const igraph_spmatrix_t *m, + igraph_real_t *ridx, igraph_real_t *cidx) { + igraph_real_t res; + long int i, n, maxidx; + + IGRAPH_ASSERT(m != NULL); + n = igraph_vector_size(&m->data); + if (n == 0) { + return 0.0; + } + + maxidx = -1; + for (i = 0; i < n; i++) + if (VECTOR(m->data)[i] != 0.0 && + (maxidx == -1 || VECTOR(m->data)[i] >= VECTOR(m->data)[maxidx])) { + maxidx = i; + } + + if (maxidx == -1) { + return 0.0; + } + + res = VECTOR(m->data)[maxidx]; + if (ridx != 0) { + *ridx = VECTOR(m->ridx)[maxidx]; + } + if (cidx != 0) { + igraph_vector_binsearch(&m->cidx, maxidx, &i); + while (VECTOR(m->cidx)[i + 1] == VECTOR(m->cidx)[i]) { + i++; + } + *cidx = (igraph_real_t)i; + } + return res; +} + +/** + * \function igraph_spmatrix_max + * \brief Returns the maximum element of a matrix. + * If the matrix is empty, zero is returned. + * + * \param m the matrix object. + * \param ridx the row index of the maximum element if not \c NULL. + * \param cidx the column index of the maximum element if not \c NULL. + * + * Time complexity: O(n), the number of nonzero elements in the matrix. + */ +igraph_real_t igraph_spmatrix_max(const igraph_spmatrix_t *m, + igraph_real_t *ridx, igraph_real_t *cidx) { + igraph_real_t res; + long int i, j, k, maxidx; + + IGRAPH_ASSERT(m != NULL); + i = igraph_vector_size(&m->data); + if (i == 0) { + return 0.0; + } + + maxidx = (long)igraph_vector_which_max(&m->data); + res = VECTOR(m->data)[maxidx]; + if (res >= 0.0 || i == m->nrow * m->ncol) { + if (ridx != 0) { + *ridx = VECTOR(m->ridx)[maxidx]; + } + if (cidx != 0) { + igraph_vector_binsearch(&m->cidx, maxidx, &i); + i--; + while (i < m->ncol - 1 && VECTOR(m->cidx)[i + 1] == VECTOR(m->cidx)[i]) { + i++; + } + *cidx = (igraph_real_t)i; + } + return res; + } + /* the maximal nonzero element is negative and there is at least a + * single zero + */ + res = 0.0; + if (cidx != 0 || ridx != 0) { + for (i = 0; i < m->ncol; i++) { + if (VECTOR(m->cidx)[i + 1] - VECTOR(m->cidx)[i] < m->nrow) { + if (cidx != 0) { + *cidx = i; + } + if (ridx != 0) { + for (j = (long int) VECTOR(m->cidx)[i], k = 0; + j < VECTOR(m->cidx)[i + 1]; j++, k++) { + if (VECTOR(m->ridx)[j] != k) { + *ridx = k; + break; + } + } + } + break; + } + } + } + + return res; +} + + +/* Unused function, temporarily disabled */ +/* +static int igraph_i_spmatrix_get_col_nonzero_indices(const igraph_spmatrix_t *m, + igraph_vector_t *res, long int col) { + long int i, n; + IGRAPH_ASSERT(m != NULL); + n = (long int) (VECTOR(m->cidx)[col + 1] - VECTOR(m->cidx)[col]); + IGRAPH_CHECK(igraph_vector_resize(res, n)); + for (i = (long int) VECTOR(m->cidx)[col], n = 0; + i < VECTOR(m->cidx)[col + 1]; i++, n++) + if (VECTOR(m->data)[i] != 0.0) { + VECTOR(*res)[n] = VECTOR(m->ridx)[i]; + } + return 0; +} +*/ + + +/** + * \section igraph_spmatrix_iterating Iterating over the non-zero elements of a sparse matrix + * + * The \type igraph_spmatrix_iter_t type represents an iterator that can + * be used to step over the non-zero elements of a sparse matrix in columnwise + * order efficiently. In general, you shouldn't modify the elements of the matrix + * while iterating over it; doing so will probably invalidate the iterator, but + * there are no checks to prevent you from doing this. + * + * To access the row index of the current element of the iterator, use its + * \c ri field. Similarly, the \c ci field stores the column index of the current + * element and the \c value field stores the value of the element. + */ + +/** + * \function igraph_spmatrix_iter_create + * \brief Creates a sparse matrix iterator corresponding to the given matrix. + * + * \param mit pointer to the matrix iterator being initialized + * \param m pointer to the matrix we will be iterating over + * \return Error code. The current implementation is always successful. + * + * Time complexity: O(1). + */ +int igraph_spmatrix_iter_create(igraph_spmatrix_iter_t *mit, const igraph_spmatrix_t *m) { + mit->m = m; + IGRAPH_CHECK(igraph_spmatrix_iter_reset(mit)); + return 0; +} + +/** + * \function igraph_spmatrix_iter_reset + * \brief Resets a sparse matrix iterator. + * + * + * After resetting, the iterator will point to the first nonzero element (if any). + * + * \param mit pointer to the matrix iterator being reset + * \return Error code. The current implementation is always successful. + * + * Time complexity: O(1). + */ +int igraph_spmatrix_iter_reset(igraph_spmatrix_iter_t *mit) { + IGRAPH_ASSERT(mit->m); + + if (igraph_spmatrix_count_nonzero(mit->m) == 0) { + mit->pos = mit->ri = mit->ci = -1L; + mit->value = -1; + return 0; + } + + mit->ci = 0; + mit->pos = -1; + + IGRAPH_CHECK(igraph_spmatrix_iter_next(mit)); + + return 0; +} + +/** + * \function igraph_spmatrix_iter_next + * \brief Moves a sparse matrix iterator to the next nonzero element. + * + * + * You should call this function only if \ref igraph_spmatrix_iter_end() + * returns FALSE (0). + * + * \param mit pointer to the matrix iterator being moved + * \return Error code. The current implementation is always successful. + * + * Time complexity: O(1). + */ +int igraph_spmatrix_iter_next(igraph_spmatrix_iter_t *mit) { + mit->pos++; + + if (igraph_spmatrix_iter_end(mit)) { + return 0; + } + + mit->ri = (long int)VECTOR(mit->m->ridx)[mit->pos]; + mit->value = VECTOR(mit->m->data)[mit->pos]; + + while (VECTOR(mit->m->cidx)[mit->ci + 1] <= mit->pos) { + mit->ci++; + } + + return 0; +} + +/** + * \function igraph_spmatrix_iter_end + * \brief Checks whether there are more elements in the iterator. + * + * + * You should call this function before calling \ref igraph_spmatrix_iter_next() + * to make sure you have more elements in the iterator. + * + * \param mit pointer to the matrix iterator being checked + * \return TRUE (1) if there are more elements in the iterator, + * FALSE (0) otherwise. + * + * Time complexity: O(1). + */ +igraph_bool_t igraph_spmatrix_iter_end(igraph_spmatrix_iter_t *mit) { + return mit->pos >= igraph_spmatrix_count_nonzero(mit->m); +} + +/** + * \function igraph_spmatrix_iter_destroy + * \brief Frees the memory used by the iterator. + * + * + * The current implementation does not allocate any memory upon + * creation, so this function does nothing. However, since there is + * no guarantee that future implementations will not allocate any + * memory in \ref igraph_spmatrix_iter_create(), you are still + * required to call this function whenever you are done with the + * iterator. + * + * \param mit pointer to the matrix iterator being destroyed + * + * Time complexity: O(1). + */ +void igraph_spmatrix_iter_destroy(igraph_spmatrix_iter_t *mit) { + IGRAPH_UNUSED(mit); + /* Nothing to do at the moment */ +} + +#ifndef USING_R +/** + * \function igraph_spmatrix_print + * \brief Prints a sparse matrix. + * + * Prints a sparse matrix to the standard output. Only the non-zero entries + * are printed. + * + * \return Error code. + * + * Time complexity: O(n), the number of non-zero elements. + */ +int igraph_spmatrix_print(const igraph_spmatrix_t* matrix) { + return igraph_spmatrix_fprint(matrix, stdout); +} +#endif + +/** + * \function igraph_spmatrix_fprint + * \brief Prints a sparse matrix to the given file. + * + * Prints a sparse matrix to the given file. Only the non-zero entries + * are printed. + * + * \return Error code. + * + * Time complexity: O(n), the number of non-zero elements. + */ +int igraph_spmatrix_fprint(const igraph_spmatrix_t* matrix, FILE *file) { + igraph_spmatrix_iter_t mit; + + IGRAPH_CHECK(igraph_spmatrix_iter_create(&mit, matrix)); + IGRAPH_FINALLY(igraph_spmatrix_iter_destroy, &mit); + while (!igraph_spmatrix_iter_end(&mit)) { + fprintf(file, "[%ld, %ld] = %.4f\n", (long int)mit.ri, + (long int)mit.ci, mit.value); + igraph_spmatrix_iter_next(&mit); + } + igraph_spmatrix_iter_destroy(&mit); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} diff --git a/src/rigraph/core/core/stack.c b/src/rigraph/core/core/stack.c new file mode 100644 index 0000000..66ce729 --- /dev/null +++ b/src/rigraph/core/core/stack.c @@ -0,0 +1,88 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_stack.h" + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "stack.pmt" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#define BASE_LONG +#include "igraph_pmt.h" +#include "stack.pmt" +#include "igraph_pmt_off.h" +#undef BASE_LONG + +#define BASE_INT +#include "igraph_pmt.h" +#include "stack.pmt" +#include "igraph_pmt_off.h" +#undef BASE_INT + +#define BASE_CHAR +#include "igraph_pmt.h" +#include "stack.pmt" +#include "igraph_pmt_off.h" +#undef BASE_CHAR + +#define BASE_BOOL +#include "igraph_pmt.h" +#include "stack.pmt" +#include "igraph_pmt_off.h" +#undef BASE_BOOL + +#define BASE_PTR +#include "igraph_pmt.h" +#include "stack.pmt" +#include "igraph_pmt_off.h" +#undef BASE_PTR + +/** + * \ingroup stack + * \brief Calls free() on all elements of a pointer stack. + */ + +void igraph_stack_ptr_free_all(igraph_stack_ptr_t* v) { + void **ptr; + IGRAPH_ASSERT(v != 0); + IGRAPH_ASSERT(v->stor_begin != 0); + for (ptr = v->stor_begin; ptr < v->end; ptr++) { + IGRAPH_FREE(*ptr); + } +} + +/** + * \ingroup stack + * \brief Calls free() on all elements and destroys the stack. + */ + +void igraph_stack_ptr_destroy_all(igraph_stack_ptr_t* v) { + IGRAPH_ASSERT(v != 0); + IGRAPH_ASSERT(v->stor_begin != 0); + igraph_stack_ptr_free_all(v); + igraph_stack_ptr_destroy(v); +} diff --git a/src/rigraph/core/core/stack.pmt b/src/rigraph/core/core/stack.pmt new file mode 100644 index 0000000..dc57fcc --- /dev/null +++ b/src/rigraph/core/core/stack.pmt @@ -0,0 +1,293 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "config.h" + +#include /* memcpy & co. */ +#include + +/** + * \ingroup stack + * \function igraph_stack_init + * \brief Initializes a stack. + * + * The initialized stack is always empty. + * \param s Pointer to an uninitialized stack. + * \param size The number of elements to allocate memory for. + * \return Error code. + * + * Time complexity: O(\p size). + */ + +int FUNCTION(igraph_stack, init) (TYPE(igraph_stack)* s, long int size) { + long int alloc_size; + IGRAPH_ASSERT(s != NULL); + if (size < 0) { + size = 0; + } + alloc_size = size > 0 ? size : 1; + s->stor_begin = IGRAPH_CALLOC(alloc_size, BASE); + if (s->stor_begin == 0) { + IGRAPH_ERROR("stack init failed", IGRAPH_ENOMEM); + } + s->stor_end = s->stor_begin + alloc_size; + s->end = s->stor_begin; + + return 0; +} + +/** + * \ingroup stack + * \function igraph_stack_destroy + * \brief Destroys a stack object. + * + * Deallocate the memory used for a stack. + * It is possible to reinitialize a destroyed stack again by + * \ref igraph_stack_init(). + * \param s The stack to destroy. + * + * Time complexity: O(1). + */ + +void FUNCTION(igraph_stack, destroy) (TYPE(igraph_stack)* s) { + IGRAPH_ASSERT(s != NULL); + if (s->stor_begin != 0) { + IGRAPH_FREE(s->stor_begin); + s->stor_begin = NULL; + } +} + +/** + * \ingroup stack + * \function igraph_stack_reserve + * \brief Reserve memory. + * + * Reserve memory for future use. The actual size of the stack is + * unchanged. + * \param s The stack object. + * \param size The number of elements to reserve memory for. If it is + * not bigger than the current size then nothing happens. + * \return Error code. + * + * Time complexity: should be around O(n), the new allocated size of + * the stack. + */ + +int FUNCTION(igraph_stack, reserve) (TYPE(igraph_stack)* s, long int size) { + long int actual_size = FUNCTION(igraph_stack, size)(s); + BASE *tmp; + IGRAPH_ASSERT(s != NULL); + IGRAPH_ASSERT(s->stor_begin != NULL); + + if (size <= actual_size) { + return 0; + } + + tmp = IGRAPH_REALLOC(s->stor_begin, (size_t) size, BASE); + if (tmp == 0) { + IGRAPH_ERROR("stack reserve failed", IGRAPH_ENOMEM); + } + s->stor_begin = tmp; + s->stor_end = s->stor_begin + size; + s->end = s->stor_begin + actual_size; + + return 0; +} + +/** + * \ingroup stack + * \function igraph_stack_empty + * \brief Decides whether a stack object is empty. + * + * \param s The stack object. + * \return Boolean, \c TRUE if the stack is empty, \c FALSE + * otherwise. + * + * Time complexity: O(1). + */ + +igraph_bool_t FUNCTION(igraph_stack, empty) (TYPE(igraph_stack)* s) { + IGRAPH_ASSERT(s != NULL); + IGRAPH_ASSERT(s->stor_begin != NULL); + IGRAPH_ASSERT(s->end != NULL); + return s->stor_begin == s->end; +} + +/** + * \ingroup stack + * \function igraph_stack_size + * \brief Returns the number of elements in a stack. + * + * \param s The stack object. + * \return The number of elements in the stack. + * + * Time complexity: O(1). + */ + +long int FUNCTION(igraph_stack, size) (const TYPE(igraph_stack)* s) { + IGRAPH_ASSERT(s != NULL); + IGRAPH_ASSERT(s->stor_begin != NULL); + return s->end - s->stor_begin; +} + +/** + * \ingroup stack + * \function igraph_stack_clear + * \brief Removes all elements from a stack. + * + * \param s The stack object. + * + * Time complexity: O(1). + */ + +void FUNCTION(igraph_stack, clear) (TYPE(igraph_stack)* s) { + IGRAPH_ASSERT(s != NULL); + IGRAPH_ASSERT(s->stor_begin != NULL); + s->end = s->stor_begin; +} + +/** + * \ingroup stack + * \function igraph_stack_push + * \brief Places an element on the top of a stack. + * + * The capacity of the stack is increased, if needed. + * \param s The stack object. + * \param elem The element to push. + * \return Error code. + * + * Time complexity: O(1) is no reallocation is needed, O(n) + * otherwise, but it is ensured that n push operations are performed + * in O(n) time. + */ + +int FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem) { + IGRAPH_ASSERT(s != NULL); + IGRAPH_ASSERT(s->stor_begin != NULL); + if (s->end == s->stor_end) { + /* full, allocate more storage */ + + BASE *bigger = NULL, *old = s->stor_begin; + + bigger = IGRAPH_CALLOC(2 * FUNCTION(igraph_stack, size)(s), BASE); + if (bigger == 0) { + IGRAPH_ERROR("stack push failed", IGRAPH_ENOMEM); + } + memcpy(bigger, s->stor_begin, + (size_t) FUNCTION(igraph_stack, size)(s)*sizeof(BASE)); + + s->end = bigger + (s->stor_end - s->stor_begin); + s->stor_end = bigger + 2 * (s->stor_end - s->stor_begin); + s->stor_begin = bigger; + + *(s->end) = elem; + (s->end) += 1; + + IGRAPH_FREE(old); + } else { + *(s->end) = elem; + (s->end) += 1; + } + return 0; +} + +/** + * \ingroup stack + * \function igraph_stack_pop + * \brief Removes and returns an element from the top of a stack. + * + * The stack must contain at least one element, call \ref + * igraph_stack_empty() to make sure of this. + * \param s The stack object. + * \return The removed top element. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_stack, pop) (TYPE(igraph_stack)* s) { + + IGRAPH_ASSERT(s != NULL); + IGRAPH_ASSERT(s->stor_begin != NULL); + IGRAPH_ASSERT(s->end != NULL); + IGRAPH_ASSERT(s->end != s->stor_begin); + + (s->end)--; + + return *(s->end); +} + +/** + * \ingroup stack + * \function igraph_stack_top + * \brief Query top element. + * + * Returns the top element of the stack, without removing it. + * The stack must be non-empty. + * \param s The stack. + * \return The top element. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_stack, top) (const TYPE(igraph_stack)* s) { + + IGRAPH_ASSERT(s != NULL); + IGRAPH_ASSERT(s->stor_begin != NULL); + IGRAPH_ASSERT(s->end != NULL); + IGRAPH_ASSERT(s->end != s->stor_begin); + + return *(s->end - 1); +} + +#if defined (OUT_FORMAT) +#ifndef USING_R + +int FUNCTION(igraph_stack, print)(const TYPE(igraph_stack) *s) { + long int i, n = FUNCTION(igraph_stack, size)(s); + if (n != 0) { + printf(OUT_FORMAT, s->stor_begin[0]); + } + for (i = 1; i < n; i++) { + printf(" " OUT_FORMAT, s->stor_begin[i]); + } + printf("\n"); + return 0; +} +#endif + +int FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack) *s, FILE *file) { + long int i, n = FUNCTION(igraph_stack, size)(s); + if (n != 0) { + fprintf(file, OUT_FORMAT, s->stor_begin[0]); + } + for (i = 1; i < n; i++) { + fprintf(file, " " OUT_FORMAT, s->stor_begin[i]); + } + fprintf(file, "\n"); + return 0; +} + +#endif diff --git a/src/rigraph/core/core/statusbar.c b/src/rigraph/core/core/statusbar.c new file mode 100644 index 0000000..0c3358d --- /dev/null +++ b/src/rigraph/core/core/statusbar.c @@ -0,0 +1,130 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_statusbar.h" +#include "igraph_error.h" + +#include "config.h" + +#include +#include + +static igraph_status_handler_t *igraph_i_status_handler = 0; + +/** + * \function igraph_status + * Report status from an igraph function. + * + * It calls the installed status handler function, if there is + * one. Otherwise it does nothing. Note that the standard way to + * report the status from an igraph function is the + * \ref IGRAPH_STATUS or \ref IGRAPH_STATUSF macro, as these + * take care of the termination of the calling function if the + * status handler returns with \c IGRAPH_INTERRUPTED. + * \param message The status message. + * \param data Additional context, with user-defined semantics. + * Existing igraph functions pass a null pointer here. + * \return Error code. If a status handler function was called + * and it did not return with \c IGRAPH_SUCCESS, then + * \c IGRAPH_INTERRUPTED is returned by \c igraph_status(). + * + * Time complexity: O(1). + */ + +int igraph_status(const char *message, void *data) { + if (igraph_i_status_handler) { + if (igraph_i_status_handler(message, data) != IGRAPH_SUCCESS) { + return IGRAPH_INTERRUPTED; + } + } + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_statusf + * Report status, more flexible printf-like version. + * + * This is the more flexible version of \ref igraph_status(), + * that has a syntax similar to the \c printf standard C library function. + * It substitutes the values of the additional arguments into the + * \p message template string and calls \ref igraph_status(). + * \param message Status message template string, the syntax is the same + * as for the \c printf function. + * \param data Additional context, with user-defined semantics. + * Existing igraph functions pass a null pointer here. + * \param ... The additional arguments to fill the template given in the + * \p message argument. + * \return Error code. If a status handler function was called + * and it did not return with \c IGRAPH_SUCCESS, then + * \c IGRAPH_INTERRUPTED is returned by \c igraph_status(). + */ + +int igraph_statusf(const char *message, void *data, ...) { + char buffer[300]; + va_list ap; + va_start(ap, data); + vsnprintf(buffer, sizeof(buffer) - 1, message, ap); + return igraph_status(buffer, data); +} + +#ifndef USING_R + +/** + * \function igraph_status_handler_stderr + * A simple predefined status handler function. + * + * A simple status handler function, that writes the status + * message to the standard errror. + * \param message The status message. + * \param data Additional context, with user-defined semantics. + * Existing igraph functions pass a null pointer here. + * \return Error code. + * + * Time complexity: O(1). + */ + +int igraph_status_handler_stderr(const char *message, void *data) { + IGRAPH_UNUSED(data); + fputs(message, stderr); + return 0; +} +#endif + +/** + * \function igraph_set_status_handler + * Install of uninstall a status handler function. + * + * To uninstall the currently installed status handler, call + * this function with a null pointer. + * \param new_handler The status handler function to install. + * \return The previously installed status handler function. + * + * Time complexity: O(1). + */ + +igraph_status_handler_t * +igraph_set_status_handler(igraph_status_handler_t new_handler) { + igraph_status_handler_t *previous_handler = igraph_i_status_handler; + igraph_i_status_handler = new_handler; + return previous_handler; +} diff --git a/src/rigraph/core/core/strvector.c b/src/rigraph/core/core/strvector.c new file mode 100644 index 0000000..080b356 --- /dev/null +++ b/src/rigraph/core/core/strvector.c @@ -0,0 +1,603 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" +#include "igraph_strvector.h" +#include "igraph_memory.h" +#include "igraph_error.h" + +#include /* memcpy & co. */ +#include + +/** + * \section igraph_strvector_t + * The igraph_strvector_t type is a vector of strings. + * The current implementation is very simple and not too efficient. It + * works fine for not too many strings, e.g. the list of attribute + * names is returned in a string vector by \ref + * igraph_cattribute_list(). Do not expect great performance from this + * type. + * + * + * \example examples/simple/igraph_strvector.c + * + */ + +/** + * \ingroup strvector + * \function igraph_strvector_init + * \brief Initialize + * + * Reserves memory for the string vector, a string vector must be + * first initialized before calling other functions on it. + * All elements of the string vector are set to the empty string. + * \param sv Pointer to an initialized string vector. + * \param len The (initial) length of the string vector. + * \return Error code. + * + * Time complexity: O(\p len). + */ + +int igraph_strvector_init(igraph_strvector_t *sv, long int len) { + long int i; + sv->data = IGRAPH_CALLOC(len, char*); + if (sv->data == 0) { + IGRAPH_ERROR("strvector init failed", IGRAPH_ENOMEM); + } + for (i = 0; i < len; i++) { + sv->data[i] = IGRAPH_CALLOC(1, char); + if (sv->data[i] == 0) { + igraph_strvector_destroy(sv); + IGRAPH_ERROR("strvector init failed", IGRAPH_ENOMEM); + } + sv->data[i][0] = '\0'; + } + sv->len = len; + + return 0; +} + +/** + * \ingroup strvector + * \function igraph_strvector_destroy + * \brief Free allocated memory + * + * Destroy a string vector. It may be reinitialized with \ref + * igraph_strvector_init() later. + * \param sv The string vector. + * + * Time complexity: O(l), the total length of the strings, maybe less + * depending on the memory manager. + */ + +void igraph_strvector_destroy(igraph_strvector_t *sv) { + long int i; + IGRAPH_ASSERT(sv != 0); + if (sv->data != 0) { + for (i = 0; i < sv->len; i++) { + if (sv->data[i] != 0) { + IGRAPH_FREE(sv->data[i]); + } + } + IGRAPH_FREE(sv->data); + } +} + +/** + * \ingroup strvector + * \function igraph_strvector_get + * \brief Indexing + * + * Query an element of a string vector. See also the \ref STR macro + * for an easier way. + * \param sv The input string vector. + * \param idx The index of the element to query. + * \param Pointer to a char*, the address of the string + * is stored here. + * + * Time complexity: O(1). + */ + +void igraph_strvector_get(const igraph_strvector_t *sv, long int idx, + char **value) { + IGRAPH_ASSERT(sv != 0); + IGRAPH_ASSERT(sv->data != 0); + IGRAPH_ASSERT(sv->data[idx] != 0); + *value = sv->data[idx]; +} + +/** + * \ingroup strvector + * \function igraph_strvector_set + * \brief Set an element + * + * The provided \p value is copied into the \p idx position in the + * string vector. + * \param sv The string vector. + * \param idx The position to set. + * \param value The new value. + * \return Error code. + * + * Time complexity: O(l), the length of the new string. Maybe more, + * depending on the memory management, if reallocation is needed. + */ + +int igraph_strvector_set(igraph_strvector_t *sv, long int idx, + const char *value) { + size_t value_len; + + IGRAPH_ASSERT(sv != 0); + IGRAPH_ASSERT(sv->data != 0); + + value_len = strlen(value); + if (sv->data[idx] == 0) { + sv->data[idx] = IGRAPH_CALLOC(value_len + 1, char); + if (sv->data[idx] == 0) { + IGRAPH_ERROR("strvector set failed", IGRAPH_ENOMEM); + } + } else { + char *tmp = IGRAPH_REALLOC(sv->data[idx], value_len + 1, char); + if (tmp == 0) { + IGRAPH_ERROR("strvector set failed", IGRAPH_ENOMEM); + } + sv->data[idx] = tmp; + } + strcpy(sv->data[idx], value); + + return 0; +} + +/** + * \ingroup strvector + * \function igraph_strvector_set2 + * \brief Sets an element. + * + * This is almost the same as \ref igraph_strvector_set, but the new + * value is not a zero terminated string, but its length is given. + * \param sv The string vector. + * \param idx The position to set. + * \param value The new value. + * \param len The length of the new value. + * \return Error code. + * + * Time complexity: O(l), the length of the new string. Maybe more, + * depending on the memory management, if reallocation is needed. + */ +int igraph_strvector_set2(igraph_strvector_t *sv, long int idx, + const char *value, int len) { + if (idx < 0 || idx >= sv->len) { + IGRAPH_ERROR("String vector index out of bounds.", IGRAPH_EINVAL); + } + IGRAPH_ASSERT(sv != 0); + IGRAPH_ASSERT(sv->data != 0); + if (sv->data[idx] == 0) { + sv->data[idx] = IGRAPH_CALLOC(len + 1, char); + if (sv->data[idx] == 0) { + IGRAPH_ERROR("strvector set failed", IGRAPH_ENOMEM); + } + } else { + char *tmp = IGRAPH_REALLOC(sv->data[idx], (size_t) len + 1, char); + if (tmp == 0) { + IGRAPH_ERROR("strvector set failed", IGRAPH_ENOMEM); + } + sv->data[idx] = tmp; + } + memcpy(sv->data[idx], value, (size_t) len * sizeof(char)); + sv->data[idx][len] = '\0'; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_remove_section + * \brief Removes a section from a string vector. + * \todo repair realloc + */ + +void igraph_strvector_remove_section(igraph_strvector_t *v, long int from, + long int to) { + long int i; + /* char **tmp; */ + + IGRAPH_ASSERT(v != 0); + IGRAPH_ASSERT(v->data != 0); + + for (i = from; i < to; i++) { + if (v->data[i] != 0) { + IGRAPH_FREE(v->data[i]); + } + } + for (i = 0; i < v->len - to; i++) { + v->data[from + i] = v->data[to + i]; + } + + v->len -= (to - from); + + /* try to make it smaller */ + /* tmp=IGRAPH_REALLOC(v->data, v->len, char*); */ + /* if (tmp!=0) { */ + /* v->data=tmp; */ + /* } */ +} + +/** + * \ingroup strvector + * \function igraph_strvector_remove + * \brief Removes a single element from a string vector. + * + * The string will be one shorter. + * \param v The string vector. + * \param elem The index of the element to remove. + * + * Time complexity: O(n), the length of the string. + */ + +void igraph_strvector_remove(igraph_strvector_t *v, long int elem) { + IGRAPH_ASSERT(v != 0); + IGRAPH_ASSERT(v->data != 0); + igraph_strvector_remove_section(v, elem, elem + 1); +} + +/** + * \ingroup strvector + * \function igraph_strvector_move_interval + * \brief Copies an interval of a string vector. + */ + +void igraph_strvector_move_interval(igraph_strvector_t *v, long int begin, + long int end, long int to) { + long int i; + IGRAPH_ASSERT(v != 0); + IGRAPH_ASSERT(v->data != 0); + for (i = to; i < to + end - begin; i++) { + if (v->data[i] != 0) { + IGRAPH_FREE(v->data[i]); + } + } + for (i = 0; i < end - begin; i++) { + if (v->data[begin + i] != 0) { + size_t len = strlen(v->data[begin + i]) + 1; + v->data[to + i] = IGRAPH_CALLOC(len, char); + memcpy(v->data[to + i], v->data[begin + i], sizeof(char)*len); + } + } +} + +/** + * \ingroup strvector + * \function igraph_strvector_copy + * \brief Initialization by copying. + * + * Initializes a string vector by copying another string vector. + * \param to Pointer to an uninitialized string vector. + * \param from The other string vector, to be copied. + * \return Error code. + * + * Time complexity: O(l), the total length of the strings in \p from. + */ + +int igraph_strvector_copy(igraph_strvector_t *to, + const igraph_strvector_t *from) { + long int i; + char *str; + IGRAPH_ASSERT(from != 0); + /* IGRAPH_ASSERT(from->data != 0); */ + to->data = IGRAPH_CALLOC(from->len, char*); + if (to->data == 0) { + IGRAPH_ERROR("Cannot copy string vector", IGRAPH_ENOMEM); + } + to->len = from->len; + + for (i = 0; i < from->len; i++) { + int ret; + igraph_strvector_get(from, i, &str); + ret = igraph_strvector_set(to, i, str); + if (ret != 0) { + igraph_strvector_destroy(to); + IGRAPH_ERROR("cannot copy string vector", ret); + } + } + + return 0; +} + +/** + * \function igraph_strvector_append + * Concatenate two string vectors. + * + * \param to The first string vector, the result is stored here. + * \param from The second string vector, it is kept unchanged. + * \return Error code. + * + * Time complexity: O(n+l2), n is the number of strings in the new + * string vector, l2 is the total length of strings in the \p from + * string vector. + */ + +int igraph_strvector_append(igraph_strvector_t *to, + const igraph_strvector_t *from) { + long int len1 = igraph_strvector_size(to), len2 = igraph_strvector_size(from); + long int i; + igraph_bool_t error = 0; + IGRAPH_CHECK(igraph_strvector_resize(to, len1 + len2)); + for (i = 0; i < len2; i++) { + if (from->data[i][0] != '\0') { + IGRAPH_FREE(to->data[len1 + i]); + to->data[len1 + i] = strdup(from->data[i]); + if (!to->data[len1 + i]) { + error = 1; + break; + } + } + } + if (error) { + igraph_strvector_resize(to, len1); + IGRAPH_ERROR("Cannot append string vector", IGRAPH_ENOMEM); + } + return 0; +} + +/** + * \function igraph_strvector_clear + * Remove all elements + * + * After this operation the string vector will be empty. + * \param sv The string vector. + * + * Time complexity: O(l), the total length of strings, maybe less, + * depending on the memory manager. + */ + +void igraph_strvector_clear(igraph_strvector_t *sv) { + long int i, n = igraph_strvector_size(sv); + char **tmp; + + for (i = 0; i < n; i++) { + IGRAPH_FREE(sv->data[i]); + } + sv->len = 0; + /* try to give back some memory */ + tmp = IGRAPH_REALLOC(sv->data, 1, char*); + if (tmp != 0) { + sv->data = tmp; + } +} + +/** + * \ingroup strvector + * \function igraph_strvector_resize + * \brief Resize + * + * If the new size is bigger then empty strings are added, if it is + * smaller then the unneeded elements are removed. + * \param v The string vector. + * \param newsize The new size. + * \return Error code. + * + * Time complexity: O(n), the number of strings if the vector is made + * bigger, O(l), the total length of the deleted strings if it is made + * smaller, maybe less, depending on memory management. + */ + +int igraph_strvector_resize(igraph_strvector_t* v, long int newsize) { + long int toadd = newsize - v->len, i, j; + char **tmp; + long int reallocsize = newsize; + + IGRAPH_ASSERT(v != 0); + IGRAPH_ASSERT(v->data != 0); + /* printf("resize %li to %li\n", v->len, newsize); */ + if (newsize < v->len) { + for (i = newsize; i < v->len; i++) { + IGRAPH_FREE(v->data[i]); + } + /* try to give back some space */ + tmp = IGRAPH_REALLOC(v->data, (size_t) reallocsize, char*); + /* printf("resize %li to %li, %p\n", v->len, newsize, tmp); */ + if (tmp != 0) { + v->data = tmp; + } + } else if (newsize > v->len) { + igraph_bool_t error = 0; + tmp = IGRAPH_REALLOC(v->data, (size_t) reallocsize, char*); + if (tmp == 0) { + IGRAPH_ERROR("cannot resize string vector", IGRAPH_ENOMEM); + } + v->data = tmp; + + for (i = 0; i < toadd; i++) { + v->data[v->len + i] = IGRAPH_CALLOC(1, char); + if (v->data[v->len + i] == 0) { + error = 1; + break; + } + v->data[v->len + i][0] = '\0'; + } + if (error) { + /* There was an error, free everything we've allocated so far */ + for (j = 0; j < i; j++) { + if (v->data[v->len + i] != 0) { + IGRAPH_FREE(v->data[v->len + i]); + } + } + /* Try to give back space */ + tmp = IGRAPH_REALLOC(v->data, (size_t) (v->len), char*); + if (tmp != 0) { + v->data = tmp; + } + IGRAPH_ERROR("Cannot resize string vector", IGRAPH_ENOMEM); + } + } + v->len = newsize; + + return 0; +} + +/** + * \ingroup strvector + * \function igraph_strvector_size + * \brief Gives the size of a string vector. + * + * \param sv The string vector. + * \return The length of the string vector. + * + * Time complexity: O(1). + */ + +long int igraph_strvector_size(const igraph_strvector_t *sv) { + IGRAPH_ASSERT(sv != 0); + IGRAPH_ASSERT(sv->data != 0); + return sv->len; +} + +/** + * \ingroup strvector + * \function igraph_strvector_add + * \brief Adds an element to the back of a string vector. + * + * \param v The string vector. + * \param value The string to add, it will be copied. + * \return Error code. + * + * Time complexity: O(n+l), n is the total number of strings, l is the + * length of the new string. + */ + +int igraph_strvector_add(igraph_strvector_t *v, const char *value) { + long int s = igraph_strvector_size(v); + long int value_len = strlen(value); + char **tmp; + IGRAPH_ASSERT(v != 0); + IGRAPH_ASSERT(v->data != 0); + tmp = IGRAPH_REALLOC(v->data, (size_t) s + 1, char*); + if (tmp == 0) { + IGRAPH_ERROR("cannot add string to string vector", IGRAPH_ENOMEM); + } + v->data = tmp; + v->data[s] = IGRAPH_CALLOC(value_len + 1, char); + if (v->data[s] == 0) { + IGRAPH_ERROR("cannot add string to string vector", IGRAPH_ENOMEM); + } + strcpy(v->data[s], value); + v->len += 1; + + return 0; +} + +/** + * \ingroup strvector + * \function igraph_strvector_permdelete + * \brief Removes elements from a string vector (for internal use) + */ + +void igraph_strvector_permdelete(igraph_strvector_t *v, const igraph_vector_t *index, + long int nremove) { + long int i; + char **tmp; + IGRAPH_ASSERT(v != 0); + IGRAPH_ASSERT(v->data != 0); + + for (i = 0; i < igraph_strvector_size(v); i++) { + if (VECTOR(*index)[i] != 0) { + v->data[ (long int) VECTOR(*index)[i] - 1 ] = v->data[i]; + } else { + IGRAPH_FREE(v->data[i]); + } + } + /* Try to make it shorter */ + tmp = IGRAPH_REALLOC(v->data, v->len - nremove ? + (size_t) (v->len - nremove) : 1, char*); + if (tmp != 0) { + v->data = tmp; + } + v->len -= nremove; +} + +/** + * \ingroup strvector + * \function igraph_strvector_remove_negidx + * \brief Removes elements from a string vector (for internal use) + */ + +void igraph_strvector_remove_negidx(igraph_strvector_t *v, const igraph_vector_t *neg, + long int nremove) { + long int i, idx = 0; + char **tmp; + IGRAPH_ASSERT(v != 0); + IGRAPH_ASSERT(v->data != 0); + for (i = 0; i < igraph_strvector_size(v); i++) { + if (VECTOR(*neg)[i] >= 0) { + v->data[idx++] = v->data[i]; + } else { + IGRAPH_FREE(v->data[i]); + } + } + /* Try to give back some memory */ + tmp = IGRAPH_REALLOC(v->data, v->len - nremove ? + (size_t) (v->len - nremove) : 1, char*); + if (tmp != 0) { + v->data = tmp; + } + v->len -= nremove; +} + +/** + * \ingroup strvector + * \function igraph_strvector_print + * \brief Prints a string vector. + * + * \param v The string vector. + * \param file The file to write to. + * \param sep The separator to print between strings. + * \return Error code. + */ +int igraph_strvector_print(const igraph_strvector_t *v, FILE *file, + const char *sep) { + + long int i, n = igraph_strvector_size(v); + if (n != 0) { + fprintf(file, "%s", STR(*v, 0)); + } + for (i = 1; i < n; i++) { + fprintf(file, "%s%s", sep, STR(*v, i)); + } + return IGRAPH_SUCCESS; +} + +int igraph_strvector_index(const igraph_strvector_t *v, + igraph_strvector_t *newv, + const igraph_vector_t *idx) { + + long int i, newlen = igraph_vector_size(idx); + IGRAPH_CHECK(igraph_strvector_resize(newv, newlen)); + + for (i = 0; i < newlen; i++) { + long int j = (long int) VECTOR(*idx)[i]; + char *str; + igraph_strvector_get(v, j, &str); + igraph_strvector_set(newv, i, str); + } + + return 0; +} diff --git a/src/rigraph/core/core/trie.c b/src/rigraph/core/core/trie.c new file mode 100644 index 0000000..184257f --- /dev/null +++ b/src/rigraph/core/core/trie.c @@ -0,0 +1,395 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_random.h" +#include "igraph_error.h" + +#include "trie.h" + +#include "config.h" + +#include + +/** + * \ingroup igraphtrie + * \brief Creates a trie node (not to be called directly) + * \return Error code: errors by igraph_strvector_init(), + * igraph_vector_ptr_init() and igraph_vector_init() might be returned. + */ + +static int igraph_i_trie_init_node(igraph_trie_node_t *t) { + IGRAPH_STRVECTOR_INIT_FINALLY(&t->strs, 0); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 0); + IGRAPH_VECTOR_INIT_FINALLY(&t->values, 0); + IGRAPH_FINALLY_CLEAN(3); + return 0; +} + +static void igraph_i_trie_destroy_node(igraph_trie_node_t *t); + +/** + * \ingroup igraphtrie + * \brief Creates a trie. + * \return Error code: errors by igraph_strvector_init(), + * igraph_vector_ptr_init() and igraph_vector_init() might be returned. + */ + +int igraph_trie_init(igraph_trie_t *t, igraph_bool_t storekeys) { + t->maxvalue = -1; + t->storekeys = storekeys; + IGRAPH_CHECK(igraph_i_trie_init_node( (igraph_trie_node_t *) t )); + IGRAPH_FINALLY(igraph_i_trie_destroy_node, (igraph_trie_node_t *) t ); + if (storekeys) { + IGRAPH_CHECK(igraph_strvector_init(&t->keys, 0)); + } + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \ingroup igraphtrie + * \brief Destroys a node of a trie (not to be called directly). + */ + +static void igraph_i_trie_destroy_node_helper(igraph_trie_node_t *t, igraph_bool_t sfree) { + long int i; + igraph_strvector_destroy(&t->strs); + for (i = 0; i < igraph_vector_ptr_size(&t->children); i++) { + igraph_trie_node_t *child = VECTOR(t->children)[i]; + if (child != 0) { + igraph_i_trie_destroy_node_helper(child, 1); + } + } + igraph_vector_ptr_destroy(&t->children); + igraph_vector_destroy(&t->values); + if (sfree) { + IGRAPH_FREE(t); + } +} + +static void igraph_i_trie_destroy_node(igraph_trie_node_t *t) { + igraph_i_trie_destroy_node_helper(t, 0); +} + +/** + * \ingroup igraphtrie + * \brief Destroys a trie (frees allocated memory). + */ + +void igraph_trie_destroy(igraph_trie_t *t) { + if (t->storekeys) { + igraph_strvector_destroy(&t->keys); + } + igraph_i_trie_destroy_node( (igraph_trie_node_t*) t); +} + + +/** + * \ingroup igraphtrie + * \brief Internal helping function for igraph_trie_t + */ + +static long int igraph_i_strdiff(const char *str, const char *key) { + + long int diff = 0; + while (key[diff] != '\0' && str[diff] != '\0' && str[diff] == key[diff]) { + diff++; + } + return diff; +} + +/** + * \ingroup igraphtrie + * \brief Search/insert in a trie (not to be called directly). + * + * @return Error code: + * - IGRAPH_ENOMEM: out of memory + */ + +int igraph_trie_get_node(igraph_trie_node_t *t, const char *key, + igraph_real_t newvalue, long int *id) { + char *str; + long int i; + igraph_bool_t add; + + /* If newvalue is negative, we don't add the node if nonexistent, only check + * for its existence */ + add = (newvalue >= 0); + + for (i = 0; i < igraph_strvector_size(&t->strs); i++) { + long int diff; + igraph_strvector_get(&t->strs, i, &str); + diff = igraph_i_strdiff(str, key); + + if (diff == 0) { + + /* ------------------------------------ */ + /* No match, next */ + + } else if (str[diff] == '\0' && key[diff] == '\0') { + + /* ------------------------------------ */ + /* They are exactly the same */ + if (VECTOR(t->values)[i] != -1) { + *id = (long int) VECTOR(t->values)[i]; + return 0; + } else { + VECTOR(t->values)[i] = newvalue; + *id = (long int) newvalue; + return 0; + } + + } else if (str[diff] == '\0') { + + /* ------------------------------------ */ + /* str is prefix of key, follow its link if there is one */ + igraph_trie_node_t *node = VECTOR(t->children)[i]; + if (node != 0) { + return igraph_trie_get_node(node, key + diff, newvalue, id); + } else if (add) { + igraph_trie_node_t *node = IGRAPH_CALLOC(1, igraph_trie_node_t); + if (node == 0) { + IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); + } + IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 1); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 1); + IGRAPH_VECTOR_INIT_FINALLY(&node->values, 1); + IGRAPH_CHECK(igraph_strvector_set(&node->strs, 0, key + diff)); + VECTOR(node->children)[0] = 0; + VECTOR(node->values)[0] = newvalue; + + VECTOR(t->children)[i] = node; + + *id = (long int) newvalue; + IGRAPH_FINALLY_CLEAN(3); + return 0; + } else { + *id = -1; + return 0; + } + + } else if (key[diff] == '\0' && add) { + + /* ------------------------------------ */ + /* key is prefix of str, the node has to be cut */ + char *str2; + + igraph_trie_node_t *node = IGRAPH_CALLOC(1, igraph_trie_node_t); + if (node == 0) { + IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); + } + IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 1); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 1); + IGRAPH_VECTOR_INIT_FINALLY(&node->values, 1); + IGRAPH_CHECK(igraph_strvector_set(&node->strs, 0, str + diff)); + + VECTOR(node->children)[0] = VECTOR(t->children)[i]; + VECTOR(node->values)[0] = VECTOR(t->values)[i]; + + str2 = strdup(str); + if (str2 == 0) { + IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); + } + str2[diff] = '\0'; + IGRAPH_FINALLY(igraph_free, str2); + IGRAPH_CHECK(igraph_strvector_set(&t->strs, i, str2)); + IGRAPH_FREE(str2); + IGRAPH_FINALLY_CLEAN(4); + + VECTOR(t->values)[i] = newvalue; + VECTOR(t->children)[i] = node; + + *id = (long int) newvalue; + return 0; + + } else if (add) { + + /* ------------------------------------ */ + /* the first diff characters match */ + char *str2; + + igraph_trie_node_t *node = IGRAPH_CALLOC(1, igraph_trie_node_t); + if (node == 0) { + IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); + } + IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 2); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 2); + IGRAPH_VECTOR_INIT_FINALLY(&node->values, 2); + IGRAPH_CHECK(igraph_strvector_set(&node->strs, 0, str + diff)); + IGRAPH_CHECK(igraph_strvector_set(&node->strs, 1, key + diff)); + VECTOR(node->children)[0] = VECTOR(t->children)[i]; + VECTOR(node->children)[1] = 0; + VECTOR(node->values)[0] = VECTOR(t->values)[i]; + VECTOR(node->values)[1] = newvalue; + + str2 = strdup(str); + if (str2 == 0) { + IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); + } + str2[diff] = '\0'; + IGRAPH_FINALLY(igraph_free, str2); + IGRAPH_CHECK(igraph_strvector_set(&t->strs, i, str2)); + IGRAPH_FREE(str2); + IGRAPH_FINALLY_CLEAN(4); + + VECTOR(t->values)[i] = -1; + VECTOR(t->children)[i] = node; + + *id = (long int) newvalue; + return 0; + } else { + + /* ------------------------------------------------- */ + /* No match, but we requested not to add the new key */ + *id = -1; + return 0; + } + } + + /* ------------------------------------ */ + /* Nothing matches */ + + if (add) { + IGRAPH_CHECK(igraph_vector_ptr_reserve(&t->children, + igraph_vector_ptr_size(&t->children) + 1)); + IGRAPH_CHECK(igraph_vector_reserve(&t->values, igraph_vector_size(&t->values) + 1)); + IGRAPH_CHECK(igraph_strvector_add(&t->strs, key)); + + igraph_vector_ptr_push_back(&t->children, 0); /* allocated */ + igraph_vector_push_back(&t->values, newvalue); /* allocated */ + *id = (long int) newvalue; + } else { + *id = -1; + } + + return 0; +} + +/** + * \ingroup igraphtrie + * \brief Search/insert in a trie. + */ + +int igraph_trie_get(igraph_trie_t *t, const char *key, long int *id) { + if (!t->storekeys) { + IGRAPH_CHECK(igraph_trie_get_node( (igraph_trie_node_t*) t, + key, t->maxvalue + 1, id)); + if (*id > t->maxvalue) { + t->maxvalue = *id; + } + return 0; + } else { + int ret; + igraph_error_handler_t *oldhandler; + oldhandler = igraph_set_error_handler(igraph_error_handler_ignore); + /* Add it to the string vector first, we can undo this later */ + ret = igraph_strvector_add(&t->keys, key); + if (ret != 0) { + igraph_set_error_handler(oldhandler); + IGRAPH_ERROR("cannot get element from trie", ret); + } + ret = igraph_trie_get_node( (igraph_trie_node_t*) t, + key, t->maxvalue + 1, id); + if (ret != 0) { + igraph_strvector_resize(&t->keys, igraph_strvector_size(&t->keys) - 1); + igraph_set_error_handler(oldhandler); + IGRAPH_ERROR("cannot get element from trie", ret); + } + + /* everything is fine */ + if (*id > t->maxvalue) { + t->maxvalue = *id; + } else { + igraph_strvector_resize(&t->keys, igraph_strvector_size(&t->keys) - 1); + } + igraph_set_error_handler(oldhandler); + } + + return 0; +} + +/** + * \ingroup igraphtrie + * \brief Search/insert in a trie (for internal use). + * + * @return Error code: + * - IGRAPH_ENOMEM: out of memory + */ + +int igraph_trie_get2(igraph_trie_t *t, const char *key, long int length, + long int *id) { + char *tmp = IGRAPH_CALLOC(length + 1, char); + + if (tmp == 0) { + IGRAPH_ERROR("Cannot get from trie", IGRAPH_ENOMEM); + } + + strncpy(tmp, key, length); + tmp[length] = '\0'; + IGRAPH_FINALLY(igraph_free, tmp); + IGRAPH_CHECK(igraph_trie_get(t, tmp, id)); + IGRAPH_FREE(tmp); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \ingroup igraphtrie + * \brief Search in a trie. + * This variant does not add \c key to the trie if it does not exist. + * In this case, a negative id is returned. + */ + +int igraph_trie_check(igraph_trie_t *t, const char *key, long int *id) { + IGRAPH_CHECK(igraph_trie_get_node( (igraph_trie_node_t*) t, + key, -1, id)); + return 0; +} + +/** + * \ingroup igraphtrie + * \brief Get an element of a trie based on its index. + */ + +void igraph_trie_idx(igraph_trie_t *t, long int idx, char **str) { + igraph_strvector_get(&t->keys, idx, str); +} + +/** + * \ingroup igraphtrie + * \brief Returns the size of a trie. + */ + +long int igraph_trie_size(igraph_trie_t *t) { + return t->maxvalue + 1; +} + +/* Hmmm, very dirty.... */ + +int igraph_trie_getkeys(igraph_trie_t *t, const igraph_strvector_t **strv) { + *strv = &t->keys; + return 0; +} diff --git a/src/rigraph/core/core/trie.h b/src/rigraph/core/core/trie.h new file mode 100644 index 0000000..d5f87ff --- /dev/null +++ b/src/rigraph/core/core/trie.h @@ -0,0 +1,72 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_CORE_TRIE_H +#define IGRAPH_CORE_TRIE_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_strvector.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" + +__BEGIN_DECLS + +/** + * Trie data type + * \ingroup internal + */ + +typedef struct s_igraph_trie_node { + igraph_strvector_t strs; + igraph_vector_ptr_t children; + igraph_vector_t values; +} igraph_trie_node_t; + +typedef struct s_igraph_trie { + igraph_strvector_t strs; + igraph_vector_ptr_t children; + igraph_vector_t values; + long int maxvalue; + igraph_bool_t storekeys; + igraph_strvector_t keys; +} igraph_trie_t; + +#define IGRAPH_TRIE_NULL { IGRAPH_STRVECTOR_NULL, IGRAPH_VECTOR_PTR_NULL, \ + IGRAPH_VECTOR_NULL, 0, 0, IGRAPH_STRVECTOR_NULL } +#define IGRAPH_TRIE_INIT_FINALLY(tr, sk) \ + do { IGRAPH_CHECK(igraph_trie_init(tr, sk)); \ + IGRAPH_FINALLY(igraph_trie_destroy, tr); } while (0) + +IGRAPH_PRIVATE_EXPORT int igraph_trie_init(igraph_trie_t *t, igraph_bool_t storekeys); +IGRAPH_PRIVATE_EXPORT void igraph_trie_destroy(igraph_trie_t *t); +IGRAPH_PRIVATE_EXPORT int igraph_trie_get(igraph_trie_t *t, const char *key, long int *id); +IGRAPH_PRIVATE_EXPORT int igraph_trie_check(igraph_trie_t *t, const char *key, long int *id); +IGRAPH_PRIVATE_EXPORT int igraph_trie_get2(igraph_trie_t *t, const char *key, long int length, + long int *id); +IGRAPH_PRIVATE_EXPORT void igraph_trie_idx(igraph_trie_t *t, long int idx, char **str); +IGRAPH_PRIVATE_EXPORT int igraph_trie_getkeys(igraph_trie_t *t, const igraph_strvector_t **strv); +IGRAPH_PRIVATE_EXPORT long int igraph_trie_size(igraph_trie_t *t); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/core/vector.c b/src/rigraph/core/core/vector.c new file mode 100644 index 0000000..2b77e6a --- /dev/null +++ b/src/rigraph/core/core/vector.c @@ -0,0 +1,534 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_complex.h" + +#include + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "vector.pmt" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#define BASE_FLOAT +#include "igraph_pmt.h" +#include "vector.pmt" +#include "igraph_pmt_off.h" +#undef BASE_FLOAT + +#define BASE_LONG +#include "igraph_pmt.h" +#include "vector.pmt" +#include "igraph_pmt_off.h" +#undef BASE_LONG + +#define BASE_CHAR +#include "igraph_pmt.h" +#include "vector.pmt" +#include "igraph_pmt_off.h" +#undef BASE_CHAR + +#define BASE_BOOL +#include "igraph_pmt.h" +#include "vector.pmt" +#include "igraph_pmt_off.h" +#undef BASE_BOOL + +#define BASE_INT +#include "igraph_pmt.h" +#include "vector.pmt" +#include "igraph_pmt_off.h" +#undef BASE_INT + +#define BASE_COMPLEX +#include "igraph_pmt.h" +#include "vector.pmt" +#include "igraph_pmt_off.h" +#undef BASE_COMPLEX + +#include "../core/math.h" + +#include "../core/indheap.h" + +/** + * \ingroup vector + * \function igraph_vector_floor + * \brief Transform a real vector to a long vector by flooring each element. + * + * + * Flooring means rounding down to the nearest integer. + * + * \param from The original real vector object. + * \param to Pointer to an initialized long vector. The result will + * be stored here. + * \return Error code: + * \c IGRAPH_ENOMEM: out of memory + * + * Time complexity: O(n), where n is the number of elements in the vector. + */ +int igraph_vector_floor(const igraph_vector_t *from, igraph_vector_long_t *to) { + long int i, n = igraph_vector_size(from); + + IGRAPH_CHECK(igraph_vector_long_resize(to, n)); + for (i = 0; i < n; i++) { + VECTOR(*to)[i] = (long int) floor(VECTOR(*from)[i]); + } + return IGRAPH_SUCCESS; +} + +int igraph_vector_round(const igraph_vector_t *from, igraph_vector_long_t *to) { + long int i, n = igraph_vector_size(from); + + IGRAPH_CHECK(igraph_vector_long_resize(to, n)); + for (i = 0; i < n; i++) { + VECTOR(*to)[i] = (long int) round(VECTOR(*from)[i]); + } + return 0; +} + +int igraph_vector_order2(igraph_vector_t *v) { + + igraph_indheap_t heap; + + igraph_indheap_init_array(&heap, VECTOR(*v), igraph_vector_size(v)); + IGRAPH_FINALLY(igraph_indheap_destroy, &heap); + + igraph_vector_clear(v); + while (!igraph_indheap_empty(&heap)) { + IGRAPH_CHECK(igraph_vector_push_back(v, igraph_indheap_max_index(&heap) - 1)); + igraph_indheap_delete_max(&heap); + } + + igraph_indheap_destroy(&heap); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_order + * \brief Calculate the order of the elements in a vector. + * + * + * The smallest element will have order zero, the second smallest + * order one, etc. + * \param v The original \type igraph_vector_t object. + * \param v2 A secondary key, another \type igraph_vector_t object. + * \param res An initialized \type igraph_vector_t object, it will be + * resized to match the size of \p v. The + * result of the computation will be stored here. + * \param nodes Hint, the largest element in \p v. + * \return Error code: + * \c IGRAPH_ENOMEM: out of memory + * + * Time complexity: O() + */ + +int igraph_vector_order(const igraph_vector_t* v, + const igraph_vector_t *v2, + igraph_vector_t* res, igraph_real_t nodes) { + long int edges = igraph_vector_size(v); + igraph_vector_t ptr; + igraph_vector_t rad; + long int i, j; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + IGRAPH_VECTOR_INIT_FINALLY(&ptr, (long int) nodes + 1); + IGRAPH_VECTOR_INIT_FINALLY(&rad, edges); + IGRAPH_CHECK(igraph_vector_resize(res, edges)); + + for (i = 0; i < edges; i++) { + long int radix = (long int) v2->stor_begin[i]; + if (VECTOR(ptr)[radix] != 0) { + VECTOR(rad)[i] = VECTOR(ptr)[radix]; + } + VECTOR(ptr)[radix] = i + 1; + } + + j = 0; + for (i = 0; i < nodes + 1; i++) { + if (VECTOR(ptr)[i] != 0) { + long int next = (long int) VECTOR(ptr)[i] - 1; + res->stor_begin[j++] = next; + while (VECTOR(rad)[next] != 0) { + next = (long int) VECTOR(rad)[next] - 1; + res->stor_begin[j++] = next; + } + } + } + + igraph_vector_null(&ptr); + igraph_vector_null(&rad); + + for (i = 0; i < edges; i++) { + long int edge = (long int) VECTOR(*res)[edges - i - 1]; + long int radix = (long int) VECTOR(*v)[edge]; + if (VECTOR(ptr)[radix] != 0) { + VECTOR(rad)[edge] = VECTOR(ptr)[radix]; + } + VECTOR(ptr)[radix] = edge + 1; + } + + j = 0; + for (i = 0; i < nodes + 1; i++) { + if (VECTOR(ptr)[i] != 0) { + long int next = (long int) VECTOR(ptr)[i] - 1; + res->stor_begin[j++] = next; + while (VECTOR(rad)[next] != 0) { + next = (long int) VECTOR(rad)[next] - 1; + res->stor_begin[j++] = next; + } + } + } + + igraph_vector_destroy(&ptr); + igraph_vector_destroy(&rad); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +int igraph_vector_order1(const igraph_vector_t* v, + igraph_vector_t* res, igraph_real_t nodes) { + long int edges = igraph_vector_size(v); + igraph_vector_t ptr; + igraph_vector_t rad; + long int i, j; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + IGRAPH_VECTOR_INIT_FINALLY(&ptr, (long int) nodes + 1); + IGRAPH_VECTOR_INIT_FINALLY(&rad, edges); + IGRAPH_CHECK(igraph_vector_resize(res, edges)); + + for (i = 0; i < edges; i++) { + long int radix = (long int) v->stor_begin[i]; + if (VECTOR(ptr)[radix] != 0) { + VECTOR(rad)[i] = VECTOR(ptr)[radix]; + } + VECTOR(ptr)[radix] = i + 1; + } + + j = 0; + for (i = 0; i < nodes + 1; i++) { + if (VECTOR(ptr)[i] != 0) { + long int next = (long int) VECTOR(ptr)[i] - 1; + res->stor_begin[j++] = next; + while (VECTOR(rad)[next] != 0) { + next = (long int) VECTOR(rad)[next] - 1; + res->stor_begin[j++] = next; + } + } + } + + igraph_vector_destroy(&ptr); + igraph_vector_destroy(&rad); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +int igraph_vector_order1_int(const igraph_vector_t* v, + igraph_vector_int_t* res, + igraph_real_t nodes) { + long int edges = igraph_vector_size(v); + igraph_vector_t ptr; + igraph_vector_t rad; + long int i, j; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + IGRAPH_VECTOR_INIT_FINALLY(&ptr, (long int) nodes + 1); + IGRAPH_VECTOR_INIT_FINALLY(&rad, edges); + IGRAPH_CHECK(igraph_vector_int_resize(res, edges)); + + for (i = 0; i < edges; i++) { + long int radix = (long int) v->stor_begin[i]; + if (VECTOR(ptr)[radix] != 0) { + VECTOR(rad)[i] = VECTOR(ptr)[radix]; + } + VECTOR(ptr)[radix] = i + 1; + } + + j = 0; + for (i = 0; i < nodes + 1; i++) { + if (VECTOR(ptr)[i] != 0) { + long int next = (long int) VECTOR(ptr)[i] - 1; + res->stor_begin[j++] = next; + while (VECTOR(rad)[next] != 0) { + next = (long int) VECTOR(rad)[next] - 1; + res->stor_begin[j++] = next; + } + } + } + + igraph_vector_destroy(&ptr); + igraph_vector_destroy(&rad); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +int igraph_vector_rank(const igraph_vector_t *v, igraph_vector_t *res, + long int nodes) { + + igraph_vector_t rad; + igraph_vector_t ptr; + long int edges = igraph_vector_size(v); + long int i, c = 0; + + IGRAPH_VECTOR_INIT_FINALLY(&rad, nodes); + IGRAPH_VECTOR_INIT_FINALLY(&ptr, edges); + IGRAPH_CHECK(igraph_vector_resize(res, edges)); + + for (i = 0; i < edges; i++) { + long int elem = (long int) VECTOR(*v)[i]; + VECTOR(ptr)[i] = VECTOR(rad)[elem]; + VECTOR(rad)[elem] = i + 1; + } + + for (i = 0; i < nodes; i++) { + long int p = (long int) VECTOR(rad)[i]; + while (p != 0) { + VECTOR(*res)[p - 1] = c++; + p = (long int) VECTOR(ptr)[p - 1]; + } + } + + igraph_vector_destroy(&ptr); + igraph_vector_destroy(&rad); + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +#ifndef USING_R +int igraph_vector_complex_print(const igraph_vector_complex_t *v) { + long int i, n = igraph_vector_complex_size(v); + if (n != 0) { + igraph_complex_t z = VECTOR(*v)[0]; + printf("%g%+gi", IGRAPH_REAL(z), IGRAPH_IMAG(z)); + } + for (i = 1; i < n; i++) { + igraph_complex_t z = VECTOR(*v)[i]; + printf(" %g%+gi", IGRAPH_REAL(z), IGRAPH_IMAG(z)); + } + printf("\n"); + return 0; +} +#endif + +int igraph_vector_complex_fprint(const igraph_vector_complex_t *v, + FILE *file) { + long int i, n = igraph_vector_complex_size(v); + if (n != 0) { + igraph_complex_t z = VECTOR(*v)[0]; + fprintf(file, "%g%+g", IGRAPH_REAL(z), IGRAPH_IMAG(z)); + } + for (i = 1; i < n; i++) { + igraph_complex_t z = VECTOR(*v)[i]; + fprintf(file, " %g%+g", IGRAPH_REAL(z), IGRAPH_IMAG(z)); + } + fprintf(file, "\n"); + return 0; +} + +int igraph_vector_complex_real(const igraph_vector_complex_t *v, + igraph_vector_t *real) { + long int i, n = igraph_vector_complex_size(v); + IGRAPH_CHECK(igraph_vector_resize(real, n)); + for (i = 0; i < n; i++) { + VECTOR(*real)[i] = IGRAPH_REAL(VECTOR(*v)[i]); + } + + return 0; +} + +int igraph_vector_complex_imag(const igraph_vector_complex_t *v, + igraph_vector_t *imag) { + long int i, n = igraph_vector_complex_size(v); + IGRAPH_CHECK(igraph_vector_resize(imag, n)); + for (i = 0; i < n; i++) { + VECTOR(*imag)[i] = IGRAPH_IMAG(VECTOR(*v)[i]); + } + + return 0; +} + +int igraph_vector_complex_realimag(const igraph_vector_complex_t *v, + igraph_vector_t *real, + igraph_vector_t *imag) { + long int i, n = igraph_vector_complex_size(v); + IGRAPH_CHECK(igraph_vector_resize(real, n)); + IGRAPH_CHECK(igraph_vector_resize(imag, n)); + for (i = 0; i < n; i++) { + igraph_complex_t z = VECTOR(*v)[i]; + VECTOR(*real)[i] = IGRAPH_REAL(z); + VECTOR(*imag)[i] = IGRAPH_IMAG(z); + } + + return 0; +} + +int igraph_vector_complex_create(igraph_vector_complex_t *v, + const igraph_vector_t *real, + const igraph_vector_t *imag) { + long int i, n = igraph_vector_size(real); + if (n != igraph_vector_size(imag)) { + IGRAPH_ERROR("Real and imag vector sizes don't match", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_complex_init(v, n)); + /* FINALLY not needed */ + + for (i = 0; i < n; i++) { + VECTOR(*v)[i] = igraph_complex(VECTOR(*real)[i], VECTOR(*imag)[i]); + } + + return 0; +} + +int igraph_vector_complex_create_polar(igraph_vector_complex_t *v, + const igraph_vector_t *r, + const igraph_vector_t *theta) { + long int i, n = igraph_vector_size(r); + if (n != igraph_vector_size(theta)) { + IGRAPH_ERROR("'r' and 'theta' vector sizes don't match", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_complex_init(v, n)); + /* FINALLY not needed */ + + for (i = 0; i < n; i++) { + VECTOR(*v)[i] = igraph_complex_polar(VECTOR(*r)[i], VECTOR(*theta)[i]); + } + + return 0; +} + +igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, + const igraph_vector_t *rhs, + igraph_real_t tol) { + long int i, s; + IGRAPH_ASSERT(lhs != 0); + IGRAPH_ASSERT(rhs != 0); + IGRAPH_ASSERT(lhs->stor_begin != 0); + IGRAPH_ASSERT(rhs->stor_begin != 0); + + s = igraph_vector_size(lhs); + if (s != igraph_vector_size(rhs)) { + return 0; + } else { + if (tol == 0) { + tol = DBL_EPSILON; + } + for (i = 0; i < s; i++) { + igraph_real_t l = VECTOR(*lhs)[i]; + igraph_real_t r = VECTOR(*rhs)[i]; + if (l < r - tol || l > r + tol) { + return 0; + } + } + return 1; + } +} + +int igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol) { + long int i, n = igraph_vector_size(v); + if (tol < 0.0) { + IGRAPH_ERROR("`tol' tolerance must be non-negative", IGRAPH_EINVAL); + } + if (tol == 0.0) { + tol = sqrt(DBL_EPSILON); + } + for (i = 0; i < n; i++) { + igraph_real_t val = VECTOR(*v)[i]; + if (val < tol && val > -tol) { + VECTOR(*v)[i] = 0.0; + } + } + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_is_nan + * \brief Check for each element if it is NaN. + * + * + * \param v The \type igraph_vector_t object to check. + * \param is_nan The resulting boolean vector indicating for each element + * whether it is NaN or not. + * \return Error code, + * \c IGRAPH_ENOMEM if there is not enough + * memory. Note that this function \em never returns an error + * if the vector \p is_nan will already be large enough. + * Time complexity: O(n), the number of elements. + */ +int igraph_vector_is_nan(const igraph_vector_t *v, igraph_vector_bool_t *is_nan) +{ + igraph_real_t *ptr; + igraph_bool_t *ptr_nan; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(is_nan != NULL); + IGRAPH_ASSERT(is_nan->stor_begin != NULL); + IGRAPH_CHECK(igraph_vector_bool_resize(is_nan, igraph_vector_size(v))); + for (ptr = v->stor_begin, ptr_nan = is_nan->stor_begin; ptr < v->end; ptr++, ptr_nan++) { + *ptr_nan = igraph_is_nan(*ptr) ? 1 : 0; + } + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_is_any_nan + * \brief Check if any element is NaN. + * + * + * \param v The \type igraph_vector_t object to check. + * \return 1 if any element is NaN, 0 otherwise. + * + * Time complexity: O(n), the number of elements. + */ +igraph_bool_t igraph_vector_is_any_nan(const igraph_vector_t *v) +{ + igraph_real_t *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + ptr = v->stor_begin; + while (ptr < v->end) { + if (igraph_is_nan(*ptr)) { + return 1; + } + ptr++; + } + return 0; +} diff --git a/src/rigraph/core/core/vector.pmt b/src/rigraph/core/core/vector.pmt new file mode 100644 index 0000000..169a1e5 --- /dev/null +++ b/src/rigraph/core/core/vector.pmt @@ -0,0 +1,2946 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_memory.h" +#include "igraph_error.h" +#include "igraph_random.h" +#include "igraph_qsort.h" + +#include /* memcpy & co. */ +#include +#include /* va_start & co */ +#include + +/** + * \ingroup vector + * \section about_igraph_vector_t_objects About \type igraph_vector_t objects + * + * The \type igraph_vector_t data type is a simple and efficient + * interface to arrays containing numbers. It is something + * similar as (but much simpler than) the \type vector template + * in the C++ standard library. + * + * Vectors are used extensively in \a igraph, all + * functions which expect or return a list of numbers use + * igraph_vector_t to achieve this. + * + * The \type igraph_vector_t type usually uses + * O(n) space + * to store n elements. Sometimes it + * uses more, this is because vectors can shrink, but even if they + * shrink, the current implementation does not free a single bit of + * memory. + * + * The elements in an \type igraph_vector_t + * object are indexed from zero, we follow the usual C convention + * here. + * + * The elements of a vector always occupy a single block of + * memory, the starting address of this memory block can be queried + * with the \ref VECTOR macro. This way, vector objects can be used + * with standard mathematical libraries, like the GNU Scientific + * Library. + */ + +/** + * \ingroup vector + * \section igraph_vector_constructors_and_destructors Constructors and + * Destructors + * + * \type igraph_vector_t objects have to be initialized before using + * them, this is analogous to calling a constructor on them. There are a + * number of \type igraph_vector_t constructors, for your + * convenience. \ref igraph_vector_init() is the basic constructor, it + * creates a vector of the given length, filled with zeros. + * \ref igraph_vector_copy() creates a new identical copy + * of an already existing and initialized vector. \ref + * igraph_vector_init_copy() creates a vector by copying a regular C array. + * \ref igraph_vector_init_seq() creates a vector containing a regular + * sequence with increment one. + * + * \ref igraph_vector_view() is a special constructor, it allows you to + * handle a regular C array as a \type vector without copying + * its elements. + * + * + * If a \type igraph_vector_t object is not needed any more, it + * should be destroyed to free its allocated memory by calling the + * \type igraph_vector_t destructor, \ref igraph_vector_destroy(). + * + * Note that vectors created by \ref igraph_vector_view() are special, + * you mustn't call \ref igraph_vector_destroy() on these. + */ + +/** + * \ingroup vector + * \function igraph_vector_init + * \brief Initializes a vector object (constructor). + * + * + * Every vector needs to be initialized before it can be used, and + * there are a number of initialization functions or otherwise called + * constructors. This function constructs a vector of the given size and + * initializes each entry to 0. Note that \ref igraph_vector_null() can be + * used to set each element of a vector to zero. However, if you want a + * vector of zeros, it is much faster to use this function than to create a + * vector and then invoke \ref igraph_vector_null(). + * + * + * Every vector object initialized by this function should be + * destroyed (ie. the memory allocated for it should be freed) when it + * is not needed anymore, the \ref igraph_vector_destroy() function is + * responsible for this. + * \param v Pointer to a not yet initialized vector object. + * \param size The size of the vector. + * \return error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: operating system dependent, the amount of + * \quote time \endquote required to allocate + * O(n) elements, + * n is the number of elements. + */ + +int FUNCTION(igraph_vector, init) (TYPE(igraph_vector)* v, int long size) { + long int alloc_size = size > 0 ? size : 1; + IGRAPH_ASSERT(size >= 0); + v->stor_begin = IGRAPH_CALLOC(alloc_size, BASE); + if (v->stor_begin == 0) { + IGRAPH_ERROR("cannot init vector", IGRAPH_ENOMEM); + } + v->stor_end = v->stor_begin + alloc_size; + v->end = v->stor_begin + size; + + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_view + * \brief Handle a regular C array as a \type igraph_vector_t. + * + * + * This is a special \type igraph_vector_t constructor. It allows to + * handle a regular C array as a \type igraph_vector_t temporarily. + * Be sure that you \em don't ever call the destructor (\ref + * igraph_vector_destroy()) on objects created by this constructor. + * \param v Pointer to an uninitialized \type igraph_vector_t object. + * \param data Pointer, the C array. It may not be \c NULL. + * \param length The length of the C array. + * \return Pointer to the vector object, the same as the + * \p v parameter, for convenience. + * + * Time complexity: O(1) + */ + +const TYPE(igraph_vector)*FUNCTION(igraph_vector, view) (const TYPE(igraph_vector) *v, + const BASE *data, + long int length) { + TYPE(igraph_vector) *v2 = (TYPE(igraph_vector)*)v; + + IGRAPH_ASSERT(data != 0); + + v2->stor_begin = (BASE*)data; + v2->stor_end = (BASE*)data + length; + v2->end = v2->stor_end; + return v; +} + +#ifndef BASE_COMPLEX + +/** + * \ingroup vector + * \function igraph_vector_init_real + * \brief Create an \type igraph_vector_t from the parameters. + * + * + * Because of how C and the C library handles variable length argument + * lists, it is required that you supply real constants to this + * function. This means that + * \verbatim igraph_vector_t v; + * igraph_vector_init_real(&v, 5, 1,2,3,4,5); \endverbatim + * is an error at runtime and the results are undefined. This is + * the proper way: + * \verbatim igraph_vector_t v; + * igraph_vector_init_real(&v, 5, 1.0,2.0,3.0,4.0,5.0); \endverbatim + * \param v Pointer to an uninitialized \type igraph_vector_t object. + * \param no Positive integer, the number of \type igraph_real_t + * parameters to follow. + * \param ... The elements of the vector. + * \return Error code, this can be \c IGRAPH_ENOMEM + * if there isn't enough memory to allocate the vector. + * + * \sa \ref igraph_vector_init_real_end(), \ref igraph_vector_init_int() for similar + * functions. + * + * Time complexity: depends on the time required to allocate memory, + * but at least O(n), the number of + * elements in the vector. + */ + +int FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector) *v, int no, ...) { + int i = 0; + va_list ap; + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, no)); + + va_start(ap, no); + for (i = 0; i < no; i++) { + VECTOR(*v)[i] = (BASE) va_arg(ap, double); + } + va_end(ap); + + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_init_real_end + * \brief Create an \type igraph_vector_t from the parameters. + * + * + * This constructor is similar to \ref igraph_vector_init_real(), the only + * difference is that instead of giving the number of elements in the + * vector, a special marker element follows the last real vector + * element. + * \param v Pointer to an uninitialized \type igraph_vector_t object. + * \param endmark This element will signal the end of the vector. It + * will \em not be part of the vector. + * \param ... The elements of the vector. + * \return Error code, \c IGRAPH_ENOMEM if there + * isn't enough memory. + * + * \sa \ref igraph_vector_init_real() and \ref igraph_vector_init_int_end() for + * similar functions. + * + * Time complexity: at least O(n) for + * n elements plus the time + * complexity of the memory allocation. + */ + +int FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector) *v, + double endmark, ...) { + int i = 0, n = 0; + va_list ap; + + va_start(ap, endmark); + while (1) { + BASE num = (BASE) va_arg(ap, double); + if (num == endmark) { + break; + } + n++; + } + va_end(ap); + + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, n)); + IGRAPH_FINALLY(FUNCTION(igraph_vector, destroy), v); + + va_start(ap, endmark); + for (i = 0; i < n; i++) { + VECTOR(*v)[i] = (BASE) va_arg(ap, double); + } + va_end(ap); + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_init_int + * \brief Create an \type igraph_vector_t containing the parameters. + * + * + * This function is similar to \ref igraph_vector_init_real(), but it expects + * \type int parameters. It is important that all parameters + * should be of this type, otherwise the result of the function call + * is undefined. + * \param v Pointer to an uninitialized \type igraph_vector_t object. + * \param no The number of \type int parameters to follow. + * \param ... The elements of the vector. + * \return Error code, \c IGRAPH_ENOMEM if there is + * not enough memory. + * \sa \ref igraph_vector_init_real() and igraph_vector_init_int_end(), these are + * similar functions. + * + * Time complexity: at least O(n) for + * n elements plus the time + * complexity of the memory allocation. + */ + +int FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector) *v, int no, ...) { + int i = 0; + va_list ap; + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, no)); + + va_start(ap, no); + for (i = 0; i < no; i++) { + VECTOR(*v)[i] = (BASE) va_arg(ap, int); + } + va_end(ap); + + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_init_int_end + * \brief Create an \type igraph_vector_t from the parameters. + * + * + * This constructor is similar to \ref igraph_vector_init_int(), the only + * difference is that instead of giving the number of elements in the + * vector, a special marker element follows the last real vector + * element. + * \param v Pointer to an uninitialized \type igraph_vector_t object. + * \param endmark This element will signal the end of the vector. It + * will \em not be part of the vector. + * \param ... The elements of the vector. + * \return Error code, \c IGRAPH_ENOMEM if there + * isn't enough memory. + * + * \sa \ref igraph_vector_init_int() and \ref igraph_vector_init_real_end() for + * similar functions. + * + * Time complexity: at least O(n) for + * n elements plus the time + * complexity of the memory allocation. + */ + +int FUNCTION(igraph_vector_init, int_end)(TYPE(igraph_vector) *v, int endmark, ...) { + int i = 0, n = 0; + va_list ap; + + va_start(ap, endmark); + while (1) { + int num = va_arg(ap, int); + if (num == endmark) { + break; + } + n++; + } + va_end(ap); + + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, n)); + IGRAPH_FINALLY(FUNCTION(igraph_vector, destroy), v); + + va_start(ap, endmark); + for (i = 0; i < n; i++) { + VECTOR(*v)[i] = (BASE) va_arg(ap, int); + } + va_end(ap); + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +#endif /* ifndef BASE_COMPLEX */ + +/** + * \ingroup vector + * \function igraph_vector_destroy + * \brief Destroys a vector object. + * + * + * All vectors initialized by \ref igraph_vector_init() should be properly + * destroyed by this function. A destroyed vector needs to be + * reinitialized by \ref igraph_vector_init(), \ref igraph_vector_init_copy() or + * another constructor. + * \param v Pointer to the (previously initialized) vector object to + * destroy. + * + * Time complexity: operating system dependent. + */ + +void FUNCTION(igraph_vector, destroy) (TYPE(igraph_vector)* v) { + IGRAPH_ASSERT(v != 0); + if (v->stor_begin != 0) { + IGRAPH_FREE(v->stor_begin); + v->stor_begin = NULL; + } +} + +/** + * \ingroup vector + * \function igraph_vector_capacity + * \brief Returns the allocated capacity of the vector + * + * Note that this might be different from the size of the vector (as + * queried by \ref igraph_vector_size(), and specifies how many elements + * the vector can hold, without reallocation. + * \param v Pointer to the (previously initialized) vector object + * to query. + * \return The allocated capacity. + * + * \sa \ref igraph_vector_size(). + * + * Time complexity: O(1). + */ + +long int FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v) { + return v->stor_end - v->stor_begin; +} + +/** + * \ingroup vector + * \function igraph_vector_reserve + * \brief Reserves memory for a vector. + * + * + * \a igraph vectors are flexible, they can grow and + * shrink. Growing + * however occasionally needs the data in the vector to be copied. + * In order to avoid this, you can call this function to reserve space for + * future growth of the vector. + * + * + * Note that this function does \em not change the size of the + * vector. Let us see a small example to clarify things: if you + * reserve space for 100 elements and the size of your + * vector was (and still is) 60, then you can surely add additional 40 + * elements to your vector before it will be copied. + * \param v The vector object. + * \param size The new \em allocated size of the vector. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: operating system dependent, should be around + * O(n), n + * is the new allocated size of the vector. + */ + +int FUNCTION(igraph_vector, reserve) (TYPE(igraph_vector)* v, long int size) { + long int actual_size = FUNCTION(igraph_vector, size)(v); + BASE *tmp; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + if (size <= actual_size) { + return 0; + } + + tmp = IGRAPH_REALLOC(v->stor_begin, (size_t) size, BASE); + if (tmp == 0) { + IGRAPH_ERROR("cannot reserve space for vector", IGRAPH_ENOMEM); + } + v->stor_begin = tmp; + v->stor_end = v->stor_begin + size; + v->end = v->stor_begin + actual_size; + + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_empty + * \brief Decides whether the size of the vector is zero. + * + * \param v The vector object. + * \return Non-zero number (true) if the size of the vector is zero and + * zero (false) otherwise. + * + * Time complexity: O(1). + */ + +igraph_bool_t FUNCTION(igraph_vector, empty) (const TYPE(igraph_vector)* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return v->stor_begin == v->end; +} + +/** + * \ingroup vector + * \function igraph_vector_size + * \brief Returns the size (=length) of the vector. + * + * \param v The vector object + * \return The size of the vector. + * + * Time complexity: O(1). + */ + +long int FUNCTION(igraph_vector, size) (const TYPE(igraph_vector)* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return v->end - v->stor_begin; +} + +/** + * \ingroup vector + * \function igraph_vector_clear + * \brief Removes all elements from a vector. + * + * + * This function simply sets the size of the vector to zero, it does + * not free any allocated memory. For that you have to call + * \ref igraph_vector_destroy(). + * \param v The vector object. + * + * Time complexity: O(1). + */ + +void FUNCTION(igraph_vector, clear) (TYPE(igraph_vector)* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + v->end = v->stor_begin; +} + +/** + * \ingroup vector + * \function igraph_vector_push_back + * \brief Appends one element to a vector. + * + * + * This function resizes the vector to be one element longer and + * sets the very last element in the vector to \p e. + * \param v The vector object. + * \param e The element to append to the vector. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory. + * + * Time complexity: operating system dependent. What is important is that + * a sequence of n + * subsequent calls to this function has time complexity + * O(n), even if there + * hadn't been any space reserved for the new elements by + * \ref igraph_vector_reserve(). This is implemented by a trick similar to the C++ + * \type vector class: each time more memory is allocated for a + * vector, the size of the additionally allocated memory is the same + * as the vector's current length. (We assume here that the time + * complexity of memory allocation is at most linear.) + */ + +int FUNCTION(igraph_vector, push_back) (TYPE(igraph_vector)* v, BASE e) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + /* full, allocate more storage */ + if (v->stor_end == v->end) { + long int new_size = FUNCTION(igraph_vector, size)(v) * 2; + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(FUNCTION(igraph_vector, reserve)(v, new_size)); + } + + *(v->end) = e; + v->end += 1; + + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_insert + * \brief Inserts a single element into a vector. + * + * Note that this function does not do range checking. Insertion will shift the + * elements from the position given to the end of the vector one position to the + * right, and the new element will be inserted in the empty space created at + * the given position. The size of the vector will increase by one. + * + * \param v The vector object. + * \param pos The position where the new element is to be inserted. + * \param value The new element to be inserted. + */ +int FUNCTION(igraph_vector, insert)(TYPE(igraph_vector) *v, long int pos, + BASE value) { + size_t size = (size_t) FUNCTION(igraph_vector, size)(v); + if (pos < 0) { + return IGRAPH_EINVAL; + } + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(v, (long) size + 1)); + if (((unsigned long)pos) < size) { + memmove(v->stor_begin + pos + 1, v->stor_begin + pos, + sizeof(BASE) * (size - (size_t) pos)); + } + v->stor_begin[pos] = value; + return 0; +} + +/** + * \ingroup vector + * \section igraph_vector_accessing_elements Accessing elements + * + * The simplest way to access an element of a vector is to use the + * \ref VECTOR macro. This macro can be used both for querying and setting + * \type igraph_vector_t elements. If you need a function, \ref + * igraph_vector_e() queries and \ref igraph_vector_set() sets an element of a + * vector. \ref igraph_vector_e_ptr() returns the address of an element. + * + * \ref igraph_vector_tail() returns the last element of a non-empty + * vector. There is no igraph_vector_head() function + * however, as it is easy to write VECTOR(v)[0] + * instead. + */ + +/** + * \ingroup vector + * \function igraph_vector_e + * \brief Access an element of a vector. + * \param v The \type igraph_vector_t object. + * \param pos The position of the element, the index of the first + * element is zero. + * \return The desired element. + * \sa \ref igraph_vector_e_ptr() and the \ref VECTOR macro. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_vector, e) (const TYPE(igraph_vector)* v, long int pos) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return * (v->stor_begin + pos); +} + +/** + * \ingroup vector + * \function igraph_vector_e_ptr + * \brief Get the address of an element of a vector + * \param v The \type igraph_vector_t object. + * \param pos The position of the element, the position of the first + * element is zero. + * \return Pointer to the desired element. + * \sa \ref igraph_vector_e() and the \ref VECTOR macro. + * + * Time complexity: O(1). + */ + +BASE* FUNCTION(igraph_vector, e_ptr) (const TYPE(igraph_vector)* v, long int pos) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return v->stor_begin + pos; +} + +/** + * \ingroup vector + * \function igraph_vector_set + * \brief Assignment to an element of a vector. + * \param v The \type igraph_vector_t element. + * \param pos Position of the element to set. + * \param value New value of the element. + * \sa \ref igraph_vector_e(). + */ + +void FUNCTION(igraph_vector, set) (TYPE(igraph_vector)* v, + long int pos, BASE value) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + *(v->stor_begin + pos) = value; +} + +/** + * \ingroup vector + * \function igraph_vector_null + * \brief Sets each element in the vector to zero. + * + * + * Note that \ref igraph_vector_init() sets the elements to zero as well, so + * it makes no sense to call this function on a just initialized + * vector. Thus if you want to construct a vector of zeros, then you should + * use \ref igraph_vector_init(). + * \param v The vector object. + * + * Time complexity: O(n), the size of + * the vector. + */ + +void FUNCTION(igraph_vector, null) (TYPE(igraph_vector)* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + if (FUNCTION(igraph_vector, size)(v) > 0) { + memset(v->stor_begin, 0, + sizeof(BASE) * (size_t) FUNCTION(igraph_vector, size)(v)); + } +} + +/** + * \function igraph_vector_fill + * \brief Fill a vector with a constant element + * + * Sets each element of the vector to the supplied constant. + * \param vector The vector to work on. + * \param e The element to fill with. + * + * Time complexity: O(n), the size of the vector. + */ + +void FUNCTION(igraph_vector, fill) (TYPE(igraph_vector)* v, BASE e) { + BASE *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + for (ptr = v->stor_begin; ptr < v->end; ptr++) { + *ptr = e; + } +} + +/** + * \ingroup vector + * \function igraph_vector_tail + * \brief Returns the last element in a vector. + * + * + * It is an error to call this function on an empty vector, the result + * is undefined. + * \param v The vector object. + * \return The last element. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_vector, tail)(const TYPE(igraph_vector) *v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return *((v->end) - 1); +} + +/** + * \ingroup vector + * \function igraph_vector_pop_back + * \brief Removes and returns the last element of a vector. + * + * + * It is an error to call this function with an empty vector. + * \param v The vector object. + * \return The removed last element. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_vector, pop_back)(TYPE(igraph_vector)* v) { + BASE tmp; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->end != v->stor_begin); + tmp = FUNCTION(igraph_vector, e)(v, FUNCTION(igraph_vector, size)(v) - 1); + v->end -= 1; + return tmp; +} + +#ifndef NOTORDERED + +/** + * \ingroup vector + * \function igraph_vector_sort_cmp + * \brief Internal comparison function of vector elements, used by + * \ref igraph_vector_sort(). + */ + +static int FUNCTION(igraph_vector, sort_cmp)(const void *a, const void *b) { + const BASE *da = (const BASE *) a; + const BASE *db = (const BASE *) b; + + return (*da > *db) - (*da < *db); +} + +/** + * \ingroup vector + * \function igraph_vector_reverse_sort_cmp + * \brief Internal comparison function of vector elements, used by + * \ref igraph_vector_reverse_sort(). + */ + +static int FUNCTION(igraph_vector, reverse_sort_cmp)(const void *a, const void *b) { + const BASE *da = (const BASE *) a; + const BASE *db = (const BASE *) b; + + return (*da < *db) - (*da > *db); +} + +/** + * \ingroup vector + * \function igraph_vector_sort + * \brief Sorts the elements of the vector into ascending order. + * + * + * If the vector contains any NaN values, the resulting ordering of + * NaN values is undefined and may appear anywhere in the vector. + * \param v Pointer to an initialized vector object. + * + * Time complexity: + * O(n log n) for n elements. + */ + +void FUNCTION(igraph_vector, sort)(TYPE(igraph_vector) *v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + igraph_qsort(v->stor_begin, (size_t) FUNCTION(igraph_vector, size)(v), + sizeof(BASE), FUNCTION(igraph_vector, sort_cmp)); +} + +/** + * \ingroup vector + * \function igraph_vector_reverse_sort + * \brief Sorts the elements of the vector into descending order. + * + * + * If the vector contains any NaN values, the resulting ordering of + * NaN values is undefined and may appear anywhere in the vector. + * \param v Pointer to an initialized vector object. + * + * Time complexity: + * O(n log n) for n elements. + */ + +void FUNCTION(igraph_vector, reverse_sort)(TYPE(igraph_vector) *v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + igraph_qsort(v->stor_begin, (size_t) FUNCTION(igraph_vector, size)(v), + sizeof(BASE), FUNCTION(igraph_vector, reverse_sort_cmp)); +} + +/** + * Ascending comparison function passed to qsort from igraph_vector_qsort_ind + */ +static int FUNCTION(igraph_vector, i_qsort_ind_cmp_asc)(const void *p1, const void *p2) { + BASE **pa = (BASE **) p1; + BASE **pb = (BASE **) p2; + if ( **pa < **pb ) { + return -1; + } + if ( **pa > **pb) { + return 1; + } + return 0; +} + +/** + * Descending comparison function passed to qsort from igraph_vector_qsort_ind + */ +static int FUNCTION(igraph_vector, i_qsort_ind_cmp_desc)(const void *p1, const void *p2) { + BASE **pa = (BASE **) p1; + BASE **pb = (BASE **) p2; + if ( **pa < **pb ) { + return 1; + } + if ( **pa > **pb) { + return -1; + } + return 0; +} + +/** + * \function igraph_vector_qsort_ind + * \brief Return a permutation of indices that sorts a vector + * + * Takes an unsorted array \c v as input and computes an array of + * indices inds such that v[ inds[i] ], with i increasing from 0, is + * an ordered array (either ascending or descending, depending on + * \v order). The order of indices for identical elements is not + * defined. If the vector contains any NaN values, the ordering of + * NaN values is undefined. + * + * \param v the array to be sorted + * \param inds the output array of indices. This must be initialized, + * but will be resized + * \param descending whether the output array should be sorted in descending + * order. + * \return Error code. + * + * This routine uses igraph's built-in qsort routine. + * Algorithm: 1) create an array of pointers to the elements of v. 2) + * Pass this array to qsort. 3) after sorting the difference between + * the pointer value and the first pointer value gives its original + * position in the array. Use this to set the values of inds. + */ + +long int FUNCTION(igraph_vector, qsort_ind)(TYPE(igraph_vector) *v, + igraph_vector_t *inds, igraph_bool_t descending) { + unsigned long int i; + BASE **vind, *first; + size_t n = (size_t) FUNCTION(igraph_vector, size)(v); + IGRAPH_CHECK(igraph_vector_resize(inds, (long) n)); + if (n == 0) { + return 0; + } + vind = IGRAPH_CALLOC(n, BASE*); + if (vind == 0) { + IGRAPH_ERROR("igraph_vector_qsort_ind failed", IGRAPH_ENOMEM); + } + for (i = 0; i < n; i++) { + vind[i] = &VECTOR(*v)[i]; + } + first = vind[0]; + if (descending) { + igraph_qsort(vind, n, sizeof(BASE**), FUNCTION(igraph_vector, i_qsort_ind_cmp_desc)); + } else { + igraph_qsort(vind, n, sizeof(BASE**), FUNCTION(igraph_vector, i_qsort_ind_cmp_asc)); + } + for (i = 0; i < n; i++) { + VECTOR(*inds)[i] = vind[i] - first; + } + IGRAPH_FREE(vind); + return 0; +} + +/** + * \function igraph_vector_lex_cmp + * \brief Lexicographical comparison of two vectors. + * + * + * If the elements of two vectors match but one is shorter, the shorter + * one comes first. Thus {1, 3} comes after {1, 2, 3}, but before {1, 3, 4}. + * + * + * This function is typically used together with \ref igraph_vector_ptr_sort(). + * + * \param lhs Pointer to a pointer to the first vector (interpreted as an igraph_vector_t **). + * \param rhs Pointer to a pointer to the second vector (interpreted as an igraph_vector_t **). + * \return -1 if \p lhs is lexicographically smaller, + * 0 if \p lhs and \p rhs are equal, else 1. + * \sa \ref igraph_vector_colex_cmp() to compare vectors starting from + * the last element. + * + * Time complexity: O(n), the number of elements in the smaller vector. + * + * \example examples/simple/igraph_vector_ptr_sort.c + */ +int FUNCTION(igraph_vector, lex_cmp)(const void *lhs, const void *rhs) { + const TYPE(igraph_vector) *a = * (TYPE(igraph_vector) **) lhs; + const TYPE(igraph_vector) *b = * (TYPE(igraph_vector) **) rhs; + long int i, sa, sb; + + sa = FUNCTION(igraph_vector, size)(a); + sb = FUNCTION(igraph_vector, size)(b); + + for (i = 0; i < sa; i++) { + if (i >= sb) { + /* b is shorter, and equal to the first part of a */ + return 1; + } + if (VECTOR(*a)[i] < VECTOR(*b)[i]) { + return -1; + } + if (VECTOR(*a)[i] > VECTOR(*b)[i]) { + return 1; + } + } + if (i == sb) { + return 0; + } + /* a is shorter, and equal to the first part of b */ + return -1; +} + +/** + * \function igraph_vector_colex_cmp + * \brief Colexicographical comparison of two vectors. + * + * + * This comparison starts from the last element of both vectors and + * moves backward. If the elements of two vectors match but one is + * shorter, the shorter one comes first. Thus {1, 2} comes after {3, 2, 1}, + * but before {0, 1, 2}. + * + * + * This function is typically used together with \ref igraph_vector_ptr_sort(). + * + * \param lhs Pointer to a pointer to the first vector (interpreted as an igraph_vector_t **). + * \param rhs Pointer to a pointer to the second vector (interpreted as an igraph_vector_t **). + * \return -1 if \p lhs in reverse order is + * lexicographically smaller than the reverse of \p rhs, + * 0 if \p lhs and \p rhs are equal, else 1. + * \sa \ref igraph_vector_lex_cmp() to compare vectors starting from + * the first element. + * + * Time complexity: O(n), the number of elements in the smaller vector. + * + * \example examples/simple/igraph_vector_ptr_sort.c + */ +int FUNCTION(igraph_vector, colex_cmp)(const void *lhs, const void *rhs) { + const TYPE(igraph_vector) *a = * (TYPE(igraph_vector) **) lhs; + const TYPE(igraph_vector) *b = * (TYPE(igraph_vector) **) rhs; + long int i, sa, sb, rai, rbi; + + sa = FUNCTION(igraph_vector, size)(a); + sb = FUNCTION(igraph_vector, size)(b); + + for (i = 0; i < sa; i++) { + if (i >= sb) { + /* b is shorter, and equal to the last part of a */ + return 1; + } + /* use reversed indexes */ + rai = sa - i - 1; + rbi = sb - i - 1; + if (VECTOR(*a)[rai] < VECTOR(*b)[rbi]) { + return -1; + } + if (VECTOR(*a)[rai] > VECTOR(*b)[rbi]) { + return 1; + } + } + if (i == sb) { + return 0; + } + /* a is shorter, and equal to the last part of b */ + return -1; +} +#endif /*NOTORDERED*/ + +/** + * \ingroup vector + * \function igraph_vector_resize + * \brief Resize the vector. + * + * + * Note that this function does not free any memory, just sets the + * size of the vector to the given one. It can on the other hand + * allocate more memory if the new size is larger than the previous + * one. In this case the newly appeared elements in the vector are + * \em not set to zero, they are uninitialized. + * \param v The vector object + * \param newsize The new size of the vector. + * \return Error code, + * \c IGRAPH_ENOMEM if there is not enough + * memory. Note that this function \em never returns an error + * if the vector is made smaller. + * \sa \ref igraph_vector_reserve() for allocating memory for future + * extensions of a vector. \ref igraph_vector_resize_min() for + * deallocating the unnneded memory for a vector. + * + * Time complexity: O(1) if the new + * size is smaller, operating system dependent if it is larger. In the + * latter case it is usually around + * O(n), + * n is the new size of the vector. + */ + +int FUNCTION(igraph_vector, resize)(TYPE(igraph_vector)* v, long int newsize) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_CHECK(FUNCTION(igraph_vector, reserve)(v, newsize)); + v->end = v->stor_begin + newsize; + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_resize_min + * \brief Deallocate the unused memory of a vector. + * + * + * Note that this function involves additional memory allocation and + * may result an out-of-memory error. + * \param v Pointer to an initialized vector. + * \return Error code. + * + * \sa \ref igraph_vector_resize(), \ref igraph_vector_reserve(). + * + * Time complexity: operating system dependent. + */ + +int FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v) { + size_t size; + BASE *tmp; + if (v->stor_end == v->end) { + return 0; + } + + size = (size_t) (v->end - v->stor_begin); + tmp = IGRAPH_REALLOC(v->stor_begin, size, BASE); + if (tmp == 0) { + IGRAPH_ERROR("cannot resize vector", IGRAPH_ENOMEM); + } else { + v->stor_begin = tmp; + v->stor_end = v->end = v->stor_begin + size; + } + + return 0; +} + +#ifndef NOTORDERED + +/* We will use x != x for NaN checks below and Clang does not like it unless + * we disable a warning */ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#endif + +/** + * \ingroup vector + * \function igraph_vector_max + * \brief Largest element of a vector. + * + * + * If the size of the vector is zero, an arbitrary number is + * returned. + * \param v The vector object. + * \return The maximum element of \p v, or NaN if any element is NaN. + * + * Time complexity: O(n), the number of elements. + */ +BASE FUNCTION(igraph_vector, max)(const TYPE(igraph_vector)* v) { + BASE max; + BASE *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->stor_begin != v->end); + max = *(v->stor_begin); +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + if (igraph_is_nan(max)) { return max; }; /* Result is NaN */ +#endif + ptr = v->stor_begin + 1; + while (ptr < v->end) { + if ((*ptr) > max) { + max = *ptr; + } +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) + return *ptr; /* Result is NaN */ +#endif + ptr++; + } + return max; +} + + +/** + * \ingroup vector + * \function igraph_vector_which_max + * \brief Gives the index of the maximum element of the vector. + * + * + * If the size of the vector is zero, -1 is returned. If the largest + * element is not unique, then the index of the first is returned. + * If the vector contains NaN values, the index of the first NaN value + * is returned. + * \param v The vector object. + * \return The index of the first maximum element. + * + * Time complexity: O(n), n is the size of the vector. + */ +long int FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v) { + if (!FUNCTION(igraph_vector, empty)(v)) { + BASE *max; + BASE *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->stor_begin != v->end); + max = ptr = v->stor_begin; +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + if (igraph_is_nan(*ptr)) { return ptr - v->stor_begin; } /* Result is NaN */ +#endif + ptr++; + while (ptr < v->end) { + if (*ptr > *max) { + max = ptr; + } +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { + return ptr - v->stor_begin; /* Result is NaN */ + } +#endif + ptr++; + } + return max - v->stor_begin; + } + return -1; +} + +/** + * \ingroup vector + * \function igraph_vector_min + * \brief Smallest element of a vector. + * + * The vector must be non-empty. + * \param v The input vector. + * \return The smallest element of \p v, or NaN if any element is NaN. + * + * Time complexity: O(n), the number of elements. + */ + +BASE FUNCTION(igraph_vector, min)(const TYPE(igraph_vector)* v) { + BASE min; + BASE *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->stor_begin != v->end); + min = *(v->stor_begin); +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + if (igraph_is_nan(min)) { return min; }; /* Result is NaN */ +#endif + ptr = v->stor_begin + 1; + while (ptr < v->end) { + if ((*ptr) < min) { + min = *ptr; + } +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { + return *ptr; /* Result is NaN */ + } +#endif + ptr++; + } + return min; +} + +/** + * \ingroup vector + * \function igraph_vector_which_min + * \brief Index of the smallest element. + * + * + * The vector must be non-empty. If the smallest element is not unique, + * then the index of the first is returned. If the vector contains NaN + * values, the index of the first NaN value is returned. + * \param v The input vector. + * \return Index of the smallest element. + * + * Time complexity: O(n), the number of elements. + */ +long int FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v) { + if (!FUNCTION(igraph_vector, empty)(v)) { + BASE *min; + BASE *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->stor_begin != v->end); + min = ptr = v->stor_begin; +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + if (igraph_is_nan(*ptr)) { return ptr - v->stor_begin; } /* Result is NaN */ +#endif + ptr++; + while (ptr < v->end) { + if (*ptr < *min) { + min = ptr; + } +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { + return ptr - v->stor_begin; /* Result is NaN */ + } +#endif + ptr++; + } + return min - v->stor_begin; + } + return -1; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif + +/** + * \ingroup vector + * \function igraph_vector_init_copy + * \brief Initializes a vector from an ordinary C array (constructor). + * + * \param v Pointer to an uninitialized vector object. + * \param data A regular C array. + * \param length The length of the C array. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: operating system specific, usually + * O(\p length). + */ + +int FUNCTION(igraph_vector, init_copy)(TYPE(igraph_vector) *v, + const BASE *data, long int length) { + v->stor_begin = IGRAPH_CALLOC(length, BASE); + if (v->stor_begin == 0) { + IGRAPH_ERROR("cannot init vector from array", IGRAPH_ENOMEM); + } + v->stor_end = v->stor_begin + length; + v->end = v->stor_end; + memcpy(v->stor_begin, data, (size_t) length * sizeof(BASE)); + + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_copy_to + * \brief Copies the contents of a vector to a C array. + * + * + * The C array should have sufficient length. + * \param v The vector object. + * \param to The C array. + * + * Time complexity: O(n), + * n is the size of the vector. + */ + +void FUNCTION(igraph_vector, copy_to)(const TYPE(igraph_vector) *v, BASE *to) { + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + if (v->end != v->stor_begin) { + memcpy(to, v->stor_begin, sizeof(BASE) * (size_t) (v->end - v->stor_begin)); + } +} + +/** + * \ingroup vector + * \function igraph_vector_copy + * \brief Initializes a vector from another vector object (constructor). + * + * + * The contents of the existing vector object will be copied to + * the new one. + * \param to Pointer to a not yet initialized vector object. + * \param from The original vector object to copy. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: operating system dependent, usually + * O(n), + * n is the size of the vector. + */ + +int FUNCTION(igraph_vector, copy)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from) { + long int from_size; + + IGRAPH_ASSERT(from != NULL); + IGRAPH_ASSERT(from->stor_begin != NULL); + + from_size = FUNCTION(igraph_vector, size)(from); + to->stor_begin = IGRAPH_CALLOC(from_size, BASE); + if (to->stor_begin == 0) { + IGRAPH_ERROR("cannot copy vector", IGRAPH_ENOMEM); + } + to->stor_end = to->stor_begin + FUNCTION(igraph_vector, size)(from); + to->end = to->stor_end; + memcpy(to->stor_begin, from->stor_begin, + (size_t) FUNCTION(igraph_vector, size)(from) * sizeof(BASE)); + + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_sum + * \brief Calculates the sum of the elements in the vector. + * + * + * For the empty vector 0.0 is returned. + * \param v The vector object. + * \return The sum of the elements. + * + * Time complexity: O(n), the size of + * the vector. + */ + +BASE FUNCTION(igraph_vector, sum)(const TYPE(igraph_vector) *v) { + BASE res = ZERO; + BASE *p; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + for (p = v->stor_begin; p < v->end; p++) { +#ifdef SUM + SUM(res, res, *p); +#else + res += *p; +#endif + } + return res; +} + +igraph_real_t FUNCTION(igraph_vector, sumsq)(const TYPE(igraph_vector) *v) { + igraph_real_t res = 0.0; + BASE *p; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + for (p = v->stor_begin; p < v->end; p++) { +#ifdef SQ + res += SQ(*p); +#else + res += (*p) * (*p); +#endif + } + return res; +} + +/** + * \ingroup vector + * \function igraph_vector_prod + * \brief Calculates the product of the elements in the vector. + * + * + * For the empty vector one (1) is returned. + * \param v The vector object. + * \return The product of the elements. + * + * Time complexity: O(n), the size of + * the vector. + */ + +BASE FUNCTION(igraph_vector, prod)(const TYPE(igraph_vector) *v) { + BASE res = ONE; + BASE *p; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + for (p = v->stor_begin; p < v->end; p++) { +#ifdef PROD + PROD(res, res, *p); +#else + res *= *p; +#endif + } + return res; +} + +/** + * \ingroup vector + * \function igraph_vector_cumsum + * \brief Calculates the cumulative sum of the elements in the vector. + * + * + * \param to An initialized vector object that will store the cumulative + * sums. Element i of this vector will store the sum of the elements + * of the 'from' vector, up to and including element i. + * \param from The input vector. + * \return Error code. + * + * Time complexity: O(n), the size of the vector. + */ + +int FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from) { + BASE res = ZERO; + BASE *p, *p2; + + IGRAPH_ASSERT(from != NULL); + IGRAPH_ASSERT(from->stor_begin != NULL); + IGRAPH_ASSERT(to != NULL); + IGRAPH_ASSERT(to->stor_begin != NULL); + + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(to, FUNCTION(igraph_vector, size)(from))); + + for (p = from->stor_begin, p2 = to->stor_begin; p < from->end; p++, p2++) { +#ifdef SUM + SUM(res, res, *p); +#else + res += *p; +#endif + *p2 = res; + } + + return 0; +} + +#ifndef NOTORDERED + +/** + * \ingroup vector + * \function igraph_vector_init_seq + * \brief Initializes a vector with a sequence. + * + * + * The vector will contain the numbers \p from, + * \p from+1, ..., \p to. + * \param v Pointer to an uninitialized vector object. + * \param from The lower limit in the sequence (inclusive). + * \param to The upper limit in the sequence (inclusive). + * \return Error code: + * \c IGRAPH_ENOMEM: out of memory. + * + * Time complexity: O(n), the number + * of elements in the vector. + */ + +int FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector) *v, + BASE from, BASE to) { + BASE *p; + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, (long int) (to - from + 1))); + + for (p = v->stor_begin; p < v->end; p++) { + *p = from++; + } + + return 0; +} + +#endif + +/** + * \ingroup vector + * \function igraph_vector_remove_section + * \brief Deletes a section from a vector. + * + * + * Note that this function does not do range checking. The result is + * undefined if you supply invalid limits. + * \param v The vector object. + * \param from The position of the first element to remove. + * \param to The position of the first element \em not to remove. + * + * Time complexity: O(n-from), + * n is the number of elements in the + * vector. + */ + +void FUNCTION(igraph_vector, remove_section)(TYPE(igraph_vector) *v, + long int from, long int to) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + /* Not removing from the end? */ + if (to < FUNCTION(igraph_vector, size)(v)) { + memmove(v->stor_begin + from, v->stor_begin + to, + sizeof(BASE) * (size_t) (v->end - v->stor_begin - to)); + } + v->end -= (to - from); +} + +/** + * \ingroup vector + * \function igraph_vector_remove + * \brief Removes a single element from a vector. + * + * Note that this function does not do range checking. + * \param v The vector object. + * \param elem The position of the element to remove. + * + * Time complexity: O(n-elem), + * n is the number of elements in the + * vector. + */ + +void FUNCTION(igraph_vector, remove)(TYPE(igraph_vector) *v, long int elem) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + FUNCTION(igraph_vector, remove_section)(v, elem, elem + 1); +} + +/** + * \ingroup vector + * \function igraph_vector_move_interval + * \brief Copies a section of a vector. + * + * + * The result of this function is undefined if the source and target + * intervals overlap. + * \param v The vector object. + * \param begin The position of the first element to move. + * \param end The position of the first element \em not to move. + * \param to The target position. + * \return Error code, the current implementation always returns with + * success. + * + * Time complexity: O(end-begin). + */ + +int FUNCTION(igraph_vector, move_interval)(TYPE(igraph_vector) *v, + long int begin, long int end, + long int to) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + memcpy(v->stor_begin + to, v->stor_begin + begin, + sizeof(BASE) * (size_t) (end - begin)); + + return 0; +} + +int FUNCTION(igraph_vector, move_interval2)(TYPE(igraph_vector) *v, + long int begin, long int end, + long int to) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + memmove(v->stor_begin + to, v->stor_begin + begin, + sizeof(BASE) * (size_t) (end - begin)); + + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_permdelete + * \brief Remove elements of a vector (for internal use). + */ + +void FUNCTION(igraph_vector, permdelete)(TYPE(igraph_vector) *v, + const igraph_vector_t *index, long int nremove) { + long int i, n; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + n = FUNCTION(igraph_vector, size)(v); + for (i = 0; i < n; i++) { + if (VECTOR(*index)[i] != 0) { + VECTOR(*v)[ (long int)VECTOR(*index)[i] - 1 ] = VECTOR(*v)[i]; + } + } + v->end -= nremove; +} + +#ifndef NOTORDERED + +/** + * \ingroup vector + * \function igraph_vector_isininterval + * \brief Checks if all elements of a vector are in the given + * interval. + * + * \param v The vector object. + * \param low The lower limit of the interval (inclusive). + * \param high The higher limit of the interval (inclusive). + * \return True (positive integer) if all vector elements are in the + * interval, false (zero) otherwise. If any element is NaN, it will + * return \c 0 (=false). + * + * Time complexity: O(n), the number + * of elements in the vector. + */ + +igraph_bool_t FUNCTION(igraph_vector, isininterval)(const TYPE(igraph_vector) *v, + BASE low, + BASE high) { + BASE *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + for (ptr = v->stor_begin; ptr < v->end; ptr++) { + if (!(*ptr >= low && *ptr <= high)) { + return 0; + } + } + return 1; +} + +/** + * \ingroup vector + * \function igraph_vector_any_smaller + * \brief Checks if any element of a vector is smaller than a limit. + * + * \param v The \type igraph_vector_t object. + * \param limit The limit. + * \return True (positive integer) if the vector contains at least one + * smaller element than \p limit, false (zero) + * otherwise. + * + * Time complexity: O(n), the number + * of elements in the vector. + */ + +igraph_bool_t FUNCTION(igraph_vector, any_smaller)(const TYPE(igraph_vector) *v, + BASE limit) { + BASE *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + for (ptr = v->stor_begin; ptr < v->end; ptr++) { + if (*ptr < limit) { + return 1; + } + } + return 0; +} + +#endif + +/** + * \ingroup vector + * \function igraph_vector_all_e + * \brief Are all elements equal? + * + * \param lhs The first vector. + * \param rhs The second vector. + * \return Positive integer (=true) if the elements in the \p lhs are all + * equal to the corresponding elements in \p rhs. Returns \c 0 + * (=false) if the lengths of the vectors don't match. + * + * Time complexity: O(n), the length of the vectors. + */ + +igraph_bool_t FUNCTION(igraph_vector, all_e)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs) { + long int i, s; + IGRAPH_ASSERT(lhs != 0); + IGRAPH_ASSERT(rhs != 0); + IGRAPH_ASSERT(lhs->stor_begin != 0); + IGRAPH_ASSERT(rhs->stor_begin != 0); + + s = FUNCTION(igraph_vector, size)(lhs); + if (s != FUNCTION(igraph_vector, size)(rhs)) { + return 0; + } else { + for (i = 0; i < s; i++) { + BASE l = VECTOR(*lhs)[i]; + BASE r = VECTOR(*rhs)[i]; +#ifdef EQ + if (!EQ(l, r)) { +#else + if (l != r) { +#endif + return 0; + } + } + return 1; + } +} + +igraph_bool_t +FUNCTION(igraph_vector, is_equal)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs) { + return FUNCTION(igraph_vector, all_e)(lhs, rhs); +} + +#ifndef NOTORDERED + +/** + * \ingroup vector + * \function igraph_vector_all_l + * \brief Are all elements less? + * + * \param lhs The first vector. + * \param rhs The second vector. + * \return Positive integer (=true) if the elements in the \p lhs are all + * less than the corresponding elements in \p rhs. Returns \c 0 + * (=false) if the lengths of the vectors don't match. If any element + * is NaN, it will return \c 0 (=false). + * + * Time complexity: O(n), the length of the vectors. + */ + +igraph_bool_t FUNCTION(igraph_vector, all_l)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs) { + long int i, s; + IGRAPH_ASSERT(lhs != 0); + IGRAPH_ASSERT(rhs != 0); + IGRAPH_ASSERT(lhs->stor_begin != 0); + IGRAPH_ASSERT(rhs->stor_begin != 0); + + s = FUNCTION(igraph_vector, size)(lhs); + if (s != FUNCTION(igraph_vector, size)(rhs)) { + return 0; + } else { + for (i = 0; i < s; i++) { + BASE l = VECTOR(*lhs)[i]; + BASE r = VECTOR(*rhs)[i]; + if (l >= r) { + return 0; + } + } + return 1; + } +} + +/** + * \ingroup vector + * \function igraph_vector_all_g + * \brief Are all elements greater? + * + * \param lhs The first vector. + * \param rhs The second vector. + * \return Positive integer (=true) if the elements in the \p lhs are all + * greater than the corresponding elements in \p rhs. Returns \c 0 + * (=false) if the lengths of the vectors don't match. If any element + * is NaN, it will return \c 0 (=false). + * + * Time complexity: O(n), the length of the vectors. + */ + +igraph_bool_t FUNCTION(igraph_vector, all_g)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs) { + + long int i, s; + IGRAPH_ASSERT(lhs != 0); + IGRAPH_ASSERT(rhs != 0); + IGRAPH_ASSERT(lhs->stor_begin != 0); + IGRAPH_ASSERT(rhs->stor_begin != 0); + + s = FUNCTION(igraph_vector, size)(lhs); + if (s != FUNCTION(igraph_vector, size)(rhs)) { + return 0; + } else { + for (i = 0; i < s; i++) { + BASE l = VECTOR(*lhs)[i]; + BASE r = VECTOR(*rhs)[i]; + if (l <= r) { + return 0; + } + } + return 1; + } +} + +/** + * \ingroup vector + * \function igraph_vector_all_le + * \brief Are all elements less or equal? + * + * \param lhs The first vector. + * \param rhs The second vector. + * \return Positive integer (=true) if the elements in the \p lhs are all + * less than or equal to the corresponding elements in \p + * rhs. Returns \c 0 (=false) if the lengths of the vectors don't + * match. If any element is NaN, it will return \c 0 (=false). + * + * Time complexity: O(n), the length of the vectors. + */ + +igraph_bool_t +FUNCTION(igraph_vector, all_le)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs) { + long int i, s; + IGRAPH_ASSERT(lhs != 0); + IGRAPH_ASSERT(rhs != 0); + IGRAPH_ASSERT(lhs->stor_begin != 0); + IGRAPH_ASSERT(rhs->stor_begin != 0); + + s = FUNCTION(igraph_vector, size)(lhs); + if (s != FUNCTION(igraph_vector, size)(rhs)) { + return 0; + } else { + for (i = 0; i < s; i++) { + BASE l = VECTOR(*lhs)[i]; + BASE r = VECTOR(*rhs)[i]; + if (l > r) { + return 0; + } + } + return 1; + } +} + +/** + * \ingroup vector + * \function igraph_vector_all_ge + * \brief Are all elements greater or equal? + * + * \param lhs The first vector. + * \param rhs The second vector. + * \return Positive integer (=true) if the elements in the \p lhs are all + * greater than or equal to the corresponding elements in \p + * rhs. Returns \c 0 (=false) if the lengths of the vectors don't + * match. If any element is NaN, it will return \c 0 (=false). + * + * Time complexity: O(n), the length of the vectors. + */ + +igraph_bool_t +FUNCTION(igraph_vector, all_ge)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs) { + long int i, s; + IGRAPH_ASSERT(lhs != 0); + IGRAPH_ASSERT(rhs != 0); + IGRAPH_ASSERT(lhs->stor_begin != 0); + IGRAPH_ASSERT(rhs->stor_begin != 0); + + s = FUNCTION(igraph_vector, size)(lhs); + if (s != FUNCTION(igraph_vector, size)(rhs)) { + return 0; + } else { + for (i = 0; i < s; i++) { + BASE l = VECTOR(*lhs)[i]; + BASE r = VECTOR(*rhs)[i]; + if (l < r) { + return 0; + } + } + return 1; + } +} + +#endif + + +#ifndef NOTORDERED + +igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vector) *v, + BASE what, long int *pos, + long int start, long int end); + +/** + * \ingroup vector + * \function igraph_vector_binsearch + * \brief Finds an element by binary searching a sorted vector. + * + * + * It is assumed that the vector is sorted. If the specified element + * (\p what) is not in the vector, then the + * position of where it should be inserted (to keep the vector sorted) + * is returned. If the vector contains any NaN values, the returned + * value is undefined and \p pos may point to any position. + * \param v The \type igraph_vector_t object. + * \param what The element to search for. + * \param pos Pointer to a \type long int. This is set to the + * position of an instance of \p what in the + * vector if it is present. If \p v does not + * contain \p what then + * \p pos is set to the position to which it + * should be inserted (to keep the the vector sorted of course). + * \return Positive integer (true) if \p what is + * found in the vector, zero (false) otherwise. + * + * Time complexity: O(log(n)), + * n is the number of elements in + * \p v. + */ + +igraph_bool_t FUNCTION(igraph_vector, binsearch)(const TYPE(igraph_vector) *v, + BASE what, long int *pos) { + return FUNCTION(igraph_i_vector, binsearch_slice)(v, what, pos, + 0, FUNCTION(igraph_vector, size)(v)); +} + +/** + * \ingroup vector + * \function igraph_vector_binsearch_slice + * \brief Finds an element by binary searching a sorted slice of a vector. + * + * + + * It is assumed that the indicated slice of the vector, from \p start to \p end, + * is sorted. If the specified element (\p what) is not in the slice of the + * vector, then the position of where it should be inserted (to keep the vector + * sorted) is returned. If the indicated slice contains any NaN values, the + * returned value is undefined and \c pos may point to any position within + * the slice. + * \param v The \type igraph_vector_t object. + * \param what The element to search for. + * \param pos Pointer to a \type long int. This is set to the position of an + * instance of \p what in the slice of the vector if it is present. If \p + * v does not contain \p what then \p pos is set to the position to which + * it should be inserted (to keep the the vector sorted). + * \param start The start position of the slice to search (inclusive). + * \param end The end position of the slice to search (exclusive). + * \return Positive integer (true) if \p what is found in the vector, + * zero (false) otherwise. + * + * Time complexity: O(log(n)), + * n is the number of elements in the slice of \p v, i.e. \p end - \p start. + */ + +igraph_bool_t FUNCTION(igraph_vector, binsearch_slice)(const TYPE(igraph_vector) *v, + BASE what, long int *pos, + long int start, long int end) { + long int left = start; + long int right = end - 1; + + if (left < 0) + IGRAPH_ERROR("Invalid start position.", IGRAPH_EINVAL); + + if (right >= FUNCTION(igraph_vector, size)(v)) + IGRAPH_ERROR("Invalid end position.", IGRAPH_EINVAL); + + if (left > right) + IGRAPH_ERROR("Invalid slice, start position must be smaller than end position.", + IGRAPH_EINVAL); + + return FUNCTION(igraph_i_vector, binsearch_slice)(v, what, pos, start, end); +} + +igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vector) *v, + BASE what, long int *pos, + long int start, long int end) { + long int left = start; + long int right = end - 1; + + while (left <= right) { + /* (right + left) / 2 could theoretically overflow for long vectors */ + long int middle = left + ((right - left) >> 1); + if (VECTOR(*v)[middle] > what) { + right = middle - 1; + } else if (VECTOR(*v)[middle] < what) { + left = middle + 1; + } else { + if (pos != 0) { + *pos = middle; + } + return 1; + } + } + + /* if we are here, the element was not found */ + if (pos != 0) { + *pos = left; + } + + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_binsearch2 + * \brief Binary search, without returning the index. + * + * + * It is assumed that the vector is sorted. + * \param v The \type igraph_vector_t object. + * \param what The element to search for. + * \return Positive integer (true) if \p what is + * found in the vector, zero (false) otherwise. + * + * Time complexity: O(log(n)), + * n is the number of elements in + * \p v. + */ + +igraph_bool_t FUNCTION(igraph_vector, binsearch2)(const TYPE(igraph_vector) *v, + BASE what) { + long int left = 0; + long int right = FUNCTION(igraph_vector, size)(v) - 1; + + while (left <= right) { + /* (right + left) / 2 could theoretically overflow for long vectors */ + long int middle = left + ((right - left) >> 1); + if (what < VECTOR(*v)[middle]) { + right = middle - 1; + } else if (what > VECTOR(*v)[middle]) { + left = middle + 1; + } else { + return 1; + } + } + + return 0; +} + +#endif + +/** + * \function igraph_vector_scale + * \brief Multiply all elements of a vector by a constant + * + * \param v The vector. + * \param by The constant. + * \return Error code. The current implementation always returns with success. + * + * Added in version 0.2. + * + * Time complexity: O(n), the number of elements in a vector. + */ + +void FUNCTION(igraph_vector, scale)(TYPE(igraph_vector) *v, BASE by) { + long int i; + for (i = 0; i < FUNCTION(igraph_vector, size)(v); i++) { +#ifdef PROD + PROD(VECTOR(*v)[i], VECTOR(*v)[i], by); +#else + VECTOR(*v)[i] *= by; +#endif + } +} + +/** + * \function igraph_vector_add_constant + * \brief Add a constant to the vector. + * + * \p plus is added to every element of \p v. Note that overflow + * might happen. + * \param v The input vector. + * \param plus The constant to add. + * + * Time complexity: O(n), the number of elements. + */ + +void FUNCTION(igraph_vector, add_constant)(TYPE(igraph_vector) *v, BASE plus) { + long int i, n = FUNCTION(igraph_vector, size)(v); + for (i = 0; i < n; i++) { +#ifdef SUM + SUM(VECTOR(*v)[i], VECTOR(*v)[i], plus); +#else + VECTOR(*v)[i] += plus; +#endif + } +} + +/** + * \function igraph_vector_contains + * \brief Linear search in a vector. + * + * Check whether the supplied element is included in the vector, by + * linear search. + * \param v The input vector. + * \param e The element to look for. + * \return \c TRUE if the element is found and \c FALSE otherwise. + * + * Time complexity: O(n), the length of the vector. + */ + +igraph_bool_t FUNCTION(igraph_vector, contains)(const TYPE(igraph_vector) *v, + BASE e) { + BASE *p = v->stor_begin; + while (p < v->end) { +#ifdef EQ + if (EQ(*p, e)) { +#else + if (*p == e) { +#endif + return 1; + } + p++; + } + return 0; +} + +/** + * \function igraph_vector_search + * \brief Search from a given position + * + * The supplied element \p what is searched in vector \p v, starting + * from element index \p from. If found then the index of the first + * instance (after \p from) is stored in \p pos. + * \param v The input vector. + * \param from The index to start searching from. No range checking is + * performed. + * \param what The element to find. + * \param pos If not \c NULL then the index of the found element is + * stored here. + * \return Boolean, \c TRUE if the element was found, \c FALSE + * otherwise. + * + * Time complexity: O(m), the number of elements to search, the length + * of the vector minus the \p from argument. + */ + +igraph_bool_t FUNCTION(igraph_vector, search)(const TYPE(igraph_vector) *v, + long int from, BASE what, + long int *pos) { + long int i, n = FUNCTION(igraph_vector, size)(v); + for (i = from; i < n; i++) { +#ifdef EQ + if (EQ(VECTOR(*v)[i], what)) { + break; + } +#else + if (VECTOR(*v)[i] == what) { + break; + } +#endif + } + + if (i < n) { + if (pos != 0) { + *pos = i; + } + return 1; + } else { + return 0; + } +} + +#ifndef NOTORDERED + +/** + * \function igraph_vector_filter_smaller + * \ingroup internal + */ + +int FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, + BASE elem) { + long int i = 0, n = FUNCTION(igraph_vector, size)(v); + long int s; + while (i < n && VECTOR(*v)[i] < elem) { + i++; + } + s = i; + + while (s < n && VECTOR(*v)[s] == elem) { + s++; + } + + FUNCTION(igraph_vector, remove_section)(v, 0, i + (s - i) / 2); + return 0; +} + +#endif + +/** + * \function igraph_vector_append + * \brief Append a vector to another one. + * + * The target vector will be resized (except when \p from is empty). + * \param to The vector to append to. + * \param from The vector to append, it is kept unchanged. + * \return Error code. + * + * Time complexity: O(n), the number of elements in the new vector. + */ + +int FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from) { + long tosize, fromsize; + + tosize = FUNCTION(igraph_vector, size)(to); + fromsize = FUNCTION(igraph_vector, size)(from); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(to, tosize + fromsize)); + memcpy(to->stor_begin + tosize, from->stor_begin, + sizeof(BASE) * (size_t) fromsize); + to->end = to->stor_begin + tosize + fromsize; + + return 0; +} + +/** + * \function igraph_vector_get_interval + */ + +int FUNCTION(igraph_vector, get_interval)(const TYPE(igraph_vector) *v, + TYPE(igraph_vector) *res, + long int from, long int to) { + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(res, to - from)); + memcpy(res->stor_begin, v->stor_begin + from, + (size_t) (to - from) * sizeof(BASE)); + return 0; +} + +#ifndef NOTORDERED + +/** + * \function igraph_vector_maxdifference + * \brief The maximum absolute difference of \p m1 and \p m2 + * + * The element with the largest absolute value in \p m1 - \p m2 is + * returned. Both vectors must be non-empty, but they not need to have + * the same length, the extra elements in the longer vector are ignored. If + * any value is NaN in the shorter vector, the result will be NaN. + * \param m1 The first vector. + * \param m2 The second vector. + * \return The maximum absolute difference of \p m1 and \p m2. + * + * Time complexity: O(n), the number of elements in the shorter + * vector. + */ + +igraph_real_t FUNCTION(igraph_vector, maxdifference)(const TYPE(igraph_vector) *m1, + const TYPE(igraph_vector) *m2) { + long int n1 = FUNCTION(igraph_vector, size)(m1); + long int n2 = FUNCTION(igraph_vector, size)(m2); + long int n = n1 < n2 ? n1 : n2; + long int i; + igraph_real_t diff = 0.0; + + for (i = 0; i < n; i++) { + igraph_real_t d = fabs((igraph_real_t)(VECTOR(*m1)[i]) - + (igraph_real_t)(VECTOR(*m2)[i])); + if (d > diff) { + diff = d; + } + #if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(d)) { /* Result is NaN */ + return d; + }; + #endif + } + + return diff; +} + +#endif + +/** + * \function igraph_vector_update + * \brief Update a vector from another one. + * + * After this operation the contents of \p to will be exactly the same + * as that of \p from. The vector \p to will be resized if it was originally + * shorter or longer than \p from. + * \param to The vector to update. + * \param from The vector to update from. + * \return Error code. + * + * Time complexity: O(n), the number of elements in \p from. + */ + +int FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from) { + size_t n = (size_t) FUNCTION(igraph_vector, size)(from); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(to, (long) n)); + memcpy(to->stor_begin, from->stor_begin, sizeof(BASE)*n); + return 0; +} + +/** + * \function igraph_vector_swap + * \brief Swap elements of two vectors. + * + * \param v1 The first vector. + * \param v2 The second vector. + * \return Error code. + * + * Time complexity: O(1). + */ + +int FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2) { + + TYPE(igraph_vector) tmp; + + tmp = *v1; + *v1 = *v2; + *v2 = tmp; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vector_swap_elements + * \brief Swap two elements in a vector. + * + * Note that currently no range checking is performed. + * \param v The input vector. + * \param i Index of the first element. + * \param j Index of the second element (may be the same as the + * first one). + * \return Error code, currently always \c IGRAPH_SUCCESS. + * + * Time complexity: O(1). + */ + +int FUNCTION(igraph_vector, swap_elements)(TYPE(igraph_vector) *v, + long int i, long int j) { + BASE tmp = VECTOR(*v)[i]; + VECTOR(*v)[i] = VECTOR(*v)[j]; + VECTOR(*v)[j] = tmp; + + return 0; +} + +/** + * \function igraph_vector_reverse + * \brief Reverse the elements of a vector. + * + * The first element will be last, the last element will be + * first, etc. + * \param v The input vector. + * \return Error code, currently always \c IGRAPH_SUCCESS. + * + * Time complexity: O(n), the number of elements. + */ + +int FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v) { + + long int n = FUNCTION(igraph_vector, size)(v), n2 = n / 2; + long int i, j; + for (i = 0, j = n - 1; i < n2; i++, j--) { + BASE tmp; + tmp = VECTOR(*v)[i]; + VECTOR(*v)[i] = VECTOR(*v)[j]; + VECTOR(*v)[j] = tmp; + } + return 0; +} + +/** + * \ingroup vector + * \function igraph_vector_shuffle + * \brief Shuffles a vector in-place using the Fisher-Yates method + * + * + * The Fisher-Yates shuffle ensures that every permutation is + * equally probable when using a proper randomness source. Of course + * this does not apply to pseudo-random generators as the cycle of + * these generators is less than the number of possible permutations + * of the vector if the vector is long enough. + * \param v The vector object. + * \return Error code, currently always \c IGRAPH_SUCCESS. + * + * Time complexity: O(n), + * n is the number of elements in the + * vector. + * + * + * References: + * \clist + * \cli (Fisher & Yates 1963) + * R. A. Fisher and F. Yates. \emb Statistical Tables for Biological, + * Agricultural and Medical Research. \eme Oliver and Boyd, 6th edition, + * 1963, page 37. + * \cli (Knuth 1998) + * D. E. Knuth. \emb Seminumerical Algorithms, \eme volume 2 of \emb The Art + * of Computer Programming. \eme Addison-Wesley, 3rd edition, 1998, page 145. + * \endclist + * + * \example examples/simple/igraph_fisher_yates_shuffle.c + */ + +int FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v) { + long int n = FUNCTION(igraph_vector, size)(v); + long int k; + BASE dummy; + + RNG_BEGIN(); + while (n > 1) { + k = RNG_INTEGER(0, n - 1); + n--; + dummy = VECTOR(*v)[n]; + VECTOR(*v)[n] = VECTOR(*v)[k]; + VECTOR(*v)[k] = dummy; + } + RNG_END(); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vector_add + * \brief Add two vectors. + * + * Add the elements of \p v2 to \p v1, the result is stored in \p + * v1. The two vectors must have the same length. + * \param v1 The first vector, the result will be stored here. + * \param v2 The second vector, its contents will be unchanged. + * \return Error code. + * + * Time complexity: O(n), the number of elements. + */ + +int FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2) { + + long int n1 = FUNCTION(igraph_vector, size)(v1); + long int n2 = FUNCTION(igraph_vector, size)(v2); + long int i; + if (n1 != n2) { + IGRAPH_ERROR("Vectors must have the same number of elements for swapping", + IGRAPH_EINVAL); + } + + for (i = 0; i < n1; i++) { +#ifdef SUM + SUM(VECTOR(*v1)[i], VECTOR(*v1)[i], VECTOR(*v2)[i]); +#else + VECTOR(*v1)[i] += VECTOR(*v2)[i]; +#endif + } + + return 0; +} + +/** + * \function igraph_vector_sub + * \brief Subtract a vector from another one. + * + * Subtract the elements of \p v2 from \p v1, the result is stored in + * \p v1. The two vectors must have the same length. + * \param v1 The first vector, to subtract from. The result is stored + * here. + * \param v2 The vector to subtract, it will be unchanged. + * \return Error code. + * + * Time complexity: O(n), the length of the vectors. + */ + +int FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2) { + + long int n1 = FUNCTION(igraph_vector, size)(v1); + long int n2 = FUNCTION(igraph_vector, size)(v2); + long int i; + if (n1 != n2) { + IGRAPH_ERROR("Vectors must have the same number of elements for swapping", + IGRAPH_EINVAL); + } + + for (i = 0; i < n1; i++) { +#ifdef DIFF + DIFF(VECTOR(*v1)[i], VECTOR(*v1)[i], VECTOR(*v2)[i]); +#else + VECTOR(*v1)[i] -= VECTOR(*v2)[i]; +#endif + } + + return 0; +} + +/** + * \function igraph_vector_mul + * \brief Multiply two vectors. + * + * \p v1 will be multiplied by \p v2, elementwise. The two vectors + * must have the same length. + * \param v1 The first vector, the result will be stored here. + * \param v2 The second vector, it is left unchanged. + * \return Error code. + * + * Time complexity: O(n), the number of elements. + */ + +int FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2) { + + long int n1 = FUNCTION(igraph_vector, size)(v1); + long int n2 = FUNCTION(igraph_vector, size)(v2); + long int i; + if (n1 != n2) { + IGRAPH_ERROR("Vectors must have the same number of elements for swapping", + IGRAPH_EINVAL); + } + + for (i = 0; i < n1; i++) { +#ifdef PROD + PROD(VECTOR(*v1)[i], VECTOR(*v1)[i], VECTOR(*v2)[i]); +#else + VECTOR(*v1)[i] *= VECTOR(*v2)[i]; +#endif + } + + return 0; +} + +/** + * \function igraph_vector_div + * \brief Divide a vector by another one. + * + * \p v1 is divided by \p v2, elementwise. They must have the same length. If the + * base type of the vector can generate divide by zero errors then + * please make sure that \p v2 contains no zero if you want to avoid + * trouble. + * \param v1 The dividend. The result is also stored here. + * \param v2 The divisor, it is left unchanged. + * \return Error code. + * + * Time complexity: O(n), the length of the vectors. + */ + +int FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2) { + + long int n1 = FUNCTION(igraph_vector, size)(v1); + long int n2 = FUNCTION(igraph_vector, size)(v2); + long int i; + if (n1 != n2) { + IGRAPH_ERROR("Vectors must have the same number of elements for swapping", + IGRAPH_EINVAL); + } + + for (i = 0; i < n1; i++) { +#ifdef DIV + DIV(VECTOR(*v1)[i], VECTOR(*v1)[i], VECTOR(*v2)[i]); +#else + VECTOR(*v1)[i] /= VECTOR(*v2)[i]; +#endif + } + + return 0; +} + +#ifndef NOABS + +int FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v) { +#ifdef UNSIGNED + /* Nothing do to, unsigned type */ + IGRAPH_UNUSED(v); +#else + long int i, n = FUNCTION(igraph_vector, size)(v); + for (i = 0; i < n; i++) { + VECTOR(*v)[i] = VECTOR(*v)[i] >= 0 ? VECTOR(*v)[i] : -VECTOR(*v)[i]; + } +#endif + + return 0; +} + +#endif + +#ifndef NOTORDERED + +/** + * \function igraph_vector_minmax + * \brief Minimum and maximum elements of a vector. + * + * Handy if you want to have both the smallest and largest element of + * a vector. The vector is only traversed once. The vector must be non-empty. + * If a vector contains at least one NaN, both \c min and \c max will be NaN. + * \param v The input vector. It must contain at least one element. + * \param min Pointer to a base type variable, the minimum is stored + * here. + * \param max Pointer to a base type variable, the maximum is stored + * here. + * \return Error code. + * + * Time complexity: O(n), the number of elements. + */ + +int FUNCTION(igraph_vector, minmax)(const TYPE(igraph_vector) *v, + BASE *min, BASE *max) { + BASE* ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->stor_begin != v->end); + *min = *max = *(v->stor_begin); + #if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + if (igraph_is_nan(*min)) { return IGRAPH_SUCCESS; }; /* Result is NaN */ + #endif + ptr = v->stor_begin + 1; + while (ptr < v->end) { + if (*ptr > *max) { + *max = *ptr; + } else if (*ptr < *min) { + *min = *ptr; + } + #if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { /* Result is NaN */ + *min = *max = *ptr; + return IGRAPH_SUCCESS; + }; + #endif + ptr++; + } + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vector_which_minmax + * \brief Index of the minimum and maximum elements + * + * + * Handy if you need the indices of the smallest and largest + * elements. The vector is traversed only once. The vector must be + * non-empty. If the minimum or maximum is not unique, the index + * of the first minimum or the first maximum is returned, respectively. + * If a vector contains at least one NaN, both \c which_min and \c which_max + * will point to the first NaN value. + * \param v The input vector. It must contain at least one element. + * \param which_min The index of the minimum element will be stored + * here. + * \param which_max The index of the maximum element will be stored + * here. + * \return Error code. + * + * Time complexity: O(n), the number of elements. + */ + +int FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v, + long int *which_min, long int *which_max) { + BASE *min, *max; + BASE *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->stor_begin != v->end); + ptr = v->stor_begin; + min = max = ptr; + #if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + if (igraph_is_nan(*ptr)) { /* Result is NaN */ + *which_min = *which_max = 0; + return IGRAPH_SUCCESS; + } + #endif + while (ptr < v->end) { + if (*ptr > *max) { + max = ptr; + } else if (*ptr < *min) { + min = ptr; + } +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { /* Result is NaN */ + *which_min = *which_max = ptr - v->stor_begin; + return IGRAPH_SUCCESS; + } +#endif + ptr++; + } + *which_min = min - v->stor_begin; + *which_max = max - v->stor_begin; + return IGRAPH_SUCCESS; +} + +#endif + +/** + * \function igraph_vector_isnull + * \brief Are all elements zero? + * + * Checks whether all elements of a vector are zero. + * \param v The input vector + * \return Boolean, \c TRUE if the vector contains only zeros, \c + * FALSE otherwise. + * + * Time complexity: O(n), the number of elements. + */ + +igraph_bool_t FUNCTION(igraph_vector, isnull)(const TYPE(igraph_vector) *v) { + + long int n = FUNCTION(igraph_vector, size)(v); + long int i = 0; + +#ifdef EQ + while (i < n && EQ(VECTOR(*v)[i], ZERO)) { +#else + while (i < n && VECTOR(*v)[i] == ZERO) { +#endif + i++; + } + + return i == n; +} + +#ifndef NOTORDERED + +int FUNCTION(igraph_i_vector, intersect_sorted)( + const TYPE(igraph_vector) *v1, long int begin1, long int end1, + const TYPE(igraph_vector) *v2, long int begin2, long int end2, + TYPE(igraph_vector) *result); + +/** + * \function igraph_vector_intersect_sorted + * \brief Calculates the intersection of two sorted vectors + * + * The elements that are contained in both vectors are stored in the result + * vector. All three vectors must be initialized. + * + * + * Instead of the naive intersection which takes O(n), this function uses + * the set intersection method of Ricardo Baeza-Yates, which is more efficient + * when one of the vectors is significantly smaller than the other, and + * gives similar performance on average when the two vectors are equal. + * + * + * The algorithm keeps the multiplicities of the elements: if an element appears + * k1 times in the first vector and k2 times in the second, the result + * will include that element min(k1, k2) times. + * + * + * Reference: Baeza-Yates R: A fast set intersection algorithm for sorted + * sequences. In: Lecture Notes in Computer Science, vol. 3109/2004, pp. + * 400--408, 2004. Springer Berlin/Heidelberg. ISBN: 978-3-540-22341-2. + * + * \param v1 the first vector + * \param v2 the second vector + * \param result the result vector, which will also be sorted. + * + * Time complexity: O(m log(n)) where m is the size of the smaller vector + * and n is the size of the larger one. + */ +int FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result) { + long int size1, size2; + + size1 = FUNCTION(igraph_vector, size)(v1); + size2 = FUNCTION(igraph_vector, size)(v2); + + FUNCTION(igraph_vector, clear)(result); + + if (size1 == 0 || size2 == 0) { + return 0; + } + + IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( + v1, 0, size1, v2, 0, size2, result)); + return 0; +} + +int FUNCTION(igraph_i_vector, intersect_sorted)( + const TYPE(igraph_vector) *v1, long int begin1, long int end1, + const TYPE(igraph_vector) *v2, long int begin2, long int end2, + TYPE(igraph_vector) *result) { + long int size1, size2, probe1, probe2; + + if (begin1 == end1 || begin2 == end2) { + return 0; + } + + size1 = end1 - begin1; + size2 = end2 - begin2; + + if (size1 < size2) { + probe1 = begin1 + (size1 >> 1); /* pick the median element */ + FUNCTION(igraph_i_vector, binsearch_slice)(v2, VECTOR(*v1)[probe1], &probe2, begin2, end2); + IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( + v1, begin1, probe1, v2, begin2, probe2, result + )); + if (!(probe2 == end2 || VECTOR(*v1)[probe1] < VECTOR(*v2)[probe2])) { + IGRAPH_CHECK(FUNCTION(igraph_vector, push_back)(result, VECTOR(*v2)[probe2])); + probe2++; + } + IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( + v1, probe1 + 1, end1, v2, probe2, end2, result + )); + } else { + probe2 = begin2 + (size2 >> 1); /* pick the median element */ + FUNCTION(igraph_i_vector, binsearch_slice)(v1, VECTOR(*v2)[probe2], &probe1, begin1, end1); + IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( + v1, begin1, probe1, v2, begin2, probe2, result + )); + if (!(probe1 == end1 || VECTOR(*v2)[probe2] < VECTOR(*v1)[probe1])) { + IGRAPH_CHECK(FUNCTION(igraph_vector, push_back)(result, VECTOR(*v2)[probe2])); + probe1++; + } + IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( + v1, probe1, end1, v2, probe2 + 1, end2, result + )); + } + + return 0; +} + +/** + * \function igraph_vector_difference_sorted + * \brief Calculates the difference between two sorted vectors (considered as sets) + * + * The elements that are contained in only the first vector but not the second are + * stored in the result vector. All three vectors must be initialized. + * + * \param v1 the first vector + * \param v2 the second vector + * \param result the result vector + */ +int FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result) { + long int i, j, i0, j0; + i0 = FUNCTION(igraph_vector, size)(v1); + j0 = FUNCTION(igraph_vector, size)(v2); + i = j = 0; + + if (i0 == 0) { + /* v1 is empty, this is easy */ + FUNCTION(igraph_vector, clear)(result); + return IGRAPH_SUCCESS; + } + + if (j0 == 0) { + /* v2 is empty, this is easy */ + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(result, i0)); + memcpy(result->stor_begin, v1->stor_begin, sizeof(BASE) * (size_t) i0); + return IGRAPH_SUCCESS; + } + + FUNCTION(igraph_vector, clear)(result); + + /* Copy the part of v1 that is less than the first element of v2 */ + while (i < i0 && VECTOR(*v1)[i] < VECTOR(*v2)[j]) { + i++; + } + if (i > 0) { + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(result, i)); + memcpy(result->stor_begin, v1->stor_begin, sizeof(BASE) * (size_t) i); + } + + while (i < i0 && j < j0) { + BASE element = VECTOR(*v1)[i]; + if (element == VECTOR(*v2)[j]) { + i++; j++; + while (i < i0 && VECTOR(*v1)[i] == element) { + i++; + } + while (j < j0 && VECTOR(*v2)[j] == element) { + j++; + } + } else if (element < VECTOR(*v2)[j]) { + IGRAPH_CHECK(FUNCTION(igraph_vector, push_back)(result, element)); + i++; + } else { + j++; + } + } + if (i < i0) { + long int oldsize = FUNCTION(igraph_vector, size)(result); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(result, oldsize + i0 - i)); + memcpy(result->stor_begin + oldsize, v1->stor_begin + i, + sizeof(BASE) * (size_t) (i0 - i)); + } + + return 0; +} + +#endif + +#if defined(OUT_FORMAT) + +#ifndef USING_R +int FUNCTION(igraph_vector, print)(const TYPE(igraph_vector) *v) { + long int i, n = FUNCTION(igraph_vector, size)(v); + if (n != 0) { +#ifdef PRINTFUNC + PRINTFUNC(VECTOR(*v)[0]); +#else + printf(OUT_FORMAT, VECTOR(*v)[0]); +#endif + } + for (i = 1; i < n; i++) { +#ifdef PRINTFUNC + putchar(' '); PRINTFUNC(VECTOR(*v)[i]); +#else + printf(" " OUT_FORMAT, VECTOR(*v)[i]); +#endif + } + printf("\n"); + return 0; +} + +int FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v, + const char *format) { + long int i, n = FUNCTION(igraph_vector, size)(v); + if (n != 0) { + printf(format, VECTOR(*v)[0]); + } + for (i = 1; i < n; i++) { + putchar(' '); printf(format, VECTOR(*v)[i]); + } + printf("\n"); + return 0; +} + +#endif + +int FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file) { + long int i, n = FUNCTION(igraph_vector, size)(v); + if (n != 0) { +#ifdef FPRINTFUNC + FPRINTFUNC(file, VECTOR(*v)[0]); +#else + fprintf(file, OUT_FORMAT, VECTOR(*v)[0]); +#endif + } + for (i = 1; i < n; i++) { +#ifdef FPRINTFUNC + fputc(' ', file); FPRINTFUNC(file, VECTOR(*v)[i]); +#else + fprintf(file, " " OUT_FORMAT, VECTOR(*v)[i]); +#endif + } + fprintf(file, "\n"); + return 0; +} + +#endif + +int FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v, + TYPE(igraph_vector) *newv, + const igraph_vector_t *idx) { + + long int i, newlen = igraph_vector_size(idx); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(newv, newlen)); + + for (i = 0; i < newlen; i++) { + long int j = (long int) VECTOR(*idx)[i]; + VECTOR(*newv)[i] = VECTOR(*v)[j]; + } + + return 0; +} + +int FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v, + const igraph_vector_int_t *idx) { + BASE *tmp; + int i, n = igraph_vector_int_size(idx); + + tmp = IGRAPH_CALLOC(n, BASE); + if (!tmp) { + IGRAPH_ERROR("Cannot index vector", IGRAPH_ENOMEM); + } + + for (i = 0; i < n; i++) { + tmp[i] = VECTOR(*v)[ VECTOR(*idx)[i] ]; + } + + IGRAPH_FREE(v->stor_begin); + v->stor_begin = tmp; + v->stor_end = v->end = tmp + n; + + return 0; +} diff --git a/src/rigraph/core/core/vector_ptr.c b/src/rigraph/core/core/vector_ptr.c new file mode 100644 index 0000000..d3358bc --- /dev/null +++ b/src/rigraph/core/core/vector_ptr.c @@ -0,0 +1,639 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" +#include "igraph_vector_ptr.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "igraph_qsort.h" + +#include /* memcpy & co. */ +#include + +/** + * \section about_igraph_vector_ptr_objects Pointer vectors + * (igraph_vector_ptr_t) + * + * The \type igraph_vector_ptr_t data type is very similar to + * the \ref igraph_vector_t type, but it stores generic pointers instead of + * real numbers. + * + * This type has the same space complexity as \ref + * igraph_vector_t, and most implemented operations work the same way + * as for \ref igraph_vector_t. + * + * This type is mostly used to pass to or receive from a set of + * graphs to some \a igraph functions, such as \ref + * igraph_decompose(), which decomposes a graph to connected + * components. + * + * The same \ref VECTOR macro used for ordinary vectors can be + * used for pointer vectors as well, please note that a typeless + * generic pointer will be provided by this macro and you may need to + * cast it to a specific pointer before starting to work with it. + * + * Pointer vectors may have an associated item destructor function + * which takes a pointer and returns nothing. The item destructor will + * be called on each item in the pointer vector when it is destroyed by + * \ref igraph_vector_ptr_destroy() or \ref igraph_vector_ptr_destroy_all(), + * or when its elements are freed by \ref igraph_vector_ptr_free_all(). + * Note that the semantics of an item destructor does not coincide with + * C++ destructors; for instance, when a pointer vector is resized to a + * smaller size, the extra items will \em not be destroyed automatically! + * Nevertheless, item destructors may become handy in many cases; for + * instance, a vector of graphs generated by \ref igraph_decompose() can + * be destroyed with a single call to \ref igraph_vector_ptr_destroy_all() + * if the item destructor is set to \ref igraph_destroy(). + */ + + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_init + * \brief Initialize a pointer vector (constructor). + * + * + * This is the constructor of the pointer vector data type. All + * pointer vectors constructed this way should be destroyed via + * calling \ref igraph_vector_ptr_destroy(). + * \param v Pointer to an uninitialized + * igraph_vector_ptr_t object, to be created. + * \param size Integer, the size of the pointer vector. + * \return Error code: + * \c IGRAPH_ENOMEM if out of memory + * + * Time complexity: operating system dependent, the amount of \quote + * time \endquote required to allocate \p size elements. + */ + +int igraph_vector_ptr_init(igraph_vector_ptr_t* v, int long size) { + long int alloc_size = size > 0 ? size : 1; + IGRAPH_ASSERT(v != NULL); + if (size < 0) { + size = 0; + } + v->stor_begin = IGRAPH_CALLOC(alloc_size, void*); + if (v->stor_begin == 0) { + IGRAPH_ERROR("vector ptr init failed", IGRAPH_ENOMEM); + } + v->stor_end = v->stor_begin + alloc_size; + v->end = v->stor_begin + size; + v->item_destructor = 0; + + return 0; +} + +/** + */ + +const igraph_vector_ptr_t *igraph_vector_ptr_view(const igraph_vector_ptr_t *v, void *const *data, + long int length) { + igraph_vector_ptr_t *v2 = (igraph_vector_ptr_t*) v; + v2->stor_begin = (void **)data; + v2->stor_end = (void**)data + length; + v2->end = v2->stor_end; + v2->item_destructor = 0; + return v; +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_destroy + * \brief Destroys a pointer vector. + * + * + * The destructor for pointer vectors. + * \param v Pointer to the pointer vector to destroy. + * + * Time complexity: operating system dependent, the \quote time + * \endquote required to deallocate O(n) bytes, n is the number of + * elements allocated for the pointer vector (not necessarily the + * number of elements in the vector). + */ + +void igraph_vector_ptr_destroy(igraph_vector_ptr_t* v) { + IGRAPH_ASSERT(v != 0); + if (v->stor_begin != 0) { + IGRAPH_FREE(v->stor_begin); + v->stor_begin = NULL; + } +} + +static void igraph_i_vector_ptr_call_item_destructor_all(igraph_vector_ptr_t* v) { + void **ptr; + + if (v->item_destructor != 0) { + for (ptr = v->stor_begin; ptr < v->end; ptr++) { + if (*ptr != 0) { + v->item_destructor(*ptr); + } + } + } +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_free_all + * \brief Frees all the elements of a pointer vector. + * + * If an item destructor is set for this pointer vector, this function will + * first call the destructor on all elements of the vector and then + * free all the elements using \ref igraph_free(). If an item destructor is not set, + * the elements will simply be freed. + * + * \param v Pointer to the pointer vector whose elements will be freed. + * + * Time complexity: operating system dependent, the \quote time + * \endquote required to call the destructor n times and then + * deallocate O(n) pointers, each pointing to a memory area of + * arbitrary size. n is the number of elements in the pointer vector. + */ + +void igraph_vector_ptr_free_all(igraph_vector_ptr_t* v) { + void **ptr; + IGRAPH_ASSERT(v != 0); + IGRAPH_ASSERT(v->stor_begin != 0); + + igraph_i_vector_ptr_call_item_destructor_all(v); + for (ptr = v->stor_begin; ptr < v->end; ptr++) { + IGRAPH_FREE(*ptr); + } +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_destroy_all + * \brief Frees all the elements and destroys the pointer vector. + * + * This function is equivalent to \ref igraph_vector_ptr_free_all() + * followed by \ref igraph_vector_ptr_destroy(). + * + * \param v Pointer to the pointer vector to destroy. + * + * Time complexity: operating system dependent, the \quote time + * \endquote required to deallocate O(n) pointers, each pointing to + * a memory area of arbitrary size, plus the \quote time \endquote + * required to deallocate O(n) bytes, n being the number of elements + * allocated for the pointer vector (not necessarily the number of + * elements in the vector). + */ + +void igraph_vector_ptr_destroy_all(igraph_vector_ptr_t* v) { + IGRAPH_ASSERT(v != 0); + IGRAPH_ASSERT(v->stor_begin != 0); + igraph_vector_ptr_free_all(v); + igraph_vector_ptr_set_item_destructor(v, 0); + igraph_vector_ptr_destroy(v); +} + +/** + * \ingroup vectorptr + * \brief Reserves memory for a pointer vector for later use. + * + * @return Error code: + * - IGRAPH_ENOMEM: out of memory + */ + +int igraph_vector_ptr_reserve(igraph_vector_ptr_t* v, long int size) { + long int actual_size = igraph_vector_ptr_size(v); + void **tmp; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + if (size <= igraph_vector_ptr_size(v)) { + return 0; + } + + tmp = IGRAPH_REALLOC(v->stor_begin, (size_t) size, void*); + if (tmp == 0) { + IGRAPH_ERROR("vector ptr reserve failed", IGRAPH_ENOMEM); + } + v->stor_begin = tmp; + v->stor_end = v->stor_begin + size; + v->end = v->stor_begin + actual_size; + + return 0; +} + +/** + * \ingroup vectorptr + * \brief Decides whether the pointer vector is empty. + */ + +igraph_bool_t igraph_vector_ptr_empty(const igraph_vector_ptr_t* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return v->stor_begin == v->end; +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_size + * \brief Gives the number of elements in the pointer vector. + * + * \param v The pointer vector object. + * \return The size of the object, i.e. the number of pointers stored. + * + * Time complexity: O(1). + */ + +long int igraph_vector_ptr_size(const igraph_vector_ptr_t* v) { + IGRAPH_ASSERT(v != NULL); + /* IGRAPH_ASSERT(v->stor_begin != NULL); */ /* TODO */ + return v->end - v->stor_begin; +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_clear + * \brief Removes all elements from a pointer vector. + * + * + * This function resizes a pointer to vector to zero length. Note that + * the pointed objects are \em not deallocated, you should call + * \ref igraph_free() on them, or make sure that their allocated memory is freed + * in some other way, you'll get memory leaks otherwise. If you have + * set up an item destructor earlier, the destructor will be called + * on every element. + * + * + * Note that the current implementation of this function does + * \em not deallocate the memory required for storing the + * pointers, so making a pointer vector smaller this way does not give + * back any memory. This behavior might change in the future. + * \param v The pointer vector to clear. + * + * Time complexity: O(1). + */ + +void igraph_vector_ptr_clear(igraph_vector_ptr_t* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + igraph_i_vector_ptr_call_item_destructor_all(v); + v->end = v->stor_begin; +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_push_back + * \brief Appends an element to the back of a pointer vector. + * + * \param v The pointer vector. + * \param e The new element to include in the pointer vector. + * \return Error code. + * \sa igraph_vector_push_back() for the corresponding operation of + * the ordinary vector type. + * + * Time complexity: O(1) or O(n), n is the number of elements in the + * vector. The pointer vector implementation ensures that n subsequent + * push_back operations need O(n) time to complete. + */ + +int igraph_vector_ptr_push_back(igraph_vector_ptr_t* v, void* e) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + /* full, allocate more storage */ + if (v->stor_end == v->end) { + long int new_size = igraph_vector_ptr_size(v) * 2; + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(igraph_vector_ptr_reserve(v, new_size)); + } + + *(v->end) = e; + v->end += 1; + + return 0; +} + +void *igraph_vector_ptr_pop_back(igraph_vector_ptr_t *v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->stor_begin != v->end); + v->end -= 1; + return *(v->end); +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_insert + * \brief Inserts a single element into a pointer vector. + * + * Note that this function does not do range checking. Insertion will shift the + * elements from the position given to the end of the vector one position to the + * right, and the new element will be inserted in the empty space created at + * the given position. The size of the vector will increase by one. + * + * \param v The pointer vector object. + * \param pos The position where the new element is inserted. + * \param e The inserted element + */ +int igraph_vector_ptr_insert(igraph_vector_ptr_t* v, long int pos, void* e) { + long int size = igraph_vector_ptr_size(v); + IGRAPH_CHECK(igraph_vector_ptr_resize(v, size + 1)); + if (pos < size) { + memmove(v->stor_begin + pos + 1, v->stor_begin + pos, + sizeof(void*) * (size_t) (size - pos)); + } + v->stor_begin[pos] = e; + return 0; +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_e + * \brief Access an element of a pointer vector. + * + * \param v Pointer to a pointer vector. + * \param pos The index of the pointer to return. + * \return The pointer at \p pos position. + * + * Time complexity: O(1). + */ + +void *igraph_vector_ptr_e(const igraph_vector_ptr_t* v, long int pos) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return *(v->stor_begin + pos); +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_set + * \brief Assign to an element of a pointer vector. + * + * \param v Pointer to a pointer vector. + * \param pos The index of the pointer to update. + * \param value The new pointer to set in the vector. + * + * Time complexity: O(1). + */ + +void igraph_vector_ptr_set(igraph_vector_ptr_t* v, long int pos, void* value) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + *(v->stor_begin + pos) = value; +} + +/** + * \ingroup vectorptr + * \brief Set all elements of a pointer vector to the NULL pointer. + */ + +void igraph_vector_ptr_null(igraph_vector_ptr_t* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + if (igraph_vector_ptr_size(v) > 0) { + memset(v->stor_begin, 0, sizeof(void*) * + (size_t) igraph_vector_ptr_size(v)); + } +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_resize + * \brief Resizes a pointer vector. + * + * + * Note that if a vector is made smaller the pointed object are not + * deallocated by this function and the item destructor is not called + * on the extra elements. + * + * \param v A pointer vector. + * \param newsize The new size of the pointer vector. + * \return Error code. + * + * Time complexity: O(1) if the vector if made smaller. Operating + * system dependent otherwise, the amount of \quote time \endquote + * needed to allocate the memory for the vector elements. + */ + +int igraph_vector_ptr_resize(igraph_vector_ptr_t* v, long int newsize) { + IGRAPH_CHECK(igraph_vector_ptr_reserve(v, newsize)); + v->end = v->stor_begin + newsize; + return 0; +} + +/** + * \ingroup vectorptr + * \brief Initializes a pointer vector from an array (constructor). + * + * \return Error code: + * \c IGRAPH_ENOMEM if out of memory + */ + +int igraph_vector_ptr_init_copy(igraph_vector_ptr_t *v, void * *data, long int length) { + v->stor_begin = IGRAPH_CALLOC(length, void*); + if (v->stor_begin == 0) { + IGRAPH_ERROR("cannot init ptr vector from array", IGRAPH_ENOMEM); + } + v->stor_end = v->stor_begin + length; + v->end = v->stor_end; + v->item_destructor = 0; + memcpy(v->stor_begin, data, (size_t) length * sizeof(void*)); + + return 0; +} + +/** + * \ingroup vectorptr + * \brief Copy the contents of a pointer vector to a regular C array. + */ + +void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + if (v->end != v->stor_begin) { + memcpy(to, v->stor_begin, sizeof(void*) * + (size_t) (v->end - v->stor_begin)); + } +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_copy + * \brief Copy a pointer vector (constructor). + * + * + * This function creates a pointer vector by copying another one. This + * is shallow copy, only the pointers in the vector will be copied. + * + * + * It is potentially dangerous to copy a pointer vector with an associated + * item destructor. The copied vector will inherit the item destructor, + * which may cause problems when both vectors are destroyed as the items + * might get destroyed twice. Make sure you know what you are doing when + * copying a pointer vector with an item destructor, or unset the item + * destructor on one of the vectors later. + * + * \param to Pointer to an uninitialized pointer vector object. + * \param from A pointer vector object. + * \return Error code: + * \c IGRAPH_ENOMEM if out of memory + * + * Time complexity: O(n) if allocating memory for n elements can be + * done in O(n) time. + */ + +int igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) { + long int from_size; + + IGRAPH_ASSERT(from != NULL); + /* IGRAPH_ASSERT(from->stor_begin != NULL); */ /* TODO */ + + from_size = igraph_vector_ptr_size(from); + + to->stor_begin = IGRAPH_CALLOC(from_size, void*); + if (to->stor_begin == 0) { + IGRAPH_ERROR("cannot copy ptr vector", IGRAPH_ENOMEM); + } + to->stor_end = to->stor_begin + igraph_vector_ptr_size(from); + to->end = to->stor_end; + to->item_destructor = from->item_destructor; + memcpy(to->stor_begin, from->stor_begin, + (size_t) igraph_vector_ptr_size(from)*sizeof(void*)); + + return 0; +} + +/** + * \ingroup vectorptr + * \brief Remove an element from a pointer vector. + */ + +void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, long int pos) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + if (pos + 1 < igraph_vector_ptr_size(v)) { /* No need to move data when removing the last element. */ + memmove(v->stor_begin + pos, v->stor_begin + pos + 1, + sizeof(void*) * (size_t) (igraph_vector_ptr_size(v) - pos - 1)); + } + v->end--; +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_sort + * \brief Sorts the pointer vector based on an external comparison function. + * + * Sometimes it is necessary to sort the pointers in the vector based on + * the property of the element being referenced by the pointer. This + * function allows us to sort the vector based on an arbitrary external + * comparison function which accepts two void * pointers \c p1 and \c p2 + * and returns an integer less than, equal to or greater than zero if the + * first argument is considered to be respectively less than, equal to, or + * greater than the second. \c p1 and \c p2 will point to the pointer in the + * vector, so they have to be double-dereferenced if one wants to get access + * to the underlying object the address of which is stored in \c v. + * + * \param v The pointer vector to be sorted. + * \param compar A qsort-compatible comparison function. It must take pointers to the + * elements of the pointer vector. For example, if the pointer vector contains + * igraph_vector_t * pointers, then the comparison function must + * interpret its arguments as igraph_vector_t **. + * + * \example examples/simple/igraph_vector_ptr_sort.c + */ +void igraph_vector_ptr_sort(igraph_vector_ptr_t *v, int (*compar)(const void*, const void*)) { + igraph_qsort(v->stor_begin, (size_t) igraph_vector_ptr_size(v), sizeof(void*), + compar); +} + +int igraph_vector_ptr_index_int(igraph_vector_ptr_t *v, + const igraph_vector_int_t *idx) { + void **tmp; + int i, n = igraph_vector_int_size(idx); + + tmp = IGRAPH_CALLOC(n, void*); + if (!tmp) { + IGRAPH_ERROR("Cannot index pointer vector", IGRAPH_ENOMEM); + } + + for (i = 0; i < n; i++) { + tmp[i] = VECTOR(*v)[ VECTOR(*idx)[i] ]; + } + + IGRAPH_FREE(v->stor_begin); + v->stor_begin = tmp; + v->stor_end = v->end = tmp + n; + + return 0; +} + +int igraph_vector_ptr_append(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) { + long int origsize = igraph_vector_ptr_size(to); + long int othersize = igraph_vector_ptr_size(from); + long int i; + + IGRAPH_CHECK(igraph_vector_ptr_resize(to, origsize + othersize)); + for (i = 0; i < othersize; i++, origsize++) { + to->stor_begin[origsize] = from->stor_begin[i]; + } + + return 0; +} + + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_set_item_destructor + * \brief Sets the item destructor for this pointer vector. + * + * The item destructor is a function which will be called on every non-null + * pointer stored in this vector when \ref igraph_vector_ptr_destroy(), + * igraph_vector_ptr_destroy_all() or \ref igraph_vector_ptr_free_all() + * is called. + * + * \return The old item destructor. + * + * Time complexity: O(1). + */ +igraph_finally_func_t* igraph_vector_ptr_set_item_destructor( + igraph_vector_ptr_t *v, igraph_finally_func_t *func) { + igraph_finally_func_t* result = v->item_destructor; + + v->item_destructor = func; + + return result; +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_get_item_destructor + * \brief Gets the current item destructor for this pointer vector. + * + * The item destructor is a function which will be called on every non-null + * pointer stored in this vector when \ref igraph_vector_ptr_destroy(), + * igraph_vector_ptr_destroy_all() or \ref igraph_vector_ptr_free_all() + * is called. + * + * \return The current item destructor. + * + * Time complexity: O(1). + */ +igraph_finally_func_t* igraph_vector_ptr_get_item_destructor(const igraph_vector_ptr_t *v) { + IGRAPH_ASSERT(v != 0); + return v->item_destructor; +} diff --git a/src/rigraph/core/f2c.h b/src/rigraph/core/f2c.h new file mode 100644 index 0000000..b8ec2e2 --- /dev/null +++ b/src/rigraph/core/f2c.h @@ -0,0 +1,238 @@ +/* f2c.h -- Standard Fortran to C header file */ + +/** barf [ba:rf] 2. "He suggested using FORTRAN, and everybody barfed." + + - From The Shogakukan DICTIONARY OF NEW ENGLISH (Second edition) */ + +#ifndef F2C_INCLUDE +#define F2C_INCLUDE + +#include "linalg/blas_internal.h" +#include "linalg/lapack_internal.h" +#include "linalg/arpack_internal.h" + +typedef int integer; +typedef unsigned int uinteger; +typedef char *address; +typedef short int shortint; +typedef float real; +typedef double doublereal; +typedef struct { + real r, i; +} f2c_complex; +typedef struct { + doublereal r, i; +} doublecomplex; +typedef int logical; +typedef short int shortlogical; +typedef char logical1; +typedef char integer1; +#ifdef INTEGER_STAR_8 /* Adjust for integer*8. */ + typedef long longint; /* system-dependent */ + typedef unsigned long ulongint; /* system-dependent */ + #define qbit_clear(a,b) ((a) & ~((ulongint)1 << (b))) + #define qbit_set(a,b) ((a) | ((ulongint)1 << (b))) +#endif + +#define TRUE_ (1) +#define FALSE_ (0) + +/* Extern is for use with -E */ +#ifndef Extern + #define Extern extern +#endif + +/* I/O stuff */ + +#ifdef f2c_i2 + /* for -i2 */ + typedef short flag; + typedef short ftnlen; + typedef short ftnint; +#else + typedef int flag; + typedef int ftnlen; + typedef int ftnint; +#endif + +/*external read, write*/ +typedef struct { + flag cierr; + ftnint ciunit; + flag ciend; + char *cifmt; + ftnint cirec; +} cilist; + +/*internal read, write*/ +typedef struct { + flag icierr; + char *iciunit; + flag iciend; + char *icifmt; + ftnint icirlen; + ftnint icirnum; +} icilist; + +/*open*/ +typedef struct { + flag oerr; + ftnint ounit; + char *ofnm; + ftnlen ofnmlen; + char *osta; + char *oacc; + char *ofm; + ftnint orl; + char *oblnk; +} olist; + +/*close*/ +typedef struct { + flag cerr; + ftnint cunit; + char *csta; +} cllist; + +/*rewind, backspace, endfile*/ +typedef struct { + flag aerr; + ftnint aunit; +} alist; + +/* inquire */ +typedef struct { + flag inerr; + ftnint inunit; + char *infile; + ftnlen infilen; + ftnint *inex; /*parameters in standard's order*/ + ftnint *inopen; + ftnint *innum; + ftnint *innamed; + char *inname; + ftnlen innamlen; + char *inacc; + ftnlen inacclen; + char *inseq; + ftnlen inseqlen; + char *indir; + ftnlen indirlen; + char *infmt; + ftnlen infmtlen; + char *inform; + ftnint informlen; + char *inunf; + ftnlen inunflen; + ftnint *inrecl; + ftnint *innrec; + char *inblank; + ftnlen inblanklen; +} inlist; + +#define VOID void + +union Multitype { /* for multiple entry points */ + integer1 g; + shortint h; + integer i; + /* longint j; */ + real r; + doublereal d; + f2c_complex c; + doublecomplex z; +}; + +typedef union Multitype Multitype; + +/*typedef long int Long;*/ /* No longer used; formerly in Namelist */ + +struct Vardesc { /* for Namelist */ + char *name; + char *addr; + ftnlen *dims; + int type; +}; +typedef struct Vardesc Vardesc; + +struct Namelist { + char *name; + Vardesc **vars; + int nvars; +}; +typedef struct Namelist Namelist; + +#define abs(x) ((x) >= 0 ? (x) : -(x)) +#define dabs(x) (doublereal)abs(x) +#ifndef min +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#endif +#ifndef max +#define max(a,b) ((a) >= (b) ? (a) : (b)) +#endif +#define dmin(a,b) (doublereal)min(a,b) +#define dmax(a,b) (doublereal)max(a,b) +#define bit_test(a,b) ((a) >> (b) & 1) +#define bit_clear(a,b) ((a) & ~((uinteger)1 << (b))) +#define bit_set(a,b) ((a) | ((uinteger)1 << (b))) + +/* procedure parameter types for -A and -C++ */ + +#define F2C_proc_par_types 1 +#ifdef __cplusplus + typedef int /* Unknown procedure type */ (*U_fp)(...); + typedef shortint (*J_fp)(...); + typedef integer (*I_fp)(...); + typedef real (*R_fp)(...); + typedef doublereal (*D_fp)(...), (*E_fp)(...); + typedef /* Complex */ VOID (*C_fp)(...); + typedef /* Double Complex */ VOID (*Z_fp)(...); + typedef logical (*L_fp)(...); + typedef shortlogical (*K_fp)(...); + typedef /* Character */ VOID (*H_fp)(...); + typedef /* Subroutine */ int (*S_fp)(...); +#else + typedef int /* Unknown procedure type */ (*U_fp)(); + typedef shortint (*J_fp)(); + typedef integer (*I_fp)(); + typedef real (*R_fp)(); + typedef doublereal (*D_fp)(), (*E_fp)(); + typedef /* Complex */ VOID (*C_fp)(); + typedef /* Double Complex */ VOID (*Z_fp)(); + typedef logical (*L_fp)(); + typedef shortlogical (*K_fp)(); + typedef /* Character */ VOID (*H_fp)(); + typedef /* Subroutine */ int (*S_fp)(); +#endif +/* E_fp is for real functions when -R is not specified */ +typedef VOID C_f; /* complex function */ +typedef VOID H_f; /* character function */ +typedef VOID Z_f; /* double complex function */ +typedef doublereal E_f; /* real function with -R not specified */ + +/* undef any lower-case symbols that your C compiler predefines, e.g.: */ + +#ifndef Skip_f2c_Undefs + #undef cray + #undef gcos + #undef mc68010 + #undef mc68020 + #undef mips + #undef pdp11 + #undef sgi + #undef sparc + #undef sun + #undef sun2 + #undef sun3 + #undef sun4 + #undef u370 + #undef u3b + #undef u3b2 + #undef u3b5 + #undef unix + #undef vax +#endif + +#include "config.h" + +#endif diff --git a/src/rigraph/core/flow/flow.c b/src/rigraph/core/flow/flow.c new file mode 100644 index 0000000..1169640 --- /dev/null +++ b/src/rigraph/core/flow/flow.c @@ -0,0 +1,2544 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_flow.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_conversion.h" +#include "igraph_constants.h" +#include "igraph_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_progress.h" +#include "igraph_operators.h" +#include "igraph_structural.h" +#include "igraph_topology.h" + +#include "../core/buckets.h" +#include "../core/cutheap.h" +#include "../core/interruption.h" +#include "../core/math.h" + +#include "config.h" + +/* + * Some general remarks about the functions in this file. + * + * The following measures can be calculated: + * ( 1) s-t maximum flow value, directed graph + * ( 2) s-t maximum flow value, undirected graph + * ( 3) s-t maximum flow, directed graph + * ( 4) s-t maximum flow, undirected graph + * ( 5) s-t minimum cut value, directed graph + * ( 6) s-t minimum cut value, undirected graph + * ( 7) minimum cut value, directed graph + * ( 8) minimum cut value, undirected graph + * ( 9) s-t minimum cut, directed graph + * (10) s-t minimum cut, undirected graph + * (11) minimum cut, directed graph + * (12) minimum cut, undirected graph + * (13) s-t edge connectivity, directed graph + * (14) s-t edge connectivity, undirected graph + * (15) edge connectivity, directed graph + * (16) edge connectivity, undirected graph + * (17) s-t vertex connectivity, directed graph + * (18) s-t vertex connectivity, undirected graph + * (19) vertex connectivity, directed graph + * (20) vertex connectivity, undirected graph + * (21) s-t number of edge disjoint paths, directed graph + * (22) s-t number of edge disjoint paths, undirected graph + * (23) s-t number of vertex disjoint paths, directed graph + * (24) s-t number of vertex disjoint paths, undirected graph + * (25) graph adhesion, directed graph + * (26) graph adhesion, undirected graph + * (27) graph cohesion, directed graph + * (28) graph cohesion, undirected graph + * + * This is how they are calculated: + * ( 1) igraph_maxflow_value, calls igraph_maxflow. + * ( 2) igraph_maxflow_value, calls igraph_maxflow, this calls + * igraph_i_maxflow_undirected. This transforms the graph into a + * directed graph, including two mutual edges instead of every + * undirected edge, then igraph_maxflow is called again with the + * directed graph. + * ( 3) igraph_maxflow, does the push-relabel algorithm, optionally + * calculates the cut, the partitions and the flow itself. + * ( 4) igraph_maxflow calls igraph_i_maxflow_undirected, this converts + * the undirected graph into a directed one, adding two mutual edges + * for each undirected edge, then igraph_maxflow is called again, + * with the directed graph. After igraph_maxflow returns, we need + * to edit the flow (and the cut) to make it sense for the + * original graph. + * ( 5) igraph_st_mincut_value, we just call igraph_maxflow_value + * ( 6) igraph_st_mincut_value, we just call igraph_maxflow_value + * ( 7) igraph_mincut_value, we call igraph_maxflow_value (|V|-1)*2 + * times, from vertex 0 to all other vertices and from all other + * vertices to vertex 0 + * ( 8) We call igraph_i_mincut_value_undirected, that calls + * igraph_i_mincut_undirected with partition=partition2=cut=NULL + * The Stoer-Wagner algorithm is used. + * ( 9) igraph_st_mincut, just calls igraph_maxflow. + * (10) igraph_st_mincut, just calls igraph_maxflow. + * (11) igraph_mincut, calls igraph_i_mincut_directed, which runs + * the maximum flow algorithm 2(|V|-1) times, from vertex zero to + * and from all other vertices and stores the smallest cut. + * (12) igraph_mincut, igraph_i_mincut_undirected is called, + * this is the Stoer-Wagner algorithm + * (13) We just call igraph_maxflow_value, back to (1) + * (14) We just call igraph_maxflow_value, back to (2) + * (15) We just call igraph_mincut_value (possibly after some basic + * checks). Back to (7) + * (16) We just call igraph_mincut_value (possibly after some basic + * checks). Back to (8). + * (17) We call igraph_i_st_vertex_connectivity_directed. + * That creates a new graph with 2*|V| vertices and smartly chosen + * edges, so that the s-t edge connectivity of this graph is the + * same as the s-t vertex connectivity of the original graph. + * So finally it calls igraph_maxflow_value, go to (1) + * (18) We call igraph_i_st_vertex_connectivity_undirected. + * We convert the graph to a directed one, + * IGRAPH_TO_DIRECTED_MUTUAL method. Then we call + * igraph_i_st_vertex_connectivity_directed, see (17). + * (19) We call igraph_i_vertex_connectivity_directed. + * That calls igraph_st_vertex_connectivity for all pairs of + * vertices. Back to (17). + * (20) We call igraph_i_vertex_connectivity_undirected. + * That converts the graph into a directed one + * (IGRAPH_TO_DIRECTED_MUTUAL) and calls the directed version, + * igraph_i_vertex_connectivity_directed, see (19). + * (21) igraph_edge_disjoint_paths, we just call igraph_maxflow_value, (1). + * (22) igraph_edge_disjoint_paths, we just call igraph_maxflow_value, (2). + * (23) igraph_vertex_disjoint_paths, if there is a connection between + * the two vertices, then we remove that (or all of them if there + * are many), as this could mess up vertex connectivity + * calculation. The we call + * igraph_i_st_vertex_connectivity_directed, see (19). + * (24) igraph_vertex_disjoint_paths, if there is a connection between + * the two vertices, then we remove that (or all of them if there + * are many), as this could mess up vertex connectivity + * calculation. The we call + * igraph_i_st_vertex_connectivity_undirected, see (20). + * (25) We just call igraph_edge_connectivity, see (15). + * (26) We just call igraph_edge_connectivity, see (16). + * (27) We just call igraph_vertex_connectivity, see (19). + * (28) We just call igraph_vertex_connectivity, see (20). + */ + +/* + * This is an internal function that calculates the maximum flow value + * on undirected graphs, either for an s-t vertex pair or for the + * graph (i.e. all vertex pairs). + * + * It does it by converting the undirected graph to a corresponding + * directed graph, including reciprocal directed edges instead of each + * undirected edge. + */ + +static int igraph_i_maxflow_undirected(const igraph_t *graph, + igraph_real_t *value, + igraph_vector_t *flow, + igraph_vector_t *cut, + igraph_vector_t *partition, + igraph_vector_t *partition2, + igraph_integer_t source, + igraph_integer_t target, + const igraph_vector_t *capacity, + igraph_maxflow_stats_t *stats) { + igraph_integer_t no_of_edges = (igraph_integer_t) igraph_ecount(graph); + igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph); + igraph_vector_t edges; + igraph_vector_t newcapacity; + igraph_t newgraph; + long int i; + + /* We need to convert this to directed by hand, since we need to be + sure that the edge ids will be handled properly to build the new + capacity vector. */ + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&newcapacity, no_of_edges * 2); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 4)); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + IGRAPH_CHECK(igraph_vector_resize(&edges, no_of_edges * 4)); + for (i = 0; i < no_of_edges; i++) { + VECTOR(edges)[no_of_edges * 2 + i * 2] = VECTOR(edges)[i * 2 + 1]; + VECTOR(edges)[no_of_edges * 2 + i * 2 + 1] = VECTOR(edges)[i * 2]; + VECTOR(newcapacity)[i] = VECTOR(newcapacity)[no_of_edges + i] = + capacity ? VECTOR(*capacity)[i] : 1.0; + } + + IGRAPH_CHECK(igraph_create(&newgraph, &edges, no_of_nodes, IGRAPH_DIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + + IGRAPH_CHECK(igraph_maxflow(&newgraph, value, flow, cut, partition, + partition2, source, target, &newcapacity, stats)); + + if (cut) { + long int i, cs = igraph_vector_size(cut); + for (i = 0; i < cs; i++) { + if (VECTOR(*cut)[i] >= no_of_edges) { + VECTOR(*cut)[i] -= no_of_edges; + } + } + } + + /* The flow has one non-zero value for each real-nonreal edge pair, + by definition, we convert it to a positive-negative vector. If + for an edge the flow is negative that means that it is going + from the bigger vertex id to the smaller one. For positive + values the direction is the opposite. */ + if (flow) { + long int i; + for (i = 0; i < no_of_edges; i++) { + VECTOR(*flow)[i] -= VECTOR(*flow)[i + no_of_edges]; + } + IGRAPH_CHECK(igraph_vector_resize(flow, no_of_edges)); + } + + igraph_destroy(&newgraph); + igraph_vector_destroy(&edges); + igraph_vector_destroy(&newcapacity); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +#define FIRST(i) (VECTOR(*first)[(i)]) +#define LAST(i) (VECTOR(*first)[(i)+1]) +#define CURRENT(i) (VECTOR(*current)[(i)]) +#define RESCAP(i) (VECTOR(*rescap)[(i)]) +#define REV(i) (VECTOR(*rev)[(i)]) +#define HEAD(i) (VECTOR(*to)[(i)]) +#define EXCESS(i) (VECTOR(*excess)[(i)]) +#define DIST(i) (VECTOR(*distance)[(i)]) +#define DISCHARGE(v) (igraph_i_mf_discharge((v), ¤t, &first, &rescap, \ + &to, &distance, &excess, \ + no_of_nodes, source, target, \ + &buckets, &ibuckets, \ + &rev, stats, &npushsince, \ + &nrelabelsince)) +#define PUSH(v,e,n) (igraph_i_mf_push((v), (e), (n), current, rescap, \ + excess, target, source, buckets, \ + ibuckets, distance, rev, stats, \ + npushsince)) +#define RELABEL(v) (igraph_i_mf_relabel((v), no_of_nodes, distance, \ + first, rescap, to, current, \ + stats, nrelabelsince)) +#define GAP(b) (igraph_i_mf_gap((b), stats, buckets, ibuckets, \ + no_of_nodes, distance)) +#define BFS() (igraph_i_mf_bfs(&bfsq, source, target, no_of_nodes, \ + &buckets, &ibuckets, &distance, \ + &first, ¤t, &to, &excess, \ + &rescap, &rev)) + +static void igraph_i_mf_gap(long int b, igraph_maxflow_stats_t *stats, + igraph_buckets_t *buckets, igraph_dbuckets_t *ibuckets, + long int no_of_nodes, + igraph_vector_long_t *distance) { + + IGRAPH_UNUSED(buckets); + + long int bo; + (stats->nogap)++; + for (bo = b + 1; bo <= no_of_nodes; bo++) { + while (!igraph_dbuckets_empty_bucket(ibuckets, bo)) { + long int n = igraph_dbuckets_pop(ibuckets, bo); + (stats->nogapnodes)++; + DIST(n) = no_of_nodes; + } + } +} + +static void igraph_i_mf_relabel(long int v, long int no_of_nodes, + igraph_vector_long_t *distance, + igraph_vector_long_t *first, + igraph_vector_t *rescap, igraph_vector_long_t *to, + igraph_vector_long_t *current, + igraph_maxflow_stats_t *stats, int *nrelabelsince) { + + long int min = no_of_nodes; + long int k, l, min_edge = 0; + (stats->norelabel)++; (*nrelabelsince)++; + DIST(v) = no_of_nodes; + for (k = FIRST(v), l = LAST(v); k < l; k++) { + if (RESCAP(k) > 0 && DIST(HEAD(k)) < min) { + min = DIST(HEAD(k)); + min_edge = k; + } + } + min++; + if (min < no_of_nodes) { + DIST(v) = min; + CURRENT(v) = min_edge; + } +} + +static void igraph_i_mf_push(long int v, long int e, long int n, + igraph_vector_long_t *current, + igraph_vector_t *rescap, igraph_vector_t *excess, + long int target, long int source, + igraph_buckets_t *buckets, igraph_dbuckets_t *ibuckets, + igraph_vector_long_t *distance, + igraph_vector_long_t *rev, igraph_maxflow_stats_t *stats, + int *npushsince) { + + + IGRAPH_UNUSED(current); + IGRAPH_UNUSED(source); + + igraph_real_t delta = + RESCAP(e) < EXCESS(v) ? RESCAP(e) : EXCESS(v); + (stats->nopush)++; (*npushsince)++; + if (EXCESS(n) == 0 && n != target) { + igraph_dbuckets_delete(ibuckets, DIST(n), n); + igraph_buckets_add(buckets, (long int) DIST(n), n); + } + RESCAP(e) -= delta; + RESCAP(REV(e)) += delta; + EXCESS(n) += delta; + EXCESS(v) -= delta; +} + +static void igraph_i_mf_discharge(long int v, + igraph_vector_long_t *current, + igraph_vector_long_t *first, + igraph_vector_t *rescap, + igraph_vector_long_t *to, + igraph_vector_long_t *distance, + igraph_vector_t *excess, + long int no_of_nodes, long int source, + long int target, igraph_buckets_t *buckets, + igraph_dbuckets_t *ibuckets, + igraph_vector_long_t *rev, + igraph_maxflow_stats_t *stats, + int *npushsince, int *nrelabelsince) { + do { + long int i; + long int start = (long int) CURRENT(v); + long int stop = (long int) LAST(v); + for (i = start; i < stop; i++) { + if (RESCAP(i) > 0) { + long int nei = HEAD(i); + if (DIST(v) == DIST(nei) + 1) { + PUSH((v), i, nei); + if (EXCESS(v) == 0) { + break; + } + } + } + } + if (i == stop) { + long int origdist = DIST(v); + RELABEL(v); + if (igraph_buckets_empty_bucket(buckets, origdist) && + igraph_dbuckets_empty_bucket(ibuckets, origdist)) { + GAP(origdist); + } + if (DIST(v) == no_of_nodes) { + break; + } + } else { + CURRENT(v) = i; + igraph_dbuckets_add(ibuckets, DIST(v), v); + break; + } + } while (1); +} + +static void igraph_i_mf_bfs(igraph_dqueue_long_t *bfsq, + long int source, long int target, + long int no_of_nodes, igraph_buckets_t *buckets, + igraph_dbuckets_t *ibuckets, + igraph_vector_long_t *distance, + igraph_vector_long_t *first, igraph_vector_long_t *current, + igraph_vector_long_t *to, igraph_vector_t *excess, + igraph_vector_t *rescap, igraph_vector_long_t *rev) { + + long int k, l; + + IGRAPH_UNUSED(source); + + igraph_buckets_clear(buckets); + igraph_dbuckets_clear(ibuckets); + igraph_vector_long_fill(distance, no_of_nodes); + DIST(target) = 0; + + igraph_dqueue_long_push(bfsq, target); + while (!igraph_dqueue_long_empty(bfsq)) { + long int node = igraph_dqueue_long_pop(bfsq); + long int ndist = DIST(node) + 1; + for (k = FIRST(node), l = LAST(node); k < l; k++) { + if (RESCAP(REV(k)) > 0) { + long int nei = HEAD(k); + if (DIST(nei) == no_of_nodes) { + DIST(nei) = ndist; + CURRENT(nei) = FIRST(nei); + if (EXCESS(nei) > 0) { + igraph_buckets_add(buckets, ndist, nei); + } else { + igraph_dbuckets_add(ibuckets, ndist, nei); + } + igraph_dqueue_long_push(bfsq, nei); + } + } + } + } +} + +/** + * \function igraph_maxflow + * Maximum network flow between a pair of vertices + * + * This function implements the Goldberg-Tarjan algorithm for + * calculating value of the maximum flow in a directed or undirected + * graph. The algorithm was given in Andrew V. Goldberg, Robert + * E. Tarjan: A New Approach to the Maximum-Flow Problem, Journal of + * the ACM, 35(4), 921-940, 1988. + * + * The input of the function is a graph, a vector + * of real numbers giving the capacity of the edges and two vertices + * of the graph, the source and the target. A flow is a function + * assigning positive real numbers to the edges and satisfying two + * requirements: (1) the flow value is less than the capacity of the + * edge and (2) at each vertex except the source and the target, the + * incoming flow (i.e. the sum of the flow on the incoming edges) is + * the same as the outgoing flow (i.e. the sum of the flow on the + * outgoing edges). The value of the flow is the incoming flow at the + * target vertex. The maximum flow is the flow with the maximum + * value. + * + * \param graph The input graph, either directed or undirected. + * \param value Pointer to a real number, the value of the maximum + * will be placed here, unless it is a null pointer. + * \param flow If not a null pointer, then it must be a pointer to an + * initialized vector. The vector will be resized, and the flow + * on each edge will be placed in it, in the order of the edge + * ids. For undirected graphs this argument is bit trickier, + * since for these the flow direction is not predetermined by + * the edge direction. For these graphs the elements of the + * \p flow vector can be negative, this means that the flow + * goes from the bigger vertex id to the smaller one. Positive + * values mean that the flow goes from the smaller vertex id to + * the bigger one. + * \param cut A null pointer or a pointer to an initialized vector. + * If not a null pointer, then the minimum cut corresponding to + * the maximum flow is stored here, i.e. all edge ids that are + * part of the minimum cut are stored in the vector. + * \param partition A null pointer or a pointer to an initialized + * vector. If not a null pointer, then the first partition of + * the minimum cut that corresponds to the maximum flow will be + * placed here. The first partition is always the one that + * contains the source vertex. + * \param partition2 A null pointer or a pointer to an initialized + * vector. If not a null pointer, then the second partition of + * the minimum cut that corresponds to the maximum flow will be + * placed here. The second partition is always the one that + * contains the target vertex. + * \param source The id of the source vertex. + * \param target The id of the target vertex. + * \param capacity Vector containing the capacity of the edges. If NULL, then + * every edge is considered to have capacity 1.0. + * \param stats Counts of the number of different operations + * preformed by the algorithm are stored here. + * \return Error code. + * + * Time complexity: O(|V|^3). In practice it is much faster, but i + * cannot prove a better lower bound for the data structure i've + * used. In fact, this implementation runs much faster than the + * \c hi_pr implementation discussed in + * B. V. Cherkassky and A. V. Goldberg: On implementing the + * push-relabel method for the maximum flow problem, (Algorithmica, + * 19:390--410, 1997) on all the graph classes i've tried. + * + * \sa \ref igraph_mincut_value(), \ref igraph_edge_connectivity(), + * \ref igraph_vertex_connectivity() for + * properties based on the maximum flow. + * + * \example examples/simple/flow.c + * \example examples/simple/flow2.c + */ + +int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, + igraph_vector_t *flow, igraph_vector_t *cut, + igraph_vector_t *partition, igraph_vector_t *partition2, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity, + igraph_maxflow_stats_t *stats) { + + igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph); + igraph_integer_t no_of_orig_edges = (igraph_integer_t) igraph_ecount(graph); + igraph_integer_t no_of_edges = 2 * no_of_orig_edges; + + igraph_vector_t rescap, excess; + igraph_vector_long_t from, to, rev, distance; + igraph_vector_t edges, rank; + igraph_vector_long_t current, first; + igraph_buckets_t buckets; + igraph_dbuckets_t ibuckets; + + igraph_dqueue_long_t bfsq; + + long int i, j, idx; + int npushsince = 0, nrelabelsince = 0; + + igraph_maxflow_stats_t local_stats; /* used if the user passed a null pointer for stats */ + + if (stats == 0) { + stats = &local_stats; + } + + if (!igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_maxflow_undirected(graph, value, flow, cut, + partition, partition2, source, + target, capacity, stats)); + return 0; + } + + if (capacity && igraph_vector_size(capacity) != no_of_orig_edges) { + IGRAPH_ERROR("Invalid capacity vector", IGRAPH_EINVAL); + } + if (source < 0 || source >= no_of_nodes || target < 0 || target >= no_of_nodes) { + IGRAPH_ERROR("Invalid source or target vertex", IGRAPH_EINVAL); + } + + stats->nopush = stats->norelabel = stats->nogap = stats->nogapnodes = + stats->nobfs = 0; + + /* + * The data structure: + * - First of all, we consider every edge twice, first the edge + * itself, but also its opposite. + * - (from, to) contain all edges (original + opposite), ordered by + * the id of the source vertex. During the algorithm we just need + * 'to', so from is destroyed soon. We only need it in the + * beginning, to create the 'first' pointers. + * - 'first' is a pointer vector for 'to', first[i] points to the + * first neighbor of vertex i and first[i+1]-1 is the last + * neighbor of vertex i. (Unless vertex i is isolate, in which + * case first[i]==first[i+1]). + * - 'rev' contains a mapping from an edge to its opposite pair + * - 'rescap' contains the residual capacities of the edges, this is + * initially equal to the capacity of the edges for the original + * edges and it is zero for the opposite edges. + * - 'excess' contains the excess flow for the vertices. I.e. the flow + * that is coming in, but it is not going out. + * - 'current' stores the next neighboring vertex to check, for every + * vertex, when excess flow is being pushed to neighbors. + * - 'distance' stores the distance of the vertices from the source. + * - 'rank' and 'edges' are only needed temporarily, for ordering and + * storing the edges. + * - we use an igraph_buckets_t data structure ('buckets') to find + * the vertices with the highest 'distance' values quickly. + * This always contains the vertices that have a positive excess + * flow. + */ +#undef FIRST +#undef LAST +#undef CURRENT +#undef RESCAP +#undef REV +#undef HEAD +#undef EXCESS +#undef DIST +#define FIRST(i) (VECTOR(first)[(i)]) +#define LAST(i) (VECTOR(first)[(i)+1]) +#define CURRENT(i) (VECTOR(current)[(i)]) +#define RESCAP(i) (VECTOR(rescap)[(i)]) +#define REV(i) (VECTOR(rev)[(i)]) +#define HEAD(i) (VECTOR(to)[(i)]) +#define EXCESS(i) (VECTOR(excess)[(i)]) +#define DIST(i) (VECTOR(distance)[(i)]) + + igraph_dqueue_long_init(&bfsq, no_of_nodes); + IGRAPH_FINALLY(igraph_dqueue_long_destroy, &bfsq); + IGRAPH_VECTOR_LONG_INIT_FINALLY(&to, no_of_edges); + IGRAPH_VECTOR_LONG_INIT_FINALLY(&rev, no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&rescap, no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&excess, no_of_nodes); + IGRAPH_VECTOR_LONG_INIT_FINALLY(&distance, no_of_nodes); + IGRAPH_VECTOR_LONG_INIT_FINALLY(&first, no_of_nodes + 1); + + IGRAPH_VECTOR_INIT_FINALLY(&rank, no_of_edges); + IGRAPH_VECTOR_LONG_INIT_FINALLY(&from, no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges); + + /* Create the basic data structure */ + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + IGRAPH_CHECK(igraph_vector_rank(&edges, &rank, no_of_nodes)); + + for (i = 0; i < no_of_edges; i += 2) { + long int pos = (long int) VECTOR(rank)[i]; + long int pos2 = (long int) VECTOR(rank)[i + 1]; + VECTOR(from)[pos] = VECTOR(edges)[i]; + VECTOR(to)[pos] = VECTOR(edges)[i + 1]; + VECTOR(from)[pos2] = VECTOR(edges)[i + 1]; + VECTOR(to)[pos2] = VECTOR(edges)[i]; + VECTOR(rev)[pos] = pos2; + VECTOR(rev)[pos2] = pos; + VECTOR(rescap)[pos] = capacity ? VECTOR(*capacity)[i / 2] : 1.0; + VECTOR(rescap)[pos2] = 0.0; + } + + /* The first pointers. This is a but trickier, than one would + think, because of the possible isolate vertices. */ + + idx = -1; + for (i = 0; i <= VECTOR(from)[0]; i++) { + idx++; VECTOR(first)[idx] = 0; + } + for (i = 1; i < no_of_edges; i++) { + long int n = (long int) (VECTOR(from)[i] - + VECTOR(from)[ (long int) VECTOR(first)[idx] ]); + for (j = 0; j < n; j++) { + idx++; VECTOR(first)[idx] = i; + } + } + idx++; + while (idx < no_of_nodes + 1) { + VECTOR(first)[idx++] = no_of_edges; + } + + igraph_vector_long_destroy(&from); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(2); + + if (!flow) { + igraph_vector_destroy(&rank); + IGRAPH_FINALLY_CLEAN(1); + } + + /* And the current pointers, initially the same as the first */ + IGRAPH_VECTOR_LONG_INIT_FINALLY(¤t, no_of_nodes); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(current)[i] = VECTOR(first)[i]; + } + + /* OK, the graph is set up, initialization */ + + IGRAPH_CHECK(igraph_buckets_init(&buckets, no_of_nodes + 1, no_of_nodes)); + IGRAPH_FINALLY(igraph_buckets_destroy, &buckets); + IGRAPH_CHECK(igraph_dbuckets_init(&ibuckets, no_of_nodes + 1, no_of_nodes)); + IGRAPH_FINALLY(igraph_dbuckets_destroy, &ibuckets); + + /* Send as much flow as possible from the source to its neighbors */ + for (i = FIRST(source), j = LAST(source); i < j; i++) { + if (HEAD(i) != source) { + igraph_real_t delta = RESCAP(i); + RESCAP(i) = 0; + RESCAP(REV(i)) += delta; + EXCESS(HEAD(i)) += delta; + } + } + + BFS(); + (stats->nobfs)++; + + while (!igraph_buckets_empty(&buckets)) { + long int vertex = igraph_buckets_popmax(&buckets); + DISCHARGE(vertex); + if (npushsince > no_of_nodes / 2 && nrelabelsince > no_of_nodes) { + (stats->nobfs)++; + BFS(); + npushsince = nrelabelsince = 0; + } + } + + /* Store the result */ + if (value) { + *value = EXCESS(target); + } + + /* If we also need the minimum cut */ + if (cut || partition || partition2) { + /* We need to find all vertices from which the target is reachable + in the residual graph. We do a breadth-first search, going + backwards. */ + igraph_dqueue_t Q; + igraph_vector_bool_t added; + long int marked = 0; + + IGRAPH_CHECK(igraph_vector_bool_init(&added, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &added); + + IGRAPH_CHECK(igraph_dqueue_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &Q); + + igraph_dqueue_push(&Q, target); + VECTOR(added)[(long int)target] = 1; + marked++; + while (!igraph_dqueue_empty(&Q)) { + long int actnode = (long int) igraph_dqueue_pop(&Q); + for (i = FIRST(actnode), j = LAST(actnode); i < j; i++) { + long int nei = HEAD(i); + if (!VECTOR(added)[nei] && RESCAP(REV(i)) > 0.0) { + VECTOR(added)[nei] = 1; + marked++; + IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); + } + } + } + igraph_dqueue_destroy(&Q); + IGRAPH_FINALLY_CLEAN(1); + + /* Now we marked each vertex that is on one side of the cut, + check the crossing edges */ + + if (cut) { + igraph_vector_clear(cut); + for (i = 0; i < no_of_orig_edges; i++) { + long int f = IGRAPH_FROM(graph, i); + long int t = IGRAPH_TO(graph, i); + if (!VECTOR(added)[f] && VECTOR(added)[t]) { + IGRAPH_CHECK(igraph_vector_push_back(cut, i)); + } + } + } + + if (partition2) { + long int x = 0; + IGRAPH_CHECK(igraph_vector_resize(partition2, marked)); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(added)[i]) { + VECTOR(*partition2)[x++] = i; + } + } + } + + if (partition) { + long int x = 0; + IGRAPH_CHECK(igraph_vector_resize(partition, + no_of_nodes - marked)); + for (i = 0; i < no_of_nodes; i++) { + if (!VECTOR(added)[i]) { + VECTOR(*partition)[x++] = i; + } + } + } + + igraph_vector_bool_destroy(&added); + IGRAPH_FINALLY_CLEAN(1); + } + + if (flow) { + /* Initialize the backward distances, with a breadth-first search + from the source */ + igraph_dqueue_t Q; + igraph_vector_int_t added; + long int j, k, l; + igraph_t flow_graph; + igraph_vector_t flow_edges; + igraph_bool_t dag; + + IGRAPH_CHECK(igraph_vector_int_init(&added, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &added); + IGRAPH_CHECK(igraph_dqueue_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &Q); + + igraph_dqueue_push(&Q, source); + igraph_dqueue_push(&Q, 0); + VECTOR(added)[(long int)source] = 1; + while (!igraph_dqueue_empty(&Q)) { + long int actnode = (long int) igraph_dqueue_pop(&Q); + long int actdist = (long int) igraph_dqueue_pop(&Q); + DIST(actnode) = actdist; + + for (i = FIRST(actnode), j = LAST(actnode); i < j; i++) { + long int nei = HEAD(i); + if (!VECTOR(added)[nei] && RESCAP(REV(i)) > 0.0) { + VECTOR(added)[nei] = 1; + IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(&Q, actdist + 1)); + } + } + } /* !igraph_dqueue_empty(&Q) */ + + igraph_vector_int_destroy(&added); + igraph_dqueue_destroy(&Q); + IGRAPH_FINALLY_CLEAN(2); + + /* Reinitialize the buckets */ + igraph_buckets_clear(&buckets); + for (i = 0; i < no_of_nodes; i++) { + if (EXCESS(i) > 0.0 && i != source && i != target) { + igraph_buckets_add(&buckets, (long int) DIST(i), i); + } + } + + /* Now we return the flow to the source */ + while (!igraph_buckets_empty(&buckets)) { + long int vertex = igraph_buckets_popmax(&buckets); + + /* DISCHARGE(vertex) comes here */ + do { + for (i = (long int) CURRENT(vertex), j = LAST(vertex); i < j; i++) { + if (RESCAP(i) > 0) { + long int nei = HEAD(i); + + if (DIST(vertex) == DIST(nei) + 1) { + igraph_real_t delta = + RESCAP(i) < EXCESS(vertex) ? RESCAP(i) : EXCESS(vertex); + RESCAP(i) -= delta; + RESCAP(REV(i)) += delta; + + if (nei != source && EXCESS(nei) == 0.0 && + DIST(nei) != no_of_nodes) { + igraph_buckets_add(&buckets, (long int) DIST(nei), nei); + } + + EXCESS(nei) += delta; + EXCESS(vertex) -= delta; + + if (EXCESS(vertex) == 0) { + break; + } + + } + } + } + + if (i == j) { + + /* RELABEL(vertex) comes here */ + igraph_real_t min; + long int min_edge = 0; + DIST(vertex) = min = no_of_nodes; + for (k = FIRST(vertex), l = LAST(vertex); k < l; k++) { + if (RESCAP(k) > 0) { + if (DIST(HEAD(k)) < min) { + min = DIST(HEAD(k)); + min_edge = k; + } + } + } + + min++; + + if (min < no_of_nodes) { + DIST(vertex) = min; + CURRENT(vertex) = min_edge; + /* Vertex is still active */ + igraph_buckets_add(&buckets, (long int) DIST(vertex), vertex); + } + + /* TODO: gap heuristics here ??? */ + + } else { + CURRENT(vertex) = FIRST(vertex); + } + + break; + + } while (1); + } + + /* We need to eliminate flow cycles now. Before that we check that + there is a cycle in the flow graph. + + First we do a couple of DFSes from the source vertex to the + target and factor out the paths we find. If there is no more + path to the target, then all remaining flow must be in flow + cycles, so we don't need it at all. + + Some details. 'stack' contains the whole path of the DFS, both + the vertices and the edges, they are alternating in the stack. + 'current' helps finding the next outgoing edge of a vertex + quickly, the next edge of 'v' is FIRST(v)+CURRENT(v). If this + is LAST(v), then there are no more edges to try. + + The 'added' vector contains 0 if the vertex was not visited + before, 1 if it is currently in 'stack', and 2 if it is not in + 'stack', but it was visited before. */ + + IGRAPH_VECTOR_INIT_FINALLY(&flow_edges, 0); + for (i = 0, j = 0; i < no_of_edges; i += 2, j++) { + long int pos = (long int) VECTOR(rank)[i]; + if ((capacity ? VECTOR(*capacity)[j] : 1.0) > RESCAP(pos)) { + IGRAPH_CHECK(igraph_vector_push_back(&flow_edges, + IGRAPH_FROM(graph, j))); + IGRAPH_CHECK(igraph_vector_push_back(&flow_edges, + IGRAPH_TO(graph, j))); + } + } + IGRAPH_CHECK(igraph_create(&flow_graph, &flow_edges, no_of_nodes, + IGRAPH_DIRECTED)); + igraph_vector_destroy(&flow_edges); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, &flow_graph); + IGRAPH_CHECK(igraph_is_dag(&flow_graph, &dag)); + igraph_destroy(&flow_graph); + IGRAPH_FINALLY_CLEAN(1); + + if (!dag) { + igraph_vector_long_t stack; + igraph_vector_t mycap; + + IGRAPH_CHECK(igraph_vector_long_init(&stack, 0)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &stack); + IGRAPH_CHECK(igraph_vector_int_init(&added, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &added); + IGRAPH_VECTOR_INIT_FINALLY(&mycap, no_of_edges); + +#define MYCAP(i) (VECTOR(mycap)[(i)]) + + for (i = 0; i < no_of_edges; i += 2) { + long int pos = (long int) VECTOR(rank)[i]; + long int pos2 = (long int) VECTOR(rank)[i + 1]; + MYCAP(pos) = (capacity ? VECTOR(*capacity)[i / 2] : 1.0) - RESCAP(pos); + MYCAP(pos2) = 0.0; + } + + do { + igraph_vector_long_null(¤t); + igraph_vector_long_clear(&stack); + igraph_vector_int_null(&added); + + IGRAPH_CHECK(igraph_vector_long_push_back(&stack, -1)); + IGRAPH_CHECK(igraph_vector_long_push_back(&stack, source)); + VECTOR(added)[(long int)source] = 1; + while (!igraph_vector_long_empty(&stack) && + igraph_vector_long_tail(&stack) != target) { + long int actnode = igraph_vector_long_tail(&stack); + long int edge = FIRST(actnode) + (long int) CURRENT(actnode); + long int nei; + while (edge < LAST(actnode) && MYCAP(edge) == 0.0) { + edge++; + } + nei = edge < LAST(actnode) ? HEAD(edge) : -1; + + if (edge < LAST(actnode) && !VECTOR(added)[nei]) { + /* Go forward along next edge, if the vertex was not + visited before */ + IGRAPH_CHECK(igraph_vector_long_push_back(&stack, edge)); + IGRAPH_CHECK(igraph_vector_long_push_back(&stack, nei)); + VECTOR(added)[nei] = 1; + CURRENT(actnode) += 1; + } else if (edge < LAST(actnode) && VECTOR(added)[nei] == 1) { + /* We found a flow cycle, factor it out. Go back in stack + until we find 'nei' again, determine the flow along the + cycle. */ + igraph_real_t thisflow = MYCAP(edge); + long int idx; + for (idx = igraph_vector_long_size(&stack) - 2; + idx >= 0 && VECTOR(stack)[idx + 1] != nei; idx -= 2) { + long int e = VECTOR(stack)[idx]; + igraph_real_t rcap = e >= 0 ? MYCAP(e) : MYCAP(edge); + if (rcap < thisflow) { + thisflow = rcap; + } + } + MYCAP(edge) -= thisflow; RESCAP(edge) += thisflow; + for (idx = igraph_vector_long_size(&stack) - 2; + idx >= 0 && VECTOR(stack)[idx + 1] != nei; idx -= 2) { + long int e = VECTOR(stack)[idx]; + if (e >= 0) { + MYCAP(e) -= thisflow; + RESCAP(e) += thisflow; + } + } + CURRENT(actnode) += 1; + } else if (edge < LAST(actnode)) { /* && VECTOR(added)[nei]==2 */ + /* The next edge leads to a vertex that was visited before, + but it is currently not in 'stack' */ + CURRENT(actnode) += 1; + } else { + /* Go backward, take out the node and the edge that leads to it */ + igraph_vector_long_pop_back(&stack); + igraph_vector_long_pop_back(&stack); + VECTOR(added)[actnode] = 2; + } + } + + /* If non-empty, then it contains a path from source to target + in the residual graph. We factor out this path from the flow. */ + if (!igraph_vector_long_empty(&stack)) { + long int pl = igraph_vector_long_size(&stack); + igraph_real_t thisflow = EXCESS(target); + for (i = 2; i < pl; i += 2) { + long int edge = VECTOR(stack)[i]; + igraph_real_t rcap = MYCAP(edge); + if (rcap < thisflow) { + thisflow = rcap; + } + } + for (i = 2; i < pl; i += 2) { + long int edge = VECTOR(stack)[i]; + MYCAP(edge) -= thisflow; + } + } + + } while (!igraph_vector_long_empty(&stack)); + + igraph_vector_destroy(&mycap); + igraph_vector_int_destroy(&added); + igraph_vector_long_destroy(&stack); + IGRAPH_FINALLY_CLEAN(3); + } + + /* ----------------------------------------------------------- */ + + IGRAPH_CHECK(igraph_vector_resize(flow, no_of_orig_edges)); + for (i = 0, j = 0; i < no_of_edges; i += 2, j++) { + long int pos = (long int) VECTOR(rank)[i]; + VECTOR(*flow)[j] = (capacity ? VECTOR(*capacity)[j] : 1.0) - + RESCAP(pos); + } + + igraph_vector_destroy(&rank); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_dbuckets_destroy(&ibuckets); + igraph_buckets_destroy(&buckets); + igraph_vector_long_destroy(¤t); + igraph_vector_long_destroy(&first); + igraph_vector_long_destroy(&distance); + igraph_vector_destroy(&excess); + igraph_vector_destroy(&rescap); + igraph_vector_long_destroy(&rev); + igraph_vector_long_destroy(&to); + igraph_dqueue_long_destroy(&bfsq); + IGRAPH_FINALLY_CLEAN(10); + + return 0; +} + +/** + * \function igraph_maxflow_value + * \brief Maximum flow in a network with the push/relabel algorithm + * + * This function implements the Goldberg-Tarjan algorithm for + * calculating value of the maximum flow in a directed or undirected + * graph. The algorithm was given in Andrew V. Goldberg, Robert + * E. Tarjan: A New Approach to the Maximum-Flow Problem, Journal of + * the ACM, 35(4), 921-940, 1988. + * + * The input of the function is a graph, a vector + * of real numbers giving the capacity of the edges and two vertices + * of the graph, the source and the target. A flow is a function + * assigning positive real numbers to the edges and satisfying two + * requirements: (1) the flow value is less than the capacity of the + * edge and (2) at each vertex except the source and the target, the + * incoming flow (i.e. the sum of the flow on the incoming edges) is + * the same as the outgoing flow (i.e. the sum of the flow on the + * outgoing edges). The value of the flow is the incoming flow at the + * target vertex. The maximum flow is the flow with the maximum + * value. + * + * According to a theorem by Ford and Fulkerson + * (L. R. Ford Jr. and D. R. Fulkerson. Maximal flow through a + * network. Canadian J. Math., 8:399-404, 1956.) the maximum flow + * between two vertices is the same as the + * minimum cut between them (also called the minimum s-t cut). So \ref + * igraph_st_mincut_value() gives the same result in all cases as \c + * igraph_maxflow_value(). + * + * Note that the value of the maximum flow is the same as the + * minimum cut in the graph. + * \param graph The input graph, either directed or undirected. + * \param value Pointer to a real number, the result will be placed here. + * \param source The id of the source vertex. + * \param target The id of the target vertex. + * \param capacity Vector containing the capacity of the edges. If NULL, then + * every edge is considered to have capacity 1.0. + * \param stats Counts of the number of different operations + * preformed by the algorithm are stored here. + * \return Error code. + * + * Time complexity: O(|V|^3). + * + * \sa \ref igraph_maxflow() to calculate the actual flow. + * \ref igraph_mincut_value(), \ref igraph_edge_connectivity(), + * \ref igraph_vertex_connectivity() for + * properties based on the maximum flow. + */ + +int igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity, + igraph_maxflow_stats_t *stats) { + + return igraph_maxflow(graph, value, /*flow=*/ 0, /*cut=*/ 0, + /*partition=*/ 0, /*partition1=*/ 0, + source, target, capacity, stats); +} + +/** + * \function igraph_st_mincut_value + * \brief The minimum s-t cut in a graph + * + * The minimum s-t cut in a weighted (=valued) graph is the + * total minimum edge weight needed to remove from the graph to + * eliminate all paths from a given vertex (\c source) to + * another vertex (\c target). Directed paths are considered in + * directed graphs, and undirected paths in undirected graphs. + * + * The minimum s-t cut between two vertices is known to be same + * as the maximum flow between these two vertices. So this function + * calls \ref igraph_maxflow_value() to do the calculation. + * \param graph The input graph. + * \param value Pointer to a real variable, the result will be stored + * here. + * \param source The id of the source vertex. + * \param target The id of the target vertex. + * \param capacity Pointer to the capacity vector, it should contain + * non-negative numbers and its length should be the same the + * the number of edges in the graph. It can be a null pointer, then + * every edge has unit capacity. + * \return Error code. + * + * Time complexity: O(|V|^3), see also the discussion for \ref + * igraph_maxflow_value(), |V| is the number of vertices. + */ + +int igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *value, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity) { + + if (source == target) { + IGRAPH_ERROR("source and target vertices are the same", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_maxflow_value(graph, value, source, target, capacity, 0)); + + return 0; +} + +/** + * \function igraph_st_mincut + * Minimum cut between a source and a target vertex + * + * Finds the edge set that has the smallest total capacity among all + * edge sets that disconnect the source and target vertices. + * + * The calculation is performed using maximum flow + * techniques, by calling \ref igraph_maxflow(). + * \param graph The input graph. + * \param value Pointer to a real variable, the value of the cut is + * stored here. + * \param cut Pointer to a real vector, the edge ids that are included + * in the cut are stored here. This argument is ignored if it + * is a null pointer. + * \param partition Pointer to a real vector, the vertex ids of the + * vertices in the first partition of the cut are stored + * here. The first partition is always the one that contains the + * source vertex. This argument is ignored if it is a null pointer. + * \param partition2 Pointer to a real vector, the vertex ids of the + * vertices in the second partition of the cut are stored here. + * The second partition is always the one that contains the + * target vertex. This argument is ignored if it is a null pointer. + * \param source Integer, the id of the source vertex. + * \param target Integer, the id of the target vertex. + * \param capacity Vector containing the capacity of the edges. If a + * null pointer, then every edge is considered to have capacity + * 1.0. + * \return Error code. + * + * \sa \ref igraph_maxflow(). + * + * Time complexity: see \ref igraph_maxflow(). + */ + +int igraph_st_mincut(const igraph_t *graph, igraph_real_t *value, + igraph_vector_t *cut, igraph_vector_t *partition, + igraph_vector_t *partition2, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity) { + + return igraph_maxflow(graph, value, /*flow=*/ 0, + cut, partition, partition2, + source, target, capacity, 0); +} + +/* This is a flow-based version, but there is a better one + for undirected graphs */ + +/* int igraph_i_mincut_value_undirected(const igraph_t *graph, */ +/* igraph_real_t *res, */ +/* const igraph_vector_t *capacity) { */ + +/* long int no_of_edges=igraph_ecount(graph); */ +/* long int no_of_nodes=igraph_vcount(graph); */ +/* igraph_vector_t edges; */ +/* igraph_vector_t newcapacity; */ +/* igraph_t newgraph; */ +/* long int i; */ + +/* /\* We need to convert this to directed by hand, since we need to be */ +/* sure that the edge ids will be handled properly to build the new */ +/* capacity vector. *\/ */ + +/* IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); */ +/* IGRAPH_VECTOR_INIT_FINALLY(&newcapacity, no_of_edges*2); */ +/* IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges*4)); */ +/* IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); */ +/* IGRAPH_CHECK(igraph_vector_resize(&edges, no_of_edges*4)); */ +/* for (i=0; i= 2) { + + long int last; + igraph_real_t acut; + long int a, n; + + igraph_vector_int_t *edges, *edges2; + igraph_vector_int_t *neis, *neis2; + + do { + a = igraph_i_cutheap_popmax(&heap); + + /* update the weights of the active vertices connected to a */ + edges = igraph_inclist_get(&inclist, a); + neis = igraph_adjlist_get(&adjlist, a); + n = igraph_vector_int_size(edges); + for (i = 0; i < n; i++) { + igraph_integer_t edge = (igraph_integer_t) VECTOR(*edges)[i]; + igraph_integer_t to = (igraph_integer_t) VECTOR(*neis)[i]; + igraph_real_t weight = capacity ? VECTOR(*capacity)[(long int)edge] : 1.0; + igraph_i_cutheap_update(&heap, to, weight); + } + + } while (igraph_i_cutheap_active_size(&heap) > 1); + + /* Now, there is only one active vertex left, + calculate the cut of the phase */ + acut = igraph_i_cutheap_maxvalue(&heap); + last = igraph_i_cutheap_popmax(&heap); + + if (acut < mincut) { + mincut = acut; + mincut_step = act_step; + } + + if (mincut == 0) { + break; + } + + /* And contract the last and the remaining vertex (a and last) */ + /* Before actually doing that, make some notes */ + act_step++; + if (calc_cut) { + IGRAPH_CHECK(igraph_vector_push_back(&mergehist, a)); + IGRAPH_CHECK(igraph_vector_push_back(&mergehist, last)); + } + /* First remove the a--last edge if there is one, a is still the + last deactivated vertex */ + edges = igraph_inclist_get(&inclist, a); + neis = igraph_adjlist_get(&adjlist, a); + n = igraph_vector_int_size(edges); + for (i = 0; i < n; ) { + if (VECTOR(*neis)[i] == last) { + VECTOR(*neis)[i] = VECTOR(*neis)[n - 1]; + VECTOR(*edges)[i] = VECTOR(*edges)[n - 1]; + igraph_vector_int_pop_back(neis); + igraph_vector_int_pop_back(edges); + n--; + } else { + i++; + } + } + + edges = igraph_inclist_get(&inclist, last); + neis = igraph_adjlist_get(&adjlist, last); + n = igraph_vector_int_size(edges); + for (i = 0; i < n; ) { + if (VECTOR(*neis)[i] == a) { + VECTOR(*neis)[i] = VECTOR(*neis)[n - 1]; + VECTOR(*edges)[i] = VECTOR(*edges)[n - 1]; + igraph_vector_int_pop_back(neis); + igraph_vector_int_pop_back(edges); + n--; + } else { + i++; + } + } + + /* Now rewrite the edge lists of last's neighbors */ + neis = igraph_adjlist_get(&adjlist, last); + n = igraph_vector_int_size(neis); + for (i = 0; i < n; i++) { + igraph_integer_t nei = (igraph_integer_t) VECTOR(*neis)[i]; + long int n2, j; + neis2 = igraph_adjlist_get(&adjlist, nei); + n2 = igraph_vector_int_size(neis2); + for (j = 0; j < n2; j++) { + if (VECTOR(*neis2)[j] == last) { + VECTOR(*neis2)[j] = a; + } + } + } + + /* And append the lists of last to the lists of a */ + edges = igraph_inclist_get(&inclist, a); + neis = igraph_adjlist_get(&adjlist, a); + edges2 = igraph_inclist_get(&inclist, last); + neis2 = igraph_adjlist_get(&adjlist, last); + IGRAPH_CHECK(igraph_vector_int_append(edges, edges2)); + IGRAPH_CHECK(igraph_vector_int_append(neis, neis2)); + igraph_vector_int_clear(edges2); /* TODO: free it */ + igraph_vector_int_clear(neis2); /* TODO: free it */ + + /* Remove the deleted vertex from the heap entirely */ + igraph_i_cutheap_reset_undefine(&heap, last); + } + + *res = mincut; + + igraph_inclist_destroy(&inclist); + igraph_adjlist_destroy(&adjlist); + igraph_i_cutheap_destroy(&heap); + IGRAPH_FINALLY_CLEAN(3); + + if (calc_cut) { + long int bignode = (long int) VECTOR(mergehist)[2 * mincut_step + 1]; + long int i, idx; + long int size = 1; + char *mark; + mark = IGRAPH_CALLOC(no_of_nodes, char); + if (!mark) { + IGRAPH_ERROR("Not enough memory for minimum cut", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, mark); + + /* first count the vertices in the partition */ + mark[bignode] = 1; + for (i = mincut_step - 1; i >= 0; i--) { + if ( mark[ (long int) VECTOR(mergehist)[2 * i] ] ) { + size++; + mark [ (long int) VECTOR(mergehist)[2 * i + 1] ] = 1; + } + } + + /* now store them, if requested */ + if (partition) { + IGRAPH_CHECK(igraph_vector_resize(partition, size)); + idx = 0; + VECTOR(*partition)[idx++] = bignode; + for (i = mincut_step - 1; i >= 0; i--) { + if (mark[ (long int) VECTOR(mergehist)[2 * i] ]) { + VECTOR(*partition)[idx++] = VECTOR(mergehist)[2 * i + 1]; + } + } + } + + /* The other partition too? */ + if (partition2) { + IGRAPH_CHECK(igraph_vector_resize(partition2, no_of_nodes - size)); + idx = 0; + for (i = 0; i < no_of_nodes; i++) { + if (!mark[i]) { + VECTOR(*partition2)[idx++] = i; + } + } + } + + /* The edges in the cut are also requested? */ + /* We want as few memory allocated for 'cut' as possible, + so we first collect the edges in mergehist, we don't + need that anymore. Then we copy it to 'cut'; */ + if (cut) { + igraph_integer_t from, to; + igraph_vector_clear(&mergehist); + for (i = 0; i < no_of_edges; i++) { + igraph_edge(graph, (igraph_integer_t) i, &from, &to); + if ((mark[(long int)from] && !mark[(long int)to]) || + (mark[(long int)to] && !mark[(long int)from])) { + IGRAPH_CHECK(igraph_vector_push_back(&mergehist, i)); + } + } + igraph_vector_clear(cut); + IGRAPH_CHECK(igraph_vector_append(cut, &mergehist)); + } + + igraph_free(mark); + igraph_vector_destroy(&mergehist); + IGRAPH_FINALLY_CLEAN(2); + } + + return 0; +} + +static int igraph_i_mincut_directed(const igraph_t *graph, + igraph_real_t *value, + igraph_vector_t *partition, + igraph_vector_t *partition2, + igraph_vector_t *cut, + const igraph_vector_t *capacity) { + long int i; + long int no_of_nodes = igraph_vcount(graph); + igraph_real_t flow; + igraph_real_t minmaxflow = IGRAPH_INFINITY; + igraph_vector_t mypartition, mypartition2, mycut; + igraph_vector_t *ppartition = 0, *ppartition2 = 0, *pcut = 0; + igraph_vector_t bestpartition, bestpartition2, bestcut; + + if (partition) { + IGRAPH_VECTOR_INIT_FINALLY(&bestpartition, 0); + } + if (partition2) { + IGRAPH_VECTOR_INIT_FINALLY(&bestpartition2, 0); + } + if (cut) { + IGRAPH_VECTOR_INIT_FINALLY(&bestcut, 0); + } + + if (partition) { + IGRAPH_VECTOR_INIT_FINALLY(&mypartition, 0); + ppartition = &mypartition; + } + if (partition2) { + IGRAPH_VECTOR_INIT_FINALLY(&mypartition2, 0); + ppartition2 = &mypartition2; + } + if (cut) { + IGRAPH_VECTOR_INIT_FINALLY(&mycut, 0); + pcut = &mycut; + } + + for (i = 1; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_maxflow(graph, /*value=*/ &flow, /*flow=*/ 0, + pcut, ppartition, ppartition2, /*source=*/ 0, + /*target=*/ (igraph_integer_t) i, capacity, 0)); + if (flow < minmaxflow) { + minmaxflow = flow; + if (cut) { + IGRAPH_CHECK(igraph_vector_update(&bestcut, &mycut)); + } + if (partition) { + IGRAPH_CHECK(igraph_vector_update(&bestpartition, &mypartition)); + } + if (partition2) { + IGRAPH_CHECK(igraph_vector_update(&bestpartition2, &mypartition2)); + } + + if (minmaxflow == 0) { + break; + } + } + IGRAPH_CHECK(igraph_maxflow(graph, /*value=*/ &flow, /*flow=*/ 0, + pcut, ppartition, ppartition2, + /*source=*/ (igraph_integer_t) i, + /*target=*/ 0, capacity, 0)); + if (flow < minmaxflow) { + minmaxflow = flow; + if (cut) { + IGRAPH_CHECK(igraph_vector_update(&bestcut, &mycut)); + } + if (partition) { + IGRAPH_CHECK(igraph_vector_update(&bestpartition, &mypartition)); + } + if (partition2) { + IGRAPH_CHECK(igraph_vector_update(&bestpartition2, &mypartition2)); + } + + if (minmaxflow == 0) { + break; + } + } + } + + if (value) { + *value = minmaxflow; + } + + if (cut) { + igraph_vector_destroy(&mycut); + IGRAPH_FINALLY_CLEAN(1); + } + if (partition) { + igraph_vector_destroy(&mypartition); + IGRAPH_FINALLY_CLEAN(1); + } + if (partition2) { + igraph_vector_destroy(&mypartition2); + IGRAPH_FINALLY_CLEAN(1); + } + if (cut) { + IGRAPH_CHECK(igraph_vector_update(cut, &bestcut)); + igraph_vector_destroy(&bestcut); + IGRAPH_FINALLY_CLEAN(1); + } + if (partition2) { + IGRAPH_CHECK(igraph_vector_update(partition2, &bestpartition2)); + igraph_vector_destroy(&bestpartition2); + IGRAPH_FINALLY_CLEAN(1); + } + if (partition) { + IGRAPH_CHECK(igraph_vector_update(partition, &bestpartition)); + igraph_vector_destroy(&bestpartition); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_mincut + * \brief Calculates the minimum cut in a graph. + * + * This function calculates the minimum cut in a graph. + * The minimum cut is the minimum set of edges which needs to be + * removed to disconnect the graph. The minimum is calculated using + * the weights (\p capacity) of the edges, so the cut with the minimum + * total capacity is calculated. + * + * For directed graphs an implementation based on + * calculating 2|V|-2 maximum flows is used. + * For undirected graphs we use the Stoer-Wagner + * algorithm, as described in M. Stoer and F. Wagner: A simple min-cut + * algorithm, Journal of the ACM, 44 585-591, 1997. + * + * + * The first implementation of the actual cut calculation for + * undirected graphs was made by Gregory Benison, thanks Greg. + * \param graph The input graph. + * \param value Pointer to a float, the value of the cut will be + * stored here. + * \param partition Pointer to an initialized vector, the ids + * of the vertices in the first partition after separating the + * graph will be stored here. The vector will be resized as + * needed. This argument is ignored if it is a NULL pointer. + * \param partition2 Pointer to an initialized vector the ids + * of the vertices in the second partition will be stored here. + * The vector will be resized as needed. This argument is ignored + * if it is a NULL pointer. + * \param cut Pointer to an initialized vector, the ids of the edges + * in the cut will be stored here. This argument is ignored if it + * is a NULL pointer. + * \param capacity A numeric vector giving the capacities of the + * edges. If a null pointer then all edges have unit capacity. + * \return Error code. + * + * \sa \ref igraph_mincut_value(), a simpler interface for calculating + * the value of the cut only. + * + * Time complexity: for directed graphs it is O(|V|^4), but see the + * remarks at \ref igraph_maxflow(). For undirected graphs it is + * O(|V||E|+|V|^2 log|V|). |V| and |E| are the number of vertices and + * edges respectively. + * + * \example examples/simple/igraph_mincut.c + */ + +int igraph_mincut(const igraph_t *graph, + igraph_real_t *value, + igraph_vector_t *partition, + igraph_vector_t *partition2, + igraph_vector_t *cut, + const igraph_vector_t *capacity) { + + if (igraph_is_directed(graph)) { + if (partition || partition2 || cut) { + igraph_i_mincut_directed(graph, value, partition, partition2, cut, + capacity); + } else { + return igraph_mincut_value(graph, value, capacity); + } + } else { + IGRAPH_CHECK(igraph_i_mincut_undirected(graph, value, partition, + partition2, cut, capacity)); + return IGRAPH_SUCCESS; + } + + return 0; +} + + +static int igraph_i_mincut_value_undirected(const igraph_t *graph, + igraph_real_t *res, + const igraph_vector_t *capacity) { + return igraph_i_mincut_undirected(graph, res, 0, 0, 0, capacity); +} + +/** + * \function igraph_mincut_value + * \brief The minimum edge cut in a graph + * + * The minimum edge cut in a graph is the total minimum + * weight of the edges needed to remove from the graph to make the + * graph \em not strongly connected. (If the original graph is not + * strongly connected then this is zero.) Note that in undirected + * graphs strong connectedness is the same as weak connectedness. + * + * The minimum cut can be calculated with maximum flow + * techniques, although the current implementation does this only for + * directed graphs and a separate non-flow based implementation is + * used for undirected graphs. See Mechthild Stoer and Frank Wagner: A + * simple min-cut algorithm, Journal of the ACM 44 585--591, 1997. + * For directed graphs + * the maximum flow is calculated between a fixed vertex and all the + * other vertices in the graph and this is done in both + * directions. Then the minimum is taken to get the minimum cut. + * + * \param graph The input graph. + * \param res Pointer to a real variable, the result will be stored + * here. + * \param capacity Pointer to the capacity vector, it should contain + * the same number of non-negative numbers as the number of edges in + * the graph. If a null pointer then all edges will have unit capacity. + * \return Error code. + * + * \sa \ref igraph_mincut(), \ref igraph_maxflow_value(), \ref + * igraph_st_mincut_value(). + * + * Time complexity: O(log(|V|)*|V|^2) for undirected graphs and + * O(|V|^4) for directed graphs, but see also the discussion at the + * documentation of \ref igraph_maxflow_value(). + */ + +int igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, + const igraph_vector_t *capacity) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_real_t minmaxflow, flow; + long int i; + + minmaxflow = IGRAPH_INFINITY; + + if (!igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_mincut_value_undirected(graph, res, capacity)); + return 0; + } + + for (i = 1; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, 0, (igraph_integer_t) i, + capacity, 0)); + if (flow < minmaxflow) { + minmaxflow = flow; + if (flow == 0) { + break; + } + } + IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, (igraph_integer_t) i, 0, + capacity, 0)); + if (flow < minmaxflow) { + minmaxflow = flow; + if (flow == 0) { + break; + } + } + } + + if (res) { + *res = minmaxflow; + } + + return 0; +} + +static int igraph_i_st_vertex_connectivity_directed(const igraph_t *graph, + igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target, + igraph_vconn_nei_t neighbors) { + + igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph); + igraph_integer_t no_of_edges = (igraph_integer_t) igraph_ecount(graph); + igraph_vector_t edges; + igraph_real_t real_res; + igraph_t newgraph; + long int i; + igraph_bool_t conn1; + + if (source < 0 || source >= no_of_nodes || target < 0 || target >= no_of_nodes) { + IGRAPH_ERROR("Invalid source or target vertex", IGRAPH_EINVAL); + } + + switch (neighbors) { + case IGRAPH_VCONN_NEI_ERROR: + IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn1)); + if (conn1) { + IGRAPH_ERROR("vertices connected", IGRAPH_EINVAL); + } + break; + case IGRAPH_VCONN_NEI_NEGATIVE: + IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn1)); + if (conn1) { + *res = -1; + return 0; + } + break; + case IGRAPH_VCONN_NEI_NUMBER_OF_NODES: + IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn1)); + if (conn1) { + *res = no_of_nodes; + return 0; + } + break; + case IGRAPH_VCONN_NEI_IGNORE: + break; + default: + IGRAPH_ERROR("Unknown `igraph_vconn_nei_t'", IGRAPH_EINVAL); + break; + } + + /* Create the new graph */ + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * (no_of_edges + no_of_nodes))); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + IGRAPH_CHECK(igraph_vector_resize(&edges, 2 * (no_of_edges + no_of_nodes))); + + for (i = 0; i < 2 * no_of_edges; i += 2) { + igraph_integer_t to = (igraph_integer_t) VECTOR(edges)[i + 1]; + if (to != source && to != target) { + VECTOR(edges)[i + 1] = no_of_nodes + to; + } + } + + for (i = 0; i < no_of_nodes; i++) { + VECTOR(edges)[ 2 * (no_of_edges + i) ] = no_of_nodes + i; + VECTOR(edges)[ 2 * (no_of_edges + i) + 1 ] = i; + } + + IGRAPH_CHECK(igraph_create(&newgraph, &edges, 2 * no_of_nodes, + igraph_is_directed(graph))); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + + /* Do the maximum flow */ + + IGRAPH_CHECK(igraph_maxflow_value(&newgraph, &real_res, + source, target, 0, 0)); + *res = (igraph_integer_t)real_res; + + igraph_destroy(&newgraph); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_st_vertex_connectivity_undirected(const igraph_t *graph, + igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target, + igraph_vconn_nei_t neighbors) { + + igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph); + igraph_t newgraph; + igraph_bool_t conn; + + if (source < 0 || source >= no_of_nodes || target < 0 || target >= no_of_nodes) { + IGRAPH_ERROR("Invalid source or target vertex", IGRAPH_EINVAL); + } + + switch (neighbors) { + case IGRAPH_VCONN_NEI_ERROR: + IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn)); + if (conn) { + IGRAPH_ERROR("vertices connected", IGRAPH_EINVAL); + } + break; + case IGRAPH_VCONN_NEI_NEGATIVE: + IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn)); + if (conn) { + *res = -1; + return 0; + } + break; + case IGRAPH_VCONN_NEI_NUMBER_OF_NODES: + IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn)); + if (conn) { + *res = no_of_nodes; + return 0; + } + break; + case IGRAPH_VCONN_NEI_IGNORE: + break; + default: + IGRAPH_ERROR("Unknown `igraph_vconn_nei_t'", IGRAPH_EINVAL); + break; + } + + IGRAPH_CHECK(igraph_copy(&newgraph, graph)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + IGRAPH_CHECK(igraph_to_directed(&newgraph, IGRAPH_TO_DIRECTED_MUTUAL)); + + IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(&newgraph, res, + source, target, + IGRAPH_VCONN_NEI_IGNORE)); + + igraph_destroy(&newgraph); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_st_vertex_connectivity + * \brief The vertex connectivity of a pair of vertices + * + * The vertex connectivity of two vertices (\c source and + * \c target) is the minimum number of vertices that have to be + * deleted to eliminate all paths from \c source to \c + * target. Directed paths are considered in directed graphs. + * + * The vertex connectivity of a pair is the same as the number + * of different (i.e. node-independent) paths from source to + * target. + * + * The current implementation uses maximum flow calculations to + * obtain the result. + * \param graph The input graph. + * \param res Pointer to an integer, the result will be stored here. + * \param source The id of the source vertex. + * \param target The id of the target vertex. + * \param neighbors A constant giving what to do if the two vertices + * are connected. Possible values: + * \c IGRAPH_VCONN_NEI_ERROR, stop with an error message, + * \c IGRAPH_VCONN_NEGATIVE, return -1. + * \c IGRAPH_VCONN_NUMBER_OF_NODES, return the number of nodes. + * \c IGRAPH_VCONN_IGNORE, ignore the fact that the two vertices + * are connected and calculate the number of vertices needed + * to eliminate all paths except for the trivial (direct) paths + * between \p source and \p vertex. TODO: what about neighbors? + * \return Error code. + * + * Time complexity: O(|V|^3), but see the discussion at \ref + * igraph_maxflow_value(). + * + * \sa \ref igraph_vertex_connectivity(), + * \ref igraph_edge_connectivity(), + * \ref igraph_maxflow_value(). + */ + +int igraph_st_vertex_connectivity(const igraph_t *graph, + igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target, + igraph_vconn_nei_t neighbors) { + + if (source == target) { + IGRAPH_ERROR("source and target vertices are the same", IGRAPH_EINVAL); + } + + if (igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(graph, res, + source, target, + neighbors)); + } else { + IGRAPH_CHECK(igraph_i_st_vertex_connectivity_undirected(graph, res, + source, target, + neighbors)); + } + + return 0; +} + +static int igraph_i_vertex_connectivity_directed(const igraph_t *graph, + igraph_integer_t *res) { + + igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph); + long int i, j; + igraph_integer_t minconn = no_of_nodes - 1, conn = 0; + + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < no_of_nodes; j++) { + if (i == j) { + continue; + } + + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_st_vertex_connectivity(graph, &conn, + (igraph_integer_t) i, + (igraph_integer_t) j, + IGRAPH_VCONN_NEI_NUMBER_OF_NODES)); + if (conn < minconn) { + minconn = conn; + if (conn == 0) { + break; + } + } + } + if (conn == 0) { + break; + } + } + + if (res) { + *res = minconn; + } + + return 0; +} + +static int igraph_i_vertex_connectivity_undirected(const igraph_t *graph, + igraph_integer_t *res) { + igraph_t newgraph; + + IGRAPH_CHECK(igraph_copy(&newgraph, graph)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + IGRAPH_CHECK(igraph_to_directed(&newgraph, IGRAPH_TO_DIRECTED_MUTUAL)); + + IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(&newgraph, res)); + + igraph_destroy(&newgraph); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/* Use that vertex.connectivity(G) <= edge.connectivity(G) <= min(degree(G)) */ +static int igraph_i_connectivity_checks(const igraph_t *graph, + igraph_integer_t *res, + igraph_bool_t *found) { + igraph_bool_t conn; + *found = 0; + + if (igraph_vcount(graph) == 0) { + *res = 0; + *found = 1; + return 0; + } + + IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_STRONG)); + if (!conn) { + *res = 0; + *found = 1; + } else { + igraph_vector_t degree; + IGRAPH_VECTOR_INIT_FINALLY(°ree, 0); + if (!igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_OUT, IGRAPH_LOOPS)); + if (igraph_vector_min(°ree) == 1) { + *res = 1; + *found = 1; + } + } else { + /* directed, check both in- & out-degree */ + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_OUT, IGRAPH_LOOPS)); + if (igraph_vector_min(°ree) == 1) { + *res = 1; + *found = 1; + } else { + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_IN, IGRAPH_LOOPS)); + if (igraph_vector_min(°ree) == 1) { + *res = 1; + *found = 1; + } + } + } + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + } + return 0; +} + +/** + * \function igraph_vertex_connectivity + * The vertex connectivity of a graph + * + * The vertex connectivity of a graph is the minimum + * vertex connectivity along each pairs of vertices in the graph. + * + * The vertex connectivity of a graph is the same as group + * cohesion as defined in Douglas R. White and Frank Harary: The + * cohesiveness of blocks in social networks: node connectivity and + * conditional density, Sociological Methodology 31:305--359, 2001. + * \param graph The input graph. + * \param res Pointer to an integer, the result will be stored here. + * \param checks Logical constant. Whether to check that the graph is + * connected and also the degree of the vertices. If the graph is + * not (strongly) connected then the connectivity is obviously zero. Otherwise + * if the minimum degree is one then the vertex connectivity is also + * one. It is a good idea to perform these checks, as they can be + * done quickly compared to the connectivity calculation itself. + * They were suggested by Peter McMahan, thanks Peter. + * \return Error code. + * + * Time complexity: O(|V|^5). + * + * \sa \ref igraph_st_vertex_connectivity(), \ref igraph_maxflow_value(), + * and \ref igraph_edge_connectivity(). + */ + +int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, + igraph_bool_t checks) { + + igraph_bool_t ret = 0; + + if (checks) { + IGRAPH_CHECK(igraph_i_connectivity_checks(graph, res, &ret)); + } + + /* Are we done yet? */ + if (!ret) { + if (igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(graph, res)); + } else { + IGRAPH_CHECK(igraph_i_vertex_connectivity_undirected(graph, res)); + } + } + + return 0; +} + +/** + * \function igraph_st_edge_connectivity + * \brief Edge connectivity of a pair of vertices + * + * The edge connectivity of two vertices (\c source and + * \c target) in a graph is the minimum number of edges that + * have to be deleted from the graph to eliminate all paths from \c + * source to \c target. + * + * This function uses the maximum flow algorithm to calculate + * the edge connectivity. + * \param graph The input graph, it has to be directed. + * \param res Pointer to an integer, the result will be stored here. + * \param source The id of the source vertex. + * \param target The id of the target vertex. + * \return Error code. + * + * Time complexity: O(|V|^3). + * + * \sa \ref igraph_maxflow_value(), \ref igraph_edge_connectivity(), + * \ref igraph_st_vertex_connectivity(), \ref + * igraph_vertex_connectivity(). + */ + +int igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target) { + igraph_real_t flow; + + if (source == target) { + IGRAPH_ERROR("source and target vertices are the same", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, source, target, 0, 0)); + *res = (igraph_integer_t) flow; + + return 0; +} + + +/** + * \function igraph_edge_connectivity + * \brief The minimum edge connectivity in a graph. + * + * This is the minimum of the edge connectivity over all + * pairs of vertices in the graph. + * + * + * The edge connectivity of a graph is the same as group adhesion as + * defined in Douglas R. White and Frank Harary: The cohesiveness of + * blocks in social networks: node connectivity and conditional + * density, Sociological Methodology 31:305--359, 2001. + * \param graph The input graph. + * \param res Pointer to an integer, the result will be stored here. + * \param checks Logical constant. Whether to check that the graph is + * connected and also the degree of the vertices. If the graph is + * not (strongly) connected then the connectivity is obviously zero. Otherwise + * if the minimum degree is one then the edge connectivity is also + * one. It is a good idea to perform these checks, as they can be + * done quickly compared to the connectivity calculation itself. + * They were suggested by Peter McMahan, thanks Peter. + * \return Error code. + * + * Time complexity: O(log(|V|)*|V|^2) for undirected graphs and + * O(|V|^4) for directed graphs, but see also the discussion at the + * documentation of \ref igraph_maxflow_value(). + * + * \sa \ref igraph_st_edge_connectivity(), \ref igraph_maxflow_value(), + * \ref igraph_vertex_connectivity(). + */ + +int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, + igraph_bool_t checks) { + igraph_bool_t ret = 0; + igraph_integer_t number_of_nodes = igraph_vcount(graph); + + /* igraph_mincut_value returns infinity for the singleton graph, + * which cannot be cast to an integer. We catch this case early + * and postulate the edge-connectivity of this graph to be 0. + * This is consistent with what other software packages return. */ + if (number_of_nodes <= 1) { + *res = 0; + return 0; + } + + /* Use that vertex.connectivity(G) <= edge.connectivity(G) <= min(degree(G)) */ + if (checks) { + IGRAPH_CHECK(igraph_i_connectivity_checks(graph, res, &ret)); + } + + if (!ret) { + igraph_real_t real_res; + IGRAPH_CHECK(igraph_mincut_value(graph, &real_res, 0)); + *res = (igraph_integer_t)real_res; + } + + return 0; +} + +/** + * \function igraph_edge_disjoint_paths + * \brief The maximum number of edge-disjoint paths between two vertices. + * + * A set of paths between two vertices is called + * edge-disjoint if they do not share any edges. The maximum number of + * edge-disjoint paths are calculated by this function using maximum + * flow techniques. Directed paths are considered in directed + * graphs. + * + * Note that the number of disjoint paths is the same as the + * edge connectivity of the two vertices using uniform edge weights. + * \param graph The input graph, can be directed or undirected. + * \param res Pointer to an integer variable, the result will be + * stored here. + * \param source The id of the source vertex. + * \param target The id of the target vertex. + * \return Error code. + * + * Time complexity: O(|V|^3), but see the discussion at \ref + * igraph_maxflow_value(). + * + * \sa \ref igraph_vertex_disjoint_paths(), \ref + * igraph_st_edge_connectivity(), \ref igraph_maxflow_value(). + */ + +int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target) { + + igraph_real_t flow; + + if (source == target) { + IGRAPH_ERROR("Not implemented for source=target", IGRAPH_UNIMPLEMENTED); + } + + IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, source, target, 0, 0)); + + *res = (igraph_integer_t) flow; + + return 0; +} + +/** + * \function igraph_vertex_disjoint_paths + * \brief Maximum number of vertex-disjoint paths between two vertices. + * + * A set of paths between two vertices is called + * vertex-disjoint if they share no vertices. The calculation is + * performed by using maximum flow techniques. + * + * Note that the number of vertex-disjoint paths is the same as + * the vertex connectivity of the two vertices in most cases (if the + * two vertices are not connected by an edge). + * \param graph The input graph. + * \param res Pointer to an integer variable, the result will be + * stored here. + * \param source The id of the source vertex. + * \param target The id of the target vertex. + * \return Error code. + * + * Time complexity: O(|V|^3). + * + * \sa \ref igraph_edge_disjoint_paths(), \ref + * igraph_vertex_connectivity(), \ref igraph_maxflow_value(). + */ + +int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target) { + + igraph_bool_t conn; + + if (source == target) { + IGRAPH_ERROR("The source==target case is not implemented", + IGRAPH_UNIMPLEMENTED); + } + + IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn)); + if (conn) { + /* We need to remove every (possibly directed) edge between source + and target and calculate the disjoint paths on the new + graph. Finally we add 1 for the removed connection(s). */ + igraph_es_t es; + igraph_vector_t v; + igraph_t newgraph; + IGRAPH_VECTOR_INIT_FINALLY(&v, 2); + VECTOR(v)[0] = source; + VECTOR(v)[1] = target; + IGRAPH_CHECK(igraph_es_multipairs(&es, &v, IGRAPH_DIRECTED)); + IGRAPH_FINALLY(igraph_es_destroy, &es); + + IGRAPH_CHECK(igraph_copy(&newgraph, graph)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + IGRAPH_CHECK(igraph_delete_edges(&newgraph, es)); + + if (igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(&newgraph, res, + source, target, + IGRAPH_VCONN_NEI_IGNORE)); + } else { + IGRAPH_CHECK(igraph_i_st_vertex_connectivity_undirected(&newgraph, res, + source, target, + IGRAPH_VCONN_NEI_IGNORE)); + } + + if (res) { + *res += 1; + } + + IGRAPH_FINALLY_CLEAN(3); + igraph_destroy(&newgraph); + igraph_es_destroy(&es); + igraph_vector_destroy(&v); + } + + /* These do nothing if the two vertices are connected, + so it is safe to call them. */ + + if (igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(graph, res, + source, target, + IGRAPH_VCONN_NEI_IGNORE)); + } else { + IGRAPH_CHECK(igraph_i_st_vertex_connectivity_undirected(graph, res, + source, target, + IGRAPH_VCONN_NEI_IGNORE)); + } + + return 0; +} + +/** + * \function igraph_adhesion + * \brief Graph adhesion, this is (almost) the same as edge connectivity. + * + * This quantity is defined by White and Harary in + * The cohesiveness of blocks in social networks: node connectivity and + * conditional density, (Sociological Methodology 31:305--359, 2001) + * and basically it is the edge connectivity of the graph + * with uniform edge weights. + * \param graph The input graph, either directed or undirected. + * \param res Pointer to an integer, the result will be stored here. + * \param checks Logical constant. Whether to check that the graph is + * connected and also the degree of the vertices. If the graph is + * not (strongly) connected then the adhesion is obviously zero. Otherwise + * if the minimum degree is one then the adhesion is also + * one. It is a good idea to perform these checks, as they can be + * done quickly compared to the edge connectivity calculation itself. + * They were suggested by Peter McMahan, thanks Peter. +* \return Error code. + * + * Time complexity: O(log(|V|)*|V|^2) for undirected graphs and + * O(|V|^4) for directed graphs, but see also the discussion at the + * documentation of \ref igraph_maxflow_value(). + * + * \sa \ref igraph_cohesion(), \ref igraph_maxflow_value(), \ref + * igraph_edge_connectivity(), \ref igraph_mincut_value(). + */ + +int igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, + igraph_bool_t checks) { + return igraph_edge_connectivity(graph, res, checks); +} + +/** + * \function igraph_cohesion + * \brief Graph cohesion, this is the same as vertex connectivity. + * + * This quantity was defined by White and Harary in The + * cohesiveness of blocks in social networks: node connectivity and + * conditional density, (Sociological Methodology 31:305--359, 2001) + * and it is the same as the vertex connectivity of a + * graph. + * \param graph The input graph. + * \param res Pointer to an integer variable, the result will be + * stored here. + * \param checks Logical constant. Whether to check that the graph is + * connected and also the degree of the vertices. If the graph is + * not (strongly) connected then the cohesion is obviously zero. Otherwise + * if the minimum degree is one then the cohesion is also + * one. It is a good idea to perform these checks, as they can be + * done quickly compared to the vertex connectivity calculation itself. + * They were suggested by Peter McMahan, thanks Peter. + * \return Error code. + * + * Time complexity: O(|V|^4), |V| is the number of vertices. In + * practice it is more like O(|V|^2), see \ref igraph_maxflow_value(). + * + * \sa \ref igraph_vertex_connectivity(), \ref igraph_adhesion(), + * \ref igraph_maxflow_value(). + */ + +int igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, + igraph_bool_t checks) { + + IGRAPH_CHECK(igraph_vertex_connectivity(graph, res, checks)); + return 0; +} + +/** + * \function igraph_gomory_hu_tree + * \brief Gomory-Hu tree of a graph. + * + * + * The Gomory-Hu tree is a concise representation of the value of all the + * maximum flows (or minimum cuts) in a graph. The vertices of the tree + * correspond exactly to the vertices of the original graph in the same order. + * Edges of the Gomory-Hu tree are annotated by flow values. The value of + * the maximum flow (or minimum cut) between an arbitrary (u,v) vertex + * pair in the original graph is then given by the minimum flow value (i.e. + * edge annotation) along the shortest path between u and v in the + * Gomory-Hu tree. + * + * This implementation uses Gusfield's algorithm to construct the + * Gomory-Hu tree. See the following paper for more details: + * + * + * Gusfield D: Very simple methods for all pairs network flow analysis. SIAM J + * Comput 19(1):143-155, 1990. + * + * \param graph The input graph. + * \param tree Pointer to an uninitialized graph; the result will be + * stored here. + * \param flows Pointer to an uninitialized vector; the flow values + * corresponding to each edge in the Gomory-Hu tree will + * be returned here. You may pass a NULL pointer here if you are + * not interested in the flow values. + * \param capacity Vector containing the capacity of the edges. If NULL, then + * every edge is considered to have capacity 1.0. + * \return Error code. + * + * Time complexity: O(|V|^4) since it performs a max-flow calculation + * between vertex zero and every other vertex and max-flow is + * O(|V|^3). + * + * \sa \ref igraph_maxflow() + */ +int igraph_gomory_hu_tree(const igraph_t *graph, igraph_t *tree, + igraph_vector_t *flows, const igraph_vector_t *capacity) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t source, target, mid, i, n; + igraph_vector_t neighbors; + igraph_vector_t flow_values; + igraph_vector_t partition; + igraph_vector_t partition2; + igraph_real_t flow_value; + + if (igraph_is_directed(graph)) { + IGRAPH_ERROR("Gomory-Hu tree can only be calculated for undirected graphs", + IGRAPH_EINVAL); + } + + /* Allocate memory */ + IGRAPH_VECTOR_INIT_FINALLY(&neighbors, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&flow_values, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&partition, 0); + IGRAPH_VECTOR_INIT_FINALLY(&partition2, 0); + + /* Initialize the tree: every edge points to node 0 */ + /* Actually, this is done implicitly since both 'neighbors' and 'flow_values' are + * initialized to zero already */ + + /* For each source vertex except vertex zero... */ + for (source = 1; source < no_of_nodes; source++) { + IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_PROGRESS("Gomory-Hu tree", (100.0 * (source - 1)) / (no_of_nodes - 1), 0); + + /* Find its current neighbor in the tree */ + target = VECTOR(neighbors)[(long int)source]; + + /* Find the maximum flow between source and target */ + IGRAPH_CHECK(igraph_maxflow(graph, &flow_value, 0, 0, &partition, &partition2, + source, target, capacity, 0)); + + /* Store the maximum flow */ + VECTOR(flow_values)[(long int)source] = flow_value; + + /* Update the tree */ + /* igraph_maxflow() guarantees that the source vertex will be in &partition + * and not in &partition2 so we need to iterate over &partition to find + * all the nodes that are of interest to us */ + n = igraph_vector_size(&partition); + for (i = 0; i < n; i++) { + mid = VECTOR(partition)[i]; + if (mid != source) { + if (VECTOR(neighbors)[(long int)mid] == target) { + VECTOR(neighbors)[(long int)mid] = source; + } else if (VECTOR(neighbors)[(long int)target] == mid) { + VECTOR(neighbors)[(long int)target] = source; + VECTOR(neighbors)[(long int)source] = mid; + VECTOR(flow_values)[(long int)source] = VECTOR(flow_values)[(long int)target]; + VECTOR(flow_values)[(long int)target] = flow_value; + } + } + } + } + + IGRAPH_PROGRESS("Gomory-Hu tree", 100.0, 0); + + /* Re-use the 'partition' vector as an edge list now */ + IGRAPH_CHECK(igraph_vector_resize(&partition, 2 * (no_of_nodes - 1))); + for (i = 1, mid = 0; i < no_of_nodes; i++, mid += 2) { + VECTOR(partition)[(long int)mid] = i; + VECTOR(partition)[(long int)mid + 1] = VECTOR(neighbors)[(long int)i]; + } + + /* Create the tree graph; we use igraph_subgraph_edges here to keep the + * graph and vertex attributes */ + IGRAPH_CHECK(igraph_subgraph_edges(graph, tree, igraph_ess_none(), 0)); + IGRAPH_CHECK(igraph_add_edges(tree, &partition, 0)); + + /* Free the allocated memory */ + igraph_vector_destroy(&partition2); + igraph_vector_destroy(&partition); + igraph_vector_destroy(&neighbors); + IGRAPH_FINALLY_CLEAN(3); + + /* Return the flow values to the caller */ + if (flows != 0) { + IGRAPH_CHECK(igraph_vector_update(flows, &flow_values)); + if (no_of_nodes > 0) { + igraph_vector_remove(flows, 0); + } + } + + /* Free the remaining allocated memory */ + igraph_vector_destroy(&flow_values); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/flow/flow_internal.h b/src/rigraph/core/flow/flow_internal.h new file mode 100644 index 0000000..ccb4784 --- /dev/null +++ b/src/rigraph/core/flow/flow_internal.h @@ -0,0 +1,41 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_FLOW_INTERNAL_H +#define IGRAPH_FLOW_INTERNAL_H + +#include "igraph_types.h" + +__BEGIN_DECLS + +IGRAPH_PRIVATE_EXPORT int igraph_i_all_st_cuts_pivot(const igraph_t *graph, + const igraph_marked_queue_t *S, + const igraph_estack_t *T, + long int source, + long int target, + long int *v, + igraph_vector_t *Isv, + void *arg); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/flow/st-cuts.c b/src/rigraph/core/flow/st-cuts.c new file mode 100644 index 0000000..f363038 --- /dev/null +++ b/src/rigraph/core/flow/st-cuts.c @@ -0,0 +1,1592 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_flow.h" + +#include "igraph_adjlist.h" +#include "igraph_constants.h" +#include "igraph_constructors.h" +#include "igraph_components.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_stack.h" +#include "igraph_visitor.h" + +#include "../core/math.h" +#include "../core/estack.h" +#include "../core/marked_queue.h" +#include "../graph/attributes.h" +#include "flow_internal.h" + +typedef int igraph_provan_shier_pivot_t(const igraph_t *graph, + const igraph_marked_queue_t *S, + const igraph_estack_t *T, + long int source, + long int target, + long int *v, + igraph_vector_t *Isv, + void *arg); + +/** + * \function igraph_even_tarjan_reduction + * Even-Tarjan reduction of a graph + * + * A digraph is created with twice as many vertices and edges. For each + * original vertex i, two vertices i'= i and i'' = i' + n are created, + * with a directed edge from i' to i''. For each original directed edge + * from i to j, two new edges are created, from i' to j'' and from i'' + * to j'. + * + * This reduction is used in the paper (observation 2): + * Arkady Kanevsky: Finding all minimum-size separating vertex sets in + * a graph, Networks 23, 533--541, 1993. + * + * The original paper where this reduction was conceived is + * Shimon Even and R. Endre Tarjan: Network Flow and Testing Graph + * Connectivity, SIAM J. Comput., 4(4), 507–518. + * + * \param graph A graph. Although directness is not checked, this function + * is commonly used only on directed graphs. + * \param graphbar Pointer to a new directed graph that will contain the + * reduction, with twice as many vertices and edges. + * \param capacity Pointer to an initialized vector or a null pointer. If + * not a null pointer, then it will be filled the capacity from + * the reduction: the first |E| elements are 1, the remaining |E| + * are equal to |V| (which is used to mean infinity). + * \return Error code. + * + * Time complexity: O(|E|+|V|). + * + * \example examples/simple/even_tarjan.c + */ + +int igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, + igraph_vector_t *capacity) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + + long int new_no_of_nodes = no_of_nodes * 2; + long int new_no_of_edges = no_of_nodes + no_of_edges * 2; + + igraph_vector_t edges; + long int edgeptr = 0, capptr = 0; + long int i; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, new_no_of_edges * 2); + + if (capacity) { + IGRAPH_CHECK(igraph_vector_resize(capacity, new_no_of_edges)); + } + + /* Every vertex 'i' is replaced by two vertices, i' and i'' */ + /* id[i'] := id[i] ; id[i''] := id[i] + no_of_nodes */ + + /* One edge for each original vertex, for i, we add (i',i'') */ + for (i = 0; i < no_of_nodes; i++) { + VECTOR(edges)[edgeptr++] = i; + VECTOR(edges)[edgeptr++] = i + no_of_nodes; + if (capacity) { + VECTOR(*capacity)[capptr++] = 1.0; + } + } + + /* Two news edges for each original edge + (from,to) becomes (from'',to'), (to'',from') */ + for (i = 0; i < no_of_edges; i++) { + long int from = IGRAPH_FROM(graph, i); + long int to = IGRAPH_TO(graph, i); + VECTOR(edges)[edgeptr++] = from + no_of_nodes; + VECTOR(edges)[edgeptr++] = to; + VECTOR(edges)[edgeptr++] = to + no_of_nodes; + VECTOR(edges)[edgeptr++] = from; + if (capacity) { + VECTOR(*capacity)[capptr++] = no_of_nodes; /* TODO: should be Inf */ + VECTOR(*capacity)[capptr++] = no_of_nodes; /* TODO: should be Inf */ + } + } + + IGRAPH_CHECK(igraph_create(graphbar, &edges, (igraph_integer_t) + new_no_of_nodes, IGRAPH_DIRECTED)); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_residual_graph(const igraph_t *graph, + const igraph_vector_t *capacity, + igraph_t *residual, + igraph_vector_t *residual_capacity, + const igraph_vector_t *flow, + igraph_vector_t *tmp) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int i, no_new_edges = 0; + long int edgeptr = 0, capptr = 0; + + for (i = 0; i < no_of_edges; i++) { + if (VECTOR(*flow)[i] < VECTOR(*capacity)[i]) { + no_new_edges++; + } + } + + IGRAPH_CHECK(igraph_vector_resize(tmp, no_new_edges * 2)); + if (residual_capacity) { + IGRAPH_CHECK(igraph_vector_resize(residual_capacity, no_new_edges)); + } + + for (i = 0; i < no_of_edges; i++) { + igraph_real_t c = VECTOR(*capacity)[i] - VECTOR(*flow)[i]; + if (c > 0) { + long int from = IGRAPH_FROM(graph, i); + long int to = IGRAPH_TO(graph, i); + VECTOR(*tmp)[edgeptr++] = from; + VECTOR(*tmp)[edgeptr++] = to; + if (residual_capacity) { + VECTOR(*residual_capacity)[capptr++] = c; + } + } + } + + IGRAPH_CHECK(igraph_create(residual, tmp, (igraph_integer_t) no_of_nodes, + IGRAPH_DIRECTED)); + + return 0; +} + +int igraph_residual_graph(const igraph_t *graph, + const igraph_vector_t *capacity, + igraph_t *residual, + igraph_vector_t *residual_capacity, + const igraph_vector_t *flow) { + + igraph_vector_t tmp; + long int no_of_edges = igraph_ecount(graph); + + if (igraph_vector_size(capacity) != no_of_edges) { + IGRAPH_ERROR("Invalid `capacity' vector size", IGRAPH_EINVAL); + } + if (igraph_vector_size(flow) != no_of_edges) { + IGRAPH_ERROR("Invalid `flow' vector size", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + + IGRAPH_CHECK(igraph_i_residual_graph(graph, capacity, residual, + residual_capacity, flow, &tmp)); + + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_reverse_residual_graph(const igraph_t *graph, + const igraph_vector_t *capacity, + igraph_t *residual, + const igraph_vector_t *flow, + igraph_vector_t *tmp) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int i, no_new_edges = 0; + long int edgeptr = 0; + + for (i = 0; i < no_of_edges; i++) { + igraph_real_t cap = capacity ? VECTOR(*capacity)[i] : 1.0; + if (VECTOR(*flow)[i] > 0) { + no_new_edges++; + } + if (VECTOR(*flow)[i] < cap) { + no_new_edges++; + } + } + + IGRAPH_CHECK(igraph_vector_resize(tmp, no_new_edges * 2)); + + for (i = 0; i < no_of_edges; i++) { + long int from = IGRAPH_FROM(graph, i); + long int to = IGRAPH_TO(graph, i); + igraph_real_t cap = capacity ? VECTOR(*capacity)[i] : 1.0; + if (VECTOR(*flow)[i] > 0) { + VECTOR(*tmp)[edgeptr++] = from; + VECTOR(*tmp)[edgeptr++] = to; + } + if (VECTOR(*flow)[i] < cap) { + VECTOR(*tmp)[edgeptr++] = to; + VECTOR(*tmp)[edgeptr++] = from; + } + } + + IGRAPH_CHECK(igraph_create(residual, tmp, (igraph_integer_t) no_of_nodes, + IGRAPH_DIRECTED)); + + return 0; +} + +int igraph_reverse_residual_graph(const igraph_t *graph, + const igraph_vector_t *capacity, + igraph_t *residual, + const igraph_vector_t *flow) { + igraph_vector_t tmp; + long int no_of_edges = igraph_ecount(graph); + + if (capacity && igraph_vector_size(capacity) != no_of_edges) { + IGRAPH_ERROR("Invalid `capacity' vector size", IGRAPH_EINVAL); + } + if (igraph_vector_size(flow) != no_of_edges) { + IGRAPH_ERROR("Invalid `flow' vector size", IGRAPH_EINVAL); + } + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + + IGRAPH_CHECK(igraph_i_reverse_residual_graph(graph, capacity, residual, + flow, &tmp)); + + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +typedef struct igraph_i_dbucket_t { + igraph_vector_long_t head; + igraph_vector_long_t next; +} igraph_i_dbucket_t; + +static int igraph_i_dbucket_init(igraph_i_dbucket_t *buck, long int size) { + IGRAPH_CHECK(igraph_vector_long_init(&buck->head, size)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &buck->head); + IGRAPH_CHECK(igraph_vector_long_init(&buck->next, size)); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +static void igraph_i_dbucket_destroy(igraph_i_dbucket_t *buck) { + igraph_vector_long_destroy(&buck->head); + igraph_vector_long_destroy(&buck->next); +} + +static int igraph_i_dbucket_insert(igraph_i_dbucket_t *buck, long int bid, + long int elem) { + /* Note: we can do this, since elem is not in any buckets */ + VECTOR(buck->next)[elem] = VECTOR(buck->head)[bid]; + VECTOR(buck->head)[bid] = elem + 1; + return 0; +} + +static long int igraph_i_dbucket_empty(const igraph_i_dbucket_t *buck, + long int bid) { + return VECTOR(buck->head)[bid] == 0; +} + +static long int igraph_i_dbucket_delete(igraph_i_dbucket_t *buck, long int bid) { + long int elem = VECTOR(buck->head)[bid] - 1; + VECTOR(buck->head)[bid] = VECTOR(buck->next)[elem]; + return elem; +} + +static int igraph_i_dominator_LINK(long int v, long int w, + igraph_vector_long_t *ancestor) { + VECTOR(*ancestor)[w] = v + 1; + return 0; +} + +/* TODO: don't always reallocate path */ + +static int igraph_i_dominator_COMPRESS(long int v, + igraph_vector_long_t *ancestor, + igraph_vector_long_t *label, + igraph_vector_long_t *semi) { + igraph_stack_long_t path; + long int w = v; + long int top, pretop; + + IGRAPH_CHECK(igraph_stack_long_init(&path, 10)); + IGRAPH_FINALLY(igraph_stack_long_destroy, &path); + + while (VECTOR(*ancestor)[w] != 0) { + IGRAPH_CHECK(igraph_stack_long_push(&path, w)); + w = VECTOR(*ancestor)[w] - 1; + } + + top = igraph_stack_long_pop(&path); + while (!igraph_stack_long_empty(&path)) { + pretop = igraph_stack_long_pop(&path); + + if (VECTOR(*semi)[VECTOR(*label)[top]] < + VECTOR(*semi)[VECTOR(*label)[pretop]]) { + VECTOR(*label)[pretop] = VECTOR(*label)[top]; + } + VECTOR(*ancestor)[pretop] = VECTOR(*ancestor)[top]; + + top = pretop; + } + + igraph_stack_long_destroy(&path); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static long int igraph_i_dominator_EVAL(long int v, + igraph_vector_long_t *ancestor, + igraph_vector_long_t *label, + igraph_vector_long_t *semi) { + if (VECTOR(*ancestor)[v] == 0) { + return v; + } else { + igraph_i_dominator_COMPRESS(v, ancestor, label, semi); + return VECTOR(*label)[v]; + } +} + +/* TODO: implement the faster version. */ + +/** + * \function igraph_dominator_tree + * Calculates the dominator tree of a flowgraph + * + * A flowgraph is a directed graph with a distinguished start (or + * root) vertex r, such that for any vertex v, there is a path from r + * to v. A vertex v dominates another vertex w (not equal to v), if + * every path from r to w contains v. Vertex v is the immediate + * dominator or w, v=idom(w), if v dominates w and every other + * dominator of w dominates v. The edges {(idom(w), w)| w is not r} + * form a directed tree, rooted at r, called the dominator tree of the + * graph. Vertex v dominates vertex w if and only if v is an ancestor + * of w in the dominator tree. + * + * This function implements the Lengauer-Tarjan algorithm + * to construct the dominator tree of a directed graph. For details + * please see Thomas Lengauer, Robert Endre Tarjan: A fast algorithm + * for finding dominators in a flowgraph, ACM Transactions on + * Programming Languages and Systems (TOPLAS) I/1, 121--141, 1979. + * + * \param graph A directed graph. If it is not a flowgraph, and it + * contains some vertices not reachable from the root vertex, + * then these vertices will be collected in the \c leftout + * vector. + * \param root The id of the root (or source) vertex, this will be the + * root of the tree. + * \param dom Pointer to an initialized vector or a null pointer. If + * not a null pointer, then the immediate dominator of each + * vertex will be stored here. For vertices that are not + * reachable from the root, NaN is stored here. For + * the root vertex itself, -1 is added. + * \param domtree Pointer to an uninitialized igraph_t, or NULL. If + * not a null pointer, then the dominator tree is returned + * here. The graph contains the vertices that are unreachable + * from the root (if any), these will be isolates. + * \param leftout Pointer to an initialized vector object, or NULL. If + * not NULL, then the ids of the vertices that are unreachable + * from the root vertex (and thus not part of the dominator + * tree) are stored here. + * \param mode Constant, must be \c IGRAPH_IN or \c IGRAPH_OUT. If it + * is \c IGRAPH_IN, then all directions are considered as + * opposite to the original one in the input graph. + * \return Error code. + * + * Time complexity: very close to O(|E|+|V|), linear in the number of + * edges and vertices. More precisely, it is O(|V|+|E|alpha(|E|,|V|)), + * where alpha(|E|,|V|) is a functional inverse of Ackermann's + * function. + * + * \example examples/simple/dominator_tree.c + */ + +int igraph_dominator_tree(const igraph_t *graph, + igraph_integer_t root, + igraph_vector_t *dom, + igraph_t *domtree, + igraph_vector_t *leftout, + igraph_neimode_t mode) { + + long int no_of_nodes = igraph_vcount(graph); + + igraph_adjlist_t succ, pred; + igraph_vector_t parent; + igraph_vector_long_t semi; /* +1 always */ + igraph_vector_t vertex; /* +1 always */ + igraph_i_dbucket_t bucket; + igraph_vector_long_t ancestor; + igraph_vector_long_t label; + + igraph_neimode_t invmode = mode == IGRAPH_IN ? IGRAPH_OUT : IGRAPH_IN; + + long int i; + + igraph_vector_t vdom, *mydom = dom; + + long int component_size = 0; + + if (root < 0 || root >= no_of_nodes) { + IGRAPH_ERROR("Invalid root vertex id for dominator tree", + IGRAPH_EINVAL); + } + + if (!igraph_is_directed(graph)) { + IGRAPH_ERROR("Dominator tree of an undirected graph requested", + IGRAPH_EINVAL); + } + + if (mode == IGRAPH_ALL) { + IGRAPH_ERROR("Invalid neighbor mode for dominator tree", + IGRAPH_EINVAL); + } + + if (dom) { + IGRAPH_CHECK(igraph_vector_resize(dom, no_of_nodes)); + } else { + mydom = &vdom; + IGRAPH_VECTOR_INIT_FINALLY(mydom, no_of_nodes); + } + igraph_vector_fill(mydom, IGRAPH_NAN); + + IGRAPH_CHECK(igraph_vector_init(&parent, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_destroy, &parent); + IGRAPH_CHECK(igraph_vector_long_init(&semi, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &semi); + IGRAPH_CHECK(igraph_vector_init(&vertex, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_destroy, &vertex); + IGRAPH_CHECK(igraph_vector_long_init(&ancestor, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &ancestor); + IGRAPH_CHECK(igraph_vector_long_init_seq(&label, 0, no_of_nodes - 1)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &label); + IGRAPH_CHECK(igraph_adjlist_init(graph, &succ, mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &succ); + IGRAPH_CHECK(igraph_adjlist_init(graph, &pred, invmode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &pred); + IGRAPH_CHECK(igraph_i_dbucket_init(&bucket, no_of_nodes)); + IGRAPH_FINALLY(igraph_i_dbucket_destroy, &bucket); + + /* DFS first, to set semi, vertex and parent, step 1 */ + + IGRAPH_CHECK(igraph_dfs(graph, root, mode, /*unreachable=*/ 0, + /*order=*/ &vertex, + /*order_out=*/ 0, /*father=*/ &parent, + /*dist=*/ 0, /*in_callback=*/ 0, + /*out_callback=*/ 0, /*extra=*/ 0)); + + for (i = 0; i < no_of_nodes; i++) { + if (IGRAPH_FINITE(VECTOR(vertex)[i])) { + long int t = (long int) VECTOR(vertex)[i]; + VECTOR(semi)[t] = component_size + 1; + VECTOR(vertex)[component_size] = t + 1; + component_size++; + } + } + if (leftout) { + long int n = no_of_nodes - component_size; + long int p = 0, j; + IGRAPH_CHECK(igraph_vector_resize(leftout, n)); + for (j = 0; j < no_of_nodes && p < n; j++) { + if (!IGRAPH_FINITE(VECTOR(parent)[j])) { + VECTOR(*leftout)[p++] = j; + } + } + } + + /* We need to go over 'pred' because it should contain only the + edges towards the target vertex. */ + for (i = 0; i < no_of_nodes; i++) { + igraph_vector_int_t *v = igraph_adjlist_get(&pred, i); + long int j, n = igraph_vector_int_size(v); + for (j = 0; j < n; ) { + long int v2 = (long int) VECTOR(*v)[j]; + if (IGRAPH_FINITE(VECTOR(parent)[v2])) { + j++; + } else { + VECTOR(*v)[j] = VECTOR(*v)[n - 1]; + igraph_vector_int_pop_back(v); + n--; + } + } + } + + /* Now comes the main algorithm, steps 2 & 3 */ + + for (i = component_size - 1; i > 0; i--) { + long int w = (long int) VECTOR(vertex)[i] - 1; + igraph_vector_int_t *predw = igraph_adjlist_get(&pred, w); + long int j, n = igraph_vector_int_size(predw); + for (j = 0; j < n; j++) { + long int v = (long int) VECTOR(*predw)[j]; + long int u = igraph_i_dominator_EVAL(v, &ancestor, &label, &semi); + if (VECTOR(semi)[u] < VECTOR(semi)[w]) { + VECTOR(semi)[w] = VECTOR(semi)[u]; + } + } + igraph_i_dbucket_insert(&bucket, (long int) + VECTOR(vertex)[ VECTOR(semi)[w] - 1 ] - 1, w); + igraph_i_dominator_LINK((long int) VECTOR(parent)[w], w, &ancestor); + while (!igraph_i_dbucket_empty(&bucket, (long int) VECTOR(parent)[w])) { + long int v = igraph_i_dbucket_delete(&bucket, (long int) VECTOR(parent)[w]); + long int u = igraph_i_dominator_EVAL(v, &ancestor, &label, &semi); + VECTOR(*mydom)[v] = VECTOR(semi)[u] < VECTOR(semi)[v] ? u : + VECTOR(parent)[w]; + } + } + + /* Finally, step 4 */ + + for (i = 1; i < component_size; i++) { + long int w = (long int) VECTOR(vertex)[i] - 1; + if (VECTOR(*mydom)[w] != VECTOR(vertex)[VECTOR(semi)[w] - 1] - 1) { + VECTOR(*mydom)[w] = VECTOR(*mydom)[(long int)VECTOR(*mydom)[w]]; + } + } + VECTOR(*mydom)[(long int)root] = -1; + + igraph_i_dbucket_destroy(&bucket); + igraph_adjlist_destroy(&pred); + igraph_adjlist_destroy(&succ); + igraph_vector_long_destroy(&label); + igraph_vector_long_destroy(&ancestor); + igraph_vector_destroy(&vertex); + igraph_vector_long_destroy(&semi); + igraph_vector_destroy(&parent); + IGRAPH_FINALLY_CLEAN(8); + + if (domtree) { + igraph_vector_t edges; + long int ptr = 0; + IGRAPH_VECTOR_INIT_FINALLY(&edges, component_size * 2 - 2); + for (i = 0; i < no_of_nodes; i++) { + if (i != root && IGRAPH_FINITE(VECTOR(*mydom)[i])) { + if (mode == IGRAPH_OUT) { + VECTOR(edges)[ptr++] = VECTOR(*mydom)[i]; + VECTOR(edges)[ptr++] = i; + } else { + VECTOR(edges)[ptr++] = i; + VECTOR(edges)[ptr++] = VECTOR(*mydom)[i]; + } + } + } + IGRAPH_CHECK(igraph_create(domtree, &edges, (igraph_integer_t) no_of_nodes, + IGRAPH_DIRECTED)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_I_ATTRIBUTE_DESTROY(domtree); + IGRAPH_I_ATTRIBUTE_COPY(domtree, graph, /*graph=*/ 1, /*vertex=*/ 1, + /*edge=*/ 0); + } + + if (!dom) { + igraph_vector_destroy(&vdom); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +typedef struct igraph_i_all_st_cuts_minimal_dfs_data_t { + igraph_stack_t *stack; + igraph_vector_bool_t *nomark; + const igraph_vector_bool_t *GammaX; + long int root; + const igraph_vector_t *map; +} igraph_i_all_st_cuts_minimal_dfs_data_t; + +static igraph_bool_t igraph_i_all_st_cuts_minimal_dfs_incb( + const igraph_t *graph, + igraph_integer_t vid, + igraph_integer_t dist, + void *extra) { + + igraph_i_all_st_cuts_minimal_dfs_data_t *data = extra; + igraph_stack_t *stack = data->stack; + igraph_vector_bool_t *nomark = data->nomark; + const igraph_vector_bool_t *GammaX = data->GammaX; + const igraph_vector_t *map = data->map; + long int realvid = (long int) VECTOR(*map)[(long int)vid]; + + IGRAPH_UNUSED(graph); IGRAPH_UNUSED(dist); + + if (VECTOR(*GammaX)[(long int)realvid]) { + if (!igraph_stack_empty(stack)) { + long int top = (long int) igraph_stack_top(stack); + VECTOR(*nomark)[top] = 1; /* we just found a smaller one */ + } + igraph_stack_push(stack, realvid); /* TODO: error check */ + } + + return 0; +} + +static igraph_bool_t igraph_i_all_st_cuts_minimal_dfs_otcb( + const igraph_t *graph, + igraph_integer_t vid, + igraph_integer_t dist, + void *extra) { + igraph_i_all_st_cuts_minimal_dfs_data_t *data = extra; + igraph_stack_t *stack = data->stack; + const igraph_vector_t *map = data->map; + long int realvid = (long int) VECTOR(*map)[(long int)vid]; + + IGRAPH_UNUSED(graph); IGRAPH_UNUSED(dist); + + if (!igraph_stack_empty(stack) && + igraph_stack_top(stack) == realvid) { + igraph_stack_pop(stack); + } + + return 0; +} + +static int igraph_i_all_st_cuts_minimal(const igraph_t *graph, + const igraph_t *domtree, + long int root, + const igraph_marked_queue_t *X, + const igraph_vector_bool_t *GammaX, + const igraph_vector_t *invmap, + igraph_vector_t *minimal) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_stack_t stack; + igraph_vector_bool_t nomark; + igraph_i_all_st_cuts_minimal_dfs_data_t data; + long int i; + + IGRAPH_UNUSED(X); + + IGRAPH_CHECK(igraph_stack_init(&stack, 10)); + IGRAPH_FINALLY(igraph_stack_destroy, &stack); + IGRAPH_CHECK(igraph_vector_bool_init(&nomark, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &nomark); + + data.stack = &stack; + data.nomark = &nomark; + data.GammaX = GammaX; + data.root = root; + data.map = invmap; + + /* We mark all GammaX elements as minimal first. + TODO: actually, we could just use GammaX to return the minimal + elements. */ + for (i = 0; i < no_of_nodes; i++) { + VECTOR(nomark)[i] = VECTOR(*GammaX)[i] == 0 ? 1 : 0; + } + + /* We do a reverse DFS from root. If, along a path we find a GammaX + vertex after (=below) another GammaX vertex, we mark the higher + one as non-minimal. */ + + IGRAPH_CHECK(igraph_dfs(domtree, (igraph_integer_t) root, IGRAPH_IN, + /*unreachable=*/ 0, /*order=*/ 0, + /*order_out=*/ 0, /*father=*/ 0, + /*dist=*/ 0, /*in_callback=*/ + igraph_i_all_st_cuts_minimal_dfs_incb, + /*out_callback=*/ + igraph_i_all_st_cuts_minimal_dfs_otcb, + /*extra=*/ &data)); + + igraph_vector_clear(minimal); + for (i = 0; i < no_of_nodes; i++) { + if (!VECTOR(nomark)[i]) { + IGRAPH_CHECK(igraph_vector_push_back(minimal, i)); + } + } + + igraph_vector_bool_destroy(&nomark); + igraph_stack_destroy(&stack); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/* not 'static' because used in igraph_all_st_cuts.c test program */ +int igraph_i_all_st_cuts_pivot(const igraph_t *graph, + const igraph_marked_queue_t *S, + const igraph_estack_t *T, + long int source, + long int target, + long int *v, + igraph_vector_t *Isv, + void *arg) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_t Sbar; + igraph_vector_t Sbar_map, Sbar_invmap; + igraph_vector_t keep; + igraph_t domtree; + igraph_vector_t leftout; + long int i, nomin, n; + long int root; + igraph_vector_t M; + igraph_vector_bool_t GammaS; + igraph_vector_t Nuv; + igraph_vector_t Isv_min; + igraph_vector_t GammaS_vec; + long int Sbar_size; + + IGRAPH_UNUSED(arg); + + /* We need to create the graph induced by Sbar */ + IGRAPH_VECTOR_INIT_FINALLY(&Sbar_map, 0); + IGRAPH_VECTOR_INIT_FINALLY(&Sbar_invmap, 0); + + IGRAPH_VECTOR_INIT_FINALLY(&keep, 0); + for (i = 0; i < no_of_nodes; i++) { + if (!igraph_marked_queue_iselement(S, i)) { + IGRAPH_CHECK(igraph_vector_push_back(&keep, i)); + } + } + Sbar_size = igraph_vector_size(&keep); + + IGRAPH_CHECK(igraph_induced_subgraph_map(graph, &Sbar, + igraph_vss_vector(&keep), + IGRAPH_SUBGRAPH_AUTO, + /* map= */ &Sbar_map, + /* invmap= */ &Sbar_invmap)); + igraph_vector_destroy(&keep); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, &Sbar); + + root = (long int) VECTOR(Sbar_map)[target] - 1; + + /* -------------------------------------------------------------*/ + /* Construct the dominator tree of Sbar */ + + IGRAPH_VECTOR_INIT_FINALLY(&leftout, 0); + IGRAPH_CHECK(igraph_dominator_tree(&Sbar, (igraph_integer_t) root, + /*dom=*/ 0, &domtree, + &leftout, IGRAPH_IN)); + IGRAPH_FINALLY(igraph_destroy, &domtree); + + /* -------------------------------------------------------------*/ + /* Identify the set M of minimal elements of Gamma(S) with respect + to the dominator relation. */ + + /* First we create GammaS */ + /* TODO: use the adjacency list, instead of neighbors() */ + IGRAPH_CHECK(igraph_vector_bool_init(&GammaS, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &GammaS); + if (igraph_marked_queue_size(S) == 0) { + VECTOR(GammaS)[(long int) VECTOR(Sbar_map)[source] - 1] = 1; + } else { + for (i = 0; i < no_of_nodes; i++) { + if (igraph_marked_queue_iselement(S, i)) { + igraph_vector_t neis; + long int j; + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, + IGRAPH_OUT)); + n = igraph_vector_size(&neis); + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(neis)[j]; + if (!igraph_marked_queue_iselement(S, nei)) { + VECTOR(GammaS)[nei] = 1; + } + } + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + } + } + } + + /* Relabel left out vertices (set K in Provan & Shier) to + correspond to node labelling of graph instead of SBar. + At the same time ensure that GammaS is a proper subset of + L, where L are the nodes in the dominator tree. */ + n = igraph_vector_size(&leftout); + for (i = 0; i < n; i++) { + VECTOR(leftout)[i] = VECTOR(Sbar_invmap)[(long int)VECTOR(leftout)[i]]; + VECTOR(GammaS)[(long int)VECTOR(leftout)[i]] = 0; + } + + IGRAPH_VECTOR_INIT_FINALLY(&M, 0); + if (igraph_ecount(&domtree) > 0) { + IGRAPH_CHECK(igraph_i_all_st_cuts_minimal(graph, &domtree, root, S, + &GammaS, &Sbar_invmap, &M)); + } + + igraph_vector_clear(Isv); + IGRAPH_VECTOR_INIT_FINALLY(&Nuv, 0); + IGRAPH_VECTOR_INIT_FINALLY(&Isv_min, 0); + IGRAPH_VECTOR_INIT_FINALLY(&GammaS_vec, 0); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(GammaS)[i]) { + IGRAPH_CHECK(igraph_vector_push_back(&GammaS_vec, i)); + } + } + + nomin = igraph_vector_size(&M); + for (i = 0; i < nomin; i++) { + /* -------------------------------------------------------------*/ + /* For each v in M find the set Nu(v)=dom(Sbar, v)-K + Nu(v) contains all vertices that are dominated by v, for every + v, this is a subtree of the dominator tree, rooted at v. The + different subtrees are disjoint. */ + long int min = (long int) VECTOR(Sbar_map)[(long int) VECTOR(M)[i] ] - 1; + long int nuvsize, isvlen, j; + IGRAPH_CHECK(igraph_dfs(&domtree, (igraph_integer_t) min, IGRAPH_IN, + /*unreachable=*/ 0, /*order=*/ &Nuv, + /*order_out=*/ 0, /*father=*/ 0, /*dist=*/ 0, + /*in_callback=*/ 0, /*out_callback=*/ 0, + /*extra=*/ 0)); + /* Remove the NAN values from the end of the vector */ + for (nuvsize = 0; nuvsize < Sbar_size; nuvsize++) { + igraph_real_t t = VECTOR(Nuv)[nuvsize]; + if (IGRAPH_FINITE(t)) { + VECTOR(Nuv)[nuvsize] = VECTOR(Sbar_invmap)[(long int) t]; + } else { + break; + } + } + igraph_vector_resize(&Nuv, nuvsize); + + /* -------------------------------------------------------------*/ + /* By a BFS search of determine I(S,v)-K. + I(S,v) contains all vertices that are in Nu(v) and that are + reachable from Gamma(S) via a path in Nu(v). */ + IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ -1, /*roots=*/ &GammaS_vec, + /*mode=*/ IGRAPH_OUT, /*unreachable=*/ 0, + /*restricted=*/ &Nuv, + /*order=*/ &Isv_min, /*rank=*/ 0, + /*father=*/ 0, /*pred=*/ 0, /*succ=*/ 0, + /*dist=*/ 0, /*callback=*/ 0, /*extra=*/ 0)); + for (isvlen = 0; isvlen < no_of_nodes; isvlen++) { + if (!IGRAPH_FINITE(VECTOR(Isv_min)[isvlen])) { + break; + } + } + igraph_vector_resize(&Isv_min, isvlen); + + /* -------------------------------------------------------------*/ + /* For each c in M check whether Isv-K is included in Tbar. If + such a v is found, compute Isv={x|v[Nu(v) U K]x} and return v and + Isv; otherwise return Isv={}. */ + for (j = 0; j < isvlen; j++) { + long int u = (long int) VECTOR(Isv_min)[j]; + if (igraph_estack_iselement(T, u) || u == target) { + break; + } + } + /* We might have found one */ + if (j == isvlen) { + *v = (long int) VECTOR(M)[i]; + /* Calculate real Isv */ + IGRAPH_CHECK(igraph_vector_append(&Nuv, &leftout)); + IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ (igraph_integer_t) *v, + /*roots=*/ 0, /*mode=*/ IGRAPH_OUT, + /*unreachable=*/ 0, /*restricted=*/ &Nuv, + /*order=*/ &Isv_min, /*rank=*/ 0, + /*father=*/ 0, /*pred=*/ 0, /*succ=*/ 0, + /*dist=*/ 0, /*callback=*/ 0, /*extra=*/ 0)); + for (isvlen = 0; isvlen < no_of_nodes; isvlen++) { + if (!IGRAPH_FINITE(VECTOR(Isv_min)[isvlen])) { + break; + } + } + igraph_vector_resize(&Isv_min, isvlen); + igraph_vector_update(Isv, &Isv_min); + + break; + } + } + + igraph_vector_destroy(&GammaS_vec); + igraph_vector_destroy(&Isv_min); + igraph_vector_destroy(&Nuv); + IGRAPH_FINALLY_CLEAN(3); + + igraph_vector_destroy(&M); + igraph_vector_bool_destroy(&GammaS); + igraph_destroy(&domtree); + igraph_vector_destroy(&leftout); + igraph_destroy(&Sbar); + igraph_vector_destroy(&Sbar_map); + igraph_vector_destroy(&Sbar_invmap); + IGRAPH_FINALLY_CLEAN(7); + + return 0; +} + +/* TODO: This is a temporary recursive version, without proper error + handling */ + +int igraph_provan_shier_list(const igraph_t *graph, + igraph_marked_queue_t *S, + igraph_estack_t *T, + long int source, + long int target, + igraph_vector_ptr_t *result, + igraph_provan_shier_pivot_t *pivot, + void *pivot_arg) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t Isv; + long int v = 0; + long int i, n; + + igraph_vector_init(&Isv, 0); + + pivot(graph, S, T, source, target, &v, &Isv, pivot_arg); + if (igraph_vector_size(&Isv) == 0) { + if (igraph_marked_queue_size(S) != 0 && + igraph_marked_queue_size(S) != no_of_nodes) { + igraph_vector_t *vec = IGRAPH_CALLOC(1, igraph_vector_t); + igraph_vector_init(vec, igraph_marked_queue_size(S)); + igraph_marked_queue_as_vector(S, vec); + IGRAPH_CHECK(igraph_vector_ptr_push_back(result, vec)); + } + } else { + /* Put v into T */ + igraph_estack_push(T, v); + + /* Go down left in the search tree */ + igraph_provan_shier_list(graph, S, T, source, target, + result, pivot, pivot_arg); + + /* Take out v from T */ + igraph_estack_pop(T); + + /* Add Isv to S */ + igraph_marked_queue_start_batch(S); + n = igraph_vector_size(&Isv); + for (i = 0; i < n; i++) { + if (!igraph_marked_queue_iselement(S, (long int) VECTOR(Isv)[i])) { + igraph_marked_queue_push(S, (long int) VECTOR(Isv)[i]); + } + } + + /* Go down right in the search tree */ + + igraph_provan_shier_list(graph, S, T, source, target, + result, pivot, pivot_arg); + + /* Take out Isv from S */ + igraph_marked_queue_pop_back_batch(S); + } + + igraph_vector_destroy(&Isv); + + return 0; +} + +/** + * \function igraph_all_st_cuts + * List all edge-cuts between two vertices in a directed graph + * + * This function lists all edge-cuts between a source and a target + * vertex. Every cut is listed exactly once. The implemented algorithm + * is described in JS Provan and DR Shier: A Paradigm for listing + * (s,t)-cuts in graphs, Algorithmica 15, 351--372, 1996. + * + * \param graph The input graph, is must be directed. + * \param cuts An initialized pointer vector, the cuts are stored + * here. It is a list of pointers to igraph_vector_t + * objects. Each vector will contain the ids of the edges in + * the cut. This argument is ignored if it is a null pointer. + * To free all memory allocated for \c cuts, you need call + * \ref igraph_vector_destroy() and then \ref igraph_free() on + * each element, before destroying the pointer vector itself. + * \param partition1s An initialized pointer vector, the list of + * vertex sets, generating the actual edge cuts, are stored + * here. Each vector contains a set of vertex ids. If X is such + * a set, then all edges going from X to the complement of X + * form an (s,t) edge-cut in the graph. This argument is + * ignored if it is a null pointer. + * To free all memory allocated for \c partition1s, you need call + * \ref igraph_vector_destroy() and then \ref igraph_free() on + * each element, before destroying the pointer vector itself. + * \param source The id of the source vertex. + * \param target The id of the target vertex. + * \return Error code. + * + * Time complexity: O(n(|V|+|E|)), where |V| is the number of + * vertices, |E| is the number of edges, and n is the number of cuts. + */ + +int igraph_all_st_cuts(const igraph_t *graph, + igraph_vector_ptr_t *cuts, + igraph_vector_ptr_t *partition1s, + igraph_integer_t source, + igraph_integer_t target) { + + /* S is a special stack, in which elements are pushed in batches. + It is then possible to remove the whole batch in one step. + + T is a stack with an is-element operation. + Every element is included at most once. + */ + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_marked_queue_t S; + igraph_estack_t T; + igraph_vector_ptr_t *mypartition1s = partition1s, vpartition1s; + long int i, nocuts; + + if (!igraph_is_directed(graph)) { + IGRAPH_ERROR("Listing all s-t cuts only implemented for " + "directed graphs", IGRAPH_UNIMPLEMENTED); + } + + if (!partition1s) { + mypartition1s = &vpartition1s; + IGRAPH_CHECK(igraph_vector_ptr_init(mypartition1s, 0)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, mypartition1s); + } else { + igraph_vector_ptr_clear(mypartition1s); + } + + IGRAPH_CHECK(igraph_marked_queue_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_marked_queue_destroy, &S); + IGRAPH_CHECK(igraph_estack_init(&T, no_of_nodes, 0)); + IGRAPH_FINALLY(igraph_estack_destroy, &T); + + if (cuts) { + igraph_vector_ptr_clear(cuts); + } + + /* We call it with S={}, T={} */ + IGRAPH_CHECK(igraph_provan_shier_list(graph, &S, &T, + source, target, mypartition1s, + igraph_i_all_st_cuts_pivot, + /*pivot_arg=*/ 0)); + + nocuts = igraph_vector_ptr_size(mypartition1s); + + if (cuts) { + igraph_vector_long_t inS; + IGRAPH_CHECK(igraph_vector_long_init(&inS, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &inS); + IGRAPH_CHECK(igraph_vector_ptr_resize(cuts, nocuts)); + for (i = 0; i < nocuts; i++) { + igraph_vector_t *cut; + igraph_vector_t *part = VECTOR(*mypartition1s)[i]; + long int cutsize = 0; + long int j, partlen = igraph_vector_size(part); + /* Mark elements */ + for (j = 0; j < partlen; j++) { + long int v = (long int) VECTOR(*part)[j]; + VECTOR(inS)[v] = i + 1; + } + /* Check how many edges */ + for (j = 0; j < no_of_edges; j++) { + long int from = IGRAPH_FROM(graph, j); + long int to = IGRAPH_TO(graph, j); + long int pfrom = VECTOR(inS)[from]; + long int pto = VECTOR(inS)[to]; + if (pfrom == i + 1 && pto != i + 1) { + cutsize++; + } + } + /* Add the edges */ + cut = IGRAPH_CALLOC(1, igraph_vector_t); + if (!cut) { + IGRAPH_ERROR("Cannot calculate s-t cuts", IGRAPH_ENOMEM); + } + IGRAPH_VECTOR_INIT_FINALLY(cut, cutsize); + cutsize = 0; + for (j = 0; j < no_of_edges; j++) { + long int from = IGRAPH_FROM(graph, j); + long int to = IGRAPH_TO(graph, j); + long int pfrom = VECTOR(inS)[from]; + long int pto = VECTOR(inS)[to]; + if ((pfrom == i + 1 && pto != i + 1)) { + VECTOR(*cut)[cutsize++] = j; + } + } + VECTOR(*cuts)[i] = cut; + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_long_destroy(&inS); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_estack_destroy(&T); + igraph_marked_queue_destroy(&S); + IGRAPH_FINALLY_CLEAN(2); + + if (!partition1s) { + for (i = 0; i < nocuts; i++) { + igraph_vector_t *cut = VECTOR(*mypartition1s)[i]; + igraph_vector_destroy(cut); + igraph_free(cut); + VECTOR(*mypartition1s)[i] = 0; + } + igraph_vector_ptr_destroy(mypartition1s); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/* We need to find the minimal active elements of Sbar. I.e. all + active Sbar elements 'v', s.t. there is no other 'w' active Sbar + element from which 'v' is reachable. (Not necessarily through + active vertices.) + + We calculate the in-degree of all vertices in Sbar first. Then we + look at the vertices with zero in-degree. If these are active, + then they are minimal. If they are are not active, then we remove + them from the graph, and check whether they resulted in more + zero-indegree vertices. +*/ + +static int igraph_i_all_st_mincuts_minimal(const igraph_t *Sbar, + const igraph_vector_bool_t *active, + const igraph_vector_t *invmap, + igraph_vector_t *minimal) { + + long int no_of_nodes = igraph_vcount(Sbar); + igraph_vector_t indeg; + long int i, minsize; + igraph_vector_t neis; + + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&indeg, no_of_nodes); + + IGRAPH_CHECK(igraph_degree(Sbar, &indeg, igraph_vss_all(), + IGRAPH_IN, /*loops=*/ 1)); + +#define ACTIVE(x) (VECTOR(*active)[(long int)VECTOR(*invmap)[(x)]]) +#define ZEROIN(x) (VECTOR(indeg)[(x)]==0) + + for (i = 0; i < no_of_nodes; i++) { + if (!ACTIVE(i)) { + long int j, n; + IGRAPH_CHECK(igraph_neighbors(Sbar, &neis, (igraph_integer_t) i, + IGRAPH_OUT)); + n = igraph_vector_size(&neis); + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(neis)[j]; + VECTOR(indeg)[nei] -= 1; + } + } + } + + for (minsize = 0, i = 0; i < no_of_nodes; i++) { + if (ACTIVE(i) && ZEROIN(i)) { + minsize++; + } + } + + IGRAPH_CHECK(igraph_vector_resize(minimal, minsize)); + + for (minsize = 0, i = 0; i < no_of_nodes; i++) { + if (ACTIVE(i) && ZEROIN(i)) { + VECTOR(*minimal)[minsize++] = i; + } + } + +#undef ACTIVE +#undef ZEROIN + + igraph_vector_destroy(&indeg); + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +typedef struct igraph_i_all_st_mincuts_data_t { + const igraph_vector_bool_t *active; +} igraph_i_all_st_mincuts_data_t; + +static int igraph_i_all_st_mincuts_pivot(const igraph_t *graph, + const igraph_marked_queue_t *S, + const igraph_estack_t *T, + long int source, + long int target, + long int *v, + igraph_vector_t *Isv, + void *arg) { + + igraph_i_all_st_mincuts_data_t *data = arg; + const igraph_vector_bool_t *active = data->active; + + long int no_of_nodes = igraph_vcount(graph); + long int i, j; + igraph_vector_t Sbar_map, Sbar_invmap; + igraph_vector_t keep; + igraph_t Sbar; + igraph_vector_t M; + long int nomin; + + IGRAPH_UNUSED(source); IGRAPH_UNUSED(target); + + if (igraph_marked_queue_size(S) == no_of_nodes) { + igraph_vector_clear(Isv); + return 0; + } + + /* Create the graph induced by Sbar */ + IGRAPH_VECTOR_INIT_FINALLY(&Sbar_map, 0); + IGRAPH_VECTOR_INIT_FINALLY(&Sbar_invmap, 0); + + IGRAPH_VECTOR_INIT_FINALLY(&keep, 0); + for (i = 0; i < no_of_nodes; i++) { + if (!igraph_marked_queue_iselement(S, i)) { + IGRAPH_CHECK(igraph_vector_push_back(&keep, i)); + } + } + + /* TODO: it is not even necessary to create Sbar explicitly, we + just need to find the M elements efficiently. See the + Provan-Shier paper for details. */ + IGRAPH_CHECK(igraph_induced_subgraph_map(graph, &Sbar, + igraph_vss_vector(&keep), + IGRAPH_SUBGRAPH_AUTO, + /* map= */ &Sbar_map, + /* invmap= */ &Sbar_invmap)); + IGRAPH_FINALLY(igraph_destroy, &Sbar); + + /* ------------------------------------------------------------- */ + /* Identify the set M of minimal elements that are active */ + IGRAPH_VECTOR_INIT_FINALLY(&M, 0); + IGRAPH_CHECK(igraph_i_all_st_mincuts_minimal(&Sbar, active, + &Sbar_invmap, &M)); + + /* ------------------------------------------------------------- */ + /* Now find a minimal element that is not in T */ + igraph_vector_clear(Isv); + nomin = igraph_vector_size(&M); + for (i = 0; i < nomin; i++) { + long int min = (long int) VECTOR(Sbar_invmap)[ (long int) VECTOR(M)[i] ]; + if (min != target) + if (!igraph_estack_iselement(T, min)) { + break; + } + } + if (i != nomin) { + /* OK, we found a pivot element. I(S,v) contains all elements + that can reach the pivot element */ + igraph_vector_t Isv_min; + IGRAPH_VECTOR_INIT_FINALLY(&Isv_min, 0); + *v = (long int) VECTOR(Sbar_invmap)[ (long int) VECTOR(M)[i] ]; + /* TODO: restricted == keep ? */ + IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ (igraph_integer_t) *v,/*roots=*/ 0, + /*mode=*/ IGRAPH_IN, /*unreachable=*/ 0, + /*restricted=*/ &keep, /*order=*/ &Isv_min, + /*rank=*/ 0, /*father=*/ 0, /*pred=*/ 0, + /*succ=*/ 0, /*dist=*/ 0, /*callback=*/ 0, + /*extra=*/ 0)); + for (j = 0; j < no_of_nodes; j++) { + igraph_real_t u = VECTOR(Isv_min)[j]; + if (!IGRAPH_FINITE(u)) { + break; + } + if (!igraph_estack_iselement(T, u)) { + IGRAPH_CHECK(igraph_vector_push_back(Isv, u)); + } + } + igraph_vector_destroy(&Isv_min); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_destroy(&M); + igraph_destroy(&Sbar); + igraph_vector_destroy(&keep); + igraph_vector_destroy(&Sbar_invmap); + igraph_vector_destroy(&Sbar_map); + IGRAPH_FINALLY_CLEAN(5); + + return 0; +} + +/** + * \function igraph_all_st_mincuts + * All minimum s-t cuts of a directed graph + * + * This function lists all edge cuts between two vertices, in a directed graph, + * with minimum total capacity. Possibly, multiple cuts may have the same total + * capacity, although there is often only one minimum cut in weighted graphs. + * It is recommended to supply integer-values capacities. Otherwise, not all + * minimum cuts may be detected because of numerical roundoff errors. + * The implemented algorithm is described in JS Provan and DR + * Shier: A Paradigm for listing (s,t)-cuts in graphs, Algorithmica 15, + * 351--372, 1996. + * + * \param graph The input graph, it must be directed. + * \param value Pointer to a real number, the value of the minimum cut + * is stored here, unless it is a null pointer. + * \param cuts An initialized pointer vector, the cuts are stored + * here. It is a list of pointers to igraph_vector_t + * objects. Each vector will contain the ids of the edges in + * the cut. This argument is ignored if it is a null pointer. + * To free all memory allocated for \c cuts, you need call + * \ref igraph_vector_destroy() and then \ref igraph_free() on + * each element, before destroying the pointer vector itself. + * \param partition1s An initialized pointer vector, the list of + * vertex sets, generating the actual edge cuts, are stored + * here. Each vector contains a set of vertex ids. If X is such + * a set, then all edges going from X to the complement of X + * form an (s,t) edge-cut in the graph. This argument is + * ignored if it is a null pointer. + * \param source The id of the source vertex. + * \param target The id of the target vertex. + * \param capacity Vector of edge capacities. All capacities must be + * strictly positive. If this is a null pointer, then all edges + * are assumed to have capacity one. + * \return Error code. + * + * Time complexity: O(n(|V|+|E|))+O(F), where |V| is the number of + * vertices, |E| is the number of edges, and n is the number of cuts; + * O(F) is the time complexity of the maximum flow algorithm, see \ref + * igraph_maxflow(). + * + * \example examples/simple/igraph_all_st_mincuts.c + */ + +int igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value, + igraph_vector_ptr_t *cuts, + igraph_vector_ptr_t *partition1s, + igraph_integer_t source, + igraph_integer_t target, + const igraph_vector_t *capacity) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_vector_t flow; + igraph_t residual; + igraph_vector_t NtoL; + long int newsource, newtarget; + igraph_marked_queue_t S; + igraph_estack_t T; + igraph_i_all_st_mincuts_data_t pivot_data; + igraph_vector_bool_t VE1bool; + igraph_vector_t VE1; + long int VE1size = 0; + long int i, nocuts; + igraph_integer_t proj_nodes; + igraph_vector_t revmap_ptr, revmap_next; + igraph_vector_ptr_t closedsets; + igraph_vector_ptr_t *mypartition1s = partition1s, vpartition1s; + igraph_maxflow_stats_t stats; + + /* -------------------------------------------------------------------- */ + /* Error checks */ + if (!igraph_is_directed(graph)) { + IGRAPH_ERROR("S-t cuts can only be listed in directed graphs", + IGRAPH_UNIMPLEMENTED); + } + if (source < 0 || source >= no_of_nodes) { + IGRAPH_ERROR("Invalid `source' vertex", IGRAPH_EINVAL); + } + if (target < 0 || target >= no_of_nodes) { + IGRAPH_ERROR("Invalid `target' vertex", IGRAPH_EINVAL); + } + if (source == target) { + IGRAPH_ERROR("`source' and 'target' are the same vertex", IGRAPH_EINVAL); + } + if (capacity != NULL && igraph_vector_min(capacity) <= 0) + { + IGRAPH_ERROR("Not all capacities are strictly positive.", IGRAPH_EINVAL); + } + + if (!partition1s) { + mypartition1s = &vpartition1s; + IGRAPH_CHECK(igraph_vector_ptr_init(mypartition1s, 0)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, mypartition1s); + } + + /* -------------------------------------------------------------------- */ + /* We need to calculate the maximum flow first */ + IGRAPH_VECTOR_INIT_FINALLY(&flow, 0); + IGRAPH_CHECK(igraph_maxflow(graph, value, &flow, /*cut=*/ 0, + /*partition1=*/ 0, /*partition2=*/ 0, + /*source=*/ source, /*target=*/ target, + capacity, &stats)); + + /* -------------------------------------------------------------------- */ + /* Then we need the reverse residual graph */ + IGRAPH_CHECK(igraph_reverse_residual_graph(graph, capacity, &residual, + &flow)); + IGRAPH_FINALLY(igraph_destroy, &residual); + + /* -------------------------------------------------------------------- */ + /* We shrink it to its strongly connected components */ + IGRAPH_VECTOR_INIT_FINALLY(&NtoL, 0); + IGRAPH_CHECK(igraph_clusters(&residual, /*membership=*/ &NtoL, + /*csize=*/ 0, /*no=*/ &proj_nodes, + IGRAPH_STRONG)); + IGRAPH_CHECK(igraph_contract_vertices(&residual, /*mapping=*/ &NtoL, + /*vertex_comb=*/ 0)); + IGRAPH_CHECK(igraph_simplify(&residual, /*multiple=*/ 1, /*loops=*/ 1, + /*edge_comb=*/ 0)); + + newsource = (long int) VECTOR(NtoL)[(long int)source]; + newtarget = (long int) VECTOR(NtoL)[(long int)target]; + + /* TODO: handle the newsource == newtarget case */ + + /* -------------------------------------------------------------------- */ + /* Determine the active vertices in the projection */ + IGRAPH_VECTOR_INIT_FINALLY(&VE1, 0); + IGRAPH_CHECK(igraph_vector_bool_init(&VE1bool, proj_nodes)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &VE1bool); + for (i = 0; i < no_of_edges; i++) { + if (VECTOR(flow)[i] > 0) { + long int from = IGRAPH_FROM(graph, i); + long int to = IGRAPH_TO(graph, i); + long int pfrom = (long int) VECTOR(NtoL)[from]; + long int pto = (long int) VECTOR(NtoL)[to]; + if (!VECTOR(VE1bool)[pfrom]) { + VECTOR(VE1bool)[pfrom] = 1; + VE1size++; + } + if (!VECTOR(VE1bool)[pto]) { + VECTOR(VE1bool)[pto] = 1; + VE1size++; + } + } + } + IGRAPH_CHECK(igraph_vector_reserve(&VE1, VE1size)); + for (i = 0; i < proj_nodes; i++) { + if (VECTOR(VE1bool)[i]) { + igraph_vector_push_back(&VE1, i); + } + } + + if (cuts) { + igraph_vector_ptr_clear(cuts); + } + if (partition1s) { + igraph_vector_ptr_clear(partition1s); + } + + /* -------------------------------------------------------------------- */ + /* Everything is ready, list the cuts, using the right PIVOT + function */ + IGRAPH_CHECK(igraph_marked_queue_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_marked_queue_destroy, &S); + IGRAPH_CHECK(igraph_estack_init(&T, no_of_nodes, 0)); + IGRAPH_FINALLY(igraph_estack_destroy, &T); + + pivot_data.active = &VE1bool; + + IGRAPH_CHECK(igraph_vector_ptr_init(&closedsets, 0)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &closedsets); /* TODO */ + IGRAPH_CHECK(igraph_provan_shier_list(&residual, &S, &T, + newsource, newtarget, &closedsets, + igraph_i_all_st_mincuts_pivot, + &pivot_data)); + + /* Convert the closed sets in the contracted graphs to cutsets in the + original graph */ + IGRAPH_VECTOR_INIT_FINALLY(&revmap_ptr, igraph_vcount(&residual)); + IGRAPH_VECTOR_INIT_FINALLY(&revmap_next, no_of_nodes); + for (i = 0; i < no_of_nodes; i++) { + long int id = (long int) VECTOR(NtoL)[i]; + VECTOR(revmap_next)[i] = VECTOR(revmap_ptr)[id]; + VECTOR(revmap_ptr)[id] = i + 1; + } + + /* Create partitions in original graph */ + nocuts = igraph_vector_ptr_size(&closedsets); + igraph_vector_ptr_clear(mypartition1s); + IGRAPH_CHECK(igraph_vector_ptr_reserve(mypartition1s, nocuts)); + for (i = 0; i < nocuts; i++) { + igraph_vector_t *supercut = VECTOR(closedsets)[i]; + long int j, supercutsize = igraph_vector_size(supercut); + igraph_vector_t *cut = IGRAPH_CALLOC(1, igraph_vector_t); + IGRAPH_VECTOR_INIT_FINALLY(cut, 0); /* TODO: better allocation */ + for (j = 0; j < supercutsize; j++) { + long int vtx = (long int) VECTOR(*supercut)[j]; + long int ovtx = (long int) VECTOR(revmap_ptr)[vtx]; + while (ovtx != 0) { + ovtx--; + IGRAPH_CHECK(igraph_vector_push_back(cut, ovtx)); + ovtx = (long int) VECTOR(revmap_next)[ovtx]; + } + } + igraph_vector_ptr_push_back(mypartition1s, cut); + IGRAPH_FINALLY_CLEAN(1); + + igraph_vector_destroy(supercut); + igraph_free(supercut); + VECTOR(closedsets)[i] = 0; + } + + igraph_vector_destroy(&revmap_next); + igraph_vector_destroy(&revmap_ptr); + igraph_vector_ptr_destroy(&closedsets); + IGRAPH_FINALLY_CLEAN(3); + + /* Create cuts in original graph */ + if (cuts) { + igraph_vector_long_t memb; + IGRAPH_CHECK(igraph_vector_long_init(&memb, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &memb); + IGRAPH_CHECK(igraph_vector_ptr_resize(cuts, nocuts)); + for (i = 0; i < nocuts; i++) { + igraph_vector_t *part = VECTOR(*mypartition1s)[i]; + long int j, n = igraph_vector_size(part); + igraph_vector_t *v; + v = IGRAPH_CALLOC(1, igraph_vector_t); + if (!v) { + IGRAPH_ERROR("Cannot list minimum s-t cuts", IGRAPH_ENOMEM); + } + IGRAPH_VECTOR_INIT_FINALLY(v, 0); + for (j = 0; j < n; j++) { + long int vtx = (long int) VECTOR(*part)[j]; + VECTOR(memb)[vtx] = i + 1; + } + for (j = 0; j < no_of_edges; j++) { + if (VECTOR(flow)[j] > 0) { + long int from = IGRAPH_FROM(graph, j); + long int to = IGRAPH_TO(graph, j); + if (VECTOR(memb)[from] == i + 1 && VECTOR(memb)[to] != i + 1) { + IGRAPH_CHECK(igraph_vector_push_back(v, j)); /* TODO: allocation */ + } + } + } + VECTOR(*cuts)[i] = v; + IGRAPH_FINALLY_CLEAN(1); + } + igraph_vector_long_destroy(&memb); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_estack_destroy(&T); + igraph_marked_queue_destroy(&S); + igraph_vector_bool_destroy(&VE1bool); + igraph_vector_destroy(&VE1); + igraph_vector_destroy(&NtoL); + igraph_destroy(&residual); + igraph_vector_destroy(&flow); + IGRAPH_FINALLY_CLEAN(7); + + if (!partition1s) { + for (i = 0; i < nocuts; i++) { + igraph_vector_t *cut = VECTOR(*mypartition1s)[i]; + igraph_vector_destroy(cut); + igraph_free(cut); + VECTOR(*mypartition1s)[i] = 0; + } + igraph_vector_ptr_destroy(mypartition1s); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} diff --git a/src/rigraph/core/games/barabasi.c b/src/rigraph/core/games/barabasi.c new file mode 100644 index 0000000..02dc616 --- /dev/null +++ b/src/rigraph/core/games/barabasi.c @@ -0,0 +1,764 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_conversion.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_psumtree.h" +#include "igraph_random.h" + +#include "../core/interruption.h" + +static int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, + igraph_integer_t m, + const igraph_vector_t *outseq, + igraph_bool_t outpref, + igraph_bool_t directed, + const igraph_t *start_from); + +static int igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph, + igraph_integer_t n, + igraph_real_t power, + igraph_integer_t m, + const igraph_vector_t *outseq, + igraph_bool_t outpref, + igraph_real_t A, + igraph_bool_t directed, + const igraph_t *start_from); + +static int igraph_i_barabasi_game_psumtree(igraph_t *graph, + igraph_integer_t n, + igraph_real_t power, + igraph_integer_t m, + const igraph_vector_t *outseq, + igraph_bool_t outpref, + igraph_real_t A, + igraph_bool_t directed, + const igraph_t *start_from); + +static int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, + igraph_integer_t m, + const igraph_vector_t *outseq, + igraph_bool_t outpref, + igraph_bool_t directed, + const igraph_t *start_from) { + + long int no_of_nodes = n; + long int no_of_neighbors = m; + long int *bag; + long int bagp = 0; + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + long int resp; + long int i, j, k; + long int bagsize, start_nodes, start_edges, new_edges, no_of_edges; + + if (!directed) { + outpref = 1; + } + + start_nodes = start_from ? igraph_vcount(start_from) : 1; + start_edges = start_from ? igraph_ecount(start_from) : 0; + if (outseq) { + if (igraph_vector_size(outseq) > 1) { + new_edges = (long int) (igraph_vector_sum(outseq) - VECTOR(*outseq)[0]); + } else { + new_edges = 0; + } + } else { + new_edges = (no_of_nodes - start_nodes) * no_of_neighbors; + } + no_of_edges = start_edges + new_edges; + resp = start_edges * 2; + bagsize = no_of_nodes + no_of_edges + (outpref ? no_of_edges : 0); + + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + + bag = IGRAPH_CALLOC(bagsize, long int); + if (bag == 0) { + IGRAPH_ERROR("barabasi_game failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, bag); + + /* The first node(s) in the bag */ + if (start_from) { + igraph_vector_t deg; + long int ii, jj, sn = igraph_vcount(start_from); + igraph_neimode_t mm = outpref ? IGRAPH_ALL : IGRAPH_IN; + + IGRAPH_VECTOR_INIT_FINALLY(°, sn); + IGRAPH_CHECK(igraph_degree(start_from, °, igraph_vss_all(), mm, + IGRAPH_LOOPS)); + for (ii = 0; ii < sn; ii++) { + long int d = (long int) VECTOR(deg)[ii]; + for (jj = 0; jj <= d; jj++) { + bag[bagp++] = ii; + } + } + + igraph_vector_destroy(°); + IGRAPH_FINALLY_CLEAN(1); + } else { + bag[bagp++] = 0; + } + + /* Initialize the edges vector */ + if (start_from) { + IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ 0)); + igraph_vector_resize(&edges, no_of_edges * 2); + } + + RNG_BEGIN(); + + /* and the others */ + + for (i = (start_from ? start_nodes : 1), k = (start_from ? 0 : 1); + i < no_of_nodes; i++, k++) { + + IGRAPH_ALLOW_INTERRUPTION(); + + /* draw edges */ + if (outseq) { + no_of_neighbors = (long int) VECTOR(*outseq)[k]; + } + for (j = 0; j < no_of_neighbors; j++) { + long int to = bag[RNG_INTEGER(0, bagp - 1)]; + VECTOR(edges)[resp++] = i; + VECTOR(edges)[resp++] = to; + } + /* update bag */ + bag[bagp++] = i; + for (j = 0; j < no_of_neighbors; j++) { + bag[bagp++] = (long int) VECTOR(edges)[resp - 2 * j - 1]; + if (outpref) { + bag[bagp++] = i; + } + } + } + + RNG_END(); + + IGRAPH_FREE(bag); + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +static int igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph, + igraph_integer_t n, + igraph_real_t power, + igraph_integer_t m, + const igraph_vector_t *outseq, + igraph_bool_t outpref, + igraph_real_t A, + igraph_bool_t directed, + const igraph_t *start_from) { + + long int no_of_nodes = n; + long int no_of_neighbors = m; + igraph_vector_t edges; + long int i, j, k; + igraph_psumtree_t sumtree; + long int edgeptr = 0; + igraph_vector_t degree; + long int start_nodes, start_edges, new_edges, no_of_edges; + + if (!directed) { + outpref = 1; + } + + start_nodes = start_from ? igraph_vcount(start_from) : 1; + start_edges = start_from ? igraph_ecount(start_from) : 0; + if (outseq) { + if (igraph_vector_size(outseq) > 1) { + new_edges = (long int) (igraph_vector_sum(outseq) - VECTOR(*outseq)[0]); + } else { + new_edges = 0; + } + } else { + new_edges = (no_of_nodes - start_nodes) * no_of_neighbors; + } + no_of_edges = start_edges + new_edges; + edgeptr = start_edges * 2; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes)); + IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + + /* first node(s) */ + if (start_from) { + long int ii, sn = igraph_vcount(start_from); + igraph_neimode_t mm = outpref ? IGRAPH_ALL : IGRAPH_IN; + IGRAPH_CHECK(igraph_degree(start_from, °ree, igraph_vss_all(), mm, + IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_vector_resize(°ree, no_of_nodes)); + for (ii = 0; ii < sn; ii++) { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, pow(VECTOR(degree)[ii], power) + A)); + } + } else { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, A)); + } + + /* Initialize the edges vector */ + if (start_from) { + IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ 0)); + igraph_vector_resize(&edges, no_of_edges * 2); + } + + RNG_BEGIN(); + + /* and the rest */ + for (i = (start_from ? start_nodes : 1), k = (start_from ? 0 : 1); + i < no_of_nodes; i++, k++) { + igraph_real_t sum = igraph_psumtree_sum(&sumtree); + long int to; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (outseq) { + no_of_neighbors = (long int) VECTOR(*outseq)[k]; + } + for (j = 0; j < no_of_neighbors; j++) { + igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum)); + VECTOR(degree)[to]++; + VECTOR(edges)[edgeptr++] = i; + VECTOR(edges)[edgeptr++] = to; + } + /* update probabilities */ + for (j = 0; j < no_of_neighbors; j++) { + long int nn = (long int) VECTOR(edges)[edgeptr - 2 * j - 1]; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, nn, pow(VECTOR(degree)[nn], power) + A)); + } + if (outpref) { + VECTOR(degree)[i] += no_of_neighbors; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, pow(VECTOR(degree)[i], power) + A)); + } else { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, A)); + } + } + + RNG_END(); + + igraph_psumtree_destroy(&sumtree); + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_barabasi_game_psumtree(igraph_t *graph, + igraph_integer_t n, + igraph_real_t power, + igraph_integer_t m, + const igraph_vector_t *outseq, + igraph_bool_t outpref, + igraph_real_t A, + igraph_bool_t directed, + const igraph_t *start_from) { + + long int no_of_nodes = n; + long int no_of_neighbors = m; + igraph_vector_t edges; + long int i, j, k; + igraph_psumtree_t sumtree; + long int edgeptr = 0; + igraph_vector_t degree; + long int start_nodes, start_edges, new_edges, no_of_edges; + + if (!directed) { + outpref = 1; + } + + start_nodes = start_from ? igraph_vcount(start_from) : 1; + start_edges = start_from ? igraph_ecount(start_from) : 0; + if (outseq) { + if (igraph_vector_size(outseq) > 1) { + new_edges = (long int) (igraph_vector_sum(outseq) - VECTOR(*outseq)[0]); + } else { + new_edges = 0; + } + } else { + new_edges = (no_of_nodes - start_nodes) * no_of_neighbors; + } + no_of_edges = start_edges + new_edges; + edgeptr = start_edges * 2; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes)); + IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + + RNG_BEGIN(); + + /* first node(s) */ + if (start_from) { + long int ii, sn = igraph_vcount(start_from); + igraph_neimode_t mm = outpref ? IGRAPH_ALL : IGRAPH_IN; + IGRAPH_CHECK(igraph_degree(start_from, °ree, igraph_vss_all(), mm, + IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_vector_resize(°ree, no_of_nodes)); + for (ii = 0; ii < sn; ii++) { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, pow(VECTOR(degree)[ii], power) + A)); + } + } else { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, A)); + } + + /* Initialize the edges vector */ + if (start_from) { + IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ 0)); + } + + /* and the rest */ + for (i = (start_from ? start_nodes : 1), k = (start_from ? 0 : 1); + i < no_of_nodes; i++, k++) { + igraph_real_t sum; + long int to; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (outseq) { + no_of_neighbors = (long int) VECTOR(*outseq)[k]; + } + if (no_of_neighbors >= i) { + /* All existing vertices are cited */ + for (to = 0; to < i; to++) { + VECTOR(degree)[to]++; + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + edgeptr += 2; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, to, pow(VECTOR(degree)[to], power) + A)); + } + } else { + for (j = 0; j < no_of_neighbors; j++) { + sum = igraph_psumtree_sum(&sumtree); + igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum)); + VECTOR(degree)[to]++; + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + edgeptr += 2; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, to, 0.0)); + } + /* update probabilities */ + for (j = 0; j < no_of_neighbors; j++) { + long int nn = (long int) VECTOR(edges)[edgeptr - 2 * j - 1]; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, nn, pow(VECTOR(degree)[nn], power) + A)); + } + } + if (outpref) { + VECTOR(degree)[i] += no_of_neighbors > i ? i : no_of_neighbors; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, pow(VECTOR(degree)[i], power) + A)); + } else { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, A)); + } + } + + RNG_END(); + + igraph_psumtree_destroy(&sumtree); + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \ingroup generators + * \function igraph_barabasi_game + * \brief Generates a graph based on the Barabási-Albert model. + * + * \param graph An uninitialized graph object. + * \param n The number of vertices in the graph. + * \param power Power of the preferential attachment. The probability + * that a vertex is cited is proportional to d^power+A, where + * d is its degree (see also the \p outpref argument), power + * and A are given by arguments. In the classic preferential + * attachment model power=1. + * \param m The number of outgoing edges generated for each + * vertex. (Only if \p outseq is \c NULL.) + * \param outseq Gives the (out-)degrees of the vertices. If this is + * constant, this can be a NULL pointer or an empty (but + * initialized!) vector, in this case \p m contains + * the constant out-degree. The very first vertex has by definition + * no outgoing edges, so the first number in this vector is + * ignored. + * \param outpref Boolean, if true not only the in- but also the out-degree + * of a vertex increases its citation probability. I.e., the + * citation probability is determined by the total degree of + * the vertices. Ignored and assumed to be true if the graph + * being generated is undirected. + * \param A The probability that a vertex is cited is proportional to + * d^power+A, where d is its degree (see also the \p outpref + * argument), power and A are given by arguments. In the + * previous versions of the function this parameter was + * implicitly set to one. + * \param directed Boolean, whether to generate a directed graph. + * \param algo The algorithm to use to generate the network. Possible + * values: + * \clist + * \cli IGRAPH_BARABASI_BAG + * This is the algorithm that was previously (before version + * 0.6) solely implemented in igraph. It works by putting the + * ids of the vertices into a bag (multiset, really), exactly + * as many times as their (in-)degree, plus once more. Then + * the required number of cited vertices are drawn from the + * bag, with replacement. This method might generate multiple + * edges. It only works if power=1 and A=1. + * \cli IGRAPH_BARABASI_PSUMTREE + * This algorithm uses a partial prefix-sum tree to generate + * the graph. It does not generate multiple edges and + * works for any power and A values. + * \cli IGRAPH_BARABASI_PSUMTREE_MULTIPLE + * This algorithm also uses a partial prefix-sum tree to + * generate the graph. The difference is, that now multiple + * edges are allowed. This method was implemented under the + * name \c igraph_nonlinear_barabasi_game before version 0.6. + * \endclist + * \param start_from Either a null pointer, or a graph. In the former + * case, the starting configuration is a clique of size \p m. + * In the latter case, the graph is a starting configuration. + * The graph must be non-empty, i.e. it must have at least one + * vertex. If a graph is supplied here and the \p outseq + * argument is also given, then \p outseq should only contain + * information on the vertices that are not in the \p + * start_from graph. + * \return Error code: + * \c IGRAPH_EINVAL: invalid \p n, + * \p m or \p outseq parameter. + * + * Time complexity: O(|V|+|E|), the + * number of vertices plus the number of edges. + * + * \example examples/simple/igraph_barabasi_game.c + * \example examples/simple/igraph_barabasi_game2.c + */ +int igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, + igraph_real_t power, + igraph_integer_t m, + const igraph_vector_t *outseq, + igraph_bool_t outpref, + igraph_real_t A, + igraph_bool_t directed, + igraph_barabasi_algorithm_t algo, + const igraph_t *start_from) { + + long int start_nodes = start_from ? igraph_vcount(start_from) : 0; + long int newn = start_from ? n - start_nodes : n; + + /* Fix obscure parameterizations */ + if (outseq && igraph_vector_size(outseq) == 0) { + outseq = 0; + } + if (!directed) { + outpref = 1; + } + + /* Check arguments */ + + if (algo != IGRAPH_BARABASI_BAG && + algo != IGRAPH_BARABASI_PSUMTREE && + algo != IGRAPH_BARABASI_PSUMTREE_MULTIPLE) { + IGRAPH_ERROR("Invalid algorithm", IGRAPH_EINVAL); + } + if (n < 0) { + IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); + } else if (newn < 0) { + IGRAPH_ERROR("Starting graph has too many vertices.", IGRAPH_EINVAL); + } + if (start_from && start_nodes == 0) { + IGRAPH_ERROR("Cannot start from an empty graph.", IGRAPH_EINVAL); + } + if (outseq != 0 && igraph_vector_size(outseq) != 0 && + igraph_vector_size(outseq) != newn) { + IGRAPH_ERROR("Invalid out-degree sequence length.", IGRAPH_EINVAL); + } + if ( (outseq == 0 || igraph_vector_size(outseq) == 0) && m < 0) { + IGRAPH_ERROR("Number of edges added per step must not be negative.", IGRAPH_EINVAL); + } + if (outseq && igraph_vector_min(outseq) < 0) { + IGRAPH_ERROR("Negative out-degree in sequence.", IGRAPH_EINVAL); + } + if (!outpref && A <= 0) { + IGRAPH_ERROR("Constant attractiveness (A) must be positive.", + IGRAPH_EINVAL); + } + if (outpref && A < 0) { + IGRAPH_ERROR("Constant attractiveness (A) must be non-negative.", + IGRAPH_EINVAL); + } + if (algo == IGRAPH_BARABASI_BAG) { + if (power != 1) { + IGRAPH_ERROR("Power must be one for 'bag' algorithm.", IGRAPH_EINVAL); + } + if (A != 1) { + IGRAPH_ERROR("Constant attractiveness (A) must be one for bag algorithm.", + IGRAPH_EINVAL); + } + } + if (start_from && directed != igraph_is_directed(start_from)) { + IGRAPH_WARNING("Directedness of the start graph and the output graph mismatch."); + } + if (start_from && !igraph_is_directed(start_from) && !outpref) { + IGRAPH_ERROR("`outpref' must be true if starting from an undirected graph.", + IGRAPH_EINVAL); + } + + if (n == 0) { + return igraph_empty(graph, 0, directed); + } + + if (algo == IGRAPH_BARABASI_BAG) { + return igraph_i_barabasi_game_bag(graph, n, m, outseq, outpref, directed, + start_from); + } else if (algo == IGRAPH_BARABASI_PSUMTREE) { + return igraph_i_barabasi_game_psumtree(graph, n, power, m, outseq, + outpref, A, directed, start_from); + } else if (algo == IGRAPH_BARABASI_PSUMTREE_MULTIPLE) { + return igraph_i_barabasi_game_psumtree_multiple(graph, n, power, m, + outseq, outpref, A, + directed, start_from); + } + + return 0; +} + +/** + * \function igraph_barabasi_aging_game + * \brief Preferential attachment with aging of vertices. + * + * + * This game starts with one vertex (if \p nodes > 0). In each step + * a new node is added, and it is connected to \p m existing nodes. + * Existing nodes to connect to are chosen with probability dependent + * on their (in-)degree (\c k) and age (\c l). + * The degree-dependent part is + * deg_coef * k^pa_exp + zero_deg_appeal, + * while the age-dependent part is + * age_coef * l^aging_exp + zero_age_appeal, + * which are summed to obtain the final weight. + * + * + * The age \c l is based on the number of vertices in the + * network and the \p aging_bins argument: the age of a node + * is incremented by 1 after each + * floor(nodes / aging_bins) + 1 + * time steps. + * + * \param graph Pointer to an uninitialized graph object. + * \param nodes The number of vertices in the graph. + * \param m The number of edges to add in each time step. + * Ignored if \p outseq is a non-zero length vector. + * \param outseq The number of edges to add in each time step. If it + * is \c NULL or a zero-length vector then it is ignored + * and the \p m argument is used instead. + * \param outpref Logical constant, whether the edges + * initiated by a vertex contribute to the probability to gain + * a new edge. + * \param pa_exp The exponent of the preferential attachment, a small + * positive number usually, the value 1 yields the classic + * linear preferential attachment. + * \param aging_exp The exponent of the aging, this is a negative + * number usually. + * \param aging_bins Integer constant, the number of age bins to use. + * \param zero_deg_appeal The degree dependent part of the + * attractiveness of the zero degree vertices. + * \param zero_age_appeal The age dependent part of the attractiveness + * of the vertices of age zero. This parameter is usually zero. + * \param deg_coef The coefficient for the degree. + * \param age_coef The coefficient for the age. + * \param directed Logical constant, whether to generate a directed + * graph. + * \return Error code. + * + * Time complexity: O((|V|+|V|/aging_bins)*log(|V|)+|E|). |V| is the number + * of vertices, |E| the number of edges. + */ +int igraph_barabasi_aging_game(igraph_t *graph, + igraph_integer_t nodes, + igraph_integer_t m, + const igraph_vector_t *outseq, + igraph_bool_t outpref, + igraph_real_t pa_exp, + igraph_real_t aging_exp, + igraph_integer_t aging_bins, + igraph_real_t zero_deg_appeal, + igraph_real_t zero_age_appeal, + igraph_real_t deg_coef, + igraph_real_t age_coef, + igraph_bool_t directed) { + long int no_of_nodes = nodes; + long int no_of_neighbors = m; + long int binwidth; + long int no_of_edges; + igraph_vector_t edges; + long int i, j, k; + igraph_psumtree_t sumtree; + long int edgeptr = 0; + igraph_vector_t degree; + + if (no_of_nodes < 0) { + IGRAPH_ERRORF("Number of nodes must not be negative, got %ld.", IGRAPH_EINVAL, no_of_nodes); + } + if (outseq != 0 && igraph_vector_size(outseq) != 0 && igraph_vector_size(outseq) != no_of_nodes) { + IGRAPH_ERRORF("The length of the out-degree sequence (%ld) does not agree with the number of nodes (%ld).", + IGRAPH_EINVAL, + igraph_vector_size(outseq), no_of_nodes); + } + if ( (outseq == 0 || igraph_vector_size(outseq) == 0) && m < 0) { + IGRAPH_ERRORF("The number of edges per time step must not be negative, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, + m); + } + if (aging_bins <= 0) { + IGRAPH_ERRORF("Number of aging bins must be positive, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, + aging_bins); + } + if (deg_coef < 0) { + IGRAPH_ERRORF("Degree coefficient must be non-negative, got %g.", + IGRAPH_EINVAL, + deg_coef); + } + if (age_coef < 0) { + IGRAPH_ERRORF("Age coefficient must be non-negative, got %g.", + IGRAPH_EINVAL, + deg_coef); + } + + if (zero_deg_appeal < 0) { + IGRAPH_ERRORF("Zero degree appeal must be non-negative, got %g.", + IGRAPH_EINVAL, + zero_deg_appeal); + } + if (zero_age_appeal < 0) { + IGRAPH_ERRORF("Zero age appeal must be non-negative, got %g.", + IGRAPH_EINVAL, + zero_age_appeal); + } + + if (no_of_nodes == 0) { + return igraph_empty(graph, 0, directed); + } + + binwidth = no_of_nodes / aging_bins + 1; + + if (outseq == 0 || igraph_vector_size(outseq) == 0) { + no_of_neighbors = m; + no_of_edges = (no_of_nodes - 1) * no_of_neighbors; + } else { + no_of_edges = 0; + for (i = 1; i < igraph_vector_size(outseq); i++) { + no_of_edges += VECTOR(*outseq)[i]; + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes)); + IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + + RNG_BEGIN(); + + /* first node */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, zero_deg_appeal * (1 + zero_age_appeal))); + + /* and the rest */ + for (i = 1; i < no_of_nodes; i++) { + igraph_real_t sum; + long int to; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (outseq != 0 && igraph_vector_size(outseq) != 0) { + no_of_neighbors = (long int) VECTOR(*outseq)[i]; + } + sum = igraph_psumtree_sum(&sumtree); + for (j = 0; j < no_of_neighbors; j++) { + igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum)); + VECTOR(degree)[to]++; + VECTOR(edges)[edgeptr++] = i; + VECTOR(edges)[edgeptr++] = to; + } + /* update probabilities */ + for (j = 0; j < no_of_neighbors; j++) { + long int n = (long int) VECTOR(edges)[edgeptr - 2 * j - 1]; + long int age = (i - n) / binwidth; + IGRAPH_CHECK(igraph_psumtree_update( + &sumtree, n, + (deg_coef * pow(VECTOR(degree)[n], pa_exp) + zero_deg_appeal) * + (age_coef * pow(age + 1, aging_exp) + zero_age_appeal) + )); + } + if (outpref) { + VECTOR(degree)[i] += no_of_neighbors; + IGRAPH_CHECK(igraph_psumtree_update( + &sumtree, i, + (zero_age_appeal + 1) * (deg_coef * pow(VECTOR(degree)[i], pa_exp) + zero_deg_appeal) + )); + } else { + IGRAPH_CHECK(igraph_psumtree_update( + &sumtree, i, (1 + zero_age_appeal) * zero_deg_appeal + )); + } + + /* aging */ + for (k = 1; binwidth * k <= i; k++) { + long int shnode = i - binwidth * k; + long int deg = (long int) VECTOR(degree)[shnode]; + long int age = (i - shnode) / binwidth; + /* igraph_real_t old=igraph_psumtree_get(&sumtree, shnode); */ + IGRAPH_CHECK(igraph_psumtree_update( + &sumtree, shnode, + (deg_coef * pow(deg, pa_exp) + zero_deg_appeal) * + (age_coef * pow(age + 2, aging_exp) + zero_age_appeal) + )); + } + } + + RNG_END(); + + igraph_vector_destroy(°ree); + igraph_psumtree_destroy(&sumtree); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/callaway_traits.c b/src/rigraph/core/games/callaway_traits.c new file mode 100644 index 0000000..3bdb897 --- /dev/null +++ b/src/rigraph/core/games/callaway_traits.c @@ -0,0 +1,200 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_memory.h" +#include "igraph_random.h" + +/** + * \function igraph_callaway_traits_game + * \brief Simulates a growing network with vertex types. + * + * + * The different types of vertices prefer to connect other types of + * vertices with a given probability. + * + * + * The simulation goes like this: in each discrete time step a new + * vertex is added to the graph. The type of this vertex is generated + * based on \p type_dist. Then two vertices are selected uniformly + * randomly from the graph. The probability that they will be + * connected depends on the types of these vertices and is taken from + * \p pref_matrix. Then another two vertices are selected and this is + * repeated \p edges_per_step times in each time step. + * + * + * References: + * + * + * D. S. Callaway, J. E. Hopcroft, J. M. Kleinberg, M. E. J. Newman, and S. H. Strogatz, + * Are randomly grown graphs really random? + * Phys. Rev. E 64, 041902 (2001). + * https://doi.org/10.1103/PhysRevE.64.041902 + * + * \param graph Pointer to an uninitialized graph. + * \param nodes The number of nodes in the graph. + * \param types Number of node types. + * \param edges_per_step The number of connections tried in each time step. + * \param type_dist Vector giving the distribution of the vertex types. + * If \c NULL, the distribution is assumed to be uniform. + * \param pref_matrix Matrix giving the connection probabilities for + * the vertex types. + * \param directed Logical, whether to generate a directed graph. + * \param node_type_vec An initialized vector or \c NULL. + * If not \c NULL, the type of each node will be stored here. + * \return Error code. + * + * Added in version 0.2. + * + * Time complexity: O(|V|*k*log(|V|)), |V| is the number of vertices, + * k is \p edges_per_step. + */ +int igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nodes, + igraph_integer_t types, igraph_integer_t edges_per_step, + const igraph_vector_t *type_dist, + const igraph_matrix_t *pref_matrix, + igraph_bool_t directed, + igraph_vector_t *node_type_vec) { + long int i, j; + igraph_vector_t edges; + igraph_vector_t cumdist; + igraph_real_t maxcum; + igraph_vector_t *nodetypes; + + /* Argument contracts */ + if(nodes < 0){ + IGRAPH_ERROR("The number of vertices must be non-negative.", IGRAPH_EINVAL); + } + + if (types < 1) { + IGRAPH_ERROR("The number of vertex types must be at least 1.", IGRAPH_EINVAL); + } + + if (type_dist) { + igraph_real_t lo; + + if (igraph_vector_size(type_dist) != types) { + IGRAPH_ERROR("The vertex type distribution vector must agree in length with the number of types.", + IGRAPH_EINVAL); + } + + lo = igraph_vector_min(type_dist); + if (lo < 0) { + IGRAPH_ERROR("The vertex type distribution vector must not contain negative values.", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo)) { + IGRAPH_ERROR("The vertex type distribution vector must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (igraph_matrix_nrow(pref_matrix) != types || igraph_matrix_ncol(pref_matrix) != types) { + IGRAPH_ERROR("The preference matrix must be square and agree in dimensions with the number of types.", IGRAPH_EINVAL); + } + + { + igraph_real_t lo, hi; + igraph_matrix_minmax(pref_matrix, &lo, &hi); + + if (lo < 0 || hi > 1) { + IGRAPH_ERROR("The preference matrix must contain probabilities in [0, 1].", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo) || igraph_is_nan(hi)) { + IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (! directed && ! igraph_matrix_is_symmetric(pref_matrix)) { + IGRAPH_ERROR("The preference matrix must be symmetric when generating undirected graphs.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types + 1); + + if (type_dist) { + VECTOR(cumdist)[0] = 0; + for (i = 0; i < types; ++i) { + VECTOR(cumdist)[i + 1] = VECTOR(cumdist)[i] + VECTOR(*type_dist)[i]; + } + } else { + for (i = 0; i < types+1; ++i) { + VECTOR(cumdist)[i] = i; + } + } + maxcum = igraph_vector_tail(&cumdist); + + if (maxcum <= 0) { + IGRAPH_ERROR("The vertex type distribution vector must contain at least one positive value.", IGRAPH_EINVAL); + } + + if (node_type_vec) { + nodetypes = node_type_vec; + IGRAPH_CHECK(igraph_vector_resize(nodetypes, nodes)); + } else { + nodetypes = IGRAPH_CALLOC(1, igraph_vector_t); + if (! nodetypes) { + IGRAPH_ERROR("Insufficient memory for callaway_traits_game.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, nodetypes); + IGRAPH_VECTOR_INIT_FINALLY(nodetypes, nodes); + } + + RNG_BEGIN(); + + for (i = 0; i < nodes; i++) { + igraph_real_t uni = RNG_UNIF(0, maxcum); + long int type; + igraph_vector_binsearch(&cumdist, uni, &type); + VECTOR(*nodetypes)[i] = type - 1; + } + + for (i = 1; i < nodes; i++) { + for (j = 0; j < edges_per_step; j++) { + long int node1 = RNG_INTEGER(0, i); + long int node2 = RNG_INTEGER(0, i); + long int type1 = (long int) VECTOR(*nodetypes)[node1]; + long int type2 = (long int) VECTOR(*nodetypes)[node2]; + /* printf("unif: %f, %f, types: %li, %li\n", uni1, uni2, type1, type2); */ + if (RNG_UNIF01() < MATRIX(*pref_matrix, type1, type2)) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, node1)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, node2)); + } + } + } + + RNG_END(); + + if (! node_type_vec) { + igraph_vector_destroy(nodetypes); + IGRAPH_FREE(nodetypes); + IGRAPH_FINALLY_CLEAN(2); + } + igraph_vector_destroy(&cumdist); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/citations.c b/src/rigraph/core/games/citations.c new file mode 100644 index 0000000..6fab65e --- /dev/null +++ b/src/rigraph/core/games/citations.c @@ -0,0 +1,480 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_memory.h" +#include "igraph_psumtree.h" +#include "igraph_random.h" +#include "igraph_interface.h" + +typedef struct { + long int no; + igraph_psumtree_t *sumtrees; +} igraph_i_citing_cited_type_game_struct_t; + +static void igraph_i_citing_cited_type_game_free ( + igraph_i_citing_cited_type_game_struct_t *s); + +/** + * \function igraph_lastcit_game + * \brief Simulates a citation network, based on time passed since the last citation. + * + * This is a quite special stochastic graph generator, it models an + * evolving graph. In each time step a single vertex is added to the + * network and it cites a number of other vertices (as specified by + * the \p edges_per_step argument). The cited vertices are selected + * based on the last time they were cited. Time is measured by the + * addition of vertices and it is binned into \p agebins bins. + * So if the current time step is \c t and the last citation to a + * given \c i vertex was made in time step \c t0, then \c + * (t-t0)/binwidth is calculated where binwidth is \c nodes/agebins+1, + * in the last expression '/' denotes integer division, so the + * fraction part is omitted. + * + * + * The \p preference argument specifies the preferences for the + * citation lags, i.e. its first elements contains the attractivity + * of the very recently cited vertices, etc. The last element is + * special, it contains the attractivity of the vertices which were + * never cited. This element should be bigger than zero. + * + * + * Note that this function generates networks with multiple edges if + * \p edges_per_step is bigger than one, call \ref igraph_simplify() + * on the result to get rid of these edges. + * \param graph Pointer to an uninitialized graph object, the result + * will be stored here. + * \param node The number of vertices in the network. + * \param edges_per_node The number of edges to add in each time + * step. + * \param agebins The number of age bins to use. + * \param preference Pointer to an initialized vector of length + * \c agebins+1. This contains the `attractivity' of the various + * age bins, the last element is the attractivity of the vertices + * which were never cited, and it should be greater than zero. + * It is a good idea to have all positive values in this vector. + * Preferences cannot be negative. + * \param directed Logical constant, whether to create directed + * networks. + * \return Error code. + * + * \sa \ref igraph_barabasi_aging_game(). + * + * Time complexity: O(|V|*a+|E|*log|V|), |V| is the number of vertices, + * |E| is the total number of edges, a is the \p agebins parameter. + */ +int igraph_lastcit_game(igraph_t *graph, + igraph_integer_t nodes, igraph_integer_t edges_per_node, + igraph_integer_t agebins, + const igraph_vector_t *preference, + igraph_bool_t directed) { + + long int no_of_nodes = nodes; + igraph_psumtree_t sumtree; + igraph_vector_t edges; + long int i, j, k; + long int *lastcit; + long int *index; + long int binwidth; + + if (agebins != igraph_vector_size(preference) - 1) { + IGRAPH_ERRORF("The `preference' vector should be of length `agebins' plus one." + "Number of agebins is %"IGRAPH_PRId", preference vector is of length %ld.", + IGRAPH_EINVAL, + agebins, igraph_vector_size(preference)); + } + if (nodes < 0 ) { + IGRAPH_ERRORF("Number of nodes should be non-negative, received %"IGRAPH_PRId".", + IGRAPH_EINVAL, + nodes); + } + if (agebins < 1 ) { + IGRAPH_ERRORF("Number of age bins should be at least 1, received %"IGRAPH_PRId".", + IGRAPH_EINVAL, + agebins); + } + if (VECTOR(*preference)[agebins] <= 0) { + IGRAPH_ERRORF("The last element of the `preference' vector needs to be positive, but is %g.", + IGRAPH_EINVAL, + VECTOR(*preference)[agebins]); + } + if (igraph_vector_min(preference) < 0) { + IGRAPH_ERRORF("The preference vector must contain only non-negative values, but found %g.", + IGRAPH_EINVAL, + igraph_vector_min(preference)); + } + + if (nodes == 0) { + IGRAPH_CHECK(igraph_empty(graph, nodes, directed)); + return IGRAPH_SUCCESS; + } + + binwidth = no_of_nodes / agebins + 1; + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + lastcit = IGRAPH_CALLOC(no_of_nodes, long int); + if (!lastcit) { + IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, lastcit); + + index = IGRAPH_CALLOC(no_of_nodes + 1, long int); + if (!index) { + IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, index); + + IGRAPH_CHECK(igraph_psumtree_init(&sumtree, nodes)); + IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree); + IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes * edges_per_node)); + + /* The first node */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, VECTOR(*preference)[agebins])); + index[0] = 0; + index[1] = 0; + + RNG_BEGIN(); + + for (i = 1; i < no_of_nodes; i++) { + + /* Add new edges */ + for (j = 0; j < edges_per_node; j++) { + long int to; + igraph_real_t sum = igraph_psumtree_sum(&sumtree); + igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum)); + igraph_vector_push_back(&edges, i); + igraph_vector_push_back(&edges, to); + lastcit[to] = i + 1; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, to, VECTOR(*preference)[0])); + } + + /* Add the node itself */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, VECTOR(*preference)[agebins])); + index[i + 1] = index[i] + edges_per_node; + + /* Update the preference of some vertices if they got to another bin. + We need to know the citations of some older vertices, this is in the index. */ + for (k = 1; i - binwidth * k >= 1; k++) { + long int shnode = i - binwidth * k; + long int m = index[shnode], n = index[shnode + 1]; + for (j = 2 * m; j < 2 * n; j += 2) { + long int cnode = (long int) VECTOR(edges)[j + 1]; + if (lastcit[cnode] == shnode + 1) { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, cnode, VECTOR(*preference)[k])); + } + } + } + + } + + RNG_END(); + + igraph_psumtree_destroy(&sumtree); + igraph_free(index); + igraph_free(lastcit); + IGRAPH_FINALLY_CLEAN(3); + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_cited_type_game + * \brief Simulates a citation based on vertex types. + * + * Function to create a network based on some vertex categories. This + * function creates a citation network: in each step a single vertex + * and \p edges_per_step citing edges are added. Nodes with + * different categories may have different probabilities to get + * cited, as given by the \p pref vector. + * + * + * Note that this function might generate networks with multiple edges + * if \p edges_per_step is greater than one. You might want to call + * \ref igraph_simplify() on the result to remove multiple edges. + * \param graph Pointer to an uninitialized graph object. + * \param nodes The number of vertices in the network. + * \param types Numeric vector giving the categories of the vertices, + * so it should contain \p nodes non-negative integer + * numbers. Types are numbered from zero. + * \param pref The attractivity of the different vertex categories in + * a vector. Its length should be the maximum element in \p types + * plus one (types are numbered from zero). + * \param edges_per_step Integer constant, the number of edges to add + * in each time step. + * \param directed Logical constant, whether to create a directed + * network. + * \return Error code. + * + * \sa \ref igraph_citing_cited_type_game() for a bit more general + * game. + * + * Time complexity: O((|V|+|E|)log|V|), |V| and |E| are number of + * vertices and edges, respectively. + */ + +int igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, + const igraph_vector_t *types, + const igraph_vector_t *pref, + igraph_integer_t edges_per_step, + igraph_bool_t directed) { + + igraph_vector_t edges; + igraph_vector_t cumsum; + igraph_real_t sum, nnval; + long int i, j, type; + long int pref_len = igraph_vector_size(pref); + + if (igraph_vector_size(types) != nodes) { + IGRAPH_ERRORF("Length of types vector (%ld) must match number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, (long) igraph_vector_size(types), nodes); + } + + if (nodes == 0) { + igraph_empty(graph, 0, directed); + return IGRAPH_SUCCESS; + } + + /* the case of zero-length type vector is caught above, safe to call vector_min here */ + if (igraph_vector_min(types) < 0) { + IGRAPH_ERRORF("Types should be non-negative, but found %g.", + IGRAPH_EINVAL, igraph_vector_min(types)); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + IGRAPH_VECTOR_INIT_FINALLY(&cumsum, 2); + IGRAPH_CHECK(igraph_vector_reserve(&cumsum, nodes + 1)); + IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes * edges_per_step)); + + /* first node */ + VECTOR(cumsum)[0] = 0; + type = (long int) VECTOR(*types)[0]; + if (type >= pref_len) { + goto err_pref_too_short; + } + nnval = VECTOR(*pref)[type]; + if (nnval < 0) { + goto err_pref_neg; + } + sum = VECTOR(cumsum)[1] = nnval; + + RNG_BEGIN(); + + for (i = 1; i < nodes; i++) { + for (j = 0; j < edges_per_step; j++) { + long int to; + if (sum > 0) { + igraph_vector_binsearch(&cumsum, RNG_UNIF(0, sum), &to); + } else { + to = i + 1; + } + igraph_vector_push_back(&edges, i); + igraph_vector_push_back(&edges, to - 1); + } + type = (long int) VECTOR(*types)[i]; + if (type >= pref_len) { + goto err_pref_too_short; + } + nnval = VECTOR(*pref)[type]; + if (nnval < 0) { + goto err_pref_neg; + } + sum += nnval; + igraph_vector_push_back(&cumsum, sum); + } + + RNG_END(); + + igraph_vector_destroy(&cumsum); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; + +err_pref_too_short: + IGRAPH_ERRORF("Preference vector should have length at least %ld with the given types.", IGRAPH_EINVAL, + (long) igraph_vector_max(types) + 1); + +err_pref_neg: + IGRAPH_ERRORF("Preferences should be non-negative, but found %g.", IGRAPH_EINVAL, + igraph_vector_min(pref)); +} + +static void igraph_i_citing_cited_type_game_free(igraph_i_citing_cited_type_game_struct_t *s) { + long int i; + if (!s->sumtrees) { + return; + } + for (i = 0; i < s->no; i++) { + igraph_psumtree_destroy(&s->sumtrees[i]); + } + igraph_free(s->sumtrees); +} + +/** + * \function igraph_citing_cited_type_game + * \brief Simulates a citation network based on vertex types. + * + * This game is similar to \ref igraph_cited_type_game() but here the + * category of the citing vertex is also considered. + * + * + * An evolving citation network is modeled here, a single vertex and + * its \p edges_per_step citation are added in each time step. The + * odds the a given vertex is cited by the new vertex depends on the + * category of both the citing and the cited vertex and is given in + * the \p pref matrix. The categories of the citing vertex correspond + * to the rows, the categories of the cited vertex to the columns of + * this matrix. I.e. the element in row \c i and column \c j gives the + * probability that a \c j vertex is cited, if the category of the + * citing vertex is \c i. + * + * + * Note that this function might generate networks with multiple edges + * if \p edges_per_step is greater than one. You might want to call + * \ref igraph_simplify() on the result to remove multiple edges. + * \param graph Pointer to an uninitialized graph object. + * \param nodes The number of vertices in the network. + * \param types A numeric matrix of length \p nodes, containing the + * categories of the vertices. The categories are numbered from + * zero. + * \param pref The preference matrix, a square matrix is required, + * both the number of rows and columns should be the maximum + * element in \p types plus one (types are numbered from zero). + * \param directed Logical constant, whether to create a directed + * network. + * \return Error code. + * + * Time complexity: O((|V|+|E|)log|V|), |V| and |E| are number of + * vertices and edges, respectively. + */ + +int igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, + const igraph_vector_t *types, + const igraph_matrix_t *pref, + igraph_integer_t edges_per_step, + igraph_bool_t directed) { + + igraph_vector_t edges; + igraph_i_citing_cited_type_game_struct_t str = { 0, NULL }; + igraph_psumtree_t *sumtrees; + igraph_vector_t sums; + long int no_of_types; + long int i, j; + + if (igraph_vector_size(types) != nodes) { + IGRAPH_ERRORF("Length of types vector (%ld) not equal to number" + " of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_size(types), nodes); + } + + /* avoid calling vector_max on empty vector */ + no_of_types = nodes == 0 ? 0 : igraph_vector_max(types) + 1; + + if (igraph_matrix_ncol(pref) != no_of_types) { + IGRAPH_ERRORF("Number of preference matrix columns (%ld) not " + "equal to number of types (%ld).", + IGRAPH_EINVAL, + igraph_matrix_ncol(pref), + no_of_types); + } + if (igraph_matrix_nrow(pref) != no_of_types) { + IGRAPH_ERRORF("Number of preference matrix rows (%ld) not " + "equal to number of types (%ld).", + IGRAPH_EINVAL, + igraph_matrix_nrow(pref), + no_of_types); + } + + /* return an empty graph if nodes is zero */ + if (nodes == 0) { + return igraph_empty(graph, 0, directed); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + str.sumtrees = sumtrees = IGRAPH_CALLOC(no_of_types, igraph_psumtree_t); + if (!sumtrees) { + IGRAPH_ERROR("Citing-cited type game failed.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_i_citing_cited_type_game_free, &str); + + for (i = 0; i < no_of_types; i++) { + IGRAPH_CHECK(igraph_psumtree_init(&sumtrees[i], nodes)); + str.no++; + } + IGRAPH_VECTOR_INIT_FINALLY(&sums, no_of_types); + + IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes * edges_per_step)); + + /* First node */ + for (i = 0; i < no_of_types; i++) { + long int type = (long int) VECTOR(*types)[0]; + if ( MATRIX(*pref, i, type) < 0) { + IGRAPH_ERRORF("Preference matrix contains negative entry: %g.", IGRAPH_EINVAL, MATRIX(*pref, i, type)); + } + IGRAPH_CHECK(igraph_psumtree_update(&sumtrees[i], 0, MATRIX(*pref, i, type))); + VECTOR(sums)[i] = MATRIX(*pref, i, type); + } + + RNG_BEGIN(); + + for (i = 1; i < nodes; i++) { + long int type = (long int) VECTOR(*types)[i]; + igraph_real_t sum = VECTOR(sums)[type]; + for (j = 0; j < edges_per_step; j++) { + long int to; + igraph_psumtree_search(&sumtrees[type], &to, RNG_UNIF(0, sum)); + igraph_vector_push_back(&edges, i); + igraph_vector_push_back(&edges, to); + } + + /* add i */ + for (j = 0; j < no_of_types; j++) { + if ( MATRIX(*pref, j, type) < 0) { + IGRAPH_ERRORF("Preference matrix contains negative entry: %g.", IGRAPH_EINVAL, MATRIX(*pref, j, type)); + } + IGRAPH_CHECK(igraph_psumtree_update(&sumtrees[j], i, MATRIX(*pref, j, type))); + VECTOR(sums)[j] += MATRIX(*pref, j, type); + } + } + + RNG_END(); + + igraph_i_citing_cited_type_game_free(&str); + IGRAPH_FINALLY_CLEAN(1); + + igraph_create(graph, &edges, nodes, directed); + igraph_vector_destroy(&edges); + igraph_vector_destroy(&sums); + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/correlated.c b/src/rigraph/core/games/correlated.c new file mode 100644 index 0000000..7eb54e2 --- /dev/null +++ b/src/rigraph/core/games/correlated.c @@ -0,0 +1,326 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_conversion.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_qsort.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +#include "../core/interruption.h" + +/* The "code" of an edge is a single index representing its location in the adjacency matrix, + * More specifically, the relevant parts of the adjacency matrix (i.e. non-diagonal in directed, + * upper triangular in undirecred) are column-wise concatenated into an array. The "code" is + * the index in this array. We use floating point numbers for the code, as it can easily + * exceed integers representable on 32 bits. + */ +#define D_CODE(f,t) (((t)==no_of_nodes-1 ? (f) : (t)) * no_of_nodes + (f)) +#define U_CODE(f,t) ((t) * ((t)-1) / 2 + (f)) +#define CODE(f,t) (directed ? D_CODE((double)(f),(double)(t)) : U_CODE((double)(f),(double)(t))) + +/* TODO: Slight speedup may be possible if repeated vertex count queries are avoided. */ +static int code_cmp(void *graph, const void *va, const void *vb) { + const igraph_real_t *a = (const igraph_real_t *) va; + const igraph_real_t *b = (const igraph_real_t *) vb; + const long int no_of_nodes = igraph_vcount((igraph_t *) graph); + const igraph_bool_t directed = igraph_is_directed((igraph_t *) graph); + const igraph_real_t codea = CODE(a[0], a[1]); + const igraph_real_t codeb = CODE(b[0], b[1]); + if (codea < codeb) { + return -1; + } else if (codea > codeb) { + return 1; + } else { + return 0; + } +} + +/* Sort an edge vector by edge codes. */ +static void sort_edges(igraph_vector_t *edges, const igraph_t *graph) { + igraph_qsort_r(VECTOR(*edges), igraph_vector_size(edges) / 2, 2*sizeof(igraph_real_t), (void *) graph, code_cmp); +} + +/** + * \function igraph_correlated_game + * \brief Generates a random graph correlated to an existing graph. + * + * Sample a new graph by perturbing the adjacency matrix of a + * given simple graph and shuffling its vertices. + * + * \param old_graph The original graph, it must be simple. + * \param new_graph The new graph will be stored here. + * \param corr A scalar in the unit interval [0,1], the target Pearson + * correlation between the adjacency matrices of the original and the + * generated graph (the adjacency matrix being used as a vector). + * \param p A numeric scalar, the probability of an edge between two + * vertices, it must in the open (0,1) interval. Typically, + * the density of \p old_graph. + * \param permutation A permutation to apply to the vertices of the + * generated graph. It can also be a null pointer, in which case + * the vertices will not be permuted. + * \return Error code + * + * \sa \ref igraph_correlated_pair_game() for generating a pair + * of correlated random graphs in one go. + */ +int igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph, + igraph_real_t corr, igraph_real_t p, + const igraph_vector_t *permutation) { + + long int no_of_nodes = igraph_vcount(old_graph); + long int no_of_edges = igraph_ecount(old_graph); + igraph_bool_t directed = igraph_is_directed(old_graph); + igraph_real_t no_of_all = directed ? no_of_nodes * (no_of_nodes - 1) : + no_of_nodes * (no_of_nodes - 1) / 2; + igraph_real_t no_of_missing = no_of_all - no_of_edges; + igraph_real_t q = p + corr * (1 - p); + igraph_real_t p_del = 1 - q; + igraph_real_t p_add = ((1 - q) * (p / (1 - p))); + igraph_vector_t add, delete, edges, newedges; + igraph_real_t last; + long int p_e = 0, p_a = 0, p_d = 0, no_add, no_del; + igraph_real_t inf = IGRAPH_INFINITY; + igraph_real_t next_e, next_a, next_d; + long int i; + igraph_bool_t simple; + + if (corr < 0 || corr > 1) { + IGRAPH_ERRORF("Correlation must be in [0,1] in correlated Erdos-Renyi game, got %g.", + IGRAPH_EINVAL, corr); + } + if (p <= 0 || p >= 1) { + IGRAPH_ERRORF("Edge probability must be in (0,1) in correlated Erdos-Renyi game, got %g.", + IGRAPH_EINVAL, p); + } + if (permutation) { + if (igraph_vector_size(permutation) != no_of_nodes) { + IGRAPH_ERROR("Invalid permutation length in correlated Erdos-Renyi game.", + IGRAPH_EINVAL); + } + } + IGRAPH_CHECK(igraph_is_simple(old_graph, &simple)); + if (! simple) { + IGRAPH_ERROR("The original graph must be simple for correlated Erdos-Renyi game.", + IGRAPH_EINVAL); + } + + /* Special cases */ + + if (corr == 0) { + return igraph_erdos_renyi_game(new_graph, IGRAPH_ERDOS_RENYI_GNP, + no_of_nodes, p, directed, + IGRAPH_NO_LOOPS); + } + if (corr == 1) { + /* We don't copy, because we don't need the attributes.... */ + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_CHECK(igraph_get_edgelist(old_graph, &edges, /* bycol= */ 0)); + if (permutation) { + int newec = igraph_vector_size(&edges); + for (i = 0; i < newec; i++) { + int tmp = VECTOR(edges)[i]; + VECTOR(edges)[i] = VECTOR(*permutation)[tmp]; + } + } + IGRAPH_CHECK(igraph_create(new_graph, &edges, no_of_nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return 0; + } + + IGRAPH_VECTOR_INIT_FINALLY(&newedges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&add, 0); + IGRAPH_VECTOR_INIT_FINALLY(&delete, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + + IGRAPH_CHECK(igraph_get_edgelist(old_graph, &edges, /* bycol= */ 0)); + /* The samping method used is analogous to the one in igraph_erdos_renyi_game_gnp(), + * and assumes that the edge list of the old graph is in order of increasing "codes". + * Even IGRAPH_EDGEORDER_TO does not guarantee this, therefore we sort explicitly. + */ + sort_edges(&edges, old_graph); + + RNG_BEGIN(); + + if (p_del > 0) { + last = RNG_GEOM(p_del); + while (last < no_of_edges) { + IGRAPH_CHECK(igraph_vector_push_back(&delete, last)); + last += RNG_GEOM(p_del); + last += 1; + } + } + no_del = igraph_vector_size(&delete); + + if (p_add > 0) { + last = RNG_GEOM(p_add); + while (last < no_of_missing) { + IGRAPH_CHECK(igraph_vector_push_back(&add, last)); + last += RNG_GEOM(p_add); + last += 1; + } + } + no_add = igraph_vector_size(&add); + + RNG_END(); + + /* Now we are merging the original edges, the edges that are removed, + and the new edges. We have the following pointers: + - p_a: the next edge to add + - p_d: the next edge to delete + - p_e: the next original edge + - next_e: the code of the next edge in 'edges' + - next_a: the code of the next edge to add + - next_d: the code of the next edge to delete */ + +#define CODEE() (CODE(VECTOR(edges)[2*p_e], VECTOR(edges)[2*p_e+1])) + + /* First we (re)code the edges to delete */ + + for (i = 0; i < no_del; i++) { + int td = VECTOR(delete)[i]; + int from = VECTOR(edges)[2 * td]; + int to = VECTOR(edges)[2 * td + 1]; + VECTOR(delete)[i] = CODE(from, to); + } + + IGRAPH_CHECK(igraph_vector_reserve(&newedges, + (no_of_edges - no_del + no_add) * 2)); + + /* Now we can do the merge. Additional edges are tricky, because + the code must be shifted by the edges in the original graph. */ + +#define UPD_E() \ + { if (p_e < no_of_edges) { next_e=CODEE(); } else { next_e = inf; } } +#define UPD_A() \ + { if (p_a < no_add) { \ + next_a = VECTOR(add)[p_a] + p_e; } else { next_a = inf; } } +#define UPD_D() \ + { if (p_d < no_del) { \ + next_d = VECTOR(delete)[p_d]; } else { next_d = inf; } } + + UPD_E(); UPD_A(); UPD_D(); + + while (next_e != inf || next_a != inf || next_d != inf) { + IGRAPH_ALLOW_INTERRUPTION(); + if (next_e <= next_a && next_e < next_d) { + + /* keep an edge */ + IGRAPH_CHECK(igraph_vector_push_back(&newedges, VECTOR(edges)[2 * p_e])); + IGRAPH_CHECK(igraph_vector_push_back(&newedges, VECTOR(edges)[2 * p_e + 1])); + p_e ++; UPD_E(); UPD_A() + + } else if (next_e <= next_a && next_e == next_d) { + + /* delete an edge */ + p_e ++; UPD_E(); UPD_A(); + p_d++; UPD_D(); + + } else { + + /* add an edge */ + long int to, from; + IGRAPH_ASSERT(igraph_finite(next_a)); + if (directed) { + to = (long int) floor(next_a / no_of_nodes); + from = (long int) (next_a - ((igraph_real_t)to) * no_of_nodes); + if (from == to) { + to = no_of_nodes - 1; + } + } else { + to = (long int) floor((sqrt(8 * next_a + 1) + 1) / 2); + from = (long int) (next_a - (((igraph_real_t)to) * (to - 1)) / 2); + } + IGRAPH_CHECK(igraph_vector_push_back(&newedges, from)); + IGRAPH_CHECK(igraph_vector_push_back(&newedges, to)); + p_a++; UPD_A(); + + } + } + + igraph_vector_destroy(&edges); + igraph_vector_destroy(&add); + igraph_vector_destroy(&delete); + IGRAPH_FINALLY_CLEAN(3); + + if (permutation) { + long int newec = igraph_vector_size(&newedges); + for (i = 0; i < newec; i++) { + long int tmp = VECTOR(newedges)[i]; + VECTOR(newedges)[i] = VECTOR(*permutation)[tmp]; + } + } + + IGRAPH_CHECK(igraph_create(new_graph, &newedges, no_of_nodes, directed)); + + igraph_vector_destroy(&newedges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +#undef D_CODE +#undef U_CODE +#undef CODE +#undef CODEE +#undef UPD_E +#undef UPD_A +#undef UPD_D + +/** + * \function igraph_correlated_pair_game + * \brief Generates pairs of correlated random graphs. + * + * Sample two random graphs, with given correlation. + * + * \param graph1 The first graph will be stored here. + * \param graph2 The second graph will be stored here. + * \param n The number of vertices in both graphs. + * \param corr A scalar in the unit interval, the target Pearson + * correlation between the adjacency matrices of the original the + * generated graph (the adjacency matrix being used as a vector). + * \param p A numeric scalar, the probability of an edge between two + * vertices, it must in the open (0,1) interval. + * \param directed Whether to generate directed graphs. + * \param permutation A permutation to apply to the vertices of the + * second graph. It can also be a null pointer, in which case + * the vertices will not be permuted. + * \return Error code + * + * \sa \ref igraph_correlated_game() for generating a correlated pair + * to a given graph. + */ +int igraph_correlated_pair_game(igraph_t *graph1, igraph_t *graph2, + igraph_integer_t n, igraph_real_t corr, igraph_real_t p, + igraph_bool_t directed, + const igraph_vector_t *permutation) { + + IGRAPH_CHECK(igraph_erdos_renyi_game(graph1, IGRAPH_ERDOS_RENYI_GNP, n, p, + directed, IGRAPH_NO_LOOPS)); + IGRAPH_CHECK(igraph_correlated_game(graph1, graph2, corr, p, permutation)); + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/degree_sequence.c b/src/rigraph/core/games/degree_sequence.c new file mode 100644 index 0000000..5b67ae0 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence.c @@ -0,0 +1,775 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_adjlist.h" +#include "igraph_constructors.h" +#include "igraph_conversion.h" +#include "igraph_graphicality.h" +#include "igraph_memory.h" +#include "igraph_random.h" +#include "igraph_vector_ptr.h" + +#include "../core/interruption.h" +#include "../core/set.h" + +static int igraph_i_degree_sequence_game_simple(igraph_t *graph, + const igraph_vector_t *out_seq, + const igraph_vector_t *in_seq) { + + long int outsum = 0, insum = 0; + igraph_bool_t directed = (in_seq != 0 && igraph_vector_size(in_seq) != 0); + igraph_bool_t degseq_ok; + long int no_of_nodes, no_of_edges; + long int *bag1 = 0, *bag2 = 0; + long int bagp1 = 0, bagp2 = 0; + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + long int i, j; + + IGRAPH_CHECK(igraph_is_graphical(out_seq, in_seq, IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW, °seq_ok)); + if (!degseq_ok) { + IGRAPH_ERROR(in_seq ? "No directed graph can realize the given degree sequences" : + "No undirected graph can realize the given degree sequence", IGRAPH_EINVAL); + } + + outsum = (long int) igraph_vector_sum(out_seq); + if (directed) { + insum = (long int) igraph_vector_sum(in_seq); + } + + no_of_nodes = igraph_vector_size(out_seq); + no_of_edges = directed ? outsum : outsum / 2; + + bag1 = IGRAPH_CALLOC(outsum, long int); + if (bag1 == 0) { + IGRAPH_ERROR("degree sequence game (simple)", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, bag1); + + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < VECTOR(*out_seq)[i]; j++) { + bag1[bagp1++] = i; + } + } + if (directed) { + bag2 = IGRAPH_CALLOC(insum, long int); + if (bag2 == 0) { + IGRAPH_ERROR("degree sequence game (simple)", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, bag2); + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < VECTOR(*in_seq)[i]; j++) { + bag2[bagp2++] = i; + } + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + + RNG_BEGIN(); + + if (directed) { + for (i = 0; i < no_of_edges; i++) { + long int from = RNG_INTEGER(0, bagp1 - 1); + long int to = RNG_INTEGER(0, bagp2 - 1); + igraph_vector_push_back(&edges, bag1[from]); /* safe, already reserved */ + igraph_vector_push_back(&edges, bag2[to]); /* ditto */ + bag1[from] = bag1[bagp1 - 1]; + bag2[to] = bag2[bagp2 - 1]; + bagp1--; bagp2--; + } + } else { + for (i = 0; i < no_of_edges; i++) { + long int from = RNG_INTEGER(0, bagp1 - 1); + long int to; + igraph_vector_push_back(&edges, bag1[from]); /* safe, already reserved */ + bag1[from] = bag1[bagp1 - 1]; + bagp1--; + to = RNG_INTEGER(0, bagp1 - 1); + igraph_vector_push_back(&edges, bag1[to]); /* ditto */ + bag1[to] = bag1[bagp1 - 1]; + bagp1--; + } + } + + RNG_END(); + + IGRAPH_FREE(bag1); + IGRAPH_FINALLY_CLEAN(1); + if (directed) { + IGRAPH_FREE(bag2); + IGRAPH_FINALLY_CLEAN(1); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_degree_sequence_game_no_multiple_undirected( + igraph_t *graph, const igraph_vector_t *seq) { + + igraph_vector_t stubs = IGRAPH_VECTOR_NULL; + igraph_vector_int_t *neis; + igraph_vector_t residual_degrees = IGRAPH_VECTOR_NULL; + igraph_set_t incomplete_vertices; + igraph_adjlist_t al; + igraph_bool_t finished, failed; + igraph_integer_t from, to, dummy; + long int i, j, k; + long int no_of_nodes, outsum = 0; + igraph_bool_t degseq_ok; + + IGRAPH_CHECK(igraph_is_graphical(seq, 0, IGRAPH_SIMPLE_SW, °seq_ok)); + if (!degseq_ok) { + IGRAPH_ERROR("No simple undirected graph can realize the given degree sequence", + IGRAPH_EINVAL); + } + + outsum = (long int) igraph_vector_sum(seq); + no_of_nodes = igraph_vector_size(seq); + + /* Allocate required data structures */ + IGRAPH_CHECK(igraph_adjlist_init_empty(&al, (igraph_integer_t) no_of_nodes)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + IGRAPH_VECTOR_INIT_FINALLY(&stubs, 0); + IGRAPH_CHECK(igraph_vector_reserve(&stubs, outsum)); + IGRAPH_VECTOR_INIT_FINALLY(&residual_degrees, no_of_nodes); + IGRAPH_CHECK(igraph_set_init(&incomplete_vertices, 0)); + IGRAPH_FINALLY(igraph_set_destroy, &incomplete_vertices); + + /* Start the RNG */ + RNG_BEGIN(); + + /* Outer loop; this will try to construct a graph several times from scratch + * until it finally succeeds. */ + finished = 0; + while (!finished) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* Be optimistic :) */ + failed = 0; + + /* Clear the adjacency list to get rid of the previous attempt (if any) */ + igraph_adjlist_clear(&al); + + /* Initialize the residual degrees from the degree sequence */ + IGRAPH_CHECK(igraph_vector_update(&residual_degrees, seq)); + + /* While there are some unconnected stubs left... */ + while (!finished && !failed) { + /* Construct the initial stub vector */ + igraph_vector_clear(&stubs); + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < VECTOR(residual_degrees)[i]; j++) { + igraph_vector_push_back(&stubs, i); + } + } + + /* Clear the skipped stub counters and the set of incomplete vertices */ + igraph_vector_null(&residual_degrees); + igraph_set_clear(&incomplete_vertices); + + /* Shuffle the stubs in-place */ + igraph_vector_shuffle(&stubs); + + /* Connect the stubs where possible */ + k = igraph_vector_size(&stubs); + for (i = 0; i < k; ) { + from = (igraph_integer_t) VECTOR(stubs)[i++]; + to = (igraph_integer_t) VECTOR(stubs)[i++]; + + if (from > to) { + dummy = from; from = to; to = dummy; + } + + neis = igraph_adjlist_get(&al, from); + if (from == to || igraph_vector_int_binsearch(neis, to, &j)) { + /* Edge exists already */ + VECTOR(residual_degrees)[from]++; + VECTOR(residual_degrees)[to]++; + IGRAPH_CHECK(igraph_set_add(&incomplete_vertices, from)); + IGRAPH_CHECK(igraph_set_add(&incomplete_vertices, to)); + } else { + /* Insert the edge */ + IGRAPH_CHECK(igraph_vector_int_insert(neis, j, to)); + } + } + + finished = igraph_set_empty(&incomplete_vertices); + + if (!finished) { + /* We are not done yet; check if the remaining stubs are feasible. This + * is done by enumerating all possible pairs and checking whether at + * least one feasible pair is found. */ + i = 0; + failed = 1; + while (failed && igraph_set_iterate(&incomplete_vertices, &i, &from)) { + j = 0; + while (igraph_set_iterate(&incomplete_vertices, &j, &to)) { + if (from == to) { + /* This is used to ensure that each pair is checked once only */ + break; + } + if (from > to) { + dummy = from; from = to; to = dummy; + } + neis = igraph_adjlist_get(&al, from); + if (!igraph_vector_int_binsearch(neis, to, 0)) { + /* Found a suitable pair, so we can continue */ + failed = 0; + break; + } + } + } + } + } + } + + /* Finish the RNG */ + RNG_END(); + + /* Clean up */ + igraph_set_destroy(&incomplete_vertices); + igraph_vector_destroy(&residual_degrees); + igraph_vector_destroy(&stubs); + IGRAPH_FINALLY_CLEAN(3); + + /* Create the graph. We cannot use IGRAPH_ALL here for undirected graphs + * because we did not add edges in both directions in the adjacency list. + * We will use igraph_to_undirected in an extra step. */ + IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, 1)); + IGRAPH_CHECK(igraph_to_undirected(graph, IGRAPH_TO_UNDIRECTED_EACH, 0)); + + /* Clear the adjacency list */ + igraph_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_degree_sequence_game_no_multiple_directed(igraph_t *graph, + const igraph_vector_t *out_seq, const igraph_vector_t *in_seq) { + igraph_adjlist_t al; + igraph_bool_t deg_seq_ok, failed, finished; + igraph_vector_t in_stubs = IGRAPH_VECTOR_NULL; + igraph_vector_t out_stubs = IGRAPH_VECTOR_NULL; + igraph_vector_int_t *neis; + igraph_vector_t residual_in_degrees = IGRAPH_VECTOR_NULL; + igraph_vector_t residual_out_degrees = IGRAPH_VECTOR_NULL; + igraph_set_t incomplete_in_vertices; + igraph_set_t incomplete_out_vertices; + igraph_integer_t from, to; + long int i, j, k; + long int no_of_nodes, outsum; + + IGRAPH_CHECK(igraph_is_graphical(out_seq, in_seq, IGRAPH_SIMPLE_SW, °_seq_ok)); + if (!deg_seq_ok) { + IGRAPH_ERROR("No simple directed graph can realize the given degree sequence", + IGRAPH_EINVAL); + } + + outsum = (long int) igraph_vector_sum(out_seq); + no_of_nodes = igraph_vector_size(out_seq); + + /* Allocate required data structures */ + IGRAPH_CHECK(igraph_adjlist_init_empty(&al, (igraph_integer_t) no_of_nodes)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + IGRAPH_VECTOR_INIT_FINALLY(&out_stubs, 0); + IGRAPH_CHECK(igraph_vector_reserve(&out_stubs, outsum)); + IGRAPH_VECTOR_INIT_FINALLY(&in_stubs, 0); + IGRAPH_CHECK(igraph_vector_reserve(&in_stubs, outsum)); + IGRAPH_VECTOR_INIT_FINALLY(&residual_out_degrees, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&residual_in_degrees, no_of_nodes); + IGRAPH_CHECK(igraph_set_init(&incomplete_out_vertices, 0)); + IGRAPH_FINALLY(igraph_set_destroy, &incomplete_out_vertices); + IGRAPH_CHECK(igraph_set_init(&incomplete_in_vertices, 0)); + IGRAPH_FINALLY(igraph_set_destroy, &incomplete_in_vertices); + + /* Start the RNG */ + RNG_BEGIN(); + + /* Outer loop; this will try to construct a graph several times from scratch + * until it finally succeeds. */ + finished = 0; + while (!finished) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* Be optimistic :) */ + failed = 0; + + /* Clear the adjacency list to get rid of the previous attempt (if any) */ + igraph_adjlist_clear(&al); + + /* Initialize the residual degrees from the degree sequences */ + IGRAPH_CHECK(igraph_vector_update(&residual_out_degrees, out_seq)); + IGRAPH_CHECK(igraph_vector_update(&residual_in_degrees, in_seq)); + + /* While there are some unconnected stubs left... */ + while (!finished && !failed) { + /* Construct the initial stub vectors */ + igraph_vector_clear(&out_stubs); + igraph_vector_clear(&in_stubs); + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < VECTOR(residual_out_degrees)[i]; j++) { + igraph_vector_push_back(&out_stubs, i); + } + for (j = 0; j < VECTOR(residual_in_degrees)[i]; j++) { + igraph_vector_push_back(&in_stubs, i); + } + } + + /* Clear the skipped stub counters and the set of incomplete vertices */ + igraph_vector_null(&residual_out_degrees); + igraph_vector_null(&residual_in_degrees); + igraph_set_clear(&incomplete_out_vertices); + igraph_set_clear(&incomplete_in_vertices); + + /* Shuffle the out-stubs in-place */ + igraph_vector_shuffle(&out_stubs); + + /* Connect the stubs where possible */ + k = igraph_vector_size(&out_stubs); + for (i = 0; i < k; i++) { + from = (igraph_integer_t) VECTOR(out_stubs)[i]; + to = (igraph_integer_t) VECTOR(in_stubs)[i]; + + neis = igraph_adjlist_get(&al, from); + if (from == to || igraph_vector_int_binsearch(neis, to, &j)) { + /* Edge exists already */ + VECTOR(residual_out_degrees)[from]++; + VECTOR(residual_in_degrees)[to]++; + IGRAPH_CHECK(igraph_set_add(&incomplete_out_vertices, from)); + IGRAPH_CHECK(igraph_set_add(&incomplete_in_vertices, to)); + } else { + /* Insert the edge */ + IGRAPH_CHECK(igraph_vector_int_insert(neis, j, to)); + } + } + + /* Are we finished? */ + finished = igraph_set_empty(&incomplete_out_vertices); + + if (!finished) { + /* We are not done yet; check if the remaining stubs are feasible. This + * is done by enumerating all possible pairs and checking whether at + * least one feasible pair is found. */ + i = 0; + failed = 1; + while (failed && igraph_set_iterate(&incomplete_out_vertices, &i, &from)) { + j = 0; + while (igraph_set_iterate(&incomplete_in_vertices, &j, &to)) { + neis = igraph_adjlist_get(&al, from); + if (from != to && !igraph_vector_int_binsearch(neis, to, 0)) { + /* Found a suitable pair, so we can continue */ + failed = 0; + break; + } + } + } + } + } + } + + /* Finish the RNG */ + RNG_END(); + + /* Clean up */ + igraph_set_destroy(&incomplete_in_vertices); + igraph_set_destroy(&incomplete_out_vertices); + igraph_vector_destroy(&residual_in_degrees); + igraph_vector_destroy(&residual_out_degrees); + igraph_vector_destroy(&in_stubs); + igraph_vector_destroy(&out_stubs); + IGRAPH_FINALLY_CLEAN(6); + + /* Create the graph */ + IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, 1)); + + /* Clear the adjacency list */ + igraph_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* swap two elements of a vector_int */ +#define SWAP_INT_ELEM(vec, i, j) \ + { \ + igraph_integer_t temp; \ + temp = VECTOR(vec)[i]; \ + VECTOR(vec)[i] = VECTOR(vec)[j]; \ + VECTOR(vec)[j] = temp; \ + } + +static int igraph_i_degree_sequence_game_no_multiple_undirected_uniform(igraph_t *graph, const igraph_vector_t *degseq) { + igraph_vector_int_t stubs; + igraph_vector_t edges; + igraph_bool_t degseq_ok; + igraph_vector_ptr_t adjlist; + long i, j; + long vcount, ecount, stub_count; + + IGRAPH_CHECK(igraph_is_graphical(degseq, NULL, IGRAPH_SIMPLE_SW, °seq_ok)); + if (!degseq_ok) { + IGRAPH_ERROR("No simple undirected graph can realize the given degree sequence", IGRAPH_EINVAL); + } + + stub_count = (long) igraph_vector_sum(degseq); + ecount = stub_count / 2; + vcount = igraph_vector_size(degseq); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&stubs, stub_count); + IGRAPH_VECTOR_INIT_FINALLY(&edges, stub_count); + + /* Fill stubs vector. */ + { + long k = 0; + for (i = 0; i < vcount; ++i) { + long deg = (long) VECTOR(*degseq)[i]; + for (j = 0; j < deg; ++j) { + VECTOR(stubs)[k++] = i; + } + } + } + + /* Build an adjacency list in terms of sets; used to check for multi-edges. */ + IGRAPH_CHECK(igraph_vector_ptr_init(&adjlist, vcount)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&adjlist, igraph_set_destroy); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &adjlist); + for (i = 0; i < vcount; ++i) { + igraph_set_t *set = IGRAPH_CALLOC(1, igraph_set_t); + if (! set) { + IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_set_init(set, 0)); + VECTOR(adjlist)[i] = set; + IGRAPH_CHECK(igraph_set_reserve(set, (long) VECTOR(*degseq)[i])); + } + + RNG_BEGIN(); + + for (;;) { + igraph_bool_t success = 1; + + /* Shuffle stubs vector with Fisher-Yates and check for self-loops and multi-edges as we go. */ + for (i = 0; i < ecount; ++i) { + long k, from, to; + + k = RNG_INTEGER(2*i, stub_count-1); + SWAP_INT_ELEM(stubs, 2*i, k); + + k = RNG_INTEGER(2*i+1, stub_count-1); + SWAP_INT_ELEM(stubs, 2*i+1, k); + + from = VECTOR(stubs)[2*i]; + to = VECTOR(stubs)[2*i+1]; + + /* self-loop, fail */ + if (from == to) { + success = 0; + break; + } + + /* multi-edge, fail */ + if (igraph_set_contains((igraph_set_t *) VECTOR(adjlist)[to], from)) { + success = 0; + break; + } + + /* sets are already reserved */ + igraph_set_add((igraph_set_t *) VECTOR(adjlist)[to], from); + igraph_set_add((igraph_set_t *) VECTOR(adjlist)[from], to); + + /* register edge */ + VECTOR(edges)[2 * i] = from; + VECTOR(edges)[2 * i + 1] = to; + } + + if (success) { + break; + } + + /* Clear adjacency list. */ + for (j = 0; j < vcount; ++j) { + igraph_set_clear((igraph_set_t *) VECTOR(adjlist)[j]); + } + + IGRAPH_ALLOW_INTERRUPTION(); + } + + RNG_END(); + + igraph_vector_ptr_destroy_all(&adjlist); + igraph_vector_int_destroy(&stubs); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(graph, &edges, vcount, /* directed = */ 0)); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +static int igraph_i_degree_sequence_game_no_multiple_directed_uniform( + igraph_t *graph, const igraph_vector_t *out_deg, const igraph_vector_t *in_deg) { + igraph_vector_int_t out_stubs, in_stubs; + igraph_vector_t edges; + igraph_bool_t degseq_ok; + igraph_vector_ptr_t adjlist; + long i, j; + long vcount, ecount; + + IGRAPH_CHECK(igraph_is_graphical(out_deg, in_deg, IGRAPH_SIMPLE_SW, °seq_ok)); + if (!degseq_ok) { + IGRAPH_ERROR("No simple directed graph can realize the given degree sequence", IGRAPH_EINVAL); + } + + ecount = (long) igraph_vector_sum(out_deg); + vcount = igraph_vector_size(out_deg); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_stubs, ecount); + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_stubs, ecount); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * ecount); + + /* Fill in- and out-stubs vectors. */ + { + long k = 0, l = 0; + for (i = 0; i < vcount; ++i) { + long dout, din; + + dout = (long) VECTOR(*out_deg)[i]; + for (j = 0; j < dout; ++j) { + VECTOR(out_stubs)[k++] = i; + } + + din = (long) VECTOR(*in_deg)[i]; + for (j = 0; j < din; ++j) { + VECTOR(in_stubs)[l++] = i; + } + } + } + + /* Build an adjacency list in terms of sets; used to check for multi-edges. */ + IGRAPH_CHECK(igraph_vector_ptr_init(&adjlist, vcount)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&adjlist, igraph_set_destroy); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &adjlist); + for (i = 0; i < vcount; ++i) { + igraph_set_t *set = IGRAPH_CALLOC(1, igraph_set_t); + if (! set) { + IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_set_init(set, 0)); + VECTOR(adjlist)[i] = set; + IGRAPH_CHECK(igraph_set_reserve(set, (long) VECTOR(*out_deg)[i])); + } + + RNG_BEGIN(); + + for (;;) { + igraph_bool_t success = 1; + + /* Shuffle out-stubs vector with Fisher-Yates and check for self-loops and multi-edges as we go. */ + for (i = 0; i < ecount; ++i) { + long k, from, to; + igraph_set_t *set; + + k = RNG_INTEGER(i, ecount-1); + SWAP_INT_ELEM(out_stubs, i, k); + + from = VECTOR(out_stubs)[i]; + to = VECTOR(in_stubs)[i]; + + /* self-loop, fail */ + if (to == from) { + success = 0; + break; + } + + /* multi-edge, fail */ + set = (igraph_set_t *) VECTOR(adjlist)[from]; + if (igraph_set_contains(set, to)) { + success = 0; + break; + } + + /* sets are already reserved */ + igraph_set_add(set, to); + + /* register edge */ + VECTOR(edges)[2 * i] = from; + VECTOR(edges)[2 * i + 1] = to; + } + + if (success) { + break; + } + + /* Clear adjacency list. */ + for (j = 0; j < vcount; ++j) { + igraph_set_clear((igraph_set_t *) VECTOR(adjlist)[j]); + } + + IGRAPH_ALLOW_INTERRUPTION(); + } + + RNG_END(); + + igraph_vector_ptr_destroy_all(&adjlist); + igraph_vector_int_destroy(&out_stubs); + igraph_vector_int_destroy(&in_stubs); + IGRAPH_FINALLY_CLEAN(3); + + IGRAPH_CHECK(igraph_create(graph, &edges, vcount, /* directed = */ 1)); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +#undef SWAP_INT_ELEM + + +/* This is in gengraph_mr-connected.cpp */ + +int igraph_degree_sequence_game_vl(igraph_t *graph, + const igraph_vector_t *out_seq, + const igraph_vector_t *in_seq); + +/** + * \ingroup generators + * \function igraph_degree_sequence_game + * \brief Generates a random graph with a given degree sequence. + * + * \param graph Pointer to an uninitialized graph object. + * \param out_deg The degree sequence for an undirected graph (if + * \p in_seq is \c NULL or of length zero), or the out-degree + * sequence of a directed graph (if \p in_deq is not + * of length zero). + * \param in_deg It is either a zero-length vector or + * \c NULL (if an undirected + * graph is generated), or the in-degree sequence. + * \param method The method to generate the graph. Possible values: + * \clist + * \cli IGRAPH_DEGSEQ_SIMPLE + * This method implements the configuration model. + * For undirected graphs, it puts all vertex IDs in a bag + * such that the multiplicity of a vertex in the bag is the same as + * its degree. Then it draws pairs from the bag until the bag becomes + * empty. This method may generate both loop (self) edges and multiple + * edges. For directed graphs, the algorithm is basically the same, + * but two separate bags are used for the in- and out-degrees. + * Undirected graphs are generated with probability proportional to + * (\prod_{i<j} A_{ij} ! \prod_i A_{ii} !!)^{-1}, + * where \c A denotes the adjacency matrix and !! denotes + * the double factorial. Here \c A is assumed to have twice the number of + * self-loops on its diagonal. + * The corresponding expression for directed graphs is + * (\prod_{i,j} A_{ij}!)^{-1}. + * Thus the probability of all simple graphs (which only have 0s and 1s + * in the adjacency matrix) is the same, while that of + * non-simple ones depends on their edge and self-loop multiplicities. + * \cli IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE + * This method generates simple graphs. + * It is similar to \c IGRAPH_DEGSEQ_SIMPLE + * but tries to avoid multiple and loop edges and restarts the + * generation from scratch if it gets stuck. It can generate all simple + * realizations of a degree sequence, but it is not guaranteed + * to sample them uniformly. This method is relatively fast and it will + * eventually succeed if the provided degree sequence is graphical, + * but there is no upper bound on the number of iterations. + * \cli IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM + * This method is identical to \c IGRAPH_DEGSEQ_SIMPLE, but if the + * generated graph is not simple, it rejects it and re-starts the + * generation. It generates all simple graphs with the same probability. + * \cli IGRAPH_DEGSEQ_VL + * This method samples undirected \em connected graphs approximately + * uniformly. It is a Monte Carlo method based on degree-preserving + * edge swaps. + * This generator should be favoured if undirected and connected + * graphs are to be generated and execution time is not a concern. + * igraph uses the original implementation of Fabien Viger; for the algorithm, + * see https://www-complexnetworks.lip6.fr/~latapy/FV/generation.html + * and the paper https://arxiv.org/abs/cs/0502085 + * \endclist + * \return Error code: + * \c IGRAPH_ENOMEM: there is not enough + * memory to perform the operation. + * \c IGRAPH_EINVAL: invalid method parameter, or + * invalid in- and/or out-degree vectors. The degree vectors + * should be non-negative, \p out_deg should sum + * up to an even integer for undirected graphs; the length + * and sum of \p out_deg and + * \p in_deg + * should match for directed graphs. + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges + * for \c IGRAPH_DEGSEQ_SIMPLE. The time complexity of the + * other modes is not known. + * + * \sa \ref igraph_barabasi_game(), \ref igraph_erdos_renyi_game(), + * \ref igraph_is_graphical() + * + * \example examples/simple/igraph_degree_sequence_game.c + */ + +int igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_t *out_deg, + const igraph_vector_t *in_deg, + igraph_degseq_t method) { + if (in_deg && igraph_vector_empty(in_deg) && !igraph_vector_empty(out_deg)) { + in_deg = 0; + } + + switch (method) { + case IGRAPH_DEGSEQ_SIMPLE: + return igraph_i_degree_sequence_game_simple(graph, out_deg, in_deg); + + case IGRAPH_DEGSEQ_VL: + return igraph_degree_sequence_game_vl(graph, out_deg, in_deg); + + case IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE: + if (in_deg == 0) { + return igraph_i_degree_sequence_game_no_multiple_undirected(graph, out_deg); + } else { + return igraph_i_degree_sequence_game_no_multiple_directed(graph, out_deg, in_deg); + } + + case IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM: + if (in_deg == 0) { + return igraph_i_degree_sequence_game_no_multiple_undirected_uniform(graph, out_deg); + } else { + return igraph_i_degree_sequence_game_no_multiple_directed_uniform(graph, out_deg, in_deg); + } + + default: + IGRAPH_ERROR("Invalid degree sequence game method", IGRAPH_EINVAL); + } +} diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.cpp new file mode 100644 index 0000000..11848d2 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.cpp @@ -0,0 +1,110 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 . + */ +#include "gengraph_box_list.h" +#include + +namespace gengraph { + +void box_list::insert(int v) { + int d = deg[v]; + if (d < 1) { + return; + } + if (d > dmax) { + dmax = d; + } + int yo = list[d - 1]; + list[d - 1] = v; + prev[v] = -1; + next[v] = yo; + if (yo >= 0) { + prev[yo] = v; + } +} + +void box_list::pop(int v) { + int p = prev[v]; + int nxt = next[v]; + if (p < 0) { + int d = deg[v]; + assert(list[d - 1] == v); + list[d - 1] = nxt; + if (d == dmax && nxt < 0) { + do { + dmax--; + } while (dmax > 0 && list[dmax - 1] < 0); + } + } else { + next[p] = nxt; + } + if (nxt >= 0) { + prev[nxt] = p; + } +} + +box_list::box_list(int n0, int *deg0) : n(n0), deg(deg0) { + next = new int[n]; + prev = new int[n]; + dmax = -1; + int i; + for (i = 0; i < n; i++) if (deg[i] > dmax) { + dmax = deg[i]; + } + list = new int[dmax]; + for (i = 0; i < dmax; i++) { + list[i] = -1; + } + for (i = 0; i < n; i++) { + insert(i); + } +} + +box_list::~box_list() { + delete[] prev; + delete[] next; + delete[] list; +} + +void box_list::pop_vertex(int v, int **neigh) { + int k = deg[v]; + if (k < 1) { + return; + } + pop(v); + int *w = neigh[v]; + while (k--) { + int v2 = *(w++); + int *w2 = neigh[v2]; + while (*w2 != v) { + w2++; + } + int *w3 = neigh[v2] + (deg[v2] - 1); + assert(w2 <= w3); + int tmp = *w3; + *w3 = *w2; + *w2 = tmp; + pop(v2); + deg[v2]--; + insert(v2); + } +} + +} // namespace gengraph diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.h new file mode 100644 index 0000000..f5c7a41 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_box_list.h @@ -0,0 +1,83 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 . + */ +// This class allows to maintain a list of vertices, +// sorted by degree (largest degrees first) +// Operations allowed : +// - get the vertex having max degree -> Cost = O(1) +// - remove any vertex from the graph -> Cost = Sum(degrees of neighbours) +// [ could be O(degree) if optimized ] + +#ifndef _BOX_LIST_H +#define _BOX_LIST_H + +namespace gengraph { + +class box_list { + +private: + int n; // INITIAL number of vertices + int dmax; // CURRENT Maximum degree + int *deg; // CURRENT Degrees (points directly to the deg[] of the graph + + // Vertices are grouped by degree: one double-chained lists for each degree + int *list; // list[d-1] is the head of list of vertices of degree d + int *next; // next[v]/prev[v] are the vertices next/previous to v + int *prev; // in the list where v belongs + void pop(int); // pop(v) just removes v from its list + void insert(int); // insert(v) insert v at the head of its list + +public: + + // Ctor. Takes O(n) time. + box_list(int n0, int *deg0); + + // Dtor + ~box_list(); + + // Self-explaining inline routines + inline bool is_empty() { + return dmax < 1; + }; + inline int get_max() { + return list[dmax - 1]; + }; + inline int get_one() { + return list[0]; + }; + inline int get_min() { + int i = 0; + while (list[i] < 0) { + i++; + } + return list[i]; + }; + + // Remove v from box_list + // Also, semi-remove vertex v from graph: all neighbours of v will swap + // their last neighbour wit hv, and then decrease their degree, so + // that any arc w->v virtually disappear + // Actually, adjacency lists are just permuted, and deg[] is changed + void pop_vertex(int v, int **neigh); +}; + +} // namespace gengraph + +#endif //_BOX_LIST_H diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_definitions.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_definitions.h new file mode 100644 index 0000000..2fdc72d --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_definitions.h @@ -0,0 +1,201 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 DEFINITIONS_H +#define DEFINITIONS_H + +#include +#include +#include + +//#include "../../internal/hacks.h" + +namespace gengraph { + +// Max line size in files +#define FBUFF_SIZE 1000000 + +// disable lousy VC++ warnings +#ifdef _ATL_VER_ + #pragma warning(disable : 4127) +#endif //_ATL_VER_ + +// Verbose +#define VERBOSE_NONE 0 +#define VERBOSE_SOME 1 +#define VERBOSE_LOTS 2 +int VERBOSE(); +void SET_VERBOSE(int v); + +// Random number generator +void my_srandom(int); +int my_random(); +int my_binomial(double pp, int n); +double my_random01(); // (0,1] + +#define MY_RAND_MAX 0x7FFFFFFF + +// IPv4 address direct translation into 32-bit uint + special IP defs +typedef unsigned int ip_addr; +#define IP_NONE 0x7FFFFFFF +#define IP_STAR 0x00000000 +#define IP_MYSELF 0x7F000001 + +//inline double round(double x) throw () { return (floor(0.5+x)); } + +// Min & Max +#ifndef min + #define defmin(type) inline type min(type a, type b) { return ab ? a : b; } + defmax(int) + defmax(double) + defmax(unsigned long) +#endif //max + +// Traceroute Sampling +#define MODE_USP 0 +#define MODE_ASP 1 +#define MODE_RSP 2 + +// Debug definitions +//#define PERFORMANCE_MONITOR +//#define OPT_ISOLATED + +// Max Int +#ifndef MAX_INT + #define MAX_INT 0x7FFFFFFF +#endif //MAX_INT + +//Edge type +typedef struct { + int from; + int to; +} edge; + +// Tag Int +#define TAG_INT 0x40000000 + +// Oldies .... +#define S_VECTOR_RAW + +//********************* +// Routine definitions +//********************* + +/* log(1+x) +inline double logp(double x) { + if(fabs(x)<1e-6) return x+0.5*x*x+0.333333333333333*x*x*x; + else return log(1.0+x); +} +*/ + + +//Fast search or replace +inline int* fast_rpl(int *m, const int a, const int b) { + while (*m != a) { + m++; + } + *m = b; + return m; +} +inline int* fast_search(int *m, const int size, const int a) { + int *p = m + size; + while (m != p--) if (*p == a) { + return p; + } + return NULL; +} + +// Lovely percentage print +// inline void print_percent(double yo, FILE *f = stderr) { +// int arf = int(100.0*yo); +// if(double(arf)>100.0*yo) arf--; +// if(arf<100) fprintf(f," "); +// if(arf<10) fprintf(f," "); +// fprintf(f,"%d.%d%%",arf,int(1000.0*yo-double(10*arf))); +// } + +// Skips non-numerical chars, then numerical chars, then non-numerical chars. +inline char skip_int(char* &c) { + while (*c < '0' || *c > '9') { + c++; + } + while (*c >= '0' && *c <= '9') { + c++; + } + while (*c != 0 && (*c < '0' || *c > '9')) { + c++; + } + return *c; +} + +// distance+1 modulo 255 for breadth-first search +inline unsigned char next_dist(const unsigned char c) { + return c == 255 ? 1 : c + 1; +} +inline unsigned char prev_dist(const unsigned char c) { + return c == 1 ? 255 : c - 1; +} + +// 1/(RANDMAX+1) +#define inv_RANDMAX (1.0/(1.0+double(MY_RAND_MAX))) + +// random number in ]0,1[, _very_ accurate around 0 +inline double random_float() { + int r = my_random(); + double mul = inv_RANDMAX; + while (r <= 0x7FFFFF) { + r <<= 8; + r += (my_random() & 0xFF); + mul *= (1.0 / 256.0); + } + return double(r) * mul; +} + +// Return true with probability p. Very accurate when p is small. +#define test_proba(p) (random_float()<(p)) + +// Random bit generator, sparwise. +static int _random_bits_stored = 0; +static int _random_bits = 0; + +inline int random_bit() { + int a = _random_bits; + _random_bits = a >> 1; + if (_random_bits_stored--) { + return a & 0x1; + } + a = my_random(); + _random_bits = a >> 1; + _random_bits_stored = 30; + return a & 0x1; +} + +// Hash Profiling (see hash.h) +void _hash_prof(); + +} // namespace gengraph + +#endif //DEFINITIONS_H diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.cpp new file mode 100644 index 0000000..0f5a784 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.cpp @@ -0,0 +1,421 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 . + */ +#include "gengraph_definitions.h" +#include "gengraph_random.h" +#include "gengraph_powerlaw.h" +#include "gengraph_degree_sequence.h" +#include "gengraph_hash.h" + +#include "igraph_statusbar.h" + +#include +#include +#include +#include +#include +#include + +// using namespace __gnu_cxx; +using namespace std; + +namespace gengraph { + +// shuffle an int[] randomly +void random_permute(int *a, int n); + +// sort an array of positive integers in time & place O(n + max) +void cumul_sort(int *q, int n); + + +void degree_sequence::detach() { + deg = NULL; +} + +degree_sequence::~degree_sequence() { + if (deg != NULL) { + delete[] deg; + } + deg = NULL; +} + +void degree_sequence::make_even(int mini, int maxi) { + if (total % 2 == 0) { + return; + } + if (maxi < 0) { + maxi = 0x7FFFFFFF; + } + int i; + for (i = 0; i < n; i++) { + if (deg[i] > mini) { + deg[i]--; + total--; + break; + } else if (deg[i] < maxi) { + deg[i]++; + total++; + break; + } + } + if (i == n) { + IGRAPH_WARNING("Warning: degree_sequence::make_even() forced one " + "degree to go over degmax"); + deg[0]++; + total++; + } +} + +void degree_sequence::shuffle() { + random_permute(deg, n); +} + +void degree_sequence::sort() { + cumul_sort(deg, n); +} + +void degree_sequence::compute_total() { + total = 0; + for (int i = 0; i < n; i++) { + total += deg[i]; + } +} + +degree_sequence:: +degree_sequence(int n0, int *degs) { + deg = degs; + n = n0; + compute_total(); +} + +degree_sequence:: +degree_sequence(const igraph_vector_t *out_seq) { + n = igraph_vector_size(out_seq); + deg = new int[n]; + for (long int i = 0; i < n; i++) { + deg[i] = VECTOR(*out_seq)[i]; + } + compute_total(); +} + +#ifndef FBUFF_SIZE + #define FBUFF_SIZE 999 +#endif //FBUFF_SIZE + +// degree_sequence::degree_sequence(FILE *f, bool DISTRIB) { +// n = 0; +// total = 0; +// char *buff = new char[FBUFF_SIZE]; +// char *c; +// vector degree; +// if(!DISTRIB) { +// // Input is a 'raw' degree sequence d0 d1 d2 d3 ... +// while(fgets(buff, FBUFF_SIZE, f)) { +// int d = strtol(buff, &c, 10); +// if(c == buff) continue; +// degree.push_back(d); +// total += d; +// } +// n = int(degree.size()); +// deg = new int[n]; +// int *yo = deg; +// vector::iterator end = degree.end(); +// for(vector::iterator it=degree.begin(); it!=end; *(yo++) = *(it++)); +// } +// else { +// // Input is a degree distribution : d0 #(degree=d0), d1 #(degree=d1), ... +// vector n_with_degree; +// int line = 0; +// int syntax = 0; +// int ignored = 0; +// int first_syntax = 0; +// int first_ignored = 0; +// while(fgets(buff, FBUFF_SIZE, f)) { +// line++; +// int d = strtol(buff, &c, 10); +// if(c == buff) { ignored++; first_ignored = line; continue; } +// char *cc; +// int i = strtol(c, &cc, 10); +// if(cc == c) { syntax++; first_syntax = line; continue; } +// n += i; +// total += i*d; +// degree.push_back(d); +// n_with_degree.push_back(i); +// if( cc != c) { syntax++; first_syntax = line; } +// } +// if(VERBOSE()) { +// if(ignored > 0) fprintf(stderr,"Ignored %d lines (first was line #%d)\n", ignored, first_ignored); +// if(syntax > 0) fprintf(stderr,"Found %d probable syntax errors (first was line #%d)\n", syntax, first_syntax); +// } +// deg = new int[n]; +// int *yo = deg; +// vector::iterator it_n = n_with_degree.begin(); +// for(vector::iterator it = degree.begin(); it != degree.end(); it++) +// for(int k = *(it_n++); k--; *yo++ = *it); +// } +// if(VERBOSE()) { +// if(total % 2 != 0) fprintf(stderr,"Warning: degree sequence is odd\n"); +// fprintf(stderr,"Degree sequence created. N=%d, 2M=%d\n", n, total); +// } +// } + +// n vertices, exponent, min degree, max degree, average degree (optional, default is -1) +degree_sequence:: +degree_sequence(int _n, double exp, int degmin, int degmax, double z) { + + n = _n; + if (exp == 0.0) { + // Binomial distribution + if (z < 0) { + throw std::invalid_argument( + "Fatal error in degree_sequence constructor: " + "positive average degree must be specified."); + } + if (degmax < 0) { + degmax = n - 1; + } + total = int(floor(double(n) * z + 0.5)); + deg = new int[n]; + KW_RNG::RNG myrand; + double p = (z - double(degmin)) / double(n); + total = 0; + for (int i = 0; i < n; i++) { + do { + deg[i] = 1 + myrand.binomial(p, n); + } while (deg[i] > degmax); + total += deg[i]; + } + } else { + // Power-law distribution + igraph_status("Creating powerlaw sampler...", 0); + powerlaw pw(exp, degmin, degmax); + if (z == -1.0) { + pw.init(); + igraph_statusf("done. Mean=%f\n", 0, pw.mean()); + } else { + double offset = pw.init_to_mean(z); + igraph_statusf("done. Offset=%f, Mean=%f\n", 0, offset, pw.mean()); + } + + deg = new int[n]; + total = 0; + int i; + + igraph_statusf("Sampling %d random numbers...", 0, n); + for (i = 0; i < n; i++) { + deg[i] = pw.sample(); + total += deg[i]; + } + + igraph_status("done\nSimple statistics on degrees...", 0); + int wanted_total = int(floor(z * n + 0.5)); + sort(); + igraph_statusf("done : Max=%d, Total=%d.\n", 0, deg[0], total); + if (z != -1.0) { + igraph_statusf("Adjusting total to %d...", 0, wanted_total); + int iterations = 0; + + while (total != wanted_total) { + sort(); + for (i = 0; i < n && total > wanted_total; i++) { + total -= deg[i]; + if (total + degmin <= wanted_total) { + deg[i] = wanted_total - total; + } else { + deg[i] = pw.sample(); + } + total += deg[i]; + } + iterations += i; + for (i = n - 1; i > 0 && total < wanted_total; i--) { + total -= deg[i]; + if (total + (deg[0] >> 1) >= wanted_total) { + deg[i] = wanted_total - total; + } else { + deg[i] = pw.sample(); + } + total += deg[i]; + } + iterations += n - 1 - i; + } + igraph_statusf("done(%d iterations).", 0, iterations); + igraph_statusf(" Now, degmax = %d\n", 0, dmax()); + } + + shuffle(); + } +} + +// void degree_sequence::print() { +// for(int i=0; ideg[i]) dmin=deg[i]; +// int *dd = new int[dmax-dmin+1]; +// for(i=dmin; i<=dmax; i++) dd[i-dmin]=0; +// if(VERBOSE()) fprintf(stderr,"Computing cumulative distribution..."); +// for(i=0; i0) printf("%d %d\n",i,dd[i-dmin]); +// delete[] dd; +// } + +bool degree_sequence::havelhakimi() { + + int i; + int dm = dmax() + 1; + // Sort vertices using basket-sort, in descending degrees + int *nb = new int[dm]; + int *sorted = new int[n]; + // init basket + for (i = 0; i < dm; i++) { + nb[i] = 0; + } + // count basket + for (i = 0; i < n; i++) { + nb[deg[i]]++; + } + // cumul + int c = 0; + for (i = dm - 1; i >= 0; i--) { + int t = nb[i]; + nb[i] = c; + c += t; + } + // sort + for (i = 0; i < n; i++) { + sorted[nb[deg[i]]++] = i; + } + +// Binding process starts + int first = 0; // vertex with biggest residual degree + int d = dm - 1; // maximum residual degree available + + for (c = total / 2; c > 0; ) { + // We design by 'v' the vertex of highest degree (indexed by first) + // look for current degree of v + while (nb[d] <= first) { + d--; + } + // store it in dv + int dv = d; + // bind it ! + c -= dv; + int dc = d; // residual degree of vertices we bind to + int fc = ++first; // position of the first vertex with degree dc + + while (dv > 0 && dc > 0) { + int lc = nb[dc]; + if (lc != fc) { + while (dv > 0 && lc > fc) { + // binds v with sorted[--lc] + dv--; + lc--; + } + fc = nb[dc]; + nb[dc] = lc; + } + dc--; + } + if (dv != 0) { // We couldn't bind entirely v + delete[] nb; + delete[] sorted; + return false; + } + } + delete[] nb; + delete[] sorted; + return true; +} + +//************************* +// Subroutines definitions +//************************* + +inline int int_adjust(double x) { + return (int(floor(x + random_float()))); +} + +void random_permute(int *a, int n) { + int j, tmp; + for (int i = 0; i < n - 1; i++) { + j = i + my_random() % (n - i); + tmp = a[i]; + a[i] = a[j]; + a[j] = tmp; + } +} + +void cumul_sort(int *q, int n) { + // looks for the maximum q[i] and minimum + if (n == 0) { + return; + } + int qmax = q[0]; + int qmin = q[0]; + int i; + for (i = 0; i < n; i++) if (q[i] > qmax) { + qmax = q[i]; + } + for (i = 0; i < n; i++) if (q[i] < qmin) { + qmin = q[i]; + } + + // counts #q[i] with given q + int *nb = new int[qmax - qmin + 1]; + for (int *onk = nb + (qmax - qmin + 1); onk != nb; * (--onk) = 0) { } + for (i = 0; i < n; i++) { + nb[q[i] - qmin]++; + } + + // counts cumulative distribution + for (i = qmax - qmin; i > 0; i--) { + nb[i - 1] += nb[i]; + } + + // sort by q[i] + int last_q; + int tmp; + int modifier = qmax - qmin + 1; + for (int current = 0; current < n; current++) { + tmp = q[current]; + if (tmp >= qmin && tmp <= qmax) { + last_q = qmin; + do { + q[current] = last_q + modifier; + last_q = tmp; + current = --nb[last_q - qmin]; + } while ((tmp = q[current]) >= qmin && tmp <= qmax); + q[current] = last_q + modifier; + } + } + delete[] nb; + for (i = 0; i < n; i++) { + q[i] = q[i] - modifier; + } +} + +} // namespace gengraph diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.h new file mode 100644 index 0000000..61c287d --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_degree_sequence.h @@ -0,0 +1,100 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 DEGREE_SEQUENCE_H +#define DEGREE_SEQUENCE_H + +#include "igraph_types.h" +#include "igraph_datatype.h" + +namespace gengraph { + +class degree_sequence { + +private: + int n; + int * deg; + int total; + +public : + // #vertices + inline int size() { + return n; + }; + inline int sum() { + return total; + }; + inline int operator[](int i) { + return deg[i]; + }; + inline int *seq() { + return deg; + }; + inline void assign(int n0, int* d0) { + n = n0; + deg = d0; + }; + inline int dmax() { + int dm = deg[0]; + for (int i = 1; i < n; i++) if (deg[i] > dm) { + dm = deg[i]; + } + return dm; + } + + void make_even(int mini = -1, int maxi = -1); + void sort(); + void shuffle(); + + // raw constructor + degree_sequence(int n, int *degs); + + // read-from-file constrictor + degree_sequence(FILE *f, bool DISTRIB = true); + + // simple power-law constructor : Pk = int((x+k0)^(-exp),x=k..k+1), with k0 so that avg(X)=z + degree_sequence(int n, double exp, int degmin, int degmax, double avg_degree = -1.0); + + // igraph constructor + degree_sequence(const igraph_vector_t *out_seq); + + // destructor + ~degree_sequence(); + + // unbind the deg[] vector (so that it doesn't get deleted when the class is destroyed) + void detach(); + + // compute total number of arcs + void compute_total(); + + // raw print (vertex by vertex) + void print(); + + // distribution print (degree frequency) + void print_cumul(); + + // is degree sequence realizable ? + bool havelhakimi(); + +}; + +} // namespace gengraph + +#endif //DEGREE_SEQUENCE_H diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp new file mode 100644 index 0000000..9722958 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp @@ -0,0 +1,1172 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 . + */ +#include "gengraph_definitions.h" +#include +#include +#include +#include +#include + +#include "gengraph_qsort.h" +#include "gengraph_hash.h" +#include "gengraph_degree_sequence.h" +#include "gengraph_graph_molloy_hash.h" + +#include "config.h" +#include "../../core/math.h" +#include "igraph_constructors.h" +#include "igraph_error.h" +#include "igraph_statusbar.h" +#include "igraph_progress.h" + +namespace gengraph { + +//_________________________________________________________________________ +void graph_molloy_hash::compute_neigh() { + int *p = links; + for (int i = 0; i < n; i++) { + neigh[i] = p; + p += HASH_SIZE(deg[i]); + } +} + +//_________________________________________________________________________ +void graph_molloy_hash::compute_size() { + size = 0; + for (int i = 0; i < n; i++) { + size += HASH_SIZE(deg[i]); + } +} + +//_________________________________________________________________________ +void graph_molloy_hash::init() { + for (int i = 0; i < size; i++) { + links[i] = HASH_NONE; + } +} + +//_________________________________________________________________________ +graph_molloy_hash::graph_molloy_hash(degree_sequence °s) { + igraph_status("Allocating memory for graph...", 0); + int s = alloc(degs); + igraph_statusf("%d bytes allocated successfully\n", 0, s); +} + +//_________________________________________________________________________ +int graph_molloy_hash::alloc(degree_sequence °s) { + n = degs.size(); + a = degs.sum(); + assert(a % 2 == 0); + + deg = degs.seq(); + compute_size(); + deg = new int[n + size]; + if (deg == NULL) { + return 0; + } + int i; + for (i = 0; i < n; i++) { + deg[i] = degs[i]; + } + links = deg + n; + init(); + neigh = new int*[n]; + if (neigh == NULL) { + return 0; + } + compute_neigh(); + return sizeof(int *)*n + sizeof(int) * (n + size); +} + +//_________________________________________________________________________ +graph_molloy_hash::~graph_molloy_hash() { + if (deg != NULL) { + delete[] deg; + } + if (neigh != NULL) { + delete[] neigh; + } + deg = NULL; + neigh = NULL; +} + +//_________________________________________________________________________ +graph_molloy_hash::graph_molloy_hash(int *svg) { + // Read n + n = *(svg++); + // Read a + a = *(svg++); + assert(a % 2 == 0); + // Read degree sequence + degree_sequence dd(n, svg); + // Build neigh[] and alloc links[] + alloc(dd); + dd.detach(); + // Read links[] + restore(svg + n); +} + +//_________________________________________________________________________ +int *graph_molloy_hash::hard_copy() { + int *hc = new int[2 + n + a / 2]; // to store n,a,deg[] and links[] + hc[0] = n; + hc[1] = a; + memcpy(hc + 2, deg, sizeof(int)*n); + int *p = hc + 2 + n; + int *l = links; + for (int i = 0; i < n; i++) for (int j = HASH_SIZE(deg[i]); j--; l++) { + int d; + if ((d = *l) != HASH_NONE && d >= i) { + *(p++) = d; + } + } + assert(p == hc + 2 + n + a / 2); + return hc; +} + +//_________________________________________________________________________ +bool graph_molloy_hash::is_connected() { + bool *visited = new bool[n]; + int *buff = new int[n]; + int comp_size = depth_search(visited, buff); + delete[] visited; + delete[] buff; + return (comp_size == n); +} + +//_________________________________________________________________________ +int* graph_molloy_hash::backup() { + int *b = new int[a / 2]; + int *c = b; + int *p = links; + for (int i = 0; i < n; i++) + for (int d = HASH_SIZE(deg[i]); d--; p++) if (*p != HASH_NONE && *p > i) { + *(c++) = *p; + } + assert(c == b + (a / 2)); + return b; +} + +//_________________________________________________________________________ +void graph_molloy_hash::restore(int* b) { + init(); + int i; + int *dd = new int[n]; + memcpy(dd, deg, sizeof(int)*n); + for (i = 0; i < n; i++) { + deg[i] = 0; + } + for (i = 0; i < n - 1; i++) { + while (deg[i] < dd[i]) { + add_edge(i, *b, dd); + b++; + } + } + delete[] dd; +} + +//_________________________________________________________________________ +bool graph_molloy_hash::isolated(int v, int K, int *Kbuff, bool *visited) { + if (K < 2) { + return false; + } +#ifdef OPT_ISOLATED + if (K <= deg[v] + 1) { + return false; + } +#endif //OPT_ISOLATED + int *seen = Kbuff; + int *known = Kbuff; + int *max = Kbuff + K; + *(known++) = v; + visited[v] = true; + bool is_isolated = true; + + while (known != seen) { + v = *(seen++); + int *ww = neigh[v]; + int w; + for (int d = HASH_SIZE(deg[v]); d--; ww++) if ((w = *ww) != HASH_NONE && !visited[w]) { +#ifdef OPT_ISOLATED + if (K <= deg[w] + 1 || known == max) { +#else //OPT_ISOLATED + if (known == max) { +#endif //OPT_ISOLATED + is_isolated = false; + goto end_isolated; + } + visited[w] = true; + *(known++) = w; + } + } +end_isolated: + // Undo the changes to visited[]... + while (known != Kbuff) { + visited[*(--known)] = false; + } + return is_isolated; +} + +//_________________________________________________________________________ +int graph_molloy_hash::random_edge_swap(int K, int *Kbuff, bool *visited) { + // Pick two random vertices a and c + int f1 = pick_random_vertex(); + int f2 = pick_random_vertex(); + // Check that f1 != f2 + if (f1 == f2) { + return 0; + } + // Get two random edges (f1,*f1t1) and (f2,*f2t2) + int *f1t1 = random_neighbour(f1); + int t1 = *f1t1; + int *f2t2 = random_neighbour(f2); + int t2 = *f2t2; + // Check simplicity + if (t1 == t2 || f1 == t2 || f2 == t1) { + return 0; + } + if (is_edge(f1, t2) || is_edge(f2, t1)) { + return 0; + } + // Swap + int *f1t2 = H_rpl(neigh[f1], deg[f1], f1t1, t2); + int *f2t1 = H_rpl(neigh[f2], deg[f2], f2t2, t1); + int *t1f2 = H_rpl(neigh[t1], deg[t1], f1, f2); + int *t2f1 = H_rpl(neigh[t2], deg[t2], f2, f1); + // isolation test + if (K <= 2) { + return 1; + } + if ( !isolated(f1, K, Kbuff, visited) && !isolated(f2, K, Kbuff, visited) ) { + return 1; + } + // undo swap + H_rpl(neigh[f1], deg[f1], f1t2, t1); + H_rpl(neigh[f2], deg[f2], f2t1, t2); + H_rpl(neigh[t1], deg[t1], t1f2, f1); + H_rpl(neigh[t2], deg[t2], t2f1, f2); + return 0; +} + +//_________________________________________________________________________ +unsigned long graph_molloy_hash::shuffle(unsigned long times, + unsigned long maxtimes, int type) { + igraph_progress("Shuffle", 0, 0); + // assert(verify()); + // counters + unsigned long nb_swaps = 0; + unsigned long all_swaps = 0; + unsigned long cost = 0; + // window + double T = double(min((unsigned long)(a), times) / 10); + if (type == OPTIMAL_HEURISTICS) { + T = double(optimal_window()); + } + if (type == BRUTE_FORCE_HEURISTICS) { + T = double(times * 2); + } + // isolation test parameter, and buffers + double K = 2.4; + int *Kbuff = new int[int(K) + 1]; + bool *visited = new bool[n]; + for (int i = 0; i < n; i++) { + visited[i] = false; + } + // Used for monitoring , active only if VERBOSE() + int failures = 0; + int successes = 0; + double avg_K = 0; + double avg_T = 0; + unsigned long next = times; + next = 0; + + // Shuffle: while #edge swap attempts validated by connectivity < times ... + while (times > nb_swaps && maxtimes > all_swaps) { + // Backup graph + int *save = backup(); + // Prepare counters, K, T + unsigned long swaps = 0; + int K_int = 0; + if (type == FINAL_HEURISTICS || type == BRUTE_FORCE_HEURISTICS) { + K_int = int(K); + } + unsigned long T_int = (unsigned long)(floor(T)); + if (T_int < 1) { + T_int = 1; + } + // compute cost + cost += T_int; + if (K_int > 2) { + cost += (unsigned long)(K_int) * (unsigned long)(T_int); + } + // Perform T edge swap attempts + for (int i = T_int; i > 0; i--) { + // try one swap + swaps += (unsigned long)(random_edge_swap(K_int, Kbuff, visited)); + all_swaps++; + // Verbose + if (nb_swaps + swaps > next) { + next = (nb_swaps + swaps) + max((unsigned long)(100), (unsigned long)(times / 1000)); + int progress = int(double(nb_swaps + swaps) / double(times)); + igraph_progress("Shuffle", progress, 0); + } + } + // test connectivity + cost += (unsigned long)(a / 2); + bool ok = is_connected(); + // performance monitor + { + avg_T += double(T_int); avg_K += double(K_int); + if (ok) { + successes++; + } else { + failures++; + } + } + // restore graph if needed, and count validated swaps + if (ok) { + nb_swaps += swaps; + } else { + restore(save); + next = nb_swaps; + } + delete[] save; + // Adjust K and T following the heuristics. + switch (type) { + int steps; + case GKAN_HEURISTICS: + if (ok) { + T += 1.0; + } else { + T *= 0.5; + } + break; + case FAB_HEURISTICS: + steps = 50 / (8 + failures + successes); + if (steps < 1) { + steps = 1; + } + while (steps--) if (ok) { + T *= 1.17182818; + } else { + T *= 0.9; + } + if (T > double(5 * a)) { + T = double(5 * a); + } + break; + case FINAL_HEURISTICS: + if (ok) { + if ((K + 10.0)*T > 5.0 * double(a)) { + K /= 1.03; + } else { + T *= 2; + } + } else { + K *= 1.35; + delete[] Kbuff; + Kbuff = new int[int(K) + 1]; + } + break; + case OPTIMAL_HEURISTICS: + if (ok) { + T = double(optimal_window()); + } + break; + case BRUTE_FORCE_HEURISTICS: + K *= 2; delete[] Kbuff; Kbuff = new int[int(K) + 1]; + break; + default: + throw std::invalid_argument("Error in graph_molloy_hash::shuffle(): Unknown heuristics type."); + } + } + + delete[] Kbuff; + delete[] visited; + + if (maxtimes <= all_swaps) { + IGRAPH_WARNING("Cannot shuffle graph, maybe it is the only realization of its degree sequence?"); + } + + // Status report + { + igraph_status("*** Shuffle Monitor ***\n", 0); + igraph_statusf(" - Average cost : %f / validated edge swap\n", 0, + double(cost) / double(nb_swaps)); + igraph_statusf(" - Connectivity tests : %d (%d successes, %d failures)\n", + 0, successes + failures, successes, failures); + igraph_statusf(" - Average window : %d\n", 0, + int(avg_T / double(successes + failures))); + if (type == FINAL_HEURISTICS || type == BRUTE_FORCE_HEURISTICS) + igraph_statusf(" - Average isolation test width : %f\n", 0, + avg_K / double(successes + failures)); + } + return nb_swaps; +} + +//_________________________________________________________________________ +void graph_molloy_hash::print(FILE *f) { + int i, j; + for (i = 0; i < n; i++) { + fprintf(f, "%d", i); + for (j = 0; j < HASH_SIZE(deg[i]); j++) if (neigh[i][j] != HASH_NONE) { + fprintf(f, " %d", neigh[i][j]); + } + fprintf(f, "\n"); + } +} + +int graph_molloy_hash::print(igraph_t *graph) { + int i, j; + long int ptr = 0; + igraph_vector_t edges; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, a); // every edge is counted twice.... + + for (i = 0; i < n; i++) { + for (j = 0; j < HASH_SIZE(deg[i]); j++) { + if (neigh[i][j] != HASH_NONE) { + if (neigh[i][j] > i) { + VECTOR(edges)[ptr++] = i; + VECTOR(edges)[ptr++] = neigh[i][j]; + } + } + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, n, /*undirected=*/ 0)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +//_________________________________________________________________________ +bool graph_molloy_hash::try_shuffle(int T, int K, int *backup_graph) { + // init all + int *Kbuff = NULL; + bool *visited = NULL; + if (K > 2) { + Kbuff = new int[K]; + visited = new bool[n]; + for (int i = 0; i < n; i++) { + visited[i] = false; + } + } + int *back = backup_graph; + if (back == NULL) { + back = backup(); + } + // perform T edge swap attempts + while (T--) { + random_edge_swap(K, Kbuff, visited); + } + // clean + if (visited != NULL) { + delete[] visited; + } + if (Kbuff != NULL) { + delete[] Kbuff; + } + // check & restore + bool yo = is_connected(); + restore(back); + if (backup_graph == NULL) { + delete[] back; + } + return yo; +} + +//_________________________________________________________________________ +#define _TRUST_BERNOULLI_LOWER 0.01 + +bool bernoulli_param_is_lower(int success, int trials, double param) { + if (double(success) >= double(trials)*param) { + return false; + } + double comb = 1.0; + double fact = 1.0; + for (int i = 0; i < success; i++) { + comb *= double(trials - i); + fact *= double(i + 1); + } + comb /= fact; + comb *= pow(param, double(success)) * exp(double(trials - success) * log1p(-param)); + double sum = comb; + while (success && sum < _TRUST_BERNOULLI_LOWER) { + comb *= double(success) * (1.0 - param) / (double(trials - success) * param); + sum += comb; + success--; + } + // fprintf(stderr,"bernoulli test : %d/%d success against p=%f -> %s\n",success, trials, param, (sum < _TRUST_BERNOULLI_LOWER) ? "lower" : "can't say"); + return (sum < _TRUST_BERNOULLI_LOWER); +} + +//_________________________________________________________________________ +#define _MIN_SUCCESS_FOR_BERNOULLI_TRUST 100 +double graph_molloy_hash::average_cost(int T, int *backup, double min_cost) { + if (T < 1) { + return 1e+99; + } + int successes = 0; + int trials = 0; + while (successes < _MIN_SUCCESS_FOR_BERNOULLI_TRUST && + !bernoulli_param_is_lower(successes, trials, 1.0 / min_cost)) { + if (try_shuffle(T, 0, backup)) { + successes++; + } + trials++; + } + if (successes >= _MIN_SUCCESS_FOR_BERNOULLI_TRUST) { + return double(trials) / double(successes) * (1.0 + double(a / 2) / double(T)); + } else { + return 2.0 * min_cost; + } +} + +//_________________________________________________________________________ +int graph_molloy_hash::optimal_window() { + int Tmax; + int optimal_T = 1; + double min_cost = 1e+99; + int *back = backup(); + // on cherche une borne sup pour Tmax + int been_greater = 0; + for (Tmax = 1; Tmax <= 5 * a ; Tmax *= 2) { + double c = average_cost(Tmax, back, min_cost); + if (c > 1.5 * min_cost) { + break; + } + if (c > 1.2 * min_cost && ++been_greater >= 3) { + break; + } + if (c < min_cost) { + min_cost = c; + optimal_T = Tmax; + } + igraph_statusf("Tmax = %d [%f]", 0, Tmax, min_cost); + } + // on cree Tmin + int Tmin = int(0.5 * double(a) / (min_cost - 1.0)); + igraph_statusf("Optimal T is in [%d, %d]\n", 0, Tmin, Tmax); + // on cherche autour + double span = 2.0; + int try_again = 4; + while (span > 1.05 && optimal_T <= 5 * a) { + igraph_statusf("Best T [cost]: %d [%f]", 0, optimal_T, min_cost); + int T_low = int(double(optimal_T) / span); + int T_high = int(double(optimal_T) * span); + double c_low = average_cost(T_low, back, min_cost); + double c_high = average_cost(T_high, back, min_cost); + if (c_low < min_cost && c_high < min_cost) { + if (try_again--) { + continue; + } + { + igraph_status("Warning: when looking for optimal T,\n", 0); + igraph_statusf("Low: %d [%f] Middle: %d [%f] High: %d [%f]\n", 0, + T_low, c_low, optimal_T, min_cost, T_high, c_high); + } + delete[] back; + return optimal_T; + } + if (c_low < min_cost) { + optimal_T = T_low; + min_cost = c_low; + } else if (c_high < min_cost) { + optimal_T = T_high; + min_cost = c_high; + }; + span = pow(span, 0.618); + } + delete[] back; + return optimal_T; +} + +//_________________________________________________________________________ +double graph_molloy_hash::eval_K(int quality) { + double K = 5.0; + double avg_K = 1.0; + for (int i = quality; i--; ) { + int int_K = int(floor(K + 0.5)); + if (try_shuffle(a / (int_K + 1), int_K)) { + K *= 0.8; /*fprintf(stderr,"+");*/ + } else { + K *= 1.25; /*fprintf(stderr,"-");*/ + } + if (i < quality / 2) { + avg_K *= K; + } + } + return pow(avg_K, 1.0 / double(quality / 2)); +} + +//_________________________________________________________________________ +double graph_molloy_hash::effective_K(int K, int quality) { + if (K < 3) { + return 0.0; + } + long sum_K = 0; + int *Kbuff = new int[K]; + bool *visited = new bool[n]; + int i; + for (i = 0; i < n; i++) { + visited[i] = false; + } + for (int i = 0; i < quality; i++) { + // assert(verify()); + int f1, f2, t1, t2; + int *f1t1, *f2t2; + do { + // Pick two random vertices + do { + f1 = pick_random_vertex(); + f2 = pick_random_vertex(); + } while (f1 == f2); + // Pick two random neighbours + f1t1 = random_neighbour(f1); + t1 = *f1t1; + f2t2 = random_neighbour(f2); + t2 = *f2t2; + // test simplicity + } while (t1 == t2 || f1 == t2 || f2 == t1 || is_edge(f1, t2) || is_edge(f2, t1)); + // swap + swap_edges(f1, t2, f2, t1); + // assert(verify()); + sum_K += effective_isolated(deg[f1] > deg[t2] ? f1 : t2, K, Kbuff, visited); + // assert(verify()); + sum_K += effective_isolated(deg[f2] > deg[t1] ? f2 : t1, K, Kbuff, visited); + // assert(verify()); + // undo swap + swap_edges(f1, t2, f2, t1); + // assert(verify()); + } + delete[] Kbuff; + delete[] visited; + return double(sum_K) / double(2 * quality); +} + +//_________________________________________________________________________ +long graph_molloy_hash::effective_isolated(int v, int K, int *Kbuff, bool *visited) { + int i; + for (i = 0; i < K; i++) { + Kbuff[i] = -1; + } + long count = 0; + int left = K; + int *KB = Kbuff; + //yapido = (my_random()%1000 == 0); + depth_isolated(v, count, left, K, KB, visited); + while (KB-- != Kbuff) { + visited[*KB] = false; + } + //if(yapido) fprintf(stderr,"\n"); + return count; +} + +//_________________________________________________________________________ +void graph_molloy_hash::depth_isolated(int v, long &calls, int &left_to_explore, int dmax, int * &Kbuff, bool *visited) { + if (left_to_explore == 0) { + return; + } +// if(yapido) fprintf(stderr,"%d ",deg[v]); + if (--left_to_explore == 0) { + return; + } + if (deg[v] + 1 >= dmax) { + left_to_explore = 0; + return; + } + *(Kbuff++) = v; + visited[v] = true; +// print(); +// fflush(stdout); + calls++; + int *copy = NULL; + int *w = neigh[v]; + if (IS_HASH(deg[v])) { + copy = new int[deg[v]]; + H_copy(copy, w, deg[v]); + w = copy; + } + qsort(deg, w, deg[v]); + w += deg[v]; + for (int i = deg[v]; i--; ) { + if (visited[*--w]) { + calls++; + } else { + depth_isolated(*w, calls, left_to_explore, dmax, Kbuff, visited); + } + if (left_to_explore == 0) { + break; + } + } + if (copy != NULL) { + delete[] copy; + } +} + +//_________________________________________________________________________ +int graph_molloy_hash::depth_search(bool *visited, int *buff, int v0) { + for (int i = 0; i < n; i++) { + visited[i] = false; + } + int *to_visit = buff; + int nb_visited = 1; + visited[v0] = true; + *(to_visit++) = v0; + while (to_visit != buff && nb_visited < n) { + int v = *(--to_visit); + int *ww = neigh[v]; + int w; + for (int k = HASH_SIZE(deg[v]); k--; ww++) { + if (HASH_NONE != (w = *ww) && !visited[w]) { + visited[w] = true; + nb_visited++; + *(to_visit++) = w; + } + } + } + return nb_visited; +} + +//_________________________________________________________________________ +// bool graph_molloy_hash::verify() { +// fprintf(stderr,"Warning: graph_molloy_hash::verify() called..\n"); +// fprintf(stderr," try to convert graph into graph_molloy_opt() instead\n"); +// return true; +// } + + +/*____________________________________________________________________________ + Not to use anymore : use graph_molloy_opt class instead + +bool graph_molloy_hash::verify() { +int i; + assert(neigh[0]==links); + // verify edges count + int sum = 0; + for(i=0; in) n=i; + n++; + // degrees ? + if(VERBOSE()) fprintf(stderr,"%d, #edges=",n); + int *degs = new int[n]; + rewind(f); + while(fgets(buff,FBUFF_SIZE,f)) { + int d = 0; + if(sscanf(buff,"%d",&i)==1) { + char *b = buff; + while(skip_int(b)) d++; + degs[i]=d; + } + } + // allocate memory + degree_sequence dd(n,degs); + if(VERBOSE()) fprintf(stderr,"%d\nAllocating memory...",dd.sum()); + alloc(dd); + // add edges + if(VERBOSE()) fprintf(stderr,"done\nCreating edges..."); + rewind(f); + for(i=0; im) m=deg[k]; + return m; +} + + +bool graph_molloy_hash::havelhakimi() { + + int i; + int dmax = max_degree()+1; + // Sort vertices using basket-sort, in descending degrees + int *nb = new int[dmax]; + int *sorted = new int[n]; + // init basket + for(i=0; i=0; i--) { + int t=nb[i]; + nb[i]=c; + c+=t; + } + // sort + for(i=0; i0; ) { + // pick a vertex. we could pick any, but here we pick the one with biggest degree + int v = sorted[first]; + // look for current degree of v + while(nb[d]<=first) d--; + // store it in dv + int dv = d; + // bind it ! + c -= dv; + int dc = d; // residual degree of vertices we bind to + int fc = ++first; // position of the first vertex with degree dc + + while(dv>0 && dc>0) { + int lc = nb[dc]; + if(lc!=fc) { + while(dv>0 && lc>fc) { + // binds v with sorted[--lc] + dv--; + int w = sorted[--lc]; + add_edge(v,w); + } + fc = nb[dc]; + nb[dc] = lc; + } + dc--; + } + if(dv != 0) { // We couldn't bind entirely v + if(VERBOSE()) { + fprintf(stderr,"Error in graph_molloy_hash::havelhakimi() :\n"); + fprintf(stderr,"Couldn't bind vertex %d entirely (%d edges remaining)\n",v,dv); + } + delete[] nb; + delete[] sorted; + return false; + } + } + assert(c==0); + delete[] nb; + delete[] sorted; + return true; +} + + +bool graph_molloy_hash::make_connected() { + assert(verify()); + if(a/2 < n-1) { + // fprintf(stderr,"\ngraph::make_connected() failed : #edges < #vertices-1\n"); + return false; + } + int i; + +// Data struct for the visit : +// - buff[] contains vertices to visit +// - dist[V] is V's distance modulo 4 to the root of its comp, or -1 if it hasn't been visited yet +#define MC_BUFF_SIZE (n+2) + int *buff = new int[MC_BUFF_SIZE]; + unsigned char * dist = new unsigned char[n]; +#define NOT_VISITED 255 +#define FORBIDDEN 254 + for(i=n; i>0; dist[--i]=NOT_VISITED); + +// Data struct to store components : either surplus trees or surplus edges are stored at buff[]'s end +// - A Tree is coded by one of its vertices +// - An edge (a,b) is coded by the TWO ints a and b + int *ffub = buff+MC_BUFF_SIZE; + edge *edges = (edge *) ffub; + int *trees = ffub; + int *min_ffub = buff+1+(MC_BUFF_SIZE%2 ? 0 : 1); + +// There will be only one "fatty" component, and trees. + edge fatty_edge; + fatty_edge.from = -1; + bool enough_edges = false; + + // start main loop + for(int v0=0; v0min_ffub) min_ffub+=2; // update limit of ffub's storage + //assert(verify()); + } + else if(dist[w]==next_dist || (w!=HASH_NONE && w>v && dist[w]==current_dist)) { + // we found a removable edge + if(is_a_tree) { + // we must first merge with the fatty component + is_a_tree = false; + if(fatty_edge.from < 0) { + // we ARE the first component! fatty is us + fatty_edge.from = v; + fatty_edge.to = w; + } + else { + // we connect to fatty + swap_edges(fatty_edge.from, fatty_edge.to, v, w); + //assert(verify()); + } + } + else { + // we have removable edges to give! + if(trees!=ffub) { + // some trees still.. Let's merge with them! + assert(trees>=min_ffub); + assert(edges==(edge *)ffub); + swap_edges(v,w,*trees,neigh[*trees][0]); + trees++; + //assert(verify()); + } + else if(!enough_edges) { + // Store the removable edge for future use + if(edges<=(edge *)min_ffub+1) + enough_edges = true; + else { + edges--; + edges->from = v; + edges->to = w; + } + } + } + } + } + } + // Mark component + while(to_visit!=buff) dist[*(--to_visit)] = FORBIDDEN; + // Check if it is a tree + if(is_a_tree ) { + assert(deg[v0]!=0); + if(edges!=(edge *)ffub) { + // let's bind the tree we found with a removable edge in stock + assert(trees == ffub); + if(edges<(edge *)min_ffub) edges=(edge *)min_ffub; + swap_edges(v0,neigh[v0][0],edges->from,edges->to); + edges++; + assert(verify()); + } + else { + // add the tree to the list of trees + assert(trees>min_ffub); + *(--trees) = v0; + assert(verify()); + } + } + } + delete[] buff; + delete[] dist; + return(trees == ffub); +} + +int64_t graph_molloy_hash::slow_connected_shuffle(int64_t times) { + assert(verify()); + int64_t nb_swaps = 0; + int T = 1; + + while(times>nb_swaps) { + // Backup graph + int *save = backup(); + // Swaps + int swaps = 0; + for(int i=T; i>0; i--) { + // Pick two random vertices a and c + int f1 = pick_random_vertex(); + int f2 = pick_random_vertex(); + // Check that f1 != f2 + if(f1==f2) continue; + // Get two random edges (f1,*f1t1) and (f2,*f2t2) + int *f1t1 = random_neighbour(f1); + int t1 = *f1t1; + int *f2t2 = random_neighbour(f2); + int t2 = *f2t2; + // Check simplicity + if(t1==t2 || f1==t2 || f2==t1) continue; + if(is_edge(f1,t2) || is_edge(f2,t1)) continue; + // Swap + H_rpl(neigh[f1],deg[f1],f1t1,t2); + H_rpl(neigh[f2],deg[f2],f2t2,t1); + H_rpl(neigh[t1],deg[t1],f1,f2); + H_rpl(neigh[t2],deg[t2],f2,f1); + swaps++; + } + // test connectivity + bool ok = is_connected(); + if(ok) { + nb_swaps += swaps; + } + else { + restore(save); + } + delete[] save; + } + return nb_swaps; +} + + +int graph_molloy_hash::width_search(unsigned char *dist, int *buff, int v0) { + for(int i=0; i. + */ +#ifndef GRAPH_MOLLOY_HASH_H +#define GRAPH_MOLLOY_HASH_H + +#include "gengraph_definitions.h" +#include "gengraph_hash.h" +#include "gengraph_degree_sequence.h" + +#include +#include +// This class handles graphs with a constant degree sequence. + +#define FINAL_HEURISTICS 0 +#define GKAN_HEURISTICS 1 +#define FAB_HEURISTICS 2 +#define OPTIMAL_HEURISTICS 3 +#define BRUTE_FORCE_HEURISTICS 4 + +namespace gengraph { + +//**************************** +// class graph_molloy_hash +//**************************** + +class graph_molloy_hash { + +private: + // Number of vertices + int n; + //Number of arcs ( = #edges * 2 ) + int a; + //Total size of links[] + int size; + // The degree sequence of the graph + int *deg; + // The array containing all links + int *links; + // The array containing pointers to adjacency list of every vertices + int **neigh; + // Counts total size + void compute_size(); + // Build neigh with deg and links + void compute_neigh(); + // Allocate memory according to degree_sequence (for constructor use only!!) + int alloc(degree_sequence &); + // Add edge (u,v). Return FALSE if vertex a is already full. + // WARNING : only to be used by havelhakimi(), restore() or constructors + inline bool add_edge(int u, int v, int *realdeg) { + int deg_u = realdeg[u]; + if (deg_u == deg[u]) { + return false; + } + // Check that edge was not already inserted + assert(fast_search(neigh[u], int((u == n - 1 ? links + size : neigh[u + 1]) - neigh[u]), v) == NULL); + assert(fast_search(neigh[v], int((v == n - 1 ? links + size : neigh[v + 1]) - neigh[v]), u) == NULL); + assert(deg[u] < deg_u); + int deg_v = realdeg[v]; + if (IS_HASH(deg_u)) { + *H_add(neigh[u], HASH_EXPAND(deg_u), v) = v; + } else { + neigh[u][deg[u]] = v; + } + if (IS_HASH(deg_v)) { + *H_add(neigh[v], HASH_EXPAND(deg_v), u) = u; + } else { + neigh[v][deg[v]] = u; + } + deg[u]++; + deg[v]++; + // Check that edge was actually inserted + assert(fast_search(neigh[u], int((u == n - 1 ? links + size : neigh[u + 1]) - neigh[u]), v) != NULL); + assert(fast_search(neigh[v], int((v == n - 1 ? links + size : neigh[v + 1]) - neigh[v]), u) != NULL); + return true; + } + // Swap edges + inline void swap_edges(int from1, int to1, int from2, int to2) { + H_rpl(neigh[from1], deg[from1], to1, to2); + H_rpl(neigh[from2], deg[from2], to2, to1); + H_rpl(neigh[to1], deg[to1], from1, from2); + H_rpl(neigh[to2], deg[to2], from2, from1); + } + // Backup graph [sizeof(int) bytes per edge] + int* backup(); + // Test if vertex is in an isolated component of size dmax. + void depth_isolated(int v, long &calls, int &left_to_explore, int dmax, int * &Kbuff, bool *visited); + + +public: + //degree of v + inline int degree(const int v) { + return deg[v]; + }; + // For debug purposes : verify validity of the graph (symetry, simplicity) + bool verify(); + // Destroy deg[], neigh[] and links[] + ~graph_molloy_hash(); + // Allocate memory for the graph. Create deg and links. No edge is created. + graph_molloy_hash(degree_sequence &); + // Create graph from hard copy + graph_molloy_hash(int *); + // Create hard copy of graph + int *hard_copy(); + // Restore from backup + void restore(int* back); + //Clear hash tables + void init(); + // nb arcs + inline int nbarcs() { + return a; + }; + // nb vertices + inline int nbvertices() { + return n; + }; + // print graph in SUCC_LIST mode, in stdout + void print(FILE *f = stdout); + int print(igraph_t *graph); + // Test if graph is connected + bool is_connected(); + // is edge ? + inline bool is_edge(int u, int v) { + assert(H_is(neigh[u], deg[u], v) == (fast_search(neigh[u], HASH_SIZE(deg[u]), v) != NULL)); + assert(H_is(neigh[v], deg[v], u) == (fast_search(neigh[v], HASH_SIZE(deg[v]), u) != NULL)); + assert(H_is(neigh[u], deg[u], v) == H_is(neigh[v], deg[v], u)); + if (deg[u] < deg[v]) { + return H_is(neigh[u], deg[u], v); + } else { + return H_is(neigh[v], deg[v], u); + } + } + // Random edge swap ATTEMPT. Return 1 if attempt was a succes, 0 otherwise + int random_edge_swap(int K = 0, int *Kbuff = NULL, bool *visited = NULL); + // Connected Shuffle + unsigned long shuffle(unsigned long, unsigned long, int type); + // Optimal window for the gkantsidis heuristics + int optimal_window(); + // Average unitary cost per post-validated edge swap, for some window + double average_cost(int T, int *back, double min_cost); + // Get caracteristic K + double eval_K(int quality = 100); + // Get effective K + double effective_K(int K, int quality = 10000); + // Try to shuffle T times. Return true if at the end, the graph was still connected. + bool try_shuffle(int T, int K, int *back = NULL); + + + /*_____________________________________________________________________________ + Not to use anymore : use graph_molloy_opt class instead + + private: + // breadth-first search. Store the distance (modulo 3) in dist[]. Returns eplorated component size. + int width_search(unsigned char *dist, int *buff, int v0=0); + + public: + // Create graph + graph_molloy_hash(FILE *f); + // Bind the graph avoiding multiple edges or self-edges (return false if fail) + bool havelhakimi(); + // Get the graph connected (return false if fail) + bool make_connected(); + // "Fab" Shuffle (Optimized heuristic of Gkantsidis algo.) + long long fab_connected_shuffle(long long); + // Naive Shuffle + long long slow_connected_shuffle(long long); + // Maximum degree + int max_degree(); + // compute vertex betweenness : for each vertex, a unique random shortest path is chosen. + // this choice is consistent (if shortest path from a to c goes through b and then d, + // then shortest path from a to d goes through b). If(trivial path), also count all the + // shortest paths where vertex is an extremity + int *vertex_betweenness_rsp(bool trivial_path); + // same, but when multiple shortest path are possible, average the weights. + double *vertex_betweenness_asp(bool trivial_path); + //___________________________________________________________________________________ + */ + +}; + +} // namespace gengraph + +#endif //GRAPH_MOLLOY_HASH_H diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp new file mode 100644 index 0000000..2859cdb --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp @@ -0,0 +1,2231 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 . + */ +#include "gengraph_definitions.h" +#include +#include +#include +#include + +#include "gengraph_qsort.h" +#include "gengraph_box_list.h" +#include "gengraph_vertex_cover.h" +#include "gengraph_degree_sequence.h" +#include "gengraph_graph_molloy_optimized.h" + +#include "igraph_error.h" +#include "igraph_statusbar.h" +#include "igraph_progress.h" + + +using namespace std; + +namespace gengraph { + +void graph_molloy_opt::breadth_search(int *dist, int v0, int *buff) { + bool tmpbuff = (buff == NULL); + if (tmpbuff) { + buff = new int[n]; + } + for (int i = 0; i < n; i++) { + dist[i] = -1; + } + dist[v0] = 0; + int *visited = buff; + int *to_visit = buff; + *to_visit++ = v0; + while (visited != to_visit) { + int v = *visited++; + int *w = neigh[v]; + int dd = dist[v] + 1; + for (int d = deg[v]; d--; w++) if (dist[*w] < 0) { + dist[*w] = dd; + *to_visit++ = *w; + } + } + if (tmpbuff) { + delete[] buff; + } +} + + +int graph_molloy_opt::max_degree() { + int m = 0; + for (int k = 0; k < n; k++) if (deg[k] > m) { + m = deg[k]; + } + return m; +} + +void graph_molloy_opt::compute_neigh() { + int *p = links; + for (int i = 0; i < n; i++) { + neigh[i] = p; + p += deg[i]; + } +} + +void graph_molloy_opt::alloc(degree_sequence °s) { + n = degs.size(); + a = degs.sum(); + assert(a % 2 == 0); + deg = new int[n + a]; + for (int i = 0; i < n; i++) { + deg[i] = degs[i]; + } + links = deg + n; + neigh = new int*[n]; + compute_neigh(); +} + +graph_molloy_opt::graph_molloy_opt(degree_sequence °s) { + alloc(degs); +} + +// graph_molloy_opt::graph_molloy_opt(FILE *f) { +// char *buff = new char[FBUFF_SIZE]; +// // How many vertices ? +// if(VERBOSE()) fprintf(stderr,"Read file: #vertices="); +// int i; +// int n=0; +// while(fgets(buff,FBUFF_SIZE,f)) if(sscanf(buff,"%d",&i)==1 && i>n) n=i; +// n++; +// // degrees ? +// if(VERBOSE()) fprintf(stderr,"%d, #edges=",n); +// int *degs = new int[n]; +// for(i=0; i= i) { + *(c++) = *p; + } + } + } + assert(c == b + (a / 2)); + return b; +} + +int *graph_molloy_opt::hard_copy() { + int *hc = new int[2 + n + a / 2]; // to store n,a,deg[] and links[] + hc[0] = n; + hc[1] = a; + memcpy(hc + 2, deg, sizeof(int)*n); + int *c = hc + 2 + n; + for (int i = 0; i < n; i++) { + int *p = neigh[i]; + for (int d = deg[i]; d--; p++) { + assert(*p != i); + if (*p >= i) { + *(c++) = *p; + } + } + } + assert(c == hc + 2 + n + a / 2); + return hc; +} + +void graph_molloy_opt::restore(int* b) { + int i; + for (i = 0; i < n; i++) { + deg[i] = 0; + } + int *p = links; + for (i = 0; i < n - 1; i++) { + p += deg[i]; + deg[i] = int(neigh[i + 1] - neigh[i]); + assert((neigh[i] + deg[i]) == neigh[i + 1]); + while (p != neigh[i + 1]) { + // b points to the current 'j' + neigh[*b][deg[*b]++] = i; + *(p++) = *(b++); + } + } +} + +int* graph_molloy_opt::backup_degs(int *b) { + if (b == NULL) { + b = new int[n]; + } + memcpy(b, deg, sizeof(int)*n); + return b; +} + +void graph_molloy_opt::restore_degs_only(int *b) { + memcpy(deg, b, sizeof(int)*n); + refresh_nbarcs(); +} + +void graph_molloy_opt::restore_degs_and_neigh(int *b) { + restore_degs_only(b); + compute_neigh(); +} + +void graph_molloy_opt::restore_degs(int last_degree) { + a = last_degree; + deg[n - 1] = last_degree; + for (int i = n - 2; i >= 0; i--) { + a += (deg[i] = int(neigh[i + 1] - neigh[i])); + } + refresh_nbarcs(); +} + +void graph_molloy_opt::clean() { + int *b = hard_copy(); + replace(b); + delete[] b; +} + +void graph_molloy_opt::replace(int *_hardcopy) { + delete[] deg; + n = *(_hardcopy++); + a = *(_hardcopy++); + deg = new int[a + n]; + memcpy(deg, _hardcopy, sizeof(int)*n); + links = deg + n; + compute_neigh(); + restore(_hardcopy + n); +} + +int* graph_molloy_opt::components(int *comp) { + int i; + // breadth-first search buffer + int *buff = new int[n]; + // comp[i] will contain the index of the component that contains vertex i + if (comp == NULL) { + comp = new int[n]; + } + memset(comp, 0, sizeof(int)*n); + // current component index + int curr_comp = 0; + // loop over all non-visited vertices... + for (int v0 = 0; v0 < n; v0++) if (comp[v0] == 0) { + curr_comp++; + // initiate breadth-first search + int *to_visit = buff; + int *visited = buff; + *(to_visit++) = v0; + comp[v0] = curr_comp; + // breadth-first search + while (visited != to_visit) { + int v = *(visited++); + int d = deg[v]; + for (int *w = neigh[v]; d--; w++) if (comp[*w] == 0) { + comp[*w] = curr_comp; + *(to_visit++) = *w; + } + } + } + // compute component sizes and store them in buff[] + int nb_comp = 0; + memset(buff, 0, sizeof(int)*n); + for (i = 0; i < n; i++) + if (buff[comp[i] - 1]++ == 0 && comp[i] > nb_comp) { + nb_comp = comp[i]; + } + // box-sort sizes + int offset = 0; + int *box = pre_boxsort(buff, nb_comp, offset); + for (i = nb_comp - 1; i >= 0; i--) { + buff[i] = --box[buff[i] - offset]; + } + delete[] box; + // reassign component indexes + for (int *c = comp + n; comp != c--; *c = buff[*c - 1]) { } + // clean.. at last! + delete[] buff; + return comp; +} + +void graph_molloy_opt::giant_comp() { + int *comp = components(); + // Clear edges of all vertices that do not belong to comp 0 + for (int i = 0; i < n; i++) if (comp[i] != 0) { + deg[i] = 0; + } + // Clean comp[] + delete[] comp; +} + +int graph_molloy_opt::nbvertices_comp() { + int *comp = components(); + // Count all vertices that belong to comp 0 + int nb = 0; + for (int i = 0; i < n; i++) if (comp[i] == 0) { + nb++; + } + // Clean comp[] + delete[] comp; + return nb; +} + +int graph_molloy_opt::nbarcs_comp() { + int *comp = components(); + // Count all vertices that belong to comp 0 + int nb = 0; + for (int i = 0; i < n; i++) if (comp[i] == 0) { + nb += deg[i]; + } + // Clean comp[] + delete[] comp; + return nb; +} + +bool graph_molloy_opt::havelhakimi() { + + int i; + int dmax = max_degree() + 1; + // Sort vertices using basket-sort, in descending degrees + int *nb = new int[dmax]; + int *sorted = new int[n]; + // init basket + for (i = 0; i < dmax; i++) { + nb[i] = 0; + } + // count basket + for (i = 0; i < n; i++) { + nb[deg[i]]++; + } + // cumul + int c = 0; + for (i = dmax - 1; i >= 0; i--) { + c += nb[i]; + nb[i] = -nb[i] + c; + } + // sort + for (i = 0; i < n; i++) { + sorted[nb[deg[i]]++] = i; + } + +// Binding process starts + int first = 0; // vertex with biggest residual degree + int d = dmax - 1; // maximum residual degree available + + for (c = a / 2; c > 0; ) { + // pick a vertex. we could pick any, but here we pick the one with biggest degree + int v = sorted[first]; + // look for current degree of v + while (nb[d] <= first) { + d--; + } + // store it in dv + int dv = d; + // bind it ! + c -= dv; + int dc = d; // residual degree of vertices we bind to + int fc = ++first; // position of the first vertex with degree dc + + while (dv > 0 && dc > 0) { + int lc = nb[dc]; + if (lc != fc) { + while (dv > 0 && lc > fc) { + // binds v with sorted[--lc] + dv--; + int w = sorted[--lc]; + *(neigh[v]++) = w; + *(neigh[w]++) = v; + } + fc = nb[dc]; + nb[dc] = lc; + } + dc--; + } + if (dv != 0) { // We couldn't bind entirely v + delete[] nb; + delete[] sorted; + compute_neigh(); + + /* Cannot use IGRAPH_ERRORF() as this function does not return + * an error code. This situation should only occur when the degree + * sequence is not graphical, but that is already checked at the top + * level. Therefore, we report EINTERNAL, as triggering this + * indicates a bug. */ + igraph_errorf("Error in graph_molloy_opt::havelhakimi(): " + "Couldn't bind vertex %d entirely (%d edges remaining)", + IGRAPH_FILE_BASENAME, __LINE__, + IGRAPH_EINTERNAL, v, dv); + return false; + } + } + assert(c == 0); + compute_neigh(); + delete[] nb; + delete[] sorted; + return true; +} + +bool graph_molloy_opt::is_connected() { + bool *visited = new bool[n]; + for (int i = n; i > 0; visited[--i] = false) { } + int *to_visit = new int[n]; + int *stop = to_visit; + int left = n - 1; + *(to_visit++) = 0; + visited[0] = true; + while (left > 0 && to_visit != stop) { + int v = *(--to_visit); + int *w = neigh[v]; + for (int k = deg[v]; k--; w++) if (!visited[*w]) { + visited[*w] = true; + left--; + *(to_visit++) = *w; + } + } + delete[] visited; + delete[] stop; + assert(left >= 0); + return (left == 0); +} + + +bool graph_molloy_opt::make_connected() { + //assert(verify()); + if (a / 2 < n - 1) { + // fprintf(stderr,"\ngraph::make_connected() failed : #edges < #vertices-1\n"); + return false; + } + int i; + +// Data struct for the visit : +// - buff[] contains vertices to visit +// - dist[V] is V's distance modulo 4 to the root of its comp, or -1 if it hasn't been visited yet +#define MC_BUFF_SIZE (n+2) + int *buff = new int[MC_BUFF_SIZE]; + unsigned char * dist = new unsigned char[n]; +#define NOT_VISITED 255 +#define FORBIDDEN 254 + for (i = n; i > 0; dist[--i] = NOT_VISITED) { } + +// Data struct to store components : either surplus trees or surplus edges are stored at buff[]'s end +// - A Tree is coded by one of its vertices +// - An edge (a,b) is coded by the TWO ints a and b + int *ffub = buff + MC_BUFF_SIZE; + edge *edges = (edge *) ffub; + int *trees = ffub; + int *min_ffub = buff + 1 + (MC_BUFF_SIZE % 2 ? 0 : 1); + +// There will be only one "fatty" component, and trees. + edge fatty_edge = { -1, -1 }; + bool enough_edges = false; + + // start main loop + for (int v0 = 0; v0 < n; v0++) if (dist[v0] == NOT_VISITED) { + // is v0 an isolated vertex? + if (deg[v0] == 0) { + delete[] dist; + delete[] buff; + /* Cannot use IGRAPH_ERROR() as this function does not return an error code. */ + igraph_error("Cannot create connected graph from degree sequence: " + "vertex with degree 0 found.", + IGRAPH_FILE_BASENAME, __LINE__, + IGRAPH_EINVAL); + return false; + } + dist[v0] = 0; // root + int *to_visit = buff; + int *current = buff; + *(to_visit++) = v0; + + // explore component connected to v0 + bool is_a_tree = true; + while (current != to_visit) { + int v = *(current++); + unsigned char current_dist = dist[v]; + unsigned char next_dist = (current_dist + 1) & 0x03; + //unsigned char prev_dist = (current_dist-1) & 0x03; + int* ww = neigh[v]; + int w; + for (int k = deg[v]; k--; ww++) { + if (dist[w = *ww] == NOT_VISITED) { + // we didn't visit *w yet + dist[w] = next_dist; + *(to_visit++) = w; + if (to_visit > min_ffub) { + min_ffub += 2; // update limit of ffub's storage + } + //assert(verify()); + } else if (dist[w] == next_dist || (w >= v && dist[w] == current_dist)) { + // we found a removable edge + if (trees != ffub) { + // some trees still.. Let's merge with them! + assert(trees >= min_ffub); + assert(edges == (edge *)ffub); + swap_edges(v, w, *trees, neigh[*trees][0]); + trees++; + //assert(verify()); + } else if (is_a_tree) { + // we must merge with the fatty component + is_a_tree = false; + if (fatty_edge.from < 0) { + // we ARE the first component! fatty is us + fatty_edge.from = v; + fatty_edge.to = w; + } else { + // we connect to fatty + swap_edges(fatty_edge.from, fatty_edge.to, v, w); + fatty_edge.to = w; + //assert(verify()); + } + } else if (!enough_edges) { + // Store the removable edge for future use + if (edges <= (edge *)min_ffub + 1) { + enough_edges = true; + } else { + edges--; + edges->from = v; + edges->to = w; + } + } + } + } + } + // Mark component + while (to_visit != buff) { + dist[*(--to_visit)] = FORBIDDEN; + } + // Check if it is a tree + if (is_a_tree ) { + assert(deg[v0] != 0); + if (edges != (edge *)ffub) { + // let's bind the tree we found with a removable edge in stock + assert(trees == ffub); + if (edges < (edge *)min_ffub) { + edges = (edge *)min_ffub; + } + swap_edges(v0, neigh[v0][0], edges->from, edges->to); + edges++; + assert(verify()); + } else if (fatty_edge.from >= 0) { + // if there is a fatty component, let's merge with it ! and discard fatty :-/ + assert(trees == ffub); + swap_edges(v0, neigh[v0][0], fatty_edge.from, fatty_edge.to); + fatty_edge.from = -1; + fatty_edge.to = -1; + assert(verify()); + } else { + // add the tree to the list of trees + assert(trees > min_ffub); + *(--trees) = v0; + assert(verify()); + } + } + } + delete[] buff; + delete[] dist; + // Should ALWAYS return true : either we have no tree left, or we are a unique, big tree + return (trees == ffub || ((trees + 1) == ffub && fatty_edge.from < 0)); +} + +bool graph_molloy_opt::swap_edges_simple(int from1, int to1, int from2, int to2) { + if (from1 == to1 || from1 == from2 || from1 == to2 || to1 == from2 || to1 == to2 || from2 == to2) { + return false; + } + if (is_edge(from1, to2) || is_edge(from2, to1)) { + return false; + } + swap_edges(from1, to1, from2, to2); + return true; +} + +long graph_molloy_opt::fab_connected_shuffle(long times) { + //assert(verify()); + long nb_swaps = 0; + double T = double(min(a, times)) / 10.0; + double q1 = 1.131; + double q2 = 0.9237; + + while (times > 0) { + long iperiod = max(1, long(T)); + // Backup graph + int *save = backup(); + //assert(verify()); + // Swaps + long swaps = 0; + for (long i = iperiod; i > 0; i--) { + // Pick two random vertices + int f1 = links[my_random() % a]; + int f2 = links[my_random() % a]; + if (f1 == f2) { + continue; + } + // Pick two random neighbours + int *f1t1 = neigh[f1] + my_random() % deg[f1]; + int *f2t2 = neigh[f2] + my_random() % deg[f2]; + int t1 = *f1t1; + int t2 = *f2t2; + // test simplicity + if (t1 != t2 && f1 != t2 && f2 != t1 && is_edge(f1, t2) && !is_edge(f2, t1)) { + // swap + *f1t1 = t2; + *f2t2 = t1; + fast_rpl(neigh[t1], f1, f2); + fast_rpl(neigh[t2], f2, f1); + swaps++; + } + } + //assert(verify()); + // test connectivity + if (is_connected()) { + nb_swaps += swaps; + times -= iperiod; + // adjust T + T *= q1; + } else { + restore(save); + //assert(verify()); + T *= q2; + } + delete[] save; + } + return nb_swaps; +} + +long graph_molloy_opt::opt_fab_connected_shuffle(long times) { + //assert(verify()); + long nb_swaps = 0; + double T = double(min(a, times)) / 10.0; + double q1 = 1.131; + double q2 = 0.9237; + + while (times > 0) { + long iperiod = max(1, long(T)); + // Backup graph + int *save = backup(); + //assert(verify()); + // Swaps + long swaps = 0; + for (long i = iperiod; i > 0; i--) { + // Pick two random vertices + int f1 = links[my_random() % a]; + int f2 = links[my_random() % a]; + if (f1 == f2) { + continue; + } + // Pick two random neighbours + int *f1t1 = neigh[f1] + my_random() % deg[f1]; + int *f2t2 = neigh[f2] + my_random() % deg[f2]; + int t1 = *f1t1; + int t2 = *f2t2; + if ( + // test simplicity + t1 != t2 && f1 != t2 && f2 != t1 && is_edge(f1, t2) && !is_edge(f2, t1) && + // test isolated pair + (deg[f1] > 1 || deg[t2] > 1) && (deg[f2] > 1 || deg[t1] > 1) + ) { + // swap + *f1t1 = t2; + *f2t2 = t1; + fast_rpl(neigh[t1], f1, f2); + fast_rpl(neigh[t2], f2, f1); + swaps++; + } + } + //assert(verify()); + // test connectivity + if (is_connected()) { + nb_swaps += swaps; + times -= iperiod; + // adjust T + T *= q1; + } else { + restore(save); + //assert(verify()); + T *= q2; + } + delete[] save; + } + return nb_swaps; +} + +long graph_molloy_opt::gkantsidis_connected_shuffle(long times) { + //assert(verify()); + long nb_swaps = 0; + long T = min(a, times) / 10; + + while (times > 0) { + // Backup graph + int *save = backup(); + //assert(verify()); + // Swaps + long swaps = 0; + for (int i = T; i > 0; i--) { + // Pick two random vertices + int f1 = links[my_random() % a]; + int f2 = links[my_random() % a]; + if (f1 == f2) { + continue; + } + // Pick two random neighbours + int *f1t1 = neigh[f1] + my_random() % deg[f1]; + int *f2t2 = neigh[f2] + my_random() % deg[f2]; + int t1 = *f1t1; + int t2 = *f2t2; + // test simplicity + if (t1 != t2 && f1 != t2 && f2 != t1 && is_edge(f1, t2) && !is_edge(f2, t1)) { + // swap + *f1t1 = t2; + *f2t2 = t1; + fast_rpl(neigh[t1], f1, f2); + fast_rpl(neigh[t2], f2, f1); + swaps++; + } + } + //assert(verify()); + // test connectivity + if (is_connected()) { + nb_swaps += swaps; + times -= T; + // adjust T + T++; + } else { + restore(save); + //assert(verify()); + T /= 2; if (T == 0) T = 1; + } + delete[] save; + } + return nb_swaps; +} + +long graph_molloy_opt::slow_connected_shuffle(long times) { + //assert(verify()); + long nb_swaps = 0; + + while (times--) { + // Pick two random vertices + int f1 = links[my_random() % a]; + int f2 = links[my_random() % a]; + if (f1 == f2) { + continue; + } + // Pick two random neighbours + int *f1t1 = neigh[f1] + my_random() % deg[f1]; + int *f2t2 = neigh[f2] + my_random() % deg[f2]; + int t1 = *f1t1; + int t2 = *f2t2; + // test simplicity + if (t1 != t2 && f1 != t2 && f2 != t1 && is_edge(f1, t2) && !is_edge(f2, t1)) { + // swap + *f1t1 = t2; + *f2t2 = t1; + int *t1f1 = fast_rpl(neigh[t1], f1, f2); + int *t2f2 = fast_rpl(neigh[t2], f2, f1); + // test connectivity + if (is_connected()) { + nb_swaps++; + } else { + // undo swap + *t1f1 = f1; *t2f2 = f2; *f1t1 = t1; *f2t2 = t2; + } + } + } + return nb_swaps; +} + +void graph_molloy_opt::print(FILE *f, bool NOZERO) { + int i, j; + for (i = 0; i < n; i++) { + if (!NOZERO || deg[i] > 0) { + fprintf(f, "%d", i); + for (j = 0; j < deg[i]; j++) { + fprintf(f, " %d", neigh[i][j]); + } + fprintf(f, "\n"); + } + } +} + +long graph_molloy_opt::effective_isolated(int v, int K, int *Kbuff, bool *visited) { + int i; + for (i = 0; i < K; i++) { + Kbuff[i] = -1; + } + long count = 0; + int left = K; + int *KB = Kbuff; + //yapido = (my_random()%1000 == 0); + depth_isolated(v, count, left, K, KB, visited); + while (KB-- != Kbuff) { + visited[*KB] = false; + } + //if(yapido) fprintf(stderr,"\n"); + return count; +} + +void graph_molloy_opt::depth_isolated(int v, long &calls, int &left_to_explore, int dmax, int * &Kbuff, bool *visited) { + if (left_to_explore == 0) { + return; + } +// if(yapido) fprintf(stderr,"%d ",deg[v]); + if (--left_to_explore == 0) { + return; + } + if (deg[v] + 1 >= dmax) { + left_to_explore = 0; + return; + } + *(Kbuff++) = v; + visited[v] = true; + calls++; + int *w = neigh[v]; + qsort(deg, w, deg[v]); + w += deg[v]; + for (int i = deg[v]; i--; ) { + if (visited[*--w]) { + calls++; + } else { + depth_isolated(*w, calls, left_to_explore, dmax, Kbuff, visited); + } + if (left_to_explore == 0) { + break; + } + } +} + +int graph_molloy_opt::depth_search(bool *visited, int *buff, int v0) { + for (int i = 0; i < n; i++) { + visited[i] = false; + } + int *to_visit = buff; + int nb_visited = 1; + visited[v0] = true; + *(to_visit++) = v0; + while (to_visit != buff && nb_visited < n) { + int v = *(--to_visit); + int *ww = neigh[v]; + int w; + for (int k = deg[v]; k--; ww++) if (!visited[w = *ww]) { + visited[w] = true; + nb_visited++; + *(to_visit++) = w; + } + } + return nb_visited; +} + +int graph_molloy_opt::width_search(unsigned char *dist, int *buff, int v0, int toclear) { + if (toclear >= 0) for (int i = 0; i < toclear; i++) { + dist[buff[i]] = 0; + } else for (int i = 0; i < n; i++) { + dist[i] = 0; + } + int *to_visit = buff; + int *to_add = buff; + int nb_visited = 1; + dist[v0] = 1; + *(to_add++) = v0; + while (to_visit != to_add && nb_visited < n) { + int v = *(to_visit++); + int *ww = neigh[v]; + int w; + unsigned char d = next_dist(dist[v]); + for (int k = deg[v]; k--; ww++) if (dist[w = *ww] == 0) { + dist[w] = d; + nb_visited++; + *(to_add++) = w; + } + } + return nb_visited; +} + +double graph_molloy_opt::avg_dist(unsigned char *dist, int *buff, int v0, int &nb_visited, int toclear) { + nb_visited = width_search(dist, buff, v0, toclear); + unsigned char curr_dist = 1; + assert(curr_dist == dist[v0]); + double total_dist = 0.0; + int current_dist = 0; + for (int p = 0; p < nb_visited; p++) { + v0 = buff[p]; + if (dist[v0] != curr_dist) { + current_dist++; + curr_dist = dist[v0]; + } + total_dist += double(current_dist); + } + nb_visited--; + return total_dist / double(nb_visited); +} + + +void graph_molloy_opt::add_traceroute_edge(int v, int k, int *newdeg, double **edge_redudancy, double red) { + int *ww = neigh[v] + k; + int w = *ww; + int k2 = 0; + // Is neigh[v][k] a new edge ? + if (k >= newdeg[v]) { + int *p = neigh[v] + (newdeg[v]++); + *ww = *p; + *p = w; + // Now, add the dual edge + ww = neigh[w]; + p = ww + (newdeg[w]); + while (ww != p && *ww != v) { + ww++; + k2++; + } + if (ww == p) { + // dual edge was not discovered.. search it and add it. + while (*ww != v) { + ww++; + k2++; + } + *ww = *p; + *p = v; + newdeg[w]++; + } + } + // if edge redudancy is asked, look for dual edge + else if (edge_redudancy != NULL) + for (int *ww = neigh[w]; * (ww++) != v; k2++) { } + // add edge redudancy + if (edge_redudancy != NULL) { + edge_redudancy[v][k] += red; + edge_redudancy[w][k2] += red; + } + assert(newdeg[v] <= deg[v]); +} + +// dist[] MUST be full of zeros !!!! +int graph_molloy_opt::breadth_path_search(int src, int *buff, double *paths, unsigned char *dist) { + unsigned char last_dist = 0; + unsigned char curr_dist = 1; + int *to_visit = buff; + int *visited = buff; + *(to_visit++) = src; + paths[src] = 1.0; + dist[src] = curr_dist; + int nb_visited = 1; + while (visited != to_visit) { + int v = *(visited++); + if (last_dist == (curr_dist = dist[v])) { + break; + } + unsigned char nd = next_dist(curr_dist); + int *ww = neigh[v]; + double p = paths[v]; + for (int k = deg[v]; k--;) { + int w = *(ww++); + unsigned char d = dist[w]; + if (d == 0) { + // not visited yet ! + *(to_visit++) = w; + dist[w] = nd; + paths[w] = p; + // is it the last one ? + if (++nb_visited == n) { + last_dist = nd; + } + } else if (d == nd) { + if ((paths[w] += p) == numeric_limits::infinity()) { + IGRAPH_ERROR("Fatal error : too many (>MAX_DOUBLE) possible" + " paths in graph", IGRAPH_EOVERFLOW); + } + } + } + } + assert(to_visit == buff + nb_visited); + return nb_visited; +} + +// dist[] MUST be full of zeros !!!! +void graph_molloy_opt::explore_usp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg, double **edge_redudancy) { + + while (--nb_vertices) { + int v = buff[nb_vertices]; + if (target[v] > 0.0) { + unsigned char pd = prev_dist(dist[v]); + int *ww = neigh[v]; + int k = 0; + // pick ONE father at random + double father_index = my_random01() * paths[v]; + double f = 0.0; + int father = -1; + while (f < father_index) { + while (dist[father = ww[k++]] != pd) { } + f += paths[father]; + } + // increase target[] of father + target[father] += target[v]; + // add edge, if necessary + if (newdeg != NULL) { + add_traceroute_edge(v, k - 1, newdeg, edge_redudancy, target[v]); + } + } + // clear dist[] + dist[v] = 0; + } + dist[buff[0]] = 0; +} + +// dist[] MUST be full of zeros !!!! +void graph_molloy_opt::explore_asp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg, double **edge_redudancy) { + + while (--nb_vertices) { + int v = buff[nb_vertices]; + if (target[v] > 0.0) { + unsigned char pd = prev_dist(dist[v]); + int *ww = neigh[v]; + int dv = deg[v]; + double f = target[v] / paths[v]; + // pick ALL fathers + int father; + for (int k = 0; k < dv; k++) if (dist[father = ww[k]] == pd) { + // increase target[] of father + target[father] += paths[father] * f; + // add edge, if necessary + if (newdeg != NULL) { + add_traceroute_edge(v, k, newdeg, edge_redudancy, target[v]); + } + } + } + // clear dist[] + dist[v] = 0; + } + dist[buff[0]] = 0; +} + +// dist[] MUST be full of zeros !!!! +void graph_molloy_opt::explore_rsp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg, double** edge_redudancy) { + + while (--nb_vertices) { + int v = buff[nb_vertices]; + if (target[v] > 0.0) { + unsigned char pd = prev_dist(dist[v]); + int *ww = neigh[v]; + // for all fathers : do we take it ? + int paths_left = int(target[v]); + double father_index = paths[v]; + int father; + for (int k = 0; k < deg[v]; k++) if (dist[father = ww[k]] == pd) { + double pf = paths[father]; + int to_add_to_father = my_binomial(pf / father_index, paths_left); + father_index -= pf; + if (to_add_to_father > 0) { + paths_left -= to_add_to_father; + // increase target[] of father + target[father] += to_add_to_father; + // add edge, if necessary + if (newdeg != NULL) { + add_traceroute_edge(v, k, newdeg, edge_redudancy, target[v]); + } + } + } + } + // clear dist[] + dist[v] = 0; + } + dist[buff[0]] = 0; +} + +double *graph_molloy_opt::vertex_betweenness(int mode, bool trivial_paths) { + char MODES[3] = {'U', 'A', 'R'}; + igraph_statusf("Computing vertex betweenness %cSP...", 0, MODES[mode]); + + // breadth-first search vertex fifo + int *buff = new int[n]; + // breadth-first search path count + double *paths = new double[n]; + // breadth-first search distance vector + unsigned char *dist = new unsigned char[n]; + // global betweenness + double *b = new double[n]; + // local betweenness (for one source) + double *target = new double[n]; + // init all + int progress = 0; + memset(dist, 0, sizeof(unsigned char)*n); + for (double *yo = target + n; (yo--) != target; *yo = 1.0) { } + for (double *yo = b + n; (yo--) != b; *yo = 0.0) { } + + int progress_steps = max(1000, n / 10); + // Main loop + for (int v0 = 0; v0 < n; v0++) { + // Verbose + if (v0 > (progress * n) / progress_steps) { + progress++; + igraph_progressf("Computing vertex betweenness %cSP", + 100.0 * double(progress) / double(progress_steps), 0, + MODES[mode]); + } + // Breadth-first search + int nb_vertices = breadth_path_search(v0, buff, paths, dist); + // initialize target[vertices in component] to 1 + //for(int *yo = buff+nb_vertices; (yo--)!=buff; target[*yo]=1.0); + // backwards-cumulative exploration + switch (mode) { + case MODE_USP: + explore_usp(target, nb_vertices, buff, paths, dist); break; + case MODE_ASP: + explore_asp(target, nb_vertices, buff, paths, dist); break; + case MODE_RSP: + explore_rsp(target, nb_vertices, buff, paths, dist); break; + default: + IGRAPH_WARNING("graph_molloy_opt::vertex_betweenness() " + "called with Invalid Mode"); + } + // add targets[vertices in component] to global betweenness and reset targets[] + if (nb_vertices == n) { + // cache optimization if all vertices are in component + double *bb = b; + double *tt_end = target + n; + if (trivial_paths) for (double *yo = target; yo != tt_end; * (bb++) += *(yo++)) {} + else { + for (double *yo = target; yo != tt_end; * (bb++) += (*(yo++) - 1.0)) { } + b[*buff] -= (target[*buff] - 1.0); + } + for (double *yo = target; yo != tt_end; * (yo++) = 1.0) { } + } else { + if (trivial_paths) + for (int *yo = buff + nb_vertices; (yo--) != buff; b[*yo] += target[*yo]) { } + else + for (int *yo = buff + nb_vertices; (--yo) != buff; b[*yo] += (target[*yo] - 1.0)) { } + for (int *yo = buff + nb_vertices; (yo--) != buff; target[*yo] = 1.0) { } + } + } + // Clean all & return + delete[] target; + delete[] dist; + delete[] buff; + delete[] paths; + igraph_status("Done\n", 0); + return b; +} + +double graph_molloy_opt::traceroute_sample(int mode, int nb_src, int *src, int nb_dst, int* dst, double *redudancy, double **edge_redudancy) { + // verify & verbose + assert(verify()); + char MODES[3] = {'U', 'A', 'R'}; + igraph_statusf("traceroute %cSP on G(N=%d,M=%d) with %d src and %d dst...", + 0, MODES[mode], nbvertices_real(), nbarcs(), nb_src, nb_dst); + + // create dst[] buffer if necessary + bool newdist = dst == NULL; + if (newdist) { + dst = new int[n]; + } + // breadth-first search vertex fifo + int *buff = new int[n]; + // breadth-first search path count + double *paths = new double[n]; + // breadth-first search distance vector + unsigned char *dist = new unsigned char[n]; + // newdeg[] allows to tag discovered edges + int *newdeg = new int[n]; + // target[v] is > 0 if v is a destination + double *target = new double[n]; + + // init all + int i; + memset(dist, 0, sizeof(unsigned char)*n); + memset(newdeg, 0, sizeof(int)*n); + for (double *yo = target + n; (yo--) != target; *yo = 0.0) { } + if (redudancy != NULL) + for (double *yo = redudancy + n; (yo--) != redudancy; *yo = 0.0) { } + + // src_0 counts the number of sources having degree 0 + int src_0 = 0; + // nopath counts the number of pairs (src,dst) having no possible path + int nopath = 0; + // nb_paths & total_dist are for the average distance estimator + int nb_paths = 0; + double total_dist = 0; + // s will be the current source + int s; + + while (nb_src--) if (deg[s = *(src++)] == 0) { + src_0++; + } else { + // breadth-first search + int nb_vertices = breadth_path_search(s, buff, paths, dist); + // do we have to pick new destinations ? + if (newdist) { + pick_random_dst(double(nb_dst), NULL, dst); + } + // mark reachable destinations as "targets" + for (i = 0; i < nb_dst; i++) { + if (dist[dst[i]] != 0) { + target[dst[i]] = 1.0; + } else { + nopath++; + } + } + // compute avg_dist estimator + int current_dist = 0; + unsigned char curr_dist = 1; + for (int p = 1; p < nb_vertices; p++) { + int v = buff[p]; + if (dist[v] != curr_dist) { + curr_dist = dist[v]; + current_dist++; + } + if (target[v] > 0.0) { + total_dist += double(current_dist); + nb_paths++; + } + } + // substract target[] to redudancy if needed + if (redudancy != NULL) for (i = 1; i < nb_vertices; i++) { + redudancy[buff[i]] -= (target[buff[i]]); + } + // traceroute exploration + switch (mode) { + case MODE_USP: + explore_usp(target, nb_vertices, buff, paths, dist, newdeg, edge_redudancy); break; + case MODE_ASP: + explore_asp(target, nb_vertices, buff, paths, dist, newdeg, edge_redudancy); break; + case MODE_RSP: + explore_rsp(target, nb_vertices, buff, paths, dist, newdeg, edge_redudancy); break; + default: + IGRAPH_WARNING("graph_molloy_opt::traceroute_sample() called " + "with Invalid Mode"); + } + // add target[] to redudancy[] if needed + if (redudancy != NULL) for (i = 1; i < nb_vertices; i++) { + redudancy[buff[i]] += (target[buff[i]]); + } + // clear target[] + for (int *yo = buff + nb_vertices; yo-- != buff; target[*yo] = 0.0) { } + } + // update degrees + for (i = 0; i < n; i++) { + deg[i] = newdeg[i]; + } + refresh_nbarcs(); + // clean all + delete[] buff; + delete[] paths; + delete[] dist; + delete[] newdeg; + delete[] target; + if (newdist) { + delete[] dst; + } + { + igraph_statusf("discovered %d vertices and %d edges\n", 0, + nbvertices_real(), nbarcs()); + if (src_0) igraph_warningf("%d sources had degree 0\n", IGRAPH_FILE_BASENAME, + __LINE__, -1, src_0); + if (nopath) igraph_warningf("%d (src,dst) pairs had no possible path\n", + IGRAPH_FILE_BASENAME, __LINE__, -1, nopath); + } + return total_dist / double(nb_paths); +} + +int graph_molloy_opt::disconnecting_edges() { + int removed = 0; + while (is_connected()) { + // replace random edge by loops + int i; + do { + i = pick_random_vertex(); + } while (i < 0 || deg[i] < 1); + int *p = neigh[i] + (my_random() % deg[i]); + int j = *p; *p = i; + fast_rpl(neigh[j], i, j); + removed++; + } + return removed; +} + +void graph_molloy_opt::vertex_covering() { + vertex_cover(n, links, deg, neigh); +} + + +// optimisations a faire : +// 1/ arreter le breadth-first search qd on a vu toutes les dst +// 2/ faire une seule redescente pour toutes les dst. + +double graph_molloy_opt::path_sampling(int *nb_dst, int *dst, double* redudancies, double **edge_redudancies) { + assert(verify()); + // do we have to store the destinations (for one src) in a temp buffer? + bool NOMEM = (dst == NULL); + if (NOMEM) { + dst = new int[n]; + } + int i; + int next_step = n + 1; + { + igraph_status("Sampling paths", 0); + next_step = 0; + } + // breadth-first search buffers buff[] and dist[] + int *buff = new int[n]; + unsigned char *dist = new unsigned char[n]; + for (i = 0; i < n; i++) { + dist[i] = 0; + } + // nb_pos[] counts the number of possible paths to get to a vertex + int *nb_pos = new int[n]; + for (i = 0; i < n; i++) { + nb_pos[i] = 0; + } + // newdeg[i] is the number of edges of vertex i "seen" by traceroute + int *newdeg = new int[n]; + for (i = 0; i < n; i++) { + newdeg[i] = 0; + } + + // src_0 counts the number of sources having degree 0 + int src_0 = 0; + // nopath counts the number of pairs (src,dst) having no possible path + int nopath = 0; + // nb_paths & total_dist are for the average distance estimator + int nb_paths = 0; + unsigned int total_dist = 0; + unsigned int total_dist64 = 0; + + // s is the source of the breadth-first search + for (int s = 0; s < n; s++) if (nb_dst[s] > 0) { + if (deg[s] == 0) { + src_0++; + } else { + if (s > next_step) { + next_step = s + (n / 1000) + 1; + igraph_progress("Sampling paths", double(s) / double(n), 0); + } + int v; + // breadth-first search + int *to_visit = buff; + int *visited = buff; + *(to_visit++) = s; + dist[s] = 1; + nb_pos[s] = 1; + while (visited != to_visit) { + v = *(visited++); + unsigned char n_dist = next_dist(dist[v]); + int *w0 = neigh[v]; + for (int *w = w0 + deg[v]; w-- != w0; ) { + unsigned char d2 = dist[*w]; + if (d2 == 0) { + dist[*w] = d2 = n_dist; + *(to_visit++) = *w; + } + if (d2 == n_dist) { + nb_pos[*w] += nb_pos[v]; + } + } + } + + // for every target, pick a random path. + int t_index = nb_dst[s]; + // create dst[] if necessary + if (NOMEM) { + pick_random_src(double(t_index), NULL, dst); + } + while (t_index--) if (dist[v = *(dst++)] == 0) { + nopath++; + } else { +#ifdef DEGSEQ_VL_DEBUG + igraph_statusf("Sampling path %d -> %d\n", 0, s, v); +#endif // DEGSEQ_VL_DEBUG + nb_paths++; + // while we haven't reached the source.. + while (v != s) { + // pick a random father + int index = my_random() % nb_pos[v]; + unsigned char p_dist = prev_dist(dist[v]); + int *w = neigh[v]; + int k = 0; + int new_father; + while (dist[new_father = w[k]] != p_dist || (index -= nb_pos[new_father]) >= 0) { + k++; + } + // add edge + add_traceroute_edge(v, k, newdeg, edge_redudancies, 1.0); + if (redudancies != NULL && new_father != s) { + redudancies[new_father] += 1.0; + } + // step down to father + v = new_father; + // increase total distance + total_dist++; + if (total_dist == 0) { + total_dist64++; + } + } + } + // reset (int *)dst if necessary + if (NOMEM) { + dst -= nb_dst[s]; + } + + // clear breadth-first search buffers + while (visited != buff) { + v = *(--visited); + dist[v] = 0; + nb_pos[v] = 0; + } + } + } + // update degrees + for (i = 0; i < n; i++) { + deg[i] = newdeg[i]; + } + refresh_nbarcs(); + // clean + delete[] newdeg; + delete[] buff; + delete[] dist; + delete[] nb_pos; + if (NOMEM) { + delete[] dst; + } + if (VERBOSE()) { + igraph_status("Sampling paths : Done \n", 0); + if (src_0) igraph_warningf("%d sources had degree 0", IGRAPH_FILE_BASENAME, + __LINE__, -1, src_0); + if (nopath) igraph_warningf("%d (src,dst) pairs had no possible path", + IGRAPH_FILE_BASENAME, __LINE__, -1, nopath); + } + double tdist = double(total_dist64); + if (total_dist64 > 0) { + tdist *= 4294967296.0; + } + tdist += double(total_dist); + return tdist / double(nb_paths); +} + +int *graph_molloy_opt::vertices_real(int &nb_v) { + int *yo; + if (nb_v < 0) { + nb_v = 0; + for (yo = deg; yo != deg + n; ) if (*(yo++) > 0) { + nb_v++; + } + } + if (nb_v == 0) { + IGRAPH_WARNING("graph is empty"); + return NULL; + } + int *buff = new int[nb_v]; + yo = buff; + for (int i = 0; i < n; i++) if (deg[i] > 0) { + *(yo++) = i; + } + if (yo != buff + nb_v) { + igraph_warningf("wrong #vertices in graph_molloy_opt::vertices_real(%d)", + IGRAPH_FILE_BASENAME, __LINE__, -1, nb_v); + delete[] buff; + return NULL; + } else { + return buff; + } +} + +int *graph_molloy_opt::pick_random_vertices(int &k, int *output, int nb_v, int *among) { + int i; + bool CREATED_AMONG = false; + if (among == NULL && k > 0) { + among = vertices_real(nb_v); + CREATED_AMONG = true; + } + if (k > nb_v) { + igraph_warningf("Warning : tried to pick %d among %d vertices. " + "Picked only %d", IGRAPH_FILE_BASENAME, __LINE__, -1, k, nb_v, nb_v); + k = nb_v; + } + if (k > 0) { + if (output == NULL) { + output = new int[k]; + } + for (i = 0; i < k; i++) { + int tmp = i + my_random() % (nb_v - i); + output[i] = among[tmp]; + among[tmp] = among[i]; + among[i] = output[i]; + } + } + if (CREATED_AMONG) { + delete[] among; + } + return output; +} + +int *graph_molloy_opt::pick_random_src(double k, int *nb, int* buff, int nb_v, int* among) { + bool AMONG_CREATED = false; + if (among == NULL || nb_v < 0) { + AMONG_CREATED = true; + among = vertices_real(nb_v); + } + int kk = int(floor(0.5 + (k >= 1.0 ? k : k * double(nb_v)))); + if (kk == 0) { + kk = 1; + } + int *yo = pick_random_vertices(kk, buff, nb_v, among); + if (nb != NULL) { + *nb = kk; + } + if (AMONG_CREATED) { + delete[] among; + } + return yo; +} + +int *graph_molloy_opt::pick_random_dst(double k, int *nb, int* buff, int nb_v, int* among) { + bool AMONG_CREATED = false; + if (among == NULL || nb_v < 0) { + AMONG_CREATED = true; + among = vertices_real(nb_v); + } + int kk = int(floor(0.5 + (k > 1.0 ? k : k * double(nb_v)))); + if (kk == 0) { + kk = 1; + } + int *yo = pick_random_vertices(kk, buff, nb_v, among); + if (nb != NULL) { + *nb = kk; + } + if (AMONG_CREATED) { + delete[] among; + } + return yo; +} + +int graph_molloy_opt::core() { + box_list b(n, deg); + int v; + int removed = 0; + while ((v = b.get_one()) >= 0) { + b.pop_vertex(v, neigh); + deg[v] = 0; + removed++; + } + refresh_nbarcs(); + return removed; +} + +int graph_molloy_opt::try_disconnect(int K, int max_tries) { + bool *visited = new bool[n]; + for (bool *p = visited + n; p != visited; * (--p) = false) { } + int *Kbuff = new int[K]; + int tries = 0; + int next_step = -1; + if (VERBOSE()) { + next_step = 0; + } + bool yo = true; + while (yo && tries < max_tries) { + if (tries == next_step) { + igraph_statusf("Trying to disconnect the graph... " + "%d edges swaps done so far", 0, tries); + next_step += 100; + } + int v1 = pick_random_vertex(); + int v2 = pick_random_vertex(); + int w1 = *(random_neighbour(v1)); + int w2 = *(random_neighbour(v2)); + if (swap_edges_simple(v1, w1, v2, w2)) { + tries++; + yo = (!isolated(v1, K, Kbuff, visited) && !isolated(v2, K, Kbuff, visited) && !is_connected()); + swap_edges(v1, w2, v2, w1); + } + } + delete[] visited; + delete[] Kbuff; + return tries; +} + +bool graph_molloy_opt::isolated(int v, int K, int *Kbuff, bool *visited) { + if (K < 2) { + return false; + } +#ifdef OPT_ISOLATED + if (K <= deg[v] + 1) { + return false; + } +#endif //OPT_ISOLATED + int *seen = Kbuff; + int *known = Kbuff; + int *max = Kbuff + (K - 1); + *(known++) = v; + visited[v] = true; + bool is_isolated = true; + + while (known != seen) { + v = *(seen++); + int *w = neigh[v]; + for (int d = deg[v]; d--; w++) if (!visited[*w]) { +#ifdef OPT_ISOLATED + if (K <= deg[*w] + 1 || known == max) { +#else //OPT_ISOLATED + if (known == max) { +#endif //OPT_ISOLATED + is_isolated = false; + goto end_isolated; + } + visited[*w] = true; + *(known++) = *w; + } + } +end_isolated: + // Undo the changes to visited[]... + while (known != Kbuff) { + visited[*(--known)] = false; + } + return is_isolated; +} + +double graph_molloy_opt::rho(int mode, int nb_src, int *src, int nb_dst, int *dst) { + assert(verify()); + + // create dst[] buffer if necessary + bool newdist = dst == NULL; + if (newdist) { + dst = new int[n]; + } + // breadth-first search vertex fifo + int *buff = new int[n]; + // breadth-first search path count + double *paths = new double[n]; + // breadth-first search distance vector + unsigned char *dist = new unsigned char[n]; + // target[v] is > 0 if v is a destination + double *target = new double[n]; + // times_seen count the times we saw each vertex + int *times_seen = new int[n]; + + // init all + int i; + memset(dist, 0, sizeof(unsigned char)*n); + memset(times_seen, 0, sizeof(int)*n); + for (double *yo = target + n; (yo--) != target; *yo = 0.0) { } + + // src_0 counts the number of sources having degree 0 + int src_0 = 0; + // nopath counts the number of pairs (src,dst) having no possible path + int nopath = 0; + // s will be the current source + int s; + + for (int nsrc = 0; nsrc < nb_src; nsrc++) if (deg[s = *(src++)] == 0) { + src_0++; + } else { + // breadth-first search + int nb_vertices = breadth_path_search(s, buff, paths, dist); + // do we have to pick new destinations ? + if (newdist) { + pick_random_dst(double(nb_dst), NULL, dst); + } + // mark reachable destinations as "targets" and substract one time_seen + for (i = 0; i < nb_dst; i++) { + if (dist[dst[i]] != 0) { + target[dst[i]] = 1.0; + } else { + nopath++; + } + } + // traceroute exploration + switch (mode) { + case MODE_USP: + explore_usp(target, nb_vertices, buff, paths, dist); break; + case MODE_ASP: + explore_asp(target, nb_vertices, buff, paths, dist); break; + case MODE_RSP: + explore_rsp(target, nb_vertices, buff, paths, dist); break; + default: + IGRAPH_WARNING("graph_molloy_opt::rho() called with Invalid Mode"); + } + // remove destinations that weren't discovered by a path coming through + for (i = 0; i < nb_dst; i++) { + int yo = dst[i]; + if (target[yo] == 1.0) { + target[yo] = 0.0; + } + } + // add target[] to times_seen[] + for (i = 1; i < nb_vertices; i++) { + int yo = buff[i]; + if (target[yo] != 0.0) { + target[yo] = 0.0; + times_seen[yo]++; + } + } + // also clear the source + target[buff[0]] = 0.0; + } + // clean all + delete[] buff; + delete[] paths; + delete[] dist; + delete[] target; + if (newdist) { + delete[] dst; + } + // compute rho + double sum_nij = 0.0; + double sum_ni = 0.0; + for (i = 0; i < n; i++) { + double d = double(times_seen[i]); + sum_ni += d; + sum_nij += d * d; + } + delete[] times_seen; + { + igraph_status("done\n", 0); + if (src_0) igraph_warningf("%d sources had degree 0", IGRAPH_FILE_BASENAME, __LINE__, + -1, src_0); + if (nopath) igraph_warningf("%d (src,dst) pairs had no possible path", + IGRAPH_FILE_BASENAME, __LINE__, -1, nopath); + } + return (sum_nij - sum_ni) * double(n) * double(nb_src) / (sum_ni * sum_ni * double(nb_src - 1)); +} + +void graph_molloy_opt::sort() { + for (int v = 0; v < n; v++) { + qsort(neigh[v], deg[v]); + } +} + +int* graph_molloy_opt::sort_vertices(int *buff) { + // pre-sort vertices by degrees + buff = boxsort(deg, n, buff); + // sort vertices having the same degrees + int i = 0; + while (i < n) { + int d = deg[buff[i]]; + int j = i + 1; + while (j < n && deg[buff[j]] == d) { + j++; + } + lex_qsort(neigh, buff + i, j - i, d); + i = j; + } + return buff; +} + +int graph_molloy_opt::cycles(int v) { + return v; +} + +// void graph_molloy_opt::remove_vertex(int v) { +// fprintf(stderr,"Warning : graph_molloy_opt::remove_vertex(%d) called",v); +// } + +bool graph_molloy_opt::verify(int mode) { + IGRAPH_UNUSED(mode); +#ifndef NDEBUG + int i, j, k; + assert(neigh[0] == links); + // verify edges count + if ((mode & VERIFY_NOARCS) == 0) { + int sum = 0; + for (i = 0; i < n; i++) { + sum += deg[i]; + } + assert(sum == a); + } + // verify neigh[] and deg[] compatibility + if ((mode & VERIFY_NONEIGH) == 0) + for (i = 0; i < n - 1; i++) { + assert(neigh[i] + deg[i] == neigh[i + 1]); + } + // verify vertex range + for (i = 0; i < a; i++) { + assert(links[i] >= 0 && links[i] < n); + } + // verify simplicity +// for(i=0; i 0); + } +#endif + return true; +} + +/*___________________________________________________________________________________ + Not to use anymore : use graph_molloy_hash class instead + +void graph_molloy_opt::shuffle(long times) { + while(times) { + int f1 = links[my_random()%a]; + int f2 = links[my_random()%a]; + int t1 = neigh[f1][my_random()%deg[f1]]; + int t2 = neigh[f2][my_random()%deg[f2]]; + if(swap_edges_simple(f1,t1,f2,t2)) times--; + } +} + + +long graph_molloy_opt::connected_shuffle(long times) { + //assert(verify()); +#ifdef PERFORMANCE_MONITOR + long failures = 0; + long successes = 0; + double avg_K = 0.0; + long avg_T = 0; +#endif //PERFORMANCE_MONITOR + + long nb_swaps = 0; + long T = min(a,times)/10; + double double_K = 1.0; + int K = int(double_K); + double Q1 = 1.35; + double Q2 = 1.01; + int *Kbuff = new int[K]; + bool *visited = new bool[n]; + for(int i=0; inb_swaps) { + // Backup graph +#ifdef PERFORMANCE_MONITOR + avg_K+=double_K; + avg_T+=T; +#endif //PERFORMANCE_MONITOR + int *save = backup(); + //assert(verify()); + // Swaps + long swaps = 0; + for(int i=T; i>0; i--) { + // Pick two random vertices + int f1 = pick_random_vertex(); + int f2 = pick_random_vertex(); + if(f1==f2) continue; + // Pick two random neighbours + int *f1t1 = random_neighbour(f1); + int t1 = *f1t1; + int *f2t2 = random_neighbour(f2); + int t2 = *f2t2; + // test simplicity + if(t1!=t2 && f1!=t2 && f2!=t1 && !is_edge(f1,t2) && !is_edge(f2,t1)) { + // swap + *f1t1 = t2; + *f2t2 = t1; + int *t1f1 = fast_rpl(neigh[t1],f1,f2); + int *t2f2 = fast_rpl(neigh[t2],f2,f1); + // isolation test + if(isolated(f1, K, Kbuff, visited) || isolated(f2, K, Kbuff, visited)) { + // undo swap + *t1f1 = f1; *t2f2 = f2; *f1t1 = t1; *f2t2 = t2; + } + else swaps++; + } + } + //assert(verify()); + // test connectivity + bool ok = is_connected(); +#ifdef PERFORMANCE_MONITOR + if(ok) successes++; else failures++; +#endif //PERFORMANCE_MONITOR + if(ok) { + nb_swaps += swaps; + // adjust K and T + if((K+10)*T>5*a) { + double_K/=Q2; + K = int(double_K); + } + else T*=2; + } + else { + restore(save); + //assert(verify()); + double_K*=Q1; + K = int(double_K); + delete[] Kbuff; + Kbuff = new int[K]; + } + delete[] save; + } +#ifdef PERFORMANCE_MONITOR + fprintf(stderr,"\n*** Performance Monitor ***\n"); + fprintf(stderr," - Connectivity test successes : %ld\n",successes); + fprintf(stderr," - Connectivity test failures : %ld\n",failures); + fprintf(stderr," - Average window : %ld\n",avg_T/long(successes+failures)); + fprintf(stderr," - Average isolation test width : %f\n",avg_K/double(successes+failures)); +#endif //PERFORMANCE_MONITOR + return nb_swaps; +} + +bool graph_molloy_opt::try_shuffle(int T, int K) { + int i; + int *Kbuff = NULL; + if(K>0) Kbuff = new int[K]; + bool *visited = new bool[n]; + for(i=0; i0; i--) { + // Pick two random vertices + int f1 = pick_random_vertex(); + int f2 = pick_random_vertex(); + if(f1==f2) continue; + // Pick two random neighbours + int *f1t1 = random_neighbour(f1); + int t1 = *f1t1; + int *f2t2 = random_neighbour(f2); + int t2 = *f2t2; + // test simplicity + if(t1!=t2 && f1!=t2 && f2!=t1 && is_edge(f1,t2) && !is_edge(f2,t1)) { + // swap + *f1t1 = t2; + *f2t2 = t1; + int *t1f1 = fast_rpl(neigh[t1],f1,f2); + int *t2f2 = fast_rpl(neigh[t2],f2,f1); + // isolation test + if(isolated(f1, K, Kbuff, visited) || isolated(f2, K, Kbuff, visited)) { + // undo swap + *t1f1 = f1; *t2f2 = f2; *f1t1 = t1; *f2t2 = t2; + } + } + } + delete[] visited; + if(Kbuff != NULL) delete[] Kbuff; + bool yo = is_connected(); + restore(back); + delete[] back; + return yo; +} + +double graph_molloy_opt::window(int K, double ratio) { + int steps = 100; + double T = double(a*10); + double q2 = 0.1; + double q1 = pow(q2,(ratio-1.0)/ratio); + + int failures = 0; + int successes = 0; + int *Kbuff = new int[K]; + bool *visited = new bool[n]; + + while(successes<10*steps) { + int *back=backup(); + for(int i=int(T); i>0; i--) { + // Pick two random vertices + int f1 = links[my_random()%a]; + int f2 = links[my_random()%a]; + if(f1==f2) continue; + // Pick two random neighbours + int *f1t1 = neigh[f1]+my_random()%deg[f1]; + int *f2t2 = neigh[f2]+my_random()%deg[f2]; + int t1 = *f1t1; + int t2 = *f2t2; + // test simplicity + if(t1!=t2 && f1!=t2 && f2!=t1 && is_edge(f1,t2) && !is_edge(f2,t1)) { + // swap + *f1t1 = t2; + *f2t2 = t1; + int *t1f1 = fast_rpl(neigh[t1],f1,f2); + int *t2f2 = fast_rpl(neigh[t2],f2,f1); + // isolation test + if(isolated(f1, K, Kbuff, visited) || isolated(f2, K, Kbuff, visited)) { + // undo swap + *t1f1 = f1; *t2f2 = f2; *f1t1 = t1; *f2t2 = t2; + } + } + } + if(is_connected()) { + T *= q1; + if(T>double(5*a)) T=double(5*a); + successes++; + if((successes%steps)==0) { + q2 = sqrt(q2); + q1 = sqrt(q1); + } + } + else { + T*=q2; + failures++; + } + if(VERBOSE()) fprintf(stderr,"."); + restore(back); + delete[] back; + } + delete[] Kbuff; + delete[] visited; + if(VERBOSE()) fprintf(stderr,"Failures:%d Successes:%d\n",failures, successes); + return T; +} + + +double graph_molloy_opt::eval_K(int quality) { + double K = 5.0; + double avg_K = 1.0; + for(int i=quality; i--; ) { + int int_K = int(floor(K+0.5)); + if(try_shuffle(a/(int_K+1),int_K)) { + K*=0.8; fprintf(stderr,"+"); } + else { + K*=1.25; fprintf(stderr,"-"); } + if(ideg[t2] ? f1 : t2, K, Kbuff, visited); + sum_K += effective_isolated(deg[f2]>deg[t1] ? f2 : t1, K, Kbuff, visited); + // undo swap + swap_edges(f1,t2,f2,t1); +// assert(verify()); + } + delete[] Kbuff; + delete[] visited; + return double(sum_K)/double(2*quality); +} + + +//___________________________________________________________________________________ +*/ + + + +/***** NOT USED ANYMORE (Modif 22/04/2005) ****** + +int64_t *graph_molloy_opt::vertex_betweenness_usp(bool trivial_paths) { + if(VERBOSE()) fprintf(stderr,"Computing vertex betweenness USP..."); + int i; + unsigned char *dist = new unsigned char[n]; + int *buff = new int[n]; + int64_t *b = new int64_t[n]; + int *bb = new int[n]; + int *dd = new int[max_degree()]; + for(i=0; i(progress*n)/1000) { + progress++; + fprintf(stderr,"\rComputing vertex betweenness USP : %d.%d%% ",progress/10,progress%10); + } + int nb_vertices = width_search(dist, buff, v0); + int nv = nb_vertices; + for(i=0; i(progress*n)/1000) { + progress++; + fprintf(stderr,"\rComputing vertex betweenness RSP : %d.%d%% ",progress/10,progress%10); + } + int nb_vertices = width_search(dist, buff, v0); + int nv = nb_vertices; + for(i=0; i1 && to_give>2*n_father) { + int o = rng.binomial(1.0/n_father,to_give); + to_give -= o; + bb[dd[--n_father]]+=o; + } + if(n_father==1) bb[dd[0]]+=to_give; + else { + while(to_give--) bb[dd[my_random()%n_father]]++; + } + } + if(trivial_paths) bb[v]++; + } + for(i=0; i0) { + if(VERBOSE()==VERBOSE_LOTS && v0>(progress*n)/1000) { + progress++; + fprintf(stderr,"\rComputing vertex betweenness ASP : %d.%d%% ",progress/10,progress%10); + } + int nb_vertices = width_search(dist, buff, v0); + if(!trivial_paths) dist[v0]=2; + int nv = nb_vertices; + for(i=0; i. + */ +#ifndef GRAPH_MOLLOY_OPT_H +#define GRAPH_MOLLOY_OPT_H + +#include "gengraph_definitions.h" +#include "gengraph_degree_sequence.h" +#include "gengraph_qsort.h" + +#include +#include "gengraph_random.h" + +namespace gengraph { + +// This class handles graphs with a constant degree sequence. + +class graph_molloy_opt { + +private: + // Random generator + KW_RNG::RNG rng; + // Number of vertices + int n; + //Number of arcs ( = #edges * 2 ) + int a; + // The degree sequence of the graph + int *deg; + // The array containing all links + int *links; + // The array containing pointers to adjacency list of every vertices + int **neigh; + // Allocate memory according to degree_sequence (for constructor use only!!) + void alloc(degree_sequence &); + // Compute #edges + inline void refresh_nbarcs() { + a = 0; + for (int* d = deg + n; d != deg; ) { + a += *(--d); + } + } + // Build neigh with deg and links + void compute_neigh(); + // Swap edges. The swap MUST be valid !!! + inline void swap_edges(int from1, int to1, int from2, int to2) { + fast_rpl(neigh[from1], to1, to2); + fast_rpl(neigh[from2], to2, to1); + fast_rpl(neigh[to1], from1, from2); + fast_rpl(neigh[to2], from2, from1); + } + + // Swap edges only if they are simple. return false if unsuccessful. + bool swap_edges_simple(int, int, int, int); + // Test if vertex is in an isolated component of size dmax. + void depth_isolated(int v, long &calls, int &left_to_explore, int dmax, int * &Kbuff, bool *visited); + // breadth-first search. Store the distance (modulo 3) in dist[]. Returns eplorated component size. + int width_search(unsigned char *dist, int *buff, int v0 = 0, int toclear = -1); + // depth-first search. + int depth_search(bool *visited, int *buff, int v0 = 0); + // breadth-first search that count the number of shortest paths going from src to each vertex + int breadth_path_search(int src, int *buff, double *paths, unsigned char *dist); + // Used by traceroute_sample() ONLY + void add_traceroute_edge(int, int, int*, double** red = NULL, double t = 1.0); + // Used by traceroute() and betweenness(). if newdeg[]=NULL, do not discover edges. + // breadth_path_search() must have been called to give the corresponding buff[],dist[],paths[] and nb_vertices + void explore_usp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg = NULL, double **edge_redudancy = NULL); + void explore_asp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg = NULL, double **edge_redudancy = NULL); + void explore_rsp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg = NULL, double **edge_redudancy = NULL); + // Return component indexes where vertices belong to, starting from 0, + // sorted by size (biggest component has index 0) + int *components(int *comp = NULL); + // pick k random vertices of degree > 0. + int *pick_random_vertices(int &k, int *output = NULL, int nb_v = -1, int *among = NULL); + +public: + // neigh[] + inline int** neighbors() { + return neigh; + }; + // deg[] + inline int* degrees() { + return deg; + }; + //adjacency list of v + inline int* operator[](const int v) { + return neigh[v]; + }; + //degree of v + inline int degree(const int v) { + return deg[v]; + }; + //compare adjacency lists + inline int compare(const int v, const int w) { + return deg[v] == deg[w] ? lex_comp(neigh[v], neigh[w], deg[v]) : (deg[v] > deg[w] ? -1 : 1); + }; + // Detach deg[] and neigh[] + void detach(); + // Destroy deg and links + ~graph_molloy_opt(); + // Create graph from file (stdin not supported unless rewind() possible) + graph_molloy_opt(FILE *f); + // Allocate memory for the graph. Create deg and links. No edge is created. + graph_molloy_opt(degree_sequence &); + // Create graph from hard copy + graph_molloy_opt(int *); + // Create hard copy of graph + int *hard_copy(); + // Remove unused edges, updates neigh[], recreate links[] + void clean(); + // nb arcs + inline int nbarcs() { + return a; + }; + // last degree + inline int last_degree() { + return deg[n - 1]; + }; + // nb vertices + inline int nbvertices() { + return n; + }; + // nb vertices having degree > 0 + inline int nbvertices_real() { + int s = 0; + for (int *d = deg + n; d-- != deg; ) if (*d) { + s++; + } + return s; + }; + // return list of vertices with degree > 0. Compute #vertices, if not given. + int *vertices_real(int &nb_v); + // Keep only giant component + void giant_comp(); + // nb vertices in giant component + int nbvertices_comp(); + // nb arcs in giant component + int nbarcs_comp(); + // print graph in SUCC_LIST mode, in stdout + void print(FILE *f = stdout, bool NOZERO = true); + // Bind the graph avoiding multiple edges or self-edges (return false if fail) + bool havelhakimi(); + // Get the graph connected (return false if fail) + bool make_connected(); + // Test if graph is connected + bool is_connected(); + // Maximum degree + int max_degree(); + // breadth-first search. Store the distance (modulo 3) in dist[]. + void breadth_search(int *dist, int v0 = 0, int* buff = NULL); + // is edge ? + inline bool is_edge(const int u, const int v) { + if (deg[v] < deg[u]) { + return (fast_search(neigh[v], deg[v], u) != NULL); + } else { + return (fast_search(neigh[u], deg[u], v) != NULL); + } + } + // Backup graph [sizeof(int) bytes per edge] + int* backup(int *here = NULL); + // Restore from backup. Assume that degrees haven't changed + void restore(int* back); + // Resplace with hard backup. + void replace(int* _hardbackup); + // Backup degs of graph + int* backup_degs(int *here = NULL); + // Restore degs from neigh[]. Need last degree, though + void restore_degs(int last_degree); + // Restore degs[] from backup. Assume that links[] has only been permuted + void restore_degs_only(int* backup_degs); + // Restore degs[] and neigh[]. Assume that links[] has only been permuted + void restore_degs_and_neigh(int* backup_degs); +// WARNING : the following shuffle() algorithms are slow. +// Use graph_molloy_hash::connected_shuffle() instead. + // "Fab" Shuffle (Optimized heuristic of Gkantsidis algo.) + long fab_connected_shuffle(long); + // "Optimized-Fab" Shuffle (Optimized heuristic of Gkantsidis algo, with isolated pairs) + long opt_fab_connected_shuffle(long); + // Gkantsidis Shuffle + long gkantsidis_connected_shuffle(long); + // Connected Shuffle + long slow_connected_shuffle(long); + // shortest paths where vertex is an extremity + double *vertex_betweenness(int mode, bool trivial_path = false); + // Sample the graph with traceroute-like exploration from src[] to dst[]. + // if dst[]=NULL, pick nb_dst new random destinations for each src + double traceroute_sample(int mode, int nb_src, int *src, int nb_dst, int* dst, double *redudancy = NULL, double **edge_redudancy = NULL); + // does one breadth-first search and returns the average_distance. + double avg_dist(unsigned char *dist, int *buff, int v0, int &nb_vertices, int toclear = -1); + // Number of edges needed to disconnect graph (one random instance) + int disconnecting_edges(); + // Compute vertex covering of the graph. Warning : this modifies degs[] + void vertex_covering(); + // Path sampling. Input is nb_dst[] and dst[]. nb_dst[v],dst[v] describe all paths (v,x) + double path_sampling(int *nb_dst, int *dst = NULL, double *redudancies = NULL, double **edge_redudancy = NULL); + // keep only core (tree parts are deleted). Returns number of removed vertices. + int core(); + // try to disconnect the graph by swapping edges (with isolation tests) + int try_disconnect(int K, int max_tries = 10000000); + // Eric & Cun-Hui estimator + double rho(int mode, int nb_src, int *src, int nb_dst, int *dst = NULL); + // sort adjacency lists + void sort(); + // sort the vertices according to their degrees (highest first) and to their adjacency lists (lexicographic) + int* sort_vertices(int *buff = NULL); + // count cycles passing through vertex v + int cycles(int v); + // remove vertex (i.e. remove all edges adjacent to vertex) + void remove_vertex(int v); + // pick k random vertices of degree > 0. If k \in [0,1[, k is understood as a density. + int *pick_random_src(double k, int *nb = NULL, int* buff = NULL, int nb_v = -1, int* among = NULL); + // pick k random vertices of degree > 0. If k \in [0,1], k is understood as a density. + int *pick_random_dst(double k, int *nb = NULL, int* buff = NULL, int nb_v = -1, int* among = NULL); + + // For debug purposes : verify validity of the graph (symetry, simplicity) +#define VERIFY_NORMAL 0 +#define VERIFY_NONEIGH 1 +#define VERIFY_NOARCS 2 + bool verify(int mode = VERIFY_NORMAL); + + /*___________________________________________________________________________________ + Not to use anymore : use graph_molloy_hash class instead + + + public: + // Shuffle. returns number of swaps done. + void shuffle(long); + // Connected Shuffle + long connected_shuffle(long); + // Get caracteristic K + double eval_K(int quality = 100); + // Get effective K + double effective_K(int K, int quality = 10000); + // Test window + double window(int K, double ratio); + // Try to shuffle n times. Return true if at the end, the graph was still connected. + bool try_shuffle(int T, int K); + + //___________________________________________________________________________________ + */ + + /*___________________________________________________________________________________ + Not to use anymore : replaced by vertex_betweenness() 22/04/2005 + + // shortest paths where vertex is an extremity + long long *vertex_betweenness_usp(bool trivial_path); + // shortest paths where vertex is an extremity + long long *vertex_betweenness_rsp(bool trivial_path); + // same, but when multiple shortest path are possible, average the weights. + double *vertex_betweenness_asp(bool trivial_path); + //___________________________________________________________________________________ + */ + +}; + +} // namespace gengraph + +#endif //GRAPH_MOLLOY_OPT_H diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_hash.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_hash.h new file mode 100644 index 0000000..7084edc --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_hash.h @@ -0,0 +1,308 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 HASH_H +#define HASH_H + +#include +#include "gengraph_definitions.h" + +//_________________________________________________________________________ +// Hash table profiling... Active only if definition below is uncommented +//_________________________________________________________________________ +//#define _HASH_PROFILE + +namespace gengraph { + +#ifdef _HASH_PROFILE + void _hash_add_iter(); + void _hash_add_call(); + void _hash_put_iter(); + void _hash_put_call(); + void _hash_rm_iter(); + void _hash_rm_call(); + void _hash_find_iter(); + void _hash_find_call(); + void _hash_rand_iter(); + void _hash_rand_call(); + void _hash_expand_call(); + void _hash_prof(); + #define _HASH_ADD_ITER() _hash_add_iter() + #define _HASH_ADD_CALL() _hash_add_call() + #define _HASH_PUT_ITER() _hash_put_iter() + #define _HASH_PUT_CALL() _hash_put_call() + #define _HASH_RM_ITER() _hash_rm_iter() + #define _HASH_RM_CALL() _hash_rm_call() + #define _HASH_FIND_ITER() _hash_find_iter() + #define _HASH_FIND_CALL() _hash_find_call() + #define _HASH_RAND_ITER() _hash_rand_iter() + #define _HASH_RAND_CALL() _hash_rand_call() + #define _HASH_EXP_CALL() _hash_expand_call() +#else + #define _HASH_ADD_ITER() {} + #define _HASH_ADD_CALL() {} + #define _HASH_PUT_ITER() {} + #define _HASH_PUT_CALL() {} + #define _HASH_RM_ITER() {} + #define _HASH_RM_CALL() {} + #define _HASH_FIND_ITER() {} + #define _HASH_FIND_CALL() {} + #define _HASH_RAND_ITER() {} + #define _HASH_RAND_CALL() {} + #define _HASH_EXP_CALL() {} +#endif + +//_________________________________________________________________________ +// Hash Table properties. Works best when HASH_SIZE_IS_POWER2 is uncommented +// but takes 2.25 times the needed space, in average (from 1.5 to 3) +// If you have memory issues, Try to comment it: tables will take 1.5 times +// the minimal space +//_________________________________________________________________________ + +#define HASH_SIZE_IS_POWER2 +#define MACRO_RATHER_THAN_INLINE + +// under HASH_MIN_SIZE, vectors are not hash table (just a simle array) +#define HASH_MIN_SIZE 100 +#define IS_HASH(x) ((x)>HASH_MIN_SIZE) +#define HASH_NONE (-1) + +#ifdef HASH_SIZE_IS_POWER2 +inline int HASH_EXPAND(int x) { + _HASH_EXP_CALL(); + x += x; + x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; + return x + 1; +} +#define HASH_KEY(x,size) ((x*2198737)&((size)-1)) +#endif //HASH_SIZE_IS_POWER2 + +#ifdef MACRO_RATHER_THAN_INLINE +#ifndef HASH_SIZE_IS_POWER2 + #define HASH_EXPAND(x) ((x)+((x)>>1)) + #define HASH_UNEXPAND(x) ((((x)<<1)+1)/3) + #define HASH_KEY(x,size) ((x)%(size)) +#endif //HASH_SIZE_IS_POWER2 +#define HASH_SIZE(x) (IS_HASH(x) ? HASH_EXPAND(x) : (x) ) +#define HASH_REKEY(k,size) ((k)==0 ? (size)-1 : (k)-1) +#else //MACRO_RATHER_THAN_INLINE +#ifndef HASH_SIZE_IS_POWER2 +inline int HASH_KEY(const int x, const int size) { + assert(x >= 0); + return x % size; +}; +inline int HASH_EXPAND(const int x) { + _HASH_EXP_CALL(); + return x + (x >> 1); +}; +inline int HASH_UNEXPAND(const int x) { + return ((x << 1) + 1) / 3; +}; +#endif //HASH_SIZE_IS_POWER2 +inline int HASH_REKEY(const int k, const int s) { + assert(k >= 0); + if (k == 0) { + return s - 1; + } else { + return k - 1; + } +}; +inline int HASH_SIZE(const int x) { + if (IS_HASH(x)) { + return HASH_EXPAND(x); + } else { + return x; + } +}; +#endif //MACRO_RATHER_THAN_INLINE + +inline int HASH_PAIR_KEY(const int x, const int y, const int size) { + return HASH_KEY(x * 1434879443 + y, size); +} + +//_________________________________________________________________________ +// Hash-only functions : table must NOT be Raw. +// the argument 'size' is the total size of the hash table +//_________________________________________________________________________ + +// copy hash table into raw vector +inline void H_copy(int *mem, int *h, int size) { + for (int i = HASH_EXPAND(size); i--; h++) if (*h != HASH_NONE) { + *(mem++) = *h; + } +} + +// Look for the place to add an element. Return NULL if element is already here. +inline int* H_add(int* h, const int size, int a) { + _HASH_ADD_CALL(); + _HASH_ADD_ITER(); + int k = HASH_KEY(a, size); + if (h[k] == HASH_NONE) { + return h + k; + } + while (h[k] != a) { + _HASH_ADD_ITER(); + k = HASH_REKEY(k, size); + if (h[k] == HASH_NONE) { + return h + k; + } + } + return NULL; +} + +// would element be well placed in newk ? +inline bool H_better(const int a, const int size, const int currentk, const int newk) { + int k = HASH_KEY(a, size); + if (newk < currentk) { + return (k < currentk && k >= newk); + } else { + return (k < currentk || k >= newk); + } +} + +// removes h[k] +inline void H_rm(int* h, const int size, int k) { + _HASH_RM_CALL(); + int lasthole = k; + do { + _HASH_RM_ITER(); + k = HASH_REKEY(k, size); + int next = h[k]; + if (next == HASH_NONE) { + break; + } + if (H_better(next, size, k, lasthole)) { + h[lasthole] = next; + lasthole = k; + } + } while (true); + h[lasthole] = HASH_NONE; +} + +//put a +inline int* H_put(int* h, const int size, const int a) { + assert(H_add(h, size, a) != NULL); + _HASH_PUT_CALL(); + _HASH_PUT_ITER(); + int k = HASH_KEY(a, size); + while (h[k] != HASH_NONE) { + k = HASH_REKEY(k, size); + _HASH_PUT_ITER(); + } + h[k] = a; + assert(H_add(h, size, a) == NULL); + return h + k; +} + +// find A +inline int H_find(int *h, int size, const int a) { + assert(H_add(h, size, a) == NULL); + _HASH_FIND_CALL(); + _HASH_FIND_ITER(); + int k = HASH_KEY(a, size); + while (h[k] != a) { + k = HASH_REKEY(k, size); + _HASH_FIND_ITER(); + } + return k; +} + +// Look for the place to add an element. Return NULL if element is already here. +inline bool H_pair_insert(int* h, const int size, int a, int b) { + _HASH_ADD_CALL(); + _HASH_ADD_ITER(); + int k = HASH_PAIR_KEY(a, b, size); + if (h[2 * k] == HASH_NONE) { + h[2 * k] = a; + h[2 * k + 1] = b; + return true; + } + while (h[2 * k] != a || h[2 * k + 1] != b) { + _HASH_ADD_ITER(); + k = HASH_REKEY(k, size); + if (h[2 * k] == HASH_NONE) { + h[2 * k] = a; + h[2 * k + 1] = b; + return true; + } + } + return false; +} + + +//_________________________________________________________________________ +// Generic functions : table can be either Hash or Raw. +// the argument 'size' is the number of elements +//_________________________________________________________________________ + +// Look for an element +inline bool H_is(int *mem, const int size, const int elem) { + if (IS_HASH(size)) { + return (H_add(mem, HASH_EXPAND(size), elem) == NULL); + } else { + return fast_search(mem, size, elem) != NULL; + } +} + +//pick random location (containing an element) +inline int* H_random(int* mem, int size) { + if (!IS_HASH(size)) { + return mem + (my_random() % size); + } + _HASH_RAND_CALL(); + size = HASH_EXPAND(size); + int* yo; + do { + yo = mem + HASH_KEY(my_random(), size); + _HASH_RAND_ITER(); + } while (*yo == HASH_NONE); + return yo; +} + +// replace *k by b +inline int* H_rpl(int *mem, int size, int* k, const int b) { + assert(!H_is(mem, size, b)); + if (!IS_HASH(size)) { + *k = b; + return k; + } else { + size = HASH_EXPAND(size); + assert(mem + int(k - mem) == k); + H_rm(mem, size, int(k - mem)); + return H_put(mem, size, b); + } +} + +// replace a by b +inline int* H_rpl(int *mem, int size, const int a, const int b) { + assert(H_is(mem, size, a)); + assert(!H_is(mem, size, b)); + if (!IS_HASH(size)) { + return fast_rpl(mem, a, b); + } else { + size = HASH_EXPAND(size); + H_rm(mem, size, H_find(mem, size, a)); + return H_put(mem, size, b); + } +} + +} // namespace gengraph + +#endif //HASH_H diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_header.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_header.h new file mode 100644 index 0000000..50e65d5 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_header.h @@ -0,0 +1,109 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 . + */ +#include "gengraph_definitions.h" +#include +#include + +#include "gengraph_random.h" + +namespace gengraph { + +static KW_RNG::RNG _my_random; +int my_random() { + return _my_random.rand_int31(); +} +void my_srandom(int x) { + _my_random.init(x, !x * 13, x * x + 1, (x >> 16) + (x << 16)); +} +int my_binomial(double pp, int n) { + return _my_random.binomial(pp, n); +} +double my_random01() { + return _my_random.rand_halfopen01(); +} + +} + +namespace gengraph { + +static int VERB; +int VERBOSE() { + return VERB; +} +void SET_VERBOSE(int v) { + VERB = v; +} + +//Hash profiling +static unsigned long _hash_rm_i = 0; +static unsigned long _hash_rm_c = 0; +static unsigned long _hash_add_i = 0; +static unsigned long _hash_add_c = 0; +static unsigned long _hash_put_i = 0; +static unsigned long _hash_put_c = 0; +static unsigned long _hash_find_i = 0; +static unsigned long _hash_find_c = 0; +static unsigned long _hash_rand_i = 0; +static unsigned long _hash_rand_c = 0; +static unsigned long _hash_expand = 0; +inline void _hash_add_iter() { + _hash_add_i++; +} +inline void _hash_add_call() { + _hash_add_c++; +} +inline void _hash_put_iter() { + _hash_put_i++; +} +inline void _hash_put_call() { + _hash_put_c++; +} +inline void _hash_rm_iter() { + _hash_rm_i++; +} +inline void _hash_rm_call() { + _hash_rm_c++; +} +inline void _hash_find_iter() { + _hash_find_i++; +} +inline void _hash_find_call() { + _hash_find_c++; +} +inline void _hash_rand_iter() { + _hash_rand_i++; +} +inline void _hash_rand_call() { + _hash_rand_c++; +} +inline void _hash_expand_call() { + _hash_expand++; +} +// void _hash_prof() { +// fprintf(stderr,"HASH_ADD : %lu / %lu\n", _hash_add_c , _hash_add_i); +// fprintf(stderr,"HASH_PUT : %lu / %lu\n", _hash_put_c , _hash_put_i); +// fprintf(stderr,"HASH_FIND: %lu / %lu\n", _hash_find_c, _hash_find_i); +// fprintf(stderr,"HASH_RM : %lu / %lu\n", _hash_rm_c , _hash_rm_i); +// fprintf(stderr,"HASH_RAND: %lu / %lu\n", _hash_rand_c, _hash_rand_i); +// fprintf(stderr,"HASH_EXPAND : %lu calls\n", _hash_expand); +// } + +} // namespace gengraph diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_mr-connected.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_mr-connected.cpp new file mode 100644 index 0000000..951523e --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_mr-connected.cpp @@ -0,0 +1,192 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 . + */ +#include "gengraph_header.h" +#include "gengraph_graph_molloy_optimized.h" +#include "gengraph_graph_molloy_hash.h" +#include "gengraph_degree_sequence.h" +#include "gengraph_random.h" + +#include "igraph_datatype.h" +#include "igraph_graphicality.h" +#include "igraph_types.h" +#include "igraph_error.h" + +#include "../../core/exceptions.h" + +namespace gengraph { + +// return negative number if program should exit +int parse_options(int &argc, char** &argv); + +// options +// static const bool MONITOR_TIME = false; +static const int SHUFFLE_TYPE = FINAL_HEURISTICS; +// static const bool RAW_DEGREES = false; +// static const FILE *Fdeg = stdin; + +//_________________________________________________________________________ +// int main(int argc, char** argv) { + +// // options +// SET_VERBOSE(VERBOSE_NONE); +// if(parse_options(argc, argv) < 0) return -1; + +// //Read degree distribution +// degree_sequence dd(Fdeg, !RAW_DEGREES); + +// //Allocate memory +// if(VERBOSE()) fprintf(stderr,"Allocate memory for graph..."); +// graph_molloy_opt g(dd); +// dd.~degree_sequence(); +// //Realize degree sequence +// if(VERBOSE()) fprintf(stderr,"done\nRealize degree sequence..."); +// bool FAILED = !g.havelhakimi(); +// if(VERBOSE()) fprintf(stderr," %s\n", FAILED ? "Failed" : "Success"); +// if(FAILED) return 2; +// //Merge connected components together +// if(VERBOSE()) fprintf(stderr,"Connecting..."); +// FAILED = !g.make_connected(); +// if(VERBOSE()) fprintf(stderr," %s\n", FAILED ? "Failed" : "Success"); +// if(FAILED) return 3; +// //Convert graph_molloy_opt to graph_molloy_hash +// if(VERBOSE()) fprintf(stderr,"Convert adjacency lists into hash tables..."); +// int *hc = g.hard_copy(); +// g.~graph_molloy_opt(); +// graph_molloy_hash gh(hc); +// delete[] hc; +// if(VERBOSE()) fprintf(stderr,"Done\n"); +// //Shuffle +// gh.shuffle(5*gh.nbarcs(), SHUFFLE_TYPE); +// //Output +// gh.print(); +// if(MONITOR_TIME) { +// double t = double(clock()) / double(CLOCKS_PER_SEC); +// fprintf(stderr,"Time used: %f\n", t); +// } +// return 0; +// } + +//_________________________________________________________________________ +// int parse_options(int &argc, char** &argv) { +// bool HELP = false; +// int argc0 = argc; +// argc = 1; +// for(int a=1; a %s returns a graph in its standard output\n",argv[0]); +// fprintf(stderr," If no file is given, %s reads its standard input\n",argv[0]); +// fprintf(stderr," [-v] and [-vv] options causes extra verbose.\n"); +// fprintf(stderr," [-g] option uses the Gkantsidis heuristics.\n"); +// fprintf(stderr," [-b] option uses the Brute Force heuristics.\n"); +// fprintf(stderr," [-f] option uses the Modified Gkantsidis heuristics.\n"); +// fprintf(stderr," [-o] option uses the Optimal Gkantsidis heuristics.\n"); +// fprintf(stderr," [-t] option monitors computation time\n"); +// fprintf(stderr," [-s] does a srandom(0) to get a constant random graph\n"); +// fprintf(stderr," [-raw] is to take raw degree sequences as input\n"); +// return -1; +// } +// return 0; +// } + + +} // namespace gengraph + +using namespace gengraph; + +extern "C" { + + int igraph_degree_sequence_game_vl(igraph_t *graph, + const igraph_vector_t *out_seq, + const igraph_vector_t *in_seq) { + IGRAPH_HANDLE_EXCEPTIONS( + igraph_bool_t is_graphical; + + if (in_seq && igraph_vector_size(in_seq) != 0) { + IGRAPH_ERROR("This generator works with undirected graphs only", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_is_graphical(out_seq, 0, IGRAPH_SIMPLE_SW, &is_graphical)); + if (!is_graphical) { + IGRAPH_ERROR("Cannot realize the given degree sequence as an undirected, simple graph", + IGRAPH_EINVAL); + } + + RNG_BEGIN(); + + degree_sequence *dd = new degree_sequence(out_seq); + + graph_molloy_opt *g = new graph_molloy_opt(*dd); + delete dd; + + if (!g->havelhakimi()) { + delete g; + RNG_END(); + IGRAPH_FATAL("g->havelhakimi() failed; please report as a bug."); + } + + if (!g->make_connected()) { + delete g; + RNG_END(); + IGRAPH_ERROR("Cannot make a connected graph from the given degree sequence", + IGRAPH_EINVAL); + } + + int *hc = g->hard_copy(); + delete g; + graph_molloy_hash *gh = new graph_molloy_hash(hc); + delete [] hc; + + gh->shuffle(5 * gh->nbarcs(), 100 * gh->nbarcs(), SHUFFLE_TYPE); + + IGRAPH_CHECK(gh->print(graph)); + delete gh; + + RNG_END(); + ); + + return IGRAPH_SUCCESS; + } + +} diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_powerlaw.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_powerlaw.cpp new file mode 100644 index 0000000..8ba0c54 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_powerlaw.cpp @@ -0,0 +1,272 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 . + */ +// Pascalou ... +#ifdef pascalou + #define my_random() random() + #define MY_RAND_MAX 0x7FFFFFFF +#else + #include "gengraph_definitions.h" +#endif + +#include "gengraph_powerlaw.h" +#include +#include +#include + +#include "igraph_error.h" + +namespace gengraph { + +// Destructor +powerlaw::~powerlaw() { + delete[] table; + if (dt != NULL) { + delete[] dt; + } +} + +// Constructor +powerlaw::powerlaw(double _alpha, int _mini, int _maxi) { + alpha = _alpha; + mini = _mini; + maxi = _maxi; + if (alpha <= 2.0 && maxi < 0) + igraph_warningf("powerlaw exponent %f should be > 2 when no " + "Maximum is specified", IGRAPH_FILE_BASENAME, __LINE__, -1, alpha); + if (alpha <= 1.0 && maxi >= 0) + igraph_warningf("powerlaw exponent %f should be > 1", IGRAPH_FILE_BASENAME, __LINE__, + -1, alpha); + if (maxi >= 0 && mini > maxi) + igraph_warningf("powerlaw max %d should be greater than min %d", + IGRAPH_FILE_BASENAME, __LINE__, -1, maxi, mini); + table = new int[POWERLAW_TABLE]; + tabulated = 0; + dt = NULL; +} + +// Sample +int powerlaw::sample() { + if (proba_big != 0 && test_proba(proba_big)) { + return int(floor(0.5 + big_sample(random_float()))); + } + int r = my_random(); + // table[] contains integer from MY_RAND_MAX downto 0, in blocks. Search block... + if (r > (MY_RAND_MAX >> max_dt)) { + return mini; + } + int k = 0; + while (k < max_dt) { + r <<= 1; + r += random_bit(); + k++; + }; + int a = 0; + int b; + while ((b = dt[k++]) < 0 || r < table[b]) { + if (b >= 0) { + a = b + 1; + if (a == tabulated - 1) { + break; + } + r <<= 1; + r += random_bit(); + } + } + + // Now that we found the good block, run a dichotomy on this block [a,b] + while (a < b) { + int c = (a + b) / 2; + if (r < table[c]) { + a = c + 1; + } else { + b = c; + } + } + return mini + a; +} + +// Proba +double powerlaw::proba(int k) { + if (k < mini || (maxi >= 0 && k > maxi)) { + return 0.0; + } + if (k >= mini + tabulated) { + return proba_big * (big_inv_sample(double(k) - 0.5) - big_inv_sample(double(k) + 0.5)); + } else { + double div = table_mul; + int prev_pos_in_table = k - mini - 1; + if (prev_pos_in_table < 0) { + return (double(MY_RAND_MAX) + 1.0 - double(table[0] >> max_dt)) * div; + } + // what block are we in ? + int k1 = 0; + while (k1 < max_dt) { + div *= 0.5; + k1++; + }; + while (dt[k1] < 0 || dt[k1] < prev_pos_in_table) { + k1++; + div *= 0.5; + }; + double prob2 = double(table[prev_pos_in_table + 1]); + if (dt[k1] == prev_pos_in_table) do { + prob2 *= 0.5; + } while (dt[++k1] < 0); + return (double(table[prev_pos_in_table]) - prob2) * div; + } +} + +// Relative Error +double powerlaw::error() { + return 1.0 / (double(tabulated) * double(tabulated)); +} + +// Mean +double powerlaw::mean() { + double sum = 0.0; + for (int i = mini + tabulated; --i >= mini; ) { + sum += double(i) * proba(i); + } + // add proba_big * integral(big_sample(t),t=0..1) + if (proba_big != 0) { + sum += proba_big * ((pow(_a + _b, _exp + 1.0) - pow(_b, _exp + 1.0)) / (_a * (_exp + 1.0)) + double(mini) - offset - sum); + } + return sum; +} + +// Median. Returns integer Med such that P(X<=Med) >= 1/2 +int powerlaw::median() { + if (proba_big > 0.5) { + return int(floor(0.5 + big_sample(1.0 - 0.5 / proba_big))); + } + double sum = 0.0; + int i = mini; + while (sum < 0.5) { + sum += proba(i++); + } + return i - 1; +} + +void powerlaw::init_to_offset(double _offset, int _tabulated) { + offset = _offset; + tabulated = _tabulated; + if (maxi >= 0 && tabulated > maxi - mini) { + tabulated = maxi - mini + 1; + } + double sum = 0.0; + double item = double(tabulated) + offset; + // Compute sum of tabulated probabilities + for (int i = tabulated; i--; ) { + sum += pow(item -= 1.0, -alpha); + } + // Compute others parameters : proba_big, table_mul, _a, _b, _exp + if (maxi > 0 && maxi <= mini + tabulated - 1) { + proba_big = 0; + table_mul = inv_RANDMAX; + } else { + if (maxi < 0) { + _b = 0.0; + } else { + _b = pow(double(maxi - mini) + 0.5 + offset, 1.0 - alpha); + } + _a = pow(double(tabulated) - 0.5 + offset, 1.0 - alpha) - _b; + _exp = 1.0 / (1.0 - alpha); + double sum_big = _a * (-_exp); + proba_big = sum_big / (sum + sum_big); + table_mul = inv_RANDMAX * sum / (sum + sum_big); + } + // How many delimiters will be necessary for the table ? + max_dt = max(0, int(floor(alpha * log(double(tabulated)) / log(2.0))) - 6); + if (dt != NULL) { + delete[] dt; + } + dt = new int[max_dt + 1]; + // Create table as decreasing integers from MY_RAND_MAX+1 (in virtual position -1) down to 0 + // Every time the index crosses a delimiter, numbers get doubled. + double ssum = 0; + double mul = (double(MY_RAND_MAX) + 1.0) * pow(2.0, max_dt) / sum; + item = double(tabulated) + offset; + int k = max_dt; + dt[k--] = tabulated - 1; + for (int i = tabulated; --i > 0; ) { + table[i] = int(floor(0.5 + ssum)); + ssum += mul * pow(item -= 1.0, -alpha); + if (ssum > double(MY_RAND_MAX / 2) && k >= 0) { + while ((ssum *= 0.5) > double(MY_RAND_MAX / 2)) { + mul *= 0.5; + dt[k--] = -1; + }; + mul *= 0.5; dt[k--] = i - 1; + } + } + table[0] = int(floor(0.5 + ssum)); + max_dt = k + 1; +} + +void powerlaw::adjust_offset_mean(double _mean, double err, double factor) { + // Set two bounds for offset + double ol = offset; + double oh = offset; + if (mean() < _mean) { + do { + ol = oh; + oh *= factor; + init_to_offset(oh, tabulated); + } while (mean() < _mean); + } else { + do { + oh = ol; + ol /= factor; + init_to_offset(ol, tabulated); + } while (mean() > _mean); + } + // Now, dichotomy + while (fabs(oh - ol) > err * ol) { + double oc = sqrt(oh * ol); + init_to_offset(oc, tabulated); + if (mean() < _mean) { + ol = oc; + } else { + oh = oc; + } + } + init_to_offset(sqrt(ol * oh), tabulated); +} + +double powerlaw::init_to_mean(double _mean) { + if (maxi >= 0 && _mean >= 0.5 * double((mini + maxi))) { + /* Cannot use IGRAPH_ERRORF() as this function does not + * return an igraph error code. */ + igraph_errorf("Fatal error in powerlaw::init_to_mean(%f): " + "Mean must be in ]min, (min+max)/2[ = ]%d, %d[", + IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EINVAL, + _mean, mini, (mini + maxi) / 2); + return (-1.0); + } + init_to_offset(_mean - double(mini), 100); + adjust_offset_mean(_mean, 0.01, 2); + init_to_offset(offset, POWERLAW_TABLE); + double eps = 1.0 / (double(POWERLAW_TABLE)); + adjust_offset_mean(_mean, eps * eps, 1.01); + return offset; +} + +} // namespace gengraph diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_powerlaw.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_powerlaw.h new file mode 100644 index 0000000..57b6b7d --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_powerlaw.h @@ -0,0 +1,86 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 _POWERLAW_H +#define _POWERLAW_H + +// pascalou +#ifndef pascalou + #include "gengraph_definitions.h" +#endif + +// Discrete integer power-law : P(X=min+k) is proportionnal to (k+k0)^-alpha +// - possibility to determine a range [Min, Max] of possible samples +// - possibility to automatically compute k0 to obtain a given mean z + +namespace gengraph { + +#define POWERLAW_TABLE 10000 + +class powerlaw { +private: + double alpha; // Exponent + int mini; // Minimum sample + int maxi; // Maximum sample + double offset; // Offset + int tabulated; // Number of values to tabulate + int *table; // Table containing cumulative distribution for k=mini..mini+tabulated-1 + int *dt; // Table delimiters + int max_dt; // number of delimiters - 1 + double proba_big; // Probability to take a non-tabulated value + double table_mul; // equal to (1-proba_big)/(RAND_MAX+1) + + // Sample a non-tabulated value >= mini+tabulated + inline double big_sample(double randomfloat) { + return double(mini) + pow(_a * randomfloat + _b, _exp) - offset; + } + inline double big_inv_sample(double s) { + return (pow(s - double(mini) + offset, 1.0 / _exp) - _b) / _a; + } + double _exp, _a, _b; // Cached values used by big_sample(); + + // Dichotomic adjust of offset, so that to_adjust() returns value with + // a precision of eps. Note that to_adjust() must be an increasing function of offset. + void adjust_offset_mean(double value, double eps, double fac); + +public: + int sample(); // Return a random integer + double proba(int); // Return probability to return integer + double error(); // Returns relative numerical error done by this class + double mean(); // Returns mean of the sampler + int median(); // Returns median of the sampler + + // Initialize the power-law sampler. + void init_to_offset(double, int); + // Same, but also returns the offset found + double init_to_mean(double); + double init_to_median(double); + + inline void init() { + init_to_offset(double(mini), POWERLAW_TABLE); + }; + + ~powerlaw(); + powerlaw(double exponent, int mini, int maxi = -1); +}; + +} // namespace gengraph + +#endif //_POWERLAW_H diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_qsort.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_qsort.h new file mode 100644 index 0000000..6af1f2c --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_qsort.h @@ -0,0 +1,564 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 QSORT_H +#define QSORT_H + +#include +#include + +namespace gengraph { + +//___________________________________________________________________________ +// check if every element is zero +inline bool check_zero(int *mem, int n) { + for (int *v = mem + n; v != mem; ) if (*(--v) != 0) { + return false; + } + return true; +} + +//___________________________________________________________________________ +// Sort simple integer arrays in ASCENDING order +//___________________________________________________________________________ +inline int med3(int a, int b, int c) { + if (a < b) { + if (c < b) { + return (a < c) ? c : a; + } else { + return b; + } + } else { + if (c < a) { + return (b < c) ? c : b; + } else { + return a; + } + } +} + +inline void isort(int *v, int t) { + if (t < 2) { + return; + } + for (int i = 1; i < t; i++) { + int *w = v + i; + int tmp = *w; + while (w != v && *(w - 1) > tmp) { + *w = *(w - 1); + w--; + } + *w = tmp; + } +} + +inline int partitionne(int *v, int t, int p) { + int i = 0; + int j = t - 1; + while (i < j) { + while (i <= j && v[i] < p) { + i++; + } + while (i <= j && v[j] > p) { + j--; + } + if (i < j) { + int tmp = v[i]; + v[i++] = v[j]; + v[j--] = tmp; + } + } + if (i == j && v[i] < p) { + i++; + } + assert(i != 0 && i != t); + return i; +} + +inline void qsort(int *v, int t) { + if (t < 15) { + isort(v, t); + } else { + int x = partitionne(v, t, med3(v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2])); + qsort(v, x); + qsort(v + x, t - x); + } +} + +inline int qsort_median(int *v, int t, int pos) { + if (t < 10) { + isort(v, t); + return v[pos]; + } + int x = partitionne(v, t, med3(v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2])); + if (pos < x) { + return qsort_median(v, x, pos); + } else { + return qsort_median(v + x, t - x, pos - x); + } +} + +inline int qsort_median(int *v, int t) { + return qsort_median(v, t, t / 2); +} + +//___________________________________________________________________________ +// Sort simple double arrays in ASCENDING order +//___________________________________________________________________________ +inline double med3(double a, double b, double c) { + if (a < b) { + if (c < b) { + return (a < c) ? c : a; + } else { + return b; + } + } else { + if (c < a) { + return (b < c) ? c : b; + } else { + return a; + } + } +} + +inline void isort(double *v, int t) { + if (t < 2) { + return; + } + for (int i = 1; i < t; i++) { + double *w = v + i; + double tmp = *w; + while (w != v && *(w - 1) > tmp) { + *w = *(w - 1); + w--; + } + *w = tmp; + } +} + +inline int partitionne(double *v, int t, double p) { + int i = 0; + int j = t - 1; + while (i < j) { + while (i <= j && v[i] < p) { + i++; + } + while (i <= j && v[j] > p) { + j--; + } + if (i < j) { + double tmp = v[i]; + v[i++] = v[j]; + v[j--] = tmp; + } + } + if (i == j && v[i] < p) { + i++; + } + assert(i != 0 && i != t); + return i; +} + +inline void qsort(double *v, int t) { + if (t < 15) { + isort(v, t); + } else { + int x = partitionne(v, t, med3(v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2])); + qsort(v, x); + qsort(v + x, t - x); + } +} + +inline double qsort_median(double *v, int t, int pos) { + if (t < 10) { + isort(v, t); + return v[pos]; + } + int x = partitionne(v, t, med3(v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2])); + if (pos < x) { + return qsort_median(v, x, pos); + } else { + return qsort_median(v + x, t - x, pos - x); + } +} + +inline double qsort_median(double *v, int t) { + return qsort_median(v, t, t / 2); +} + +//___________________________________________________________________________ +// Sort integer arrays according to value stored in mem[], in ASCENDING order +inline void isort(int *mem, int *v, int t) { + if (t < 2) { + return; + } + for (int i = 1; i < t; i++) { + int vtmp = v[i]; + int tmp = mem[vtmp]; + int j; + for (j = i; j > 0 && tmp < mem[v[j - 1]]; j--) { + v[j] = v[j - 1]; + } + v[j] = vtmp; + } +} + +inline void qsort(int *mem, int *v, int t) { + if (t < 15) { + isort(mem, v, t); + } else { + int p = med3(mem[v[t >> 1]], mem[v[(t >> 2) + 3]], mem[v[t - (t >> 1) - 3]]); + int i = 0; + int j = t - 1; + while (i < j) { + while (i <= j && mem[v[i]] < p) { + i++; + } + while (i <= j && mem[v[j]] > p) { + j--; + } + if (i < j) { + int tmp = v[i]; + v[i++] = v[j]; + v[j--] = tmp; + } + } + if (i == j && mem[v[i]] < p) { + i++; + } + assert(i != 0 && i != t); + qsort(mem, v, i); + qsort(mem, v + i, t - i); + } +} + +//Box-Sort 1..n according to value stored in mem[], in DESCENDING order. +inline int *pre_boxsort(int *mem, int n, int &offset) { + int *yo; + // maximum and minimum + int mx = mem[0]; + int mn = mem[0]; + for (yo = mem + n - 1; yo != mem; yo--) { + int x = *yo; + if (x > mx) { + mx = x; + } + if (x < mn) { + mn = x; + } + } + // box + int c = mx - mn + 1; + int *box = new int[c]; + for (yo = box + c; yo != box; * (--yo) = 0) { } + for (yo = mem + n; yo != mem; box[*(--yo) - mn]++) { } + // cumul sum + int sum = 0; + for (yo = box + c; yo != box; ) { + sum += *(--yo); + *yo = sum; + } + offset = mn; + return box; +} + +inline int *boxsort(int *mem, int n, int *buff = NULL) { + int i; + if (n <= 0) { + return buff; + } + int offset = 0; + int *box = pre_boxsort(mem, n, offset); + // sort + if (buff == NULL) { + buff = new int[n]; + } + for (i = 0; i < n; i++) { + buff[--box[mem[i] - offset]] = i; + } + // clean + delete[] box; + return buff; +} + +// merge two sorted arays in their intersection. Store the result in first array, and return length +inline int intersect(int *a, int a_len, int *b, int b_len) { + if (a_len == 0 || b_len == 0) { + return 0; + } + int *asup = a + a_len; + int *bsup = b + b_len; + int len = 0; + int *p = a; + do { + if (*a == *b) { + p[len++] = *a; + } + do if (++a == asup) { + return len; + } while (*a < *b); + if (*a == *b) { + p[len++] = *a; + } + do if (++b == bsup) { + return len; + } while (*b < *a); + } while (true); +} + +// merge two sorted arays in their union, store result in m +inline int unify(int *m, int *a, int a_len, int *b, int b_len) { + int *asup = a + a_len; + int *bsup = b + b_len; + int len = 0; + while (a != asup && b != bsup) { + if (*a < *b) { + m[len++] = *(a++); + } else { + if (*a == *b) { + a++; + } + m[len++] = *(b++); + } + } + while (a != asup) { + m[len++] = *(a++); + } + while (b != asup) { + m[len++] = *(b++); + } + return len; +} + +// lexicographic compare +inline int lex_comp(int *v1, int *v2, int n) { + int *stop = v1 + n; + while (v1 != stop && *v1 == *v2) { + v1++; + v2++; + }; + if (v1 == stop) { + return 0; + } else if (*v1 < *v2) { + return -1; + } else { + return 1; + } +} +// lexicographic median of three +inline int *lex_med3(int *a, int *b, int *c, int s) { + int ab = lex_comp(a, b, s); + if (ab == 0) { + return a; + } else { + int cb = lex_comp(c, b, s); + if (cb == 0) { + return b; + } + int ca = lex_comp(c, a, s); + if (ab < 0) { + if (cb > 0) { + return b; + } else { + return (ca > 0) ? c : a; + } + } else { + if (cb < 0) { + return b; + } else { + return (ca < 0) ? c : a; + } + } + } +} + +// Lexicographic sort +inline void lex_isort(int **l, int *v, int t, int s) { + if (t < 2) { + return; + } + for (int i = 1; i < t; i++) { + int *w = v + i; + int tmp = *w; + while (w != v && lex_comp(l[tmp], l[*(w - 1)], s) < 0) { + *w = *(w - 1); + w--; + } + *w = tmp; + } +} + +#ifdef _STABLE_SORT_ONLY + #define _CRITICAL_SIZE_QSORT 0x7FFFFFFF + #warning "lex_qsort will be replaced by lex_isort" +#else + #define _CRITICAL_SIZE_QSORT 15 +#endif + +inline void lex_qsort(int **l, int *v, int t, int s) { + + if (t < _CRITICAL_SIZE_QSORT) { + lex_isort(l, v, t, s); + } else { + int *p = lex_med3(l[v[t >> 1]], l[v[(t >> 2) + 2]], l[v[t - (t >> 1) - 2]], s); + int i = 0; + int j = t - 1; +// printf("pivot = %d\n",p); + while (i < j) { +// for(int k=0; k 0) { + j--; + } + if (i < j) { +// printf(" swap %d[%d] with %d[%d]\n",i,v[i],j,v[j]); + int tmp = v[i]; + v[i++] = v[j]; + v[j--] = tmp; + } + } + if (i == j && lex_comp(l[v[i]], p, s) < 0) { + i++; + } + assert(i != 0 && i != t); + lex_qsort(l, v, i, s); + lex_qsort(l, v + i, t - i, s); + } +} + +// lexicographic indirect compare +inline int lex_comp_indirect(int *key, int *v1, int *v2, int n) { + int *stop = v1 + n; + while (v1 != stop && key[*v1] == key[*v2]) { + v1++; + v2++; + }; + if (v1 == stop) { + return 0; + } else if (key[*v1] < key[*v2]) { + return -1; + } else { + return 1; + } +} + +inline int qsort_min(const int a, const int b) { + return a <= b ? a : b; +} + +// mix indirect compare +inline int mix_comp_indirect(int *key, int a, int b, int **neigh, int *degs) { + if (key[a] < key[b]) { + return -1; + } else if (key[a] > key[b]) { + return 1; + } else { + int cmp = lex_comp_indirect(key, neigh[a], neigh[b], qsort_min(degs[a], degs[b])); + if (cmp == 0) { + if (degs[a] > degs[b]) { + return -1; + } + if (degs[a] < degs[b]) { + return 1; + } + } + return cmp; + } +} +// lexicographic indirect median of three +inline int mix_med3_indirect(int *key, int a, int b, int c, int **neigh, int *degs) { + int ab = mix_comp_indirect(key, a, b, neigh, degs); + if (ab == 0) { + return a; + } else { + int cb = mix_comp_indirect(key, c, b, neigh, degs); + if (cb == 0) { + return b; + } + int ca = mix_comp_indirect(key, c, a, neigh, degs); + if (ab < 0) { + if (cb > 0) { + return b; + } else { + return (ca > 0) ? c : a; + } + } else { + if (cb < 0) { + return b; + } else { + return (ca < 0) ? c : a; + } + } + } +} + +// Sort integer arrays in ASCENDING order +inline void mix_isort_indirect(int *key, int *v, int t, int **neigh, int *degs) { + if (t < 2) { + return; + } + for (int i = 1; i < t; i++) { + int *w = v + i; + int tmp = *w; + while (w != v && mix_comp_indirect(key, tmp, *(w - 1), neigh, degs) < 0) { + *w = *(w - 1); + w--; + } + *w = tmp; + } +} + +inline void mix_qsort_indirect(int *key, int *v, int t, int **neigh, int *degs) { + if (t < 15) { + mix_isort_indirect(key, v, t, neigh, degs); + } else { + int p = mix_med3_indirect(key, v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2], neigh, degs); + int i = 0; + int j = t - 1; +// printf("pivot = %d\n",p); + while (i < j) { +// for(int k=0; k 0) { + j--; + } + if (i < j) { +// printf(" swap %d[%d] with %d[%d]\n",i,v[i],j,v[j]); + int tmp = v[i]; + v[i++] = v[j]; + v[j--] = tmp; + } + } + if (i == j && mix_comp_indirect(key, v[i], p, neigh, degs) < 0) { + i++; + } + assert(i != 0 && i != t); + mix_qsort_indirect(key, v, i, neigh, degs); + mix_qsort_indirect(key, v + i, t - i, neigh, degs); + } +} + +} // namespace gengraph + +#endif //QSORT_H diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_random.cpp b/src/rigraph/core/games/degree_sequence_vl/gengraph_random.cpp new file mode 100644 index 0000000..c4d08cb --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_random.cpp @@ -0,0 +1,277 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 . + */ +#define RNG_C + +#ifdef RCSID + static const char rcsid[] = "$Id: random.cpp,v 1.15 2003/05/14 03:04:45 wilder Exp wilder $"; +#endif + +//________________________________________________________________________ +// See the header file random.h for a description of the contents of this +// file as well as references and credits. + +#include "gengraph_random.h" +#include + +using namespace std; +using namespace KW_RNG; + +//________________________________________________________________________ +// RNG::RNOR generates normal variates with rejection. +// nfix() generates variates after rejection in RNOR. +// Despite rejection, this method is much faster than Box-Muller. + +// double RNG::nfix(slong h, ulong i) +// { +// const double r = 3.442620f; // The starting of the right tail +// static double x, y; + +// for(;;) { +// x = h * wn[i]; + +// // If i == 0, handle the base strip +// if (i==0){ +// do { +// x = -log(rand_open01()) * 0.2904764; // .2904764 is 1/r +// y = -log(rand_open01()); +// } while (y + y < x * x); +// return ((h > 0) ? r + x : -r - x); +// } + +// // If i > 0, handle the wedges of other strips +// if (fn[i] + rand_open01() * (fn[i - 1] - fn[i]) < exp(-.5 * x * x) ) +// return x; + +// // start all over +// h = rand_int32(); +// i = h & 127; +// if ((ulong) abs((sint) h) < kn[i]) +// return (h * wn[i]); +// } + +// } // RNG::nfix + +// // __________________________________________________________________________ +// // RNG::RNOR generates exponential variates with rejection. +// // efix() generates variates after rejection in REXP. + +// double RNG::efix(ulong j, ulong i) +// { +// double x; +// for (;;) +// { +// if (i == 0) +// return (7.69711 - log(rand_open01())); + +// x = j * we[i]; +// if (fe[i] + rand_open01() * (fe[i - 1] - fe[i]) < exp(-x)) +// return (x); + +// j = rand_int32(); +// i = (j & 255); +// if (j < ke[i]) +// return (j * we[i]); +// } + +// } // RNG::efix + +// // __________________________________________________________________________ +// // This procedure creates the tables used by RNOR and REXP + +// void RNG::zigset() +// { +// const double m1 = 2147483648.0; // 2^31 +// const double m2 = 4294967296.0; // 2^32 + +// const double vn = 9.91256303526217e-3; +// const double ve = 3.949659822581572e-3; + +// double dn = 3.442619855899, tn = dn; +// double de = 7.697117470131487, te = de; + +// int i; + +// // Set up tables for RNOR +// double q = vn / exp(-.5 * dn * dn); +// kn[0] = (ulong) ((dn / q) * m1); +// kn[1] = 0; +// wn[0] = q / m1; +// wn[127] = dn / m1; +// fn[0]=1.; +// fn[127] = exp(-.5 * dn * dn); +// for(i = 126; i >= 1; i--) +// { +// dn = sqrt(-2 * log(vn / dn + exp(-.5 * dn * dn))); +// kn[i + 1] = (ulong) ((dn / tn) * m1); +// tn = dn; +// fn[i] = exp(-.5 * dn * dn); +// wn[i] = dn / m1; +// } + +// // Set up tables for REXP +// q = ve / exp(-de); +// ke[0] = (ulong) ((de / q) * m2); +// ke[1] = 0; +// we[0] = q / m2; +// we[255] = de / m2; +// fe[0] = 1.; +// fe[255] = exp(-de); +// for (i = 254; i >= 1; i--) +// { +// de = -log(ve / de + exp(-de)); +// ke[i+1] = (ulong) ((de / te) * m2); +// te = de; +// fe[i] = exp(-de); +// we[i] = de / m2; +// } + +// } // RNG::zigset + +// // __________________________________________________________________________ +// // Generate a gamma variate with parameters 'shape' and 'scale' + +// double RNG::gamma(double shape, double scale) +// { +// if (shape < 1) +// return gamma(shape + 1, scale) * pow(rand_open01(), 1.0 / shape); + +// const double d = shape - 1.0 / 3.0; +// const double c = 1.0 / sqrt(9.0 * d); +// double x, v, u; +// for (;;) { +// do { +// x = RNOR(); +// v = 1.0 + c * x; +// } while (v <= 0.0); +// v = v * v * v; +// u = rand_open01(); +// if (u < 1.0 - 0.0331 * x * x * x * x) +// return (d * v / scale); +// if (log(u) < 0.5 * x * x + d * (1.0 - v + log(v))) +// return (d * v / scale); +// } + +// } // RNG::gamma + +// // __________________________________________________________________________ +// // gammalog returns the logarithm of the gamma function. From Numerical +// // Recipes. + +// double gammalog(double xx) +// { +// static double cof[6]={ +// 76.18009172947146, -86.50532032941677, 24.01409824083091, +// -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5}; + +// double x = xx; +// double y = xx; +// double tmp = x + 5.5; +// tmp -= (x + 0.5) * log(tmp); +// double ser=1.000000000190015; +// for (int j=0; j<=5; j++) +// ser += cof[j] / ++y; +// return -tmp + log(2.5066282746310005 * ser / x); +// } + +// // __________________________________________________________________________ +// // Generate a Poisson variate +// // This is essentially the algorithm from Numerical Recipes + +// double RNG::poisson(double lambda) +// { +// static double sq, alxm, g, oldm = -1.0; +// double em, t, y; + +// if (lambda < 12.0) { +// if (lambda != oldm) { +// oldm = lambda; +// g = exp(-lambda); +// } +// em = -1; +// t = 1.0; +// do { +// ++em; +// t *= rand_open01(); +// } while (t > g); +// } else { +// if (lambda != oldm) { +// oldm = lambda; +// sq = sqrt(2.0 * lambda); +// alxm = log(lambda); +// g = lambda * alxm - gammalog(lambda + 1.0); +// } +// do { +// do { +// y = tan(PI * rand_open01()); +// em = sq * y + lambda; +// } while (em < 0.0); +// em = floor(em); +// t = 0.9 * (1.0 + y * y) * exp(em * alxm - gammalog(em + 1.0)-g); +// } while (rand_open01() > t); +// } +// return em; + +// } // RNG::poisson + +// // __________________________________________________________________________ +// // Generate a binomial variate +// // This is essentially the algorithm from Numerical Recipes + +// int RNG::binomial(double pp, int n) +// { +// if(n==0) return 0; +// if(pp==0.0) return 0; +// if(pp==1.0) return n; +// double p = (pp<0.5 ? pp : 1.0-pp); +// double am = n*p; +// int bnl = 0; +// if(n<25) { +// for(int j=n; j--; ) if(rand_closed01()= en + 1.0); +// em = floor(em); +// t = 1.2 * sq * (1 + y * y) * exp(oldg - gammalog(em + 1.0) - +// gammalog(en - em + 1.0) + em * log(p) + (en - em) * log(pc)); +// } while (rand_closed01() > t); +// bnl = int(em); +// } +// if (p!=pp) bnl=n-bnl; +// return bnl; +// } // RNG::binomial + +// __________________________________________________________________________ +// rng.C diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_random.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_random.h new file mode 100644 index 0000000..90d86b5 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_random.h @@ -0,0 +1,213 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 RNG_H +#define RNG_H + +#include "igraph_random.h" + +namespace KW_RNG { + +typedef signed int sint; +typedef unsigned int uint; +typedef signed long slong; +typedef unsigned long ulong; + +class RNG { +public: + RNG() { } + RNG(ulong z_, ulong w_, ulong jsr_, ulong jcong_ ) { + IGRAPH_UNUSED(z_); IGRAPH_UNUSED(w_); IGRAPH_UNUSED(jsr_); + IGRAPH_UNUSED(jcong_); + }; + ~RNG() { } + + void init(ulong z_, ulong w_, ulong jsr_, ulong jcong_ ) { + IGRAPH_UNUSED(z_); IGRAPH_UNUSED(w_); IGRAPH_UNUSED(jsr_); + IGRAPH_UNUSED(jcong_); + } + long rand_int31() { + return RNG_INT31(); + } + double rand_halfopen01() { // (0,1] + return RNG_UNIF01(); + } + int binomial(double pp, int n) { + return RNG_BINOM(n, pp); + } +}; + +} // namespace KW_RNG + +/* This was the original RNG, but now we use the igraph version */ + +// __________________________________________________________________________ +// random.h - a Random Number Generator Class +// random.cpp - contains the non-inline class methods + +// __________________________________________________________________________ +// This C++ code uses the simple, very fast "KISS" (Keep It Simple +// Stupid) random number generator suggested by George Marsaglia in a +// Usenet posting from 1999. He describes it as "one of my favorite +// generators". It generates high-quality random numbers that +// apparently pass all commonly used tests for randomness. In fact, it +// generates random numbers by combining the results of three other good +// random number generators that have different periods and are +// constructed from completely different algorithms. It does not have +// the ultra-long period of some other generators - a "problem" that can +// be fixed fairly easily - but that seems to be its only potential +// problem. The period is about 2^123. + +// The ziggurat method of Marsaglia is used to generate exponential and +// normal variates. The method as well as source code can be found in +// the article "The Ziggurat Method for Generating Random Variables" by +// Marsaglia and Tsang, Journal of Statistical Software 5, 2000. + +// The method for generating gamma variables appears in "A Simple Method +// for Generating Gamma Variables" by Marsaglia and Tsang, ACM +// Transactions on Mathematical Software, Vol. 26, No 3, Sep 2000, pages +// 363-372. + +// The code for Poisson and Binomial random numbers comes from +// Numerical Recipes in C. + +// Some of this code is unlikely to work correctly as is on 64 bit +// machines. + +// #include +// #include +// #ifdef _WIN32 +// #include +// #define getpid _getpid +// #else +// #include +// #endif + +// //#ifdef _WIN32 +// static const double PI = 3.1415926535897932; +// static const double AD_l = 0.6931471805599453; +// static const double AD_a = 5.7133631526454228; +// static const double AD_b = 3.4142135623730950; +// static const double AD_c = -1.6734053240284925; +// static const double AD_p = 0.9802581434685472; +// static const double AD_A = 5.6005707569738080; +// static const double AD_B = 3.3468106480569850; +// static const double AD_H = 0.0026106723602095; +// static const double AD_D = 0.0857864376269050; +// //#endif //_WIN32 + +// namespace KW_RNG { + +// class RNG +// { +// private: +// ulong z, w, jsr, jcong; // Seeds + +// ulong kn[128], ke[256]; +// double wn[128],fn[128], we[256],fe[256]; + +// /* +// #ifndef _WIN32 +// static const double PI = 3.1415926535897932; +// static const double AD_l = 0.6931471805599453; +// static const double AD_a = 5.7133631526454228; +// static const double AD_b = 3.4142135623730950; +// static const double AD_c = -1.6734053240284925; +// static const double AD_p = 0.9802581434685472; +// static const double AD_A = 5.6005707569738080; +// static const double AD_B = 3.3468106480569850; +// static const double AD_H = 0.0026106723602095; +// static const double AD_D = 0.0857864376269050; +// #endif //_WIN32 +// */ + +// public: +// RNG() { init(); zigset(); } +// RNG(ulong z_, ulong w_, ulong jsr_, ulong jcong_ ) : +// z(z_), w(w_), jsr(jsr_), jcong(jcong_) { zigset(); } +// ~RNG() { } + + +// inline ulong znew() +// { return (z = 36969 * (z & 65535) + (z >> 16)); } +// inline ulong wnew() +// { return (w = 18000 * (w & 65535) + (w >> 16)); } +// inline ulong MWC() +// { return (((znew() & 65535) << 16) + wnew()); } +// inline ulong SHR3() +// { jsr ^= ((jsr & 32767) << 17); jsr ^= (jsr >> 13); return (jsr ^= ((jsr << 5) & 0xFFFFFFFF)); } +// inline ulong CONG() +// { return (jcong = (69069 * jcong + 1234567) & 0xFFFFFFFF); } +// inline double RNOR() { +// slong h = rand_int32(); +// ulong i = h & 127; +// return (((ulong) abs((sint) h) < kn[i]) ? h * wn[i] : nfix(h, i)); +// } +// inline double REXP() { +// ulong j = rand_int32(); +// ulong i = j & 255; +// return ((j < ke[i]) ? j * we[i] : efix(j, i)); +// } + +// double nfix(slong h, ulong i); +// double efix(ulong j, ulong i); +// void zigset(); + +// inline void init() +// { ulong yo = time(0) + getpid(); +// z = w = jsr = jcong = yo; } +// inline void init(ulong z_, ulong w_, ulong jsr_, ulong jcong_ ) +// { z = z_; w = w_; jsr = jsr_; jcong = jcong_; } + +// inline ulong rand_int32() // [0,2^32-1] +// { return ((MWC() ^ CONG()) + SHR3()) & 0xFFFFFFFF; } +// inline long rand_int31() // [0,2^31-1] +// { return long(rand_int32() >> 1);} +// inline double rand_closed01() // [0,1] +// { return ((double) rand_int32() / 4294967295.0); } +// inline double rand_open01() // (0,1) +// { return (((double) rand_int32() + 0.5) / 4294967296.0); } +// inline double rand_halfclosed01() // [0,1) +// { return ((double) rand_int32() / 4294967296.0); } +// inline double rand_halfopen01() // (0,1] +// { return (((double) rand_int32() + 0.5) / 4294967295.5); } + +// // Continuous Distributions +// inline double uniform(double x = 0.0, double y = 1.0) +// { return rand_closed01() * (y - x) + x; } +// inline double normal(double mu = 0.0, double sd = 1.0) +// { return RNOR() * sd + mu; } +// inline double exponential(double lambda = 1) +// { return REXP() / lambda; } +// double gamma(double shape = 1, double scale = 1); +// double chi_square(double df) +// { return gamma(df / 2.0, 0.5); } +// double beta(double a1, double a2) +// { double x1 = gamma(a1, 1); return (x1 / (x1 + gamma(a2, 1))); } + +// // Discrete Distributions +// double poisson(double lambda); +// int binomial(double pp, int n); + +// }; // class RNG + +// } // namespace + +#endif // RNG_H diff --git a/src/rigraph/core/games/degree_sequence_vl/gengraph_vertex_cover.h b/src/rigraph/core/games/degree_sequence_vl/gengraph_vertex_cover.h new file mode 100644 index 0000000..25b8e84 --- /dev/null +++ b/src/rigraph/core/games/degree_sequence_vl/gengraph_vertex_cover.h @@ -0,0 +1,72 @@ +/* + * + * gengraph - generation of random simple connected graphs with prescribed + * degree sequence + * + * Copyright (C) 2006 Fabien Viger + * + * 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 2 of the License, or + * (at your option) 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 _VERTEX_COVER_H +#define _VERTEX_COVER_H + +// vertex_cover() builds a list of vertices which covers every edge of the graph +// Input is a classical adjacency-list graph +// As an output, vertex_cover() modify the degrees in degs[], so that +// any vertex with a degree > 0 belongs to the vertex coverage. +// Moreover, vertex_cover() keeps links[] intact, permuting only the adjacency lists + +#include "gengraph_box_list.h" +#include + +namespace gengraph { + +void vertex_cover(int n, int *links, int *deg, int **neigh = NULL) { + int i; + // create and initialize neigh[] + if (neigh == NULL) { + neigh = new int*[n]; + neigh[0] = links; + for (i = 1; i < n; i++) { + neigh[i] = neigh[i - 1] + deg[i]; + } + } + // create box_list + box_list bl(n, deg); + do { + int v; + // remove vertices adjacent to vertices of degree 1 + while ((v = bl.get_one()) >= 0) { + bl.pop_vertex(v, neigh); + } + // remove vertex of max degree and its highest-degree neighbour + if (!bl.is_empty()) { + v = bl.get_max(); + int *w = neigh[v]; + int v2 = *(w++); + int dm = deg[v2]; + int k = deg[v] - 1; + while (k--) if (deg[*(w++)] > dm) { + v2 = *(w - 1); + dm = deg[v2]; + }; + bl.pop_vertex(v, neigh); + bl.pop_vertex(v2, neigh); + } + } while (!bl.is_empty()); +} + +} // namespace gengraph + +#endif //_VERTEX_COVER_H diff --git a/src/rigraph/core/games/dotproduct.c b/src/rigraph/core/games/dotproduct.c new file mode 100644 index 0000000..c4bf614 --- /dev/null +++ b/src/rigraph/core/games/dotproduct.c @@ -0,0 +1,280 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2014 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" +#include "igraph_random.h" +#include "igraph_constructors.h" +#include "igraph_blas.h" + +/** + * \function igraph_dot_product_game + * \brief Generates a random dot product graph. + * + * In this model, each vertex is represented by a latent + * position vector. Probability of an edge between two vertices are given + * by the dot product of their latent position vectors. + * + * + * See also Christine Leigh Myers Nickel: Random dot product graphs, a + * model for social networks. Dissertation, Johns Hopkins University, + * Maryland, USA, 2006. + * + * \param graph The output graph is stored here. + * \param vecs A matrix in which each latent position vector is a + * column. The dot product of the latent position vectors should be + * in the [0,1] interval, otherwise a warning is given. For + * negative dot products, no edges are added; dot products that are + * larger than one always add an edge. + * \param directed Should the generated graph be directed? + * \return Error code. + * + * Time complexity: O(n*n*m), where n is the number of vertices, + * and m is the length of the latent vectors. + * + * \sa \ref igraph_sample_dirichlet(), \ref + * igraph_sample_sphere_volume(), \ref igraph_sample_sphere_surface() + * for functions to generate the latent vectors. + */ + +int igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *vecs, + igraph_bool_t directed) { + + igraph_integer_t nrow = igraph_matrix_nrow(vecs); + igraph_integer_t ncol = igraph_matrix_ncol(vecs); + int i, j; + igraph_vector_t edges; + igraph_bool_t warned_neg = 0, warned_big = 0; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + RNG_BEGIN(); + + for (i = 0; i < ncol; i++) { + int from = directed ? 0 : i + 1; + igraph_vector_t v1; + igraph_vector_view(&v1, &MATRIX(*vecs, 0, i), nrow); + for (j = from; j < ncol; j++) { + igraph_real_t prob; + igraph_vector_t v2; + if (i == j) { + continue; + } + igraph_vector_view(&v2, &MATRIX(*vecs, 0, j), nrow); + igraph_blas_ddot(&v1, &v2, &prob); + if (prob < 0 && ! warned_neg) { + warned_neg = 1; + IGRAPH_WARNING("Negative connection probability in " + "dot-product graph"); + } else if (prob > 1 && ! warned_big) { + warned_big = 1; + IGRAPH_WARNING("Greater than 1 connection probability in " + "dot-product graph"); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); + } else if (RNG_UNIF01() < prob) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); + } + } + } + + RNG_END(); + + igraph_create(graph, &edges, ncol, directed); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_sample_sphere_surface + * Sample points uniformly from the surface of a sphere + * + * The center of the sphere is at the origin. + * + * \param dim The dimension of the random vectors. + * \param n The number of vectors to sample. + * \param radius Radius of the sphere, it must be positive. + * \param positive Whether to restrict sampling to the positive + * orthant. + * \param res Pointer to an initialized matrix, the result is + * stored here, each column will be a sampled vector. The matrix is + * resized, as needed. + * \return Error code. + * + * Time complexity: O(n*dim*g), where g is the time complexity of + * generating a standard normal random number. + * + * \sa \ref igraph_sample_sphere_volume(), \ref + * igraph_sample_dirichlet() for other similar samplers. + */ + +int igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer_t n, + igraph_real_t radius, + igraph_bool_t positive, + igraph_matrix_t *res) { + igraph_integer_t i, j; + + if (dim < 2) { + IGRAPH_ERROR("Sphere must be at least two dimensional to sample from " + "surface", IGRAPH_EINVAL); + } + if (n < 0) { + IGRAPH_ERROR("Number of samples must be non-negative", IGRAPH_EINVAL); + } + if (radius <= 0) { + IGRAPH_ERROR("Sphere radius must be positive", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_matrix_resize(res, dim, n)); + + RNG_BEGIN(); + + for (i = 0; i < n; i++) { + igraph_real_t *col = &MATRIX(*res, 0, i); + igraph_real_t sum = 0.0; + for (j = 0; j < dim; j++) { + col[j] = RNG_NORMAL(0, 1); + sum += col[j] * col[j]; + } + sum = sqrt(sum); + for (j = 0; j < dim; j++) { + col[j] = radius * col[j] / sum; + } + if (positive) { + for (j = 0; j < dim; j++) { + col[j] = fabs(col[j]); + } + } + } + + RNG_END(); + + return 0; +} + +/** + * \function igraph_sample_sphere_volume + * Sample points uniformly from the volume of a sphere + * + * The center of the sphere is at the origin. + * + * \param dim The dimension of the random vectors. + * \param n The number of vectors to sample. + * \param radius Radius of the sphere, it must be positive. + * \param positive Whether to restrict sampling to the positive + * orthant. + * \param res Pointer to an initialized matrix, the result is + * stored here, each column will be a sampled vector. The matrix is + * resized, as needed. + * \return Error code. + * + * Time complexity: O(n*dim*g), where g is the time complexity of + * generating a standard normal random number. + * + * \sa \ref igraph_sample_sphere_surface(), \ref + * igraph_sample_dirichlet() for other similar samplers. + */ + + +int igraph_sample_sphere_volume(igraph_integer_t dim, igraph_integer_t n, + igraph_real_t radius, + igraph_bool_t positive, + igraph_matrix_t *res) { + + igraph_integer_t i, j; + + /* Arguments are checked by the following call */ + + IGRAPH_CHECK(igraph_sample_sphere_surface(dim, n, radius, positive, res)); + + RNG_BEGIN(); + + for (i = 0; i < n; i++) { + igraph_real_t *col = &MATRIX(*res, 0, i); + igraph_real_t U = pow(RNG_UNIF01(), 1.0 / dim); + for (j = 0; j < dim; j++) { + col[j] *= U; + } + } + + RNG_END(); + + return 0; +} + +/** + * \function igraph_sample_dirichlet + * Sample points from a Dirichlet distribution + * + * \param n The number of vectors to sample. + * \param alpha The parameters of the Dirichlet distribution. They + * must be positive. The length of this vector gives the dimension + * of the generated samples. + * \param res Pointer to an initialized matrix, the result is stored + * here, one sample in each column. It will be resized, as needed. + * \return Error code. + * + * Time complexity: O(n * dim * g), where dim is the dimension of the + * sample vectors, set by the length of alpha, and g is the time + * complexity of sampling from a Gamma distribution. + * + * \sa \ref igraph_sample_sphere_surface() and + * \ref igraph_sample_sphere_volume() for other methods to sample + * latent vectors. + */ + +int igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha, + igraph_matrix_t *res) { + + igraph_integer_t len = igraph_vector_size(alpha); + igraph_integer_t i; + igraph_vector_t vec; + + if (n < 0) { + IGRAPH_ERROR("Number of samples should be non-negative", + IGRAPH_EINVAL); + } + if (len < 2) { + IGRAPH_ERROR("Dirichlet parameter vector too short, must " + "have at least two entries", IGRAPH_EINVAL); + } + if (igraph_vector_min(alpha) <= 0) { + IGRAPH_ERROR("Dirichlet concentration parameters must be positive", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_matrix_resize(res, len, n)); + + RNG_BEGIN(); + + for (i = 0; i < n; i++) { + igraph_vector_view(&vec, &MATRIX(*res, 0, i), len); + igraph_rng_get_dirichlet(igraph_rng_default(), alpha, &vec); + } + + RNG_END(); + + return 0; +} diff --git a/src/rigraph/core/games/erdos_renyi.c b/src/rigraph/core/games/erdos_renyi.c new file mode 100644 index 0000000..c0b1acf --- /dev/null +++ b/src/rigraph/core/games/erdos_renyi.c @@ -0,0 +1,285 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_nongraph.h" +#include "igraph_random.h" + +/** + * \section about_games + * + * Games are randomized graph generators. Randomization means that + * they generate a different graph every time you call them. + */ + +int igraph_erdos_renyi_game_gnp( + igraph_t *graph, igraph_integer_t n, igraph_real_t p, + igraph_bool_t directed, igraph_bool_t loops +) { + + long int no_of_nodes = n; + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + igraph_vector_t s = IGRAPH_VECTOR_NULL; + int retval = 0; + long int vsize; + + if (n < 0) { + IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); + } + if (p < 0.0 || p > 1.0) { + IGRAPH_ERROR("Invalid probability given", IGRAPH_EINVAL); + } + + if (p == 0.0 || no_of_nodes == 0) { + IGRAPH_CHECK(retval = igraph_empty(graph, n, directed)); + } else if (p == 1.0) { + IGRAPH_CHECK(retval = igraph_full(graph, n, directed, loops)); + } else { + + long int i; + double maxedges = n, last; + if (directed && loops) { + maxedges *= n; + } else if (directed && !loops) { + maxedges *= (n - 1); + } else if (!directed && loops) { + maxedges *= (n + 1) / 2.0; + } else { + maxedges *= (n - 1) / 2.0; + } + + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) (maxedges * p * 1.1))); + + RNG_BEGIN(); + + last = RNG_GEOM(p); + while (last < maxedges) { + IGRAPH_CHECK(igraph_vector_push_back(&s, last)); + last += RNG_GEOM(p); + last += 1; + } + + RNG_END(); + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&s) * 2)); + + vsize = igraph_vector_size(&s); + if (directed && loops) { + for (i = 0; i < vsize; i++) { + long int to = (long int) floor(VECTOR(s)[i] / no_of_nodes); + long int from = (long int) (VECTOR(s)[i] - ((igraph_real_t)to) * no_of_nodes); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } + } else if (directed && !loops) { + for (i = 0; i < vsize; i++) { + long int to = (long int) floor(VECTOR(s)[i] / no_of_nodes); + long int from = (long int) (VECTOR(s)[i] - ((igraph_real_t)to) * no_of_nodes); + if (from == to) { + to = no_of_nodes - 1; + } + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } + } else if (!directed && loops) { + for (i = 0; i < vsize; i++) { + long int to = (long int) floor((sqrt(8 * VECTOR(s)[i] + 1) - 1) / 2); + long int from = (long int) (VECTOR(s)[i] - (((igraph_real_t)to) * (to + 1)) / 2); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } + } else { /* !directed && !loops */ + for (i = 0; i < vsize; i++) { + long int to = (long int) floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2); + long int from = (long int) (VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } + } + + igraph_vector_destroy(&s); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(retval = igraph_create(graph, &edges, n, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + } + + return retval; +} + +int igraph_erdos_renyi_game_gnm( + igraph_t *graph, igraph_integer_t n, igraph_real_t m, + igraph_bool_t directed, igraph_bool_t loops +) { + + igraph_integer_t no_of_nodes = n; + igraph_integer_t no_of_edges = (igraph_integer_t) m; + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + igraph_vector_t s = IGRAPH_VECTOR_NULL; + int retval = 0; + + if (n < 0) { + IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); + } + if (m < 0) { + IGRAPH_ERROR("Invalid number of edges", IGRAPH_EINVAL); + } + + if (m == 0.0 || no_of_nodes == 0) { + IGRAPH_CHECK(retval = igraph_empty(graph, n, directed)); + } else { + + long int i; + double maxedges = n; + if (directed && loops) { + maxedges *= n; + } else if (directed && !loops) { + maxedges *= (n - 1); + } else if (!directed && loops) { + maxedges *= (n + 1) / 2.0; + } else { + maxedges *= (n - 1) / 2.0; + } + + if (no_of_edges > maxedges) { + IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL); + } + + if (maxedges == no_of_edges) { + retval = igraph_full(graph, n, directed, loops); + } else { + + long int slen; + + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + IGRAPH_CHECK(igraph_random_sample(&s, 0, maxedges - 1, + (igraph_integer_t) no_of_edges)); + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&s) * 2)); + + slen = igraph_vector_size(&s); + if (directed && loops) { + for (i = 0; i < slen; i++) { + long int to = (long int) floor(VECTOR(s)[i] / no_of_nodes); + long int from = (long int) (VECTOR(s)[i] - ((igraph_real_t)to) * no_of_nodes); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } + } else if (directed && !loops) { + for (i = 0; i < slen; i++) { + long int from = (long int) floor(VECTOR(s)[i] / (no_of_nodes - 1)); + long int to = (long int) (VECTOR(s)[i] - ((igraph_real_t)from) * (no_of_nodes - 1)); + if (from == to) { + to = no_of_nodes - 1; + } + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } + } else if (!directed && loops) { + for (i = 0; i < slen; i++) { + long int to = (long int) floor((sqrt(8 * VECTOR(s)[i] + 1) - 1) / 2); + long int from = (long int) (VECTOR(s)[i] - (((igraph_real_t)to) * (to + 1)) / 2); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } + } else { /* !directed && !loops */ + for (i = 0; i < slen; i++) { + long int to = (long int) floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2); + long int from = (long int) (VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } + } + + igraph_vector_destroy(&s); + IGRAPH_FINALLY_CLEAN(1); + retval = igraph_create(graph, &edges, n, directed); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + } + } + + return retval; +} + +/** + * \ingroup generators + * \function igraph_erdos_renyi_game + * \brief Generates a random (Erdős-Rényi) graph. + * + * \param graph Pointer to an uninitialized graph object. + * \param type The type of the random graph, possible values: + * \clist + * \cli IGRAPH_ERDOS_RENYI_GNM + * G(n,m) graph, + * m edges are + * selected uniformly randomly in a graph with + * n vertices. + * \cli IGRAPH_ERDOS_RENYI_GNP + * G(n,p) graph, + * every possible edge is included in the graph with + * probability p. + * \endclist + * \param n The number of vertices in the graph. + * \param p_or_m This is the p parameter for + * G(n,p) graphs and the + * m + * parameter for G(n,m) graphs. + * \param directed Logical, whether to generate a directed graph. + * \param loops Logical, whether to generate loops (self) edges. + * \return Error code: + * \c IGRAPH_EINVAL: invalid + * \p type, \p n, + * \p p or \p m + * parameter. + * \c IGRAPH_ENOMEM: there is not enough + * memory for the operation. + * + * Time complexity: O(|V|+|E|), the + * number of vertices plus the number of edges in the graph. + * + * \sa \ref igraph_barabasi_game(), \ref igraph_growing_random_game() + * + * \example examples/simple/igraph_erdos_renyi_game.c + */ +int igraph_erdos_renyi_game(igraph_t *graph, igraph_erdos_renyi_t type, + igraph_integer_t n, igraph_real_t p_or_m, + igraph_bool_t directed, igraph_bool_t loops) { + int retval = 0; + + if (type == IGRAPH_ERDOS_RENYI_GNP) { + retval = igraph_erdos_renyi_game_gnp(graph, n, p_or_m, directed, loops); + } else if (type == IGRAPH_ERDOS_RENYI_GNM) { + retval = igraph_erdos_renyi_game_gnm(graph, n, p_or_m, directed, loops); + } else { + IGRAPH_ERROR("Invalid type", IGRAPH_EINVAL); + } + + return retval; +} diff --git a/src/rigraph/core/games/establishment.c b/src/rigraph/core/games/establishment.c new file mode 100644 index 0000000..552858c --- /dev/null +++ b/src/rigraph/core/games/establishment.c @@ -0,0 +1,186 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_memory.h" +#include "igraph_nongraph.h" +#include "igraph_random.h" + +/** + * \function igraph_establishment_game + * \brief Generates a graph with a simple growing model with vertex types. + * + * + * The simulation goes like this: a single vertex is added at each + * time step. This new vertex tries to connect to \p k vertices in the + * graph. The probability that such a connection is realized depends + * on the types of the vertices involved. + * + * \param graph Pointer to an uninitialized graph. + * \param nodes The number of vertices in the graph. + * \param types The number of vertex types. + * \param k The number of connections tried in each time step. + * \param type_dist Vector giving the distribution of vertex types. + * If \c NULL, the distribution is assumed to be uniform. + * \param pref_matrix Matrix giving the connection probabilities for + * different vertex types. + * \param directed Logical, whether to generate a directed graph. + * \param node_type_vec An initialized vector or \c NULL. + * If not \c NULL, the type of each node will be stored here. + * \return Error code. + * + * Added in version 0.2. + * + * Time complexity: O(|V|*k*log(|V|)), |V| is the number of vertices + * and k is the \p k parameter. + */ +int igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes, + igraph_integer_t types, igraph_integer_t k, + const igraph_vector_t *type_dist, + const igraph_matrix_t *pref_matrix, + igraph_bool_t directed, + igraph_vector_t *node_type_vec) { + long int i, j; + igraph_vector_t edges; + igraph_vector_t cumdist; + igraph_vector_t potneis; + igraph_real_t maxcum; + igraph_vector_t *nodetypes; + + /* Argument contracts */ + if(nodes < 0){ + IGRAPH_ERROR("The number of vertices must be non-negative.", IGRAPH_EINVAL); + } + + if (types < 1) { + IGRAPH_ERROR("The number of vertex types must be at least 1.", IGRAPH_EINVAL); + } + + if (type_dist) { + igraph_real_t lo; + + if (igraph_vector_size(type_dist) != types) { + IGRAPH_ERROR("The vertex type distribution vector must agree in length with the number of types.", + IGRAPH_EINVAL); + } + + lo = igraph_vector_min(type_dist); + if (lo < 0) { + IGRAPH_ERROR("The vertex type distribution vector must not contain negative values.", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo)) { + IGRAPH_ERROR("The vertex type distribution vector must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (igraph_matrix_nrow(pref_matrix) != types || igraph_matrix_ncol(pref_matrix) != types) { + IGRAPH_ERROR("The preference matrix must be square and agree in dimensions with the number of types.", IGRAPH_EINVAL); + } + + { + igraph_real_t lo, hi; + igraph_matrix_minmax(pref_matrix, &lo, &hi); + + if (lo < 0 || hi > 1) { + IGRAPH_ERROR("The preference matrix must contain probabilities in [0, 1].", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo) || igraph_is_nan(hi)) { + IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (! directed && ! igraph_matrix_is_symmetric(pref_matrix)) { + IGRAPH_ERROR("The preference matrix must be symmetric when generating undirected graphs.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types + 1); + IGRAPH_VECTOR_INIT_FINALLY(&potneis, k); + + if (type_dist) { + VECTOR(cumdist)[0] = 0; + for (i = 0; i < types; ++i) { + VECTOR(cumdist)[i + 1] = VECTOR(cumdist)[i] + VECTOR(*type_dist)[i]; + } + } else { + for (i = 0; i < types+1; ++i) { + VECTOR(cumdist)[i] = i; + } + } + maxcum = igraph_vector_tail(&cumdist); + + if (maxcum <= 0) { + IGRAPH_ERROR("The vertex type distribution vector must contain at least one positive value.", IGRAPH_EINVAL); + } + + if (node_type_vec) { + nodetypes = node_type_vec; + IGRAPH_CHECK(igraph_vector_resize(nodetypes, nodes)); + } else { + nodetypes = IGRAPH_CALLOC(1, igraph_vector_t); + if (! nodetypes) { + IGRAPH_ERROR("Insufficient memory for establishment_game.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, nodetypes); + IGRAPH_VECTOR_INIT_FINALLY(nodetypes, nodes); + } + + RNG_BEGIN(); + + for (i = 0; i < nodes; i++) { + igraph_real_t uni = RNG_UNIF(0, maxcum); + long int type; + igraph_vector_binsearch(&cumdist, uni, &type); + VECTOR(*nodetypes)[i] = type - 1; + } + + for (i = k; i < nodes; i++) { + long int type1 = (long int) VECTOR(*nodetypes)[i]; + igraph_random_sample(&potneis, 0, i - 1, k); + for (j = 0; j < k; j++) { + long int type2 = (long int) VECTOR(*nodetypes)[(long int)VECTOR(potneis)[j]]; + if (RNG_UNIF01() < MATRIX(*pref_matrix, type1, type2)) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, VECTOR(potneis)[j])); + } + } + } + + RNG_END(); + + if (! node_type_vec) { + igraph_vector_destroy(nodetypes); + IGRAPH_FREE(nodetypes); + IGRAPH_FINALLY_CLEAN(2); + } + igraph_vector_destroy(&potneis); + igraph_vector_destroy(&cumdist); + IGRAPH_FINALLY_CLEAN(2); + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/forestfire.c b/src/rigraph/core/games/forestfire.c new file mode 100644 index 0000000..433cf46 --- /dev/null +++ b/src/rigraph/core/games/forestfire.c @@ -0,0 +1,263 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" +#include "igraph_memory.h" +#include "igraph_random.h" +#include "igraph_progress.h" +#include "igraph_interface.h" +#include "igraph_constructors.h" +#include "igraph_dqueue.h" + +#include "../core/interruption.h" + +typedef struct igraph_i_forest_fire_data_t { + igraph_vector_t *inneis; + igraph_vector_t *outneis; + long int no_of_nodes; +} igraph_i_forest_fire_data_t; + + +static void igraph_i_forest_fire_free(igraph_i_forest_fire_data_t *data) { + long int i; + for (i = 0; i < data->no_of_nodes; i++) { + igraph_vector_destroy(data->inneis + i); + igraph_vector_destroy(data->outneis + i); + } +} + +/** + * \function igraph_forest_fire_game + * \brief Generates a network according to the \quote forest fire game \endquote. + * + * The forest fire model intends to reproduce the following network + * characteristics, observed in real networks: + * \ilist + * \ili Heavy-tailed in-degree distribution. + * \ili Heavy-tailed out-degree distribution. + * \ili Communities. + * \ili Densification power-law. The network is densifying in time, + * according to a power-law rule. + * \ili Shrinking diameter. The diameter of the network decreases in + * time. + * \endilist + * + * + * The network is generated in the following way. One vertex is added at + * a time. This vertex connects to (cites) ambs vertices already + * present in the network, chosen uniformly random. Now, for each cited + * vertex v we do the following procedure: + * \olist + * \oli We generate two random numbers, x and y, that are + * geometrically distributed with means p/(1-p) and + * rp(1-rp). (p is \p fw_prob, r is + * \p bw_factor.) The new vertex cites x outgoing neighbors + * and y incoming neighbors of v, from those which are + * not yet cited by the new vertex. If there are less than x or + * y such vertices available then we cite all of them. + * \oli The same procedure is applied to all the newly cited + * vertices. + * \endolist + * + * See also: + * Jure Leskovec, Jon Kleinberg and Christos Faloutsos. Graphs over time: + * densification laws, shrinking diameters and possible explanations. + * \emb KDD '05: Proceeding of the eleventh ACM SIGKDD international + * conference on Knowledge discovery in data mining \eme, 177--187, 2005. + * + * Note however, that the version of the model in the published paper is incorrect + * in the sense that it cannot generate the kind of graphs the authors + * claim. A corrected version is available from + * http://cs.stanford.edu/people/jure/pubs/powergrowth-tkdd.pdf , our + * implementation is based on this. + * + * \param graph Pointer to an uninitialized graph object. + * \param nodes The number of vertices in the graph. + * \param fw_prob The forward burning probability. + * \param bw_factor The backward burning ratio. The backward burning + probability is calculated as bw.factor*fw.prob. + * \param pambs The number of ambassador vertices. + * \param directed Whether to create a directed graph. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, + igraph_real_t fw_prob, igraph_real_t bw_factor, + igraph_integer_t pambs, igraph_bool_t directed) { + + igraph_vector_long_t visited; + long int no_of_nodes = nodes, actnode, i; + igraph_vector_t edges; + igraph_vector_t *inneis, *outneis; + igraph_i_forest_fire_data_t data; + igraph_dqueue_t neiq; + long int ambs = pambs; + igraph_real_t param_geom_out = 1 - fw_prob; + igraph_real_t param_geom_in = 1 - fw_prob * bw_factor; + + if (fw_prob < 0 || fw_prob >= 1) { + IGRAPH_ERROR("Forest fire model: 'fw_prob' must satisfy 0 <= fw_prob < 1.", + IGRAPH_EINVAL); + } + if (bw_factor * fw_prob < 0 || bw_factor * fw_prob >= 1) { + IGRAPH_ERROR("Forest fire model: 'bw_factor' must satisfy 0 <= bw_factor * fw_prob < 1.", + IGRAPH_EINVAL); + } + if (ambs < 0) { + IGRAPH_ERROR("Forest fire model: Number of ambassadors must not be negative.", + IGRAPH_EINVAL); + } + + if (ambs == 0) { + IGRAPH_CHECK(igraph_empty(graph, nodes, directed)); + return 0; + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + inneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_t); + if (!inneis) { + IGRAPH_ERROR("Cannot run forest fire model.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, inneis); + outneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_t); + if (!outneis) { + IGRAPH_ERROR("Cannot run forest fire model.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, outneis); + data.inneis = inneis; + data.outneis = outneis; + data.no_of_nodes = no_of_nodes; + IGRAPH_FINALLY(igraph_i_forest_fire_free, &data); + for (i = 0; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_vector_init(inneis + i, 0)); + IGRAPH_CHECK(igraph_vector_init(outneis + i, 0)); + } + + IGRAPH_CHECK(igraph_vector_long_init(&visited, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &visited); + IGRAPH_DQUEUE_INIT_FINALLY(&neiq, 10); + + RNG_BEGIN(); + +#define ADD_EDGE_TO(nei) \ + if (VECTOR(visited)[(nei)] != actnode+1) { \ + VECTOR(visited)[(nei)] = actnode+1; \ + IGRAPH_CHECK(igraph_dqueue_push(&neiq, nei)); \ + IGRAPH_CHECK(igraph_vector_push_back(&edges, actnode)); \ + IGRAPH_CHECK(igraph_vector_push_back(&edges, nei)); \ + IGRAPH_CHECK(igraph_vector_push_back(outneis+actnode, nei)); \ + IGRAPH_CHECK(igraph_vector_push_back(inneis+nei, actnode)); \ + } + + IGRAPH_PROGRESS("Forest fire: ", 0.0, NULL); + + for (actnode = 1; actnode < no_of_nodes; actnode++) { + + IGRAPH_PROGRESS("Forest fire: ", 100.0 * actnode / no_of_nodes, NULL); + + IGRAPH_ALLOW_INTERRUPTION(); + + /* We don't want to visit the current vertex */ + VECTOR(visited)[actnode] = actnode + 1; + + /* Choose ambassador(s) */ + for (i = 0; i < ambs; i++) { + long int a = RNG_INTEGER(0, actnode - 1); + ADD_EDGE_TO(a); + } + + while (!igraph_dqueue_empty(&neiq)) { + long int actamb = (long int) igraph_dqueue_pop(&neiq); + igraph_vector_t *outv = outneis + actamb; + igraph_vector_t *inv = inneis + actamb; + long int no_in = igraph_vector_size(inv); + long int no_out = igraph_vector_size(outv); + long int neis_out = (long int) RNG_GEOM(param_geom_out); + long int neis_in = (long int) RNG_GEOM(param_geom_in); + /* outgoing neighbors */ + if (neis_out >= no_out) { + for (i = 0; i < no_out; i++) { + long int nei = (long int) VECTOR(*outv)[i]; + ADD_EDGE_TO(nei); + } + } else { + long int oleft = no_out; + for (i = 0; i < neis_out && oleft > 0; ) { + long int which = RNG_INTEGER(0, oleft - 1); + long int nei = (long int) VECTOR(*outv)[which]; + VECTOR(*outv)[which] = VECTOR(*outv)[oleft - 1]; + VECTOR(*outv)[oleft - 1] = nei; + if (VECTOR(visited)[nei] != actnode + 1) { + ADD_EDGE_TO(nei); + i++; + } + oleft--; + } + } + /* incoming neighbors */ + if (neis_in >= no_in) { + for (i = 0; i < no_in; i++) { + long int nei = (long int) VECTOR(*inv)[i]; + ADD_EDGE_TO(nei); + } + } else { + long int ileft = no_in; + for (i = 0; i < neis_in && ileft > 0; ) { + long int which = RNG_INTEGER(0, ileft - 1); + long int nei = (long int) VECTOR(*inv)[which]; + VECTOR(*inv)[which] = VECTOR(*inv)[ileft - 1]; + VECTOR(*inv)[ileft - 1] = nei; + if (VECTOR(visited)[nei] != actnode + 1) { + ADD_EDGE_TO(nei); + i++; + } + ileft--; + } + } + + } /* while neiq not empty */ + + } /* actnode < no_of_nodes */ + +#undef ADD_EDGE_TO + + RNG_END(); + + IGRAPH_PROGRESS("Forest fire: ", 100.0, NULL); + + igraph_dqueue_destroy(&neiq); + igraph_vector_long_destroy(&visited); + igraph_i_forest_fire_free(&data); + igraph_free(outneis); + igraph_free(inneis); + IGRAPH_FINALLY_CLEAN(5); + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/grg.c b/src/rigraph/core/games/grg.c new file mode 100644 index 0000000..2f0173b --- /dev/null +++ b/src/rigraph/core/games/grg.c @@ -0,0 +1,166 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_random.h" + +#include "../core/interruption.h" + +/** + * \function igraph_grg_game + * \brief Generates a geometric random graph. + * + * A geometric random graph is created by dropping points (i.e. vertices) + * randomly on the unit square and then connecting all those pairs + * which are less than \c radius apart in Euclidean distance. + * + * + * Original code contributed by Keith Briggs, thanks Keith. + * + * \param graph Pointer to an uninitialized graph object. + * \param nodes The number of vertices in the graph. + * \param radius The radius within which the vertices will be connected. + * \param torus Logical constant. If true, periodic boundary conditions + * will be used, i.e. the vertices are assumed to be on a torus + * instead of a square. + * \param x An initialized vector or \c NULL. If not \c NULL, the points' + * x coordinates will be returned here. + * \param y An initialized vector or \c NULL. If not \c NULL, the points' + * y coordinates will be returned here. + * \return Error code. + * + * Time complexity: TODO, less than O(|V|^2+|E|). + * + * \example examples/simple/igraph_grg_game.c + */ +int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, + igraph_real_t radius, igraph_bool_t torus, + igraph_vector_t *x, igraph_vector_t *y) { + + long int i; + igraph_vector_t myx, myy, *xx = &myx, *yy = &myy, edges; + igraph_real_t r2 = radius * radius; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes)); + + if (x) { + xx = x; + IGRAPH_CHECK(igraph_vector_resize(xx, nodes)); + } else { + IGRAPH_VECTOR_INIT_FINALLY(xx, nodes); + } + if (y) { + yy = y; + IGRAPH_CHECK(igraph_vector_resize(yy, nodes)); + } else { + IGRAPH_VECTOR_INIT_FINALLY(yy, nodes); + } + + RNG_BEGIN(); + + for (i = 0; i < nodes; i++) { + VECTOR(*xx)[i] = RNG_UNIF01(); + VECTOR(*yy)[i] = RNG_UNIF01(); + } + + RNG_END(); + + igraph_vector_sort(xx); + + if (!torus) { + for (i = 0; i < nodes; i++) { + igraph_real_t xx1 = VECTOR(*xx)[i]; + igraph_real_t yy1 = VECTOR(*yy)[i]; + long int j = i + 1; + igraph_real_t dx, dy; + + IGRAPH_ALLOW_INTERRUPTION(); + + while ( j < nodes && (dx = VECTOR(*xx)[j] - xx1) < radius) { + dy = VECTOR(*yy)[j] - yy1; + if (dx * dx + dy * dy < r2) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); + } + j++; + } + } + } else { + for (i = 0; i < nodes; i++) { + igraph_real_t xx1 = VECTOR(*xx)[i]; + igraph_real_t yy1 = VECTOR(*yy)[i]; + long int j = i + 1; + igraph_real_t dx, dy; + + IGRAPH_ALLOW_INTERRUPTION(); + + while ( j < nodes && (dx = VECTOR(*xx)[j] - xx1) < radius) { + dy = fabs(VECTOR(*yy)[j] - yy1); + if (dx > 0.5) { + dx = 1 - dx; + } + if (dy > 0.5) { + dy = 1 - dy; + } + if (dx * dx + dy * dy < r2) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); + } + j++; + } + if (j == nodes) { + j = 0; + while (j < i && (dx = 1 - xx1 + VECTOR(*xx)[j]) < radius && + xx1 - VECTOR(*xx)[j] >= radius) { + dy = fabs(VECTOR(*yy)[j] - yy1); + if (dy > 0.5) { + dy = 1 - dy; + } + if (dx * dx + dy * dy < r2) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); + } + j++; + } + } + } + } + + if (!y) { + igraph_vector_destroy(yy); + IGRAPH_FINALLY_CLEAN(1); + } + if (!x) { + igraph_vector_destroy(xx); + IGRAPH_FINALLY_CLEAN(1); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, IGRAPH_UNDIRECTED)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/growing_random.c b/src/rigraph/core/games/growing_random.c new file mode 100644 index 0000000..68fdefc --- /dev/null +++ b/src/rigraph/core/games/growing_random.c @@ -0,0 +1,103 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_random.h" + +/** + * \ingroup generators + * \function igraph_growing_random_game + * \brief Generates a growing random graph. + * + * + * This function simulates a growing random graph. We start out with + * one vertex. In each step a new vertex is added and a number of new + * edges are also added. These graphs are known to be different + * from standard (not growing) random graphs. + * \param graph Uninitialized graph object. + * \param n The number of vertices in the graph. + * \param m The number of edges to add in a time step (i.e. after + * adding a vertex). + * \param directed Boolean, whether to generate a directed graph. + * \param citation Boolean, if \c TRUE, the edges always + * originate from the most recently added vertex and are + * connected to a previous vertex. + * \return Error code: + * \c IGRAPH_EINVAL: invalid + * \p n or \p m + * parameter. + * + * Time complexity: O(|V|+|E|), the + * number of vertices plus the number of edges. + */ +int igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, + igraph_integer_t m, igraph_bool_t directed, + igraph_bool_t citation) { + + long int no_of_nodes = n; + long int no_of_neighbors = m; + long int no_of_edges; + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + + long int resp = 0; + + long int i, j; + + if (n < 0) { + IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); + } + if (m < 0) { + IGRAPH_ERROR("Invalid number of edges per step (m)", IGRAPH_EINVAL); + } + + no_of_edges = no_of_nodes > 0 ? (no_of_nodes - 1) * no_of_neighbors : 0; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + + RNG_BEGIN(); + + for (i = 1; i < no_of_nodes; i++) { + for (j = 0; j < no_of_neighbors; j++) { + if (citation) { + long int to = RNG_INTEGER(0, i - 1); + VECTOR(edges)[resp++] = i; + VECTOR(edges)[resp++] = to; + } else { + long int from = RNG_INTEGER(0, i); + long int to = RNG_INTEGER(1, i); + VECTOR(edges)[resp++] = from; + VECTOR(edges)[resp++] = to; + } + } + } + + RNG_END(); + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} diff --git a/src/rigraph/core/games/islands.c b/src/rigraph/core/games/islands.c new file mode 100644 index 0000000..7bb0004 --- /dev/null +++ b/src/rigraph/core/games/islands.c @@ -0,0 +1,158 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_random.h" + +/** + * \ingroup generators + * \function igraph_simple_interconnected_islands_game + * \brief Generates a random graph made of several interconnected islands, each island being a random graph. + * + * \param graph Pointer to an uninitialized graph object. + * \param islands_n The number of islands in the graph. + * \param islands_size The size of islands in the graph. + * \param islands_pin The probability to create each possible edge into each island. + * \param n_inter The number of edges to create between two islands. + * + * \return Error code: + * \c IGRAPH_EINVAL: invalid parameter + * \c IGRAPH_ENOMEM: there is not enough + * memory for the operation. + * + * Time complexity: O(|V|+|E|), the + * number of vertices plus the number of edges in the graph. + * + */ +int igraph_simple_interconnected_islands_game( + igraph_t *graph, + igraph_integer_t islands_n, + igraph_integer_t islands_size, + igraph_real_t islands_pin, + igraph_integer_t n_inter) { + + + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + igraph_vector_t s = IGRAPH_VECTOR_NULL; + int nbNodes; + double maxpossibleedgesPerIsland; + double maxedgesPerIsland; + int nbEdgesInterIslands; + double maxedges; + int startIsland = 0; + int endIsland = 0; + int i, j, is; + double myrand, last; + long int vsize; + + if (islands_n < 0) { + IGRAPH_ERRORF("Number of islands cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, islands_n); + } + if (islands_size < 0) { + IGRAPH_ERRORF("Size of islands cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, islands_size); + } + if (islands_pin < 0 || islands_pin > 1) { + IGRAPH_ERRORF("Edge probability within islands should be between 0 and 1, got %g.", IGRAPH_EINVAL, islands_pin); + } + if (n_inter < 0) { + IGRAPH_ERRORF("Number of inter-island links cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, n_inter); + } + + /* how much memory ? */ + nbNodes = islands_n * islands_size; + maxpossibleedgesPerIsland = ((double)islands_size * ((double)islands_size - (double)1)) / (double)2; + maxedgesPerIsland = islands_pin * maxpossibleedgesPerIsland; + nbEdgesInterIslands = n_inter * (islands_n * (islands_n - 1)) / 2; + maxedges = maxedgesPerIsland * islands_n + nbEdgesInterIslands; + + /* reserve enough space for all the edges */ + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, (long int) maxedges)); + + RNG_BEGIN(); + + /* first create all the islands */ + for (is = 0; is < islands_n; is++) { /* for each island */ + + /* index for start and end of nodes in this island */ + startIsland = islands_size * is; + endIsland = startIsland + islands_size - 1; + + /* create the random numbers to be used (into s) */ + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) maxedgesPerIsland)); + + last = RNG_GEOM(islands_pin); + while (last < maxpossibleedgesPerIsland) { /* maxedgesPerIsland */ + IGRAPH_CHECK(igraph_vector_push_back(&s, last)); + myrand = RNG_GEOM(islands_pin); + last += myrand; /* RNG_GEOM(islands_pin); */ + last += 1; + } + + + + /* change this to edges ! */ + vsize = igraph_vector_size(&s); + for (i = 0; i < vsize; i++) { + long int to = (long int) floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2); + long int from = (long int) (VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2); + to += startIsland; + from += startIsland; + + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } + + /* clear the memory used for random number for this island */ + igraph_vector_destroy(&s); + IGRAPH_FINALLY_CLEAN(1); + + + /* create the links with other islands */ + for (i = is + 1; i < islands_n; i++) { /* for each other island (not the previous ones) */ + + for (j = 0; j < n_inter; j++) { /* for each link between islands */ + long int from = RNG_INTEGER(startIsland, endIsland); + long int to = RNG_INTEGER(i * islands_size, (i + 1) * islands_size - 1); + + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } + + } + } + + RNG_END(); + + /* actually fill the graph object */ + IGRAPH_CHECK(igraph_create(graph, &edges, nbNodes, 0)); + + /* clean remaining things */ + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/k_regular.c b/src/rigraph/core/games/k_regular.c new file mode 100644 index 0000000..ff989ec --- /dev/null +++ b/src/rigraph/core/games/k_regular.c @@ -0,0 +1,85 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_interface.h" + +/** + * \ingroup generators + * \function igraph_k_regular_game + * \brief Generates a random graph where each vertex has the same degree. + * + * This game generates a directed or undirected random graph where the + * degrees of vertices are equal to a predefined constant k. For undirected + * graphs, at least one of k and the number of vertices must be even. + * + * + * Currently, this game simply uses \ref igraph_degree_sequence_game with + * the \c SIMPLE_NO_MULTIPLE method and appropriately constructed degree sequences. + * Thefore, it does not sample uniformly: while it can generate all k-regular graphs + * with the given number of vertices, it does not generate each one with the same + * probability. + * + * \param graph Pointer to an uninitialized graph object. + * \param no_of_nodes The number of nodes in the generated graph. + * \param k The degree of each vertex in an undirected graph, or + * the out-degree and in-degree of each vertex in a + * directed graph. + * \param directed Whether the generated graph will be directed. + * \param multiple Whether to allow multiple edges in the generated graph. + * + * \return Error code: + * \c IGRAPH_EINVAL: invalid parameter; e.g., negative number of nodes, + * or odd number of nodes and odd k for undirected + * graphs. + * \c IGRAPH_ENOMEM: there is not enough memory for the operation. + * + * Time complexity: O(|V|+|E|) if \c multiple is true, otherwise not known. + */ +int igraph_k_regular_game(igraph_t *graph, + igraph_integer_t no_of_nodes, igraph_integer_t k, + igraph_bool_t directed, igraph_bool_t multiple) { + igraph_vector_t degseq; + igraph_degseq_t mode = multiple ? IGRAPH_DEGSEQ_SIMPLE : IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE; + + /* Note to self: we are not using IGRAPH_DEGSEQ_VL when multiple = false + * because the VL method is not really good at generating k-regular graphs. + * Actually, that's why we have added SIMPLE_NO_MULTIPLE. */ + + if (no_of_nodes < 0) { + IGRAPH_ERROR("number of nodes must be non-negative", IGRAPH_EINVAL); + } + if (k < 0) { + IGRAPH_ERROR("degree must be non-negative", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(°seq, no_of_nodes); + igraph_vector_fill(°seq, k); + IGRAPH_CHECK(igraph_degree_sequence_game(graph, °seq, directed ? °seq : 0, mode)); + + igraph_vector_destroy(°seq); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/preference.c b/src/rigraph/core/games/preference.c new file mode 100644 index 0000000..554f7a6 --- /dev/null +++ b/src/rigraph/core/games/preference.c @@ -0,0 +1,631 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_memory.h" +#include "igraph_random.h" + +#include "../core/interruption.h" + +static void igraph_i_preference_game_free_vids_by_type(igraph_vector_ptr_t *vecs) { + int i = 0, n; + igraph_vector_t *v; + + n = (int) igraph_vector_ptr_size(vecs); + for (i = 0; i < n; i++) { + v = (igraph_vector_t*)VECTOR(*vecs)[i]; + if (v) { + igraph_vector_destroy(v); + } + } + igraph_vector_ptr_destroy_all(vecs); +} + +/** + * \function igraph_preference_game + * \brief Generates a graph with vertex types and connection preferences. + * + * + * This is practically the nongrowing variant of + * \ref igraph_establishment_game(). A given number of vertices are + * generated. Every vertex is assigned to a vertex type according to + * the given type probabilities. Finally, every + * vertex pair is evaluated and an edge is created between them with a + * probability depending on the types of the vertices involved. + * + * + * In other words, this function generates a graph according to a + * block-model. Vertices are divided into groups (or blocks), and + * the probability the two vertices are connected depends on their + * groups only. + * + * \param graph Pointer to an uninitialized graph. + * \param nodes The number of vertices in the graph. + * \param types The number of vertex types. + * \param type_dist Vector giving the distribution of vertex types. If + * \c NULL, all vertex types will have equal probability. See also the + * \p fixed_sizes argument. + * \param fixed_sizes Boolean. If true, then the number of vertices with a + * given vertex type is fixed and the \p type_dist argument gives these + * numbers for each vertex type. If true, and \p type_dist is \c NULL, + * then the function tries to make vertex groups of the same size. If this + * is not possible, then some groups will have an extra vertex. + * \param pref_matrix Matrix giving the connection probabilities for + * different vertex types. This should be symmetric if the requested + * graph is undirected. + * \param node_type_vec A vector where the individual generated vertex types + * will be stored. If \c NULL, the vertex types won't be saved. + * \param directed Logical, whether to generate a directed graph. If undirected + * graphs are requested, only the lower left triangle of the preference + * matrix is considered. + * \param loops Logical, whether loop edges are allowed. + * \return Error code. + * + * Added in version 0.3. + * + * Time complexity: O(|V|+|E|), the + * number of vertices plus the number of edges in the graph. + * + * \sa \ref igraph_asymmetric_preference_game(), + * \ref igraph_establishment_game(), \ref igraph_callaway_traits_game() + */ + +int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, + igraph_integer_t types, + const igraph_vector_t *type_dist, + igraph_bool_t fixed_sizes, + const igraph_matrix_t *pref_matrix, + igraph_vector_t *node_type_vec, + igraph_bool_t directed, + igraph_bool_t loops) { + + long int i, j; + igraph_vector_t edges, s; + igraph_vector_t* nodetypes; + igraph_vector_ptr_t vids_by_type; + igraph_real_t maxcum, maxedges; + + if(nodes < 0){ + IGRAPH_ERROR("The number of vertices must be non-negative.", IGRAPH_EINVAL); + } + + if (types < 1) { + IGRAPH_ERROR("The number of vertex types must be at least 1.", IGRAPH_EINVAL); + } + + if (type_dist) { + igraph_real_t lo; + + if (igraph_vector_size(type_dist) != types) { + IGRAPH_ERROR("The vertex type distribution vector must agree in length with the number of types.", + IGRAPH_EINVAL); + } + + lo = igraph_vector_min(type_dist); + if (lo < 0) { + IGRAPH_ERROR("The vertex type distribution vector must not contain negative values.", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo)) { + IGRAPH_ERROR("The vertex type distribution vector must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (igraph_matrix_nrow(pref_matrix) != types || igraph_matrix_ncol(pref_matrix) != types) { + IGRAPH_ERROR("The preference matrix must be square and agree in dimensions with the number of types.", IGRAPH_EINVAL); + } + + { + igraph_real_t lo, hi; + igraph_matrix_minmax(pref_matrix, &lo, &hi); + + if (lo < 0 || hi > 1) { + IGRAPH_ERROR("The preference matrix must contain probabilities in [0, 1].", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo) || igraph_is_nan(hi)) { + IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (! directed && ! igraph_matrix_is_symmetric(pref_matrix)) { + IGRAPH_ERROR("The preference matrix must be symmetric when generating undirected graphs.", IGRAPH_EINVAL); + } + + if (fixed_sizes && type_dist) { + if (igraph_vector_sum(type_dist) != nodes) { + IGRAPH_ERROR("Invalid group sizes, their sum must match the number of vertices.", IGRAPH_EINVAL); + } + } + + if (node_type_vec) { + IGRAPH_CHECK(igraph_vector_resize(node_type_vec, nodes)); + nodetypes = node_type_vec; + } else { + nodetypes = IGRAPH_CALLOC(1, igraph_vector_t); + if (nodetypes == 0) { + IGRAPH_ERROR("Insufficient memory for preference_game.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, nodetypes); + IGRAPH_VECTOR_INIT_FINALLY(nodetypes, nodes); + } + + IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_type, types)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_type); + for (i = 0; i < types; i++) { + VECTOR(vids_by_type)[i] = IGRAPH_CALLOC(1, igraph_vector_t); + if (VECTOR(vids_by_type)[i] == 0) { + IGRAPH_ERROR("Insufficient memory for preference_game.", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(VECTOR(vids_by_type)[i], 0)); + } + IGRAPH_FINALLY_CLEAN(1); /* removing igraph_vector_ptr_destroy_all */ + IGRAPH_FINALLY(igraph_i_preference_game_free_vids_by_type, &vids_by_type); + + RNG_BEGIN(); + + if (!fixed_sizes) { + + igraph_vector_t cumdist; + IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types + 1); + + VECTOR(cumdist)[0] = 0; + if (type_dist) { + for (i = 0; i < types; i++) { + VECTOR(cumdist)[i + 1] = VECTOR(cumdist)[i] + VECTOR(*type_dist)[i]; + } + } else { + for (i = 0; i < types; i++) { + VECTOR(cumdist)[i + 1] = i + 1; + } + } + maxcum = igraph_vector_tail(&cumdist); + + for (i = 0; i < nodes; i++) { + long int type1; + igraph_real_t uni1 = RNG_UNIF(0, maxcum); + igraph_vector_binsearch(&cumdist, uni1, &type1); + VECTOR(*nodetypes)[i] = type1 - 1; + IGRAPH_CHECK(igraph_vector_push_back( + (igraph_vector_t*)VECTOR(vids_by_type)[type1 - 1], i)); + } + + igraph_vector_destroy(&cumdist); + IGRAPH_FINALLY_CLEAN(1); + + } else { + long int an = 0; + if (type_dist) { + for (i = 0; i < types; i++) { + long int no = (long int) VECTOR(*type_dist)[i]; + igraph_vector_t *v = VECTOR(vids_by_type)[i]; + for (j = 0; j < no && an < nodes; j++) { + VECTOR(*nodetypes)[an] = i; + IGRAPH_CHECK(igraph_vector_push_back(v, an)); + an++; + } + } + } else { + long int fixno = (long int) ceil( (double)nodes / types); + for (i = 0; i < types; i++) { + igraph_vector_t *v = VECTOR(vids_by_type)[i]; + for (j = 0; j < fixno && an < nodes; j++) { + VECTOR(*nodetypes)[an++] = i; + IGRAPH_CHECK(igraph_vector_push_back(v, an)); + an++; + } + } + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + + for (i = 0; i < types; i++) { + for (j = 0; j < types; j++) { + /* Generating the random subgraph between vertices of type i and j */ + long int k, l; + igraph_real_t p, last; + igraph_vector_t *v1, *v2; + long int v1_size, v2_size; + + IGRAPH_ALLOW_INTERRUPTION(); + + v1 = (igraph_vector_t*)VECTOR(vids_by_type)[i]; + v2 = (igraph_vector_t*)VECTOR(vids_by_type)[j]; + v1_size = igraph_vector_size(v1); + v2_size = igraph_vector_size(v2); + + p = MATRIX(*pref_matrix, i, j); + igraph_vector_clear(&s); + if (i != j) { + /* The two vertex sets are disjoint, this is the easier case */ + if (i > j && !directed) { + continue; + } + maxedges = v1_size * v2_size; + } else { + if (directed && loops) { + maxedges = v1_size * v1_size; + } else if (directed && !loops) { + maxedges = v1_size * (v1_size - 1); + } else if (!directed && loops) { + maxedges = v1_size * (v1_size + 1) / 2; + } else { + maxedges = v1_size * (v1_size - 1) / 2; + } + } + + IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) (maxedges * p * 1.1))); + + last = RNG_GEOM(p); + while (last < maxedges) { + IGRAPH_CHECK(igraph_vector_push_back(&s, last)); + last += RNG_GEOM(p); + last += 1; + } + l = igraph_vector_size(&s); + + IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&edges) + l * 2)); + + if (i != j) { + /* Generating the subgraph between vertices of type i and j */ + for (k = 0; k < l; k++) { + long int to = (long int) floor(VECTOR(s)[k] / v1_size); + long int from = (long int) (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size); + igraph_vector_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_push_back(&edges, VECTOR(*v2)[to]); + } + } else { + /* Generating the subgraph among vertices of type i */ + if (directed && loops) { + for (k = 0; k < l; k++) { + long int to = (long int) floor(VECTOR(s)[k] / v1_size); + long int from = (long int) (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size); + igraph_vector_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_push_back(&edges, VECTOR(*v1)[to]); + } + } else if (directed && !loops) { + for (k = 0; k < l; k++) { + long int to = (long int) floor(VECTOR(s)[k] / v1_size); + long int from = (long int) (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size); + if (from == to) { + to = v1_size - 1; + } + igraph_vector_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_push_back(&edges, VECTOR(*v1)[to]); + } + } else if (!directed && loops) { + for (k = 0; k < l; k++) { + long int to = (long int) floor((sqrt(8 * VECTOR(s)[k] + 1) - 1) / 2); + long int from = (long int) (VECTOR(s)[k] - (((igraph_real_t)to) * (to + 1)) / 2); + igraph_vector_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_push_back(&edges, VECTOR(*v1)[to]); + } + } else { + for (k = 0; k < l; k++) { + long int to = (long int) floor((sqrt(8 * VECTOR(s)[k] + 1) + 1) / 2); + long int from = (long int) (VECTOR(s)[k] - (((igraph_real_t)to) * (to - 1)) / 2); + igraph_vector_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_push_back(&edges, VECTOR(*v1)[to]); + } + } + } + } + } + + RNG_END(); + + igraph_vector_destroy(&s); + igraph_i_preference_game_free_vids_by_type(&vids_by_type); + IGRAPH_FINALLY_CLEAN(2); + + if (node_type_vec == 0) { + igraph_vector_destroy(nodetypes); + IGRAPH_FREE(nodetypes); + IGRAPH_FINALLY_CLEAN(2); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_asymmetric_preference_game + * \brief Generates a graph with asymmetric vertex types and connection preferences. + * + * + * This is the asymmetric variant of \ref igraph_preference_game(). + * A given number of vertices are generated. Every vertex is assigned to an + * "outgoing" and an "incoming " vertex type according to the given joint + * type probabilities. Finally, every vertex pair is evaluated and a + * directed edge is created between them with a probability depending on the + * "outgoing" type of the source vertex and the "incoming" type of the target + * vertex. + * + * \param graph Pointer to an uninitialized graph. + * \param nodes The number of vertices in the graph. + * \param out_types The number of vertex out-types. + * \param in_types The number of vertex in-types. + * \param type_dist_matrix Matrix of size out_types * in_types, + * giving the joint distribution of vertex types. + * If \c NULL, incoming and outgoing vertex types are independent and uniformly + * distributed. + * \param pref_matrix Matrix of size out_types * in_types, + * giving the connection probabilities for different vertex types. + * \param node_type_out_vec A vector where the individual generated "outgoing" + * vertex types will be stored. If \c NULL, the vertex types won't be saved. + * \param node_type_in_vec A vector where the individual generated "incoming" + * vertex types will be stored. If \c NULL, the vertex types won't be saved. + * \param loops Logical, whether loop edges are allowed. + * \return Error code. + * + * Added in version 0.3. + * + * Time complexity: O(|V|+|E|), the + * number of vertices plus the number of edges in the graph. + * + * \sa \ref igraph_preference_game() + */ + +int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, + igraph_integer_t out_types, + igraph_integer_t in_types, + const igraph_matrix_t *type_dist_matrix, + const igraph_matrix_t *pref_matrix, + igraph_vector_t *node_type_out_vec, + igraph_vector_t *node_type_in_vec, + igraph_bool_t loops) { + + long int i, j, k; + igraph_vector_t edges, cumdist, s, intersect; + igraph_vector_t *nodetypes_in; + igraph_vector_t *nodetypes_out; + igraph_vector_ptr_t vids_by_intype, vids_by_outtype; + igraph_real_t maxcum, maxedges; + + if(nodes < 0){ + IGRAPH_ERROR("The number of vertices must not be negative.", IGRAPH_EINVAL); + } + + if (in_types < 1) { + IGRAPH_ERROR("The number of vertex in-types must be at least 1.", IGRAPH_EINVAL); + } + + if (out_types < 1) { + IGRAPH_ERROR("The number of vertex out-types must be at least 1.", IGRAPH_EINVAL); + } + + if (type_dist_matrix) { + igraph_real_t lo; + + if (igraph_matrix_nrow(type_dist_matrix) != out_types || + igraph_matrix_ncol(type_dist_matrix) != in_types) { + IGRAPH_ERROR("The type distribution matrix must have dimensions out_types * in_types.", IGRAPH_EINVAL); + } + + lo = igraph_matrix_min(type_dist_matrix); + if (lo < 0) { + IGRAPH_ERROR("The type distribution matrix must not contain negative values.", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo)) { + IGRAPH_ERROR("The type distribution matrix must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (igraph_matrix_nrow(pref_matrix) != out_types || + igraph_matrix_ncol(pref_matrix) != in_types) { + IGRAPH_ERROR("The preference matrix must have dimensions out_types * in_types.", IGRAPH_EINVAL); + } + + { + igraph_real_t lo, hi; + igraph_matrix_minmax(pref_matrix, &lo, &hi); + + if (lo < 0 || hi > 1) { + IGRAPH_ERROR("The preference matrix must contain probabilities in [0, 1].", IGRAPH_EINVAL); + } + if (igraph_is_nan(lo) || igraph_is_nan(hi)) { + IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&cumdist, in_types * out_types + 1); + + if (node_type_in_vec) { + nodetypes_in = node_type_in_vec; + IGRAPH_CHECK(igraph_vector_resize(nodetypes_in, nodes)); + } else { + nodetypes_in = IGRAPH_CALLOC(1, igraph_vector_t); + if (nodetypes_in == 0) { + IGRAPH_ERROR("Insufficient memory for asymmetric_preference_game.", IGRAPH_ENOMEM); + } + IGRAPH_VECTOR_INIT_FINALLY(nodetypes_in, nodes); + } + + if (node_type_out_vec) { + nodetypes_out = node_type_out_vec; + IGRAPH_CHECK(igraph_vector_resize(nodetypes_out, nodes)); + } else { + nodetypes_out = IGRAPH_CALLOC(1, igraph_vector_t); + if (nodetypes_out == 0) { + IGRAPH_ERROR("Insufficient memory for asymmetric_preference_game.", IGRAPH_ENOMEM); + } + IGRAPH_VECTOR_INIT_FINALLY(nodetypes_out, nodes); + } + + IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_intype, in_types)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_intype); + IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_outtype, out_types)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_outtype); + for (i = 0; i < in_types; i++) { + VECTOR(vids_by_intype)[i] = IGRAPH_CALLOC(1, igraph_vector_t); + if (! VECTOR(vids_by_intype)[i]) { + IGRAPH_ERROR("Insufficient memory for asymmetric_preference_game.", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(VECTOR(vids_by_intype)[i], 0)); + } + for (i = 0; i < out_types; i++) { + VECTOR(vids_by_outtype)[i] = IGRAPH_CALLOC(1, igraph_vector_t); + if (! VECTOR(vids_by_outtype)[i]) { + IGRAPH_ERROR("Insufficient memory for asymmetric_preference_game.", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(VECTOR(vids_by_outtype)[i], 0)); + } + IGRAPH_FINALLY_CLEAN(2); /* removing igraph_vector_ptr_destroy_all */ + IGRAPH_FINALLY(igraph_i_preference_game_free_vids_by_type, &vids_by_intype); + IGRAPH_FINALLY(igraph_i_preference_game_free_vids_by_type, &vids_by_outtype); + + VECTOR(cumdist)[0] = 0; + if (type_dist_matrix) { + for (i = 0, k = 0; i < out_types; i++) { + for (j = 0; j < in_types; j++, k++) { + VECTOR(cumdist)[k + 1] = VECTOR(cumdist)[k] + MATRIX(*type_dist_matrix, i, j); + } + } + } else { + for (i = 0; i < out_types * in_types; i++) { + VECTOR(cumdist)[i + 1] = i + 1; + } + } + maxcum = igraph_vector_tail(&cumdist); + + RNG_BEGIN(); + + for (i = 0; i < nodes; i++) { + long int type1, type2; + igraph_real_t uni1 = RNG_UNIF(0, maxcum); + igraph_vector_binsearch(&cumdist, uni1, &type1); + type2 = (type1 - 1) % (long int) out_types; + type1 = (type1 - 1) / (long int) out_types; + VECTOR(*nodetypes_in)[i] = type1; + VECTOR(*nodetypes_out)[i] = type2; + IGRAPH_CHECK(igraph_vector_push_back( + (igraph_vector_t*)VECTOR(vids_by_intype)[type1], i)); + IGRAPH_CHECK(igraph_vector_push_back( + (igraph_vector_t*)VECTOR(vids_by_outtype)[type2], i)); + } + + igraph_vector_destroy(&cumdist); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + IGRAPH_VECTOR_INIT_FINALLY(&intersect, 0); + for (i = 0; i < out_types; i++) { + for (j = 0; j < in_types; j++) { + long int kk, l, c = 0; + igraph_real_t p, last; + igraph_vector_t *v1, *v2; + long int v1_size, v2_size; + + IGRAPH_ALLOW_INTERRUPTION(); + + v1 = (igraph_vector_t*)VECTOR(vids_by_outtype)[i]; + v2 = (igraph_vector_t*)VECTOR(vids_by_intype)[j]; + v1_size = igraph_vector_size(v1); + v2_size = igraph_vector_size(v2); + + maxedges = v1_size * v2_size; + if (!loops) { + IGRAPH_CHECK(igraph_vector_intersect_sorted(v1, v2, &intersect)); + c = igraph_vector_size(&intersect); + maxedges -= c; + } + + p = MATRIX(*pref_matrix, i, j); + igraph_vector_clear(&s); + IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) (maxedges * p * 1.1))); + + last = RNG_GEOM(p); + while (last < maxedges) { + IGRAPH_CHECK(igraph_vector_push_back(&s, last)); + last += RNG_GEOM(p); + last += 1; + } + l = igraph_vector_size(&s); + + IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&edges) + l * 2)); + + if (!loops && c > 0) { + for (kk = 0; kk < l; kk++) { + long int to = (long int) floor(VECTOR(s)[kk] / v1_size); + long int from = (long int) (VECTOR(s)[kk] - ((igraph_real_t)to) * v1_size); + if (VECTOR(*v1)[from] == VECTOR(*v2)[to]) { + /* remap loop edges */ + to = v2_size - 1; + igraph_vector_binsearch(&intersect, VECTOR(*v1)[from], &c); + from = v1_size - 1; + if (VECTOR(*v1)[from] == VECTOR(*v2)[to]) { + from--; + } + while (c > 0) { + c--; from--; + if (VECTOR(*v1)[from] == VECTOR(*v2)[to]) { + from--; + } + } + } + igraph_vector_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_push_back(&edges, VECTOR(*v2)[to]); + } + } else { + for (kk = 0; kk < l; kk++) { + long int to = (long int) floor(VECTOR(s)[kk] / v1_size); + long int from = (long int) (VECTOR(s)[kk] - ((igraph_real_t)to) * v1_size); + igraph_vector_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_push_back(&edges, VECTOR(*v2)[to]); + } + } + } + } + + RNG_END(); + + igraph_vector_destroy(&s); + igraph_vector_destroy(&intersect); + igraph_i_preference_game_free_vids_by_type(&vids_by_intype); + igraph_i_preference_game_free_vids_by_type(&vids_by_outtype); + IGRAPH_FINALLY_CLEAN(4); + + if (node_type_out_vec == 0) { + igraph_vector_destroy(nodetypes_out); + IGRAPH_FREE(nodetypes_out); + IGRAPH_FINALLY_CLEAN(1); + } + + if (node_type_in_vec == 0) { + igraph_vector_destroy(nodetypes_in); + IGRAPH_FREE(nodetypes_in); + IGRAPH_FINALLY_CLEAN(1); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, 1)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/recent_degree.c b/src/rigraph/core/games/recent_degree.c new file mode 100644 index 0000000..188bb3e --- /dev/null +++ b/src/rigraph/core/games/recent_degree.c @@ -0,0 +1,372 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_psumtree.h" +#include "igraph_random.h" +#include "igraph_interface.h" + +/** + * \function igraph_recent_degree_game + * \brief Stochastic graph generator based on the number of incident edges a node has gained recently. + * + * \param graph Pointer to an uninitialized graph object. + * \param nodes The number of vertices in the graph, this is the same as + * the number of time steps. + * \param power The exponent, the probability that a node gains a + * new edge is proportional to the number of edges it has + * gained recently (in the last \p window time steps) to \p + * power. + * \param time_window Integer constant, the size of the time window to use + * to count the number of recent edges. + * \param m Integer constant, the number of edges to add per time + * step if the \p outseq parameter is a null pointer or a + * zero-length vector. + * \param outseq The number of edges to add in each time step. This + * argument is ignored if it is a null pointer or a zero length + * vector. In this case the constant \p m parameter is used. + * \param outpref Logical constant, if true the edges originated by a + * vertex also count as recent incident edges. + * For most applications it is reasonable to set it to false. + * \param zero_appeal Constant giving the attractiveness of the + * vertices which haven't gained any edge recently. + * \param directed Logical constant, whether to generate a directed + * graph. + * \return Error code. + * + * Time complexity: O(|V|*log(|V|)+|E|), |V| is the number of + * vertices, |E| is the number of edges in the graph. + * + */ +int igraph_recent_degree_game(igraph_t *graph, igraph_integer_t nodes, + igraph_real_t power, + igraph_integer_t time_window, + igraph_integer_t m, + const igraph_vector_t *outseq, + igraph_bool_t outpref, + igraph_real_t zero_appeal, + igraph_bool_t directed) { + + long int no_of_nodes = nodes; + long int no_of_neighbors = 0; + long int no_of_edges; + igraph_vector_t edges; + long int i, j; + igraph_psumtree_t sumtree; + long int edgeptr = 0; + igraph_vector_t degree; + igraph_dqueue_t history; + igraph_bool_t have_outseq = outseq && igraph_vector_size(outseq) > 0; + + if (no_of_nodes < 0) { + IGRAPH_ERRORF("Number of vertices cannot be negative, got %ld.", IGRAPH_EINVAL, no_of_nodes); + } + if (have_outseq && igraph_vector_size(outseq) != no_of_nodes) { + IGRAPH_ERRORF("Out-degree sequence is specified, but its length (%ld) does not equal the number of nodes (%ld).", + IGRAPH_EINVAL, (long) igraph_vector_size(outseq), no_of_nodes); + } + if (!have_outseq && m < 0) { + IGRAPH_ERRORF("Numer of edges per step cannot be negative, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, m); + } + if (time_window < 0) { + IGRAPH_ERRORF("Time window cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, time_window); + } + if (zero_appeal < 0) { + IGRAPH_ERRORF("The zero appeal cannot be negative, got %g.", IGRAPH_EINVAL, zero_appeal); + } + + if (nodes == 0) { + igraph_empty(graph, 0, directed); + return IGRAPH_SUCCESS; + } + + if (!have_outseq) { + no_of_neighbors = m; + no_of_edges = (no_of_nodes - 1) * no_of_neighbors; + } else { + long int outseq_len = igraph_vector_size(outseq); + no_of_edges = 0; + for (i = 1; i < outseq_len; i++) { + no_of_edges += VECTOR(*outseq)[i]; + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes)); + IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_dqueue_init(&history, + 1.5 * time_window * no_of_edges / no_of_nodes + 10)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &history); + + RNG_BEGIN(); + + /* first node */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, zero_appeal)); + igraph_dqueue_push(&history, -1); + + /* and the rest */ + for (i = 1; i < no_of_nodes; i++) { + igraph_real_t sum; + long int to; + if (have_outseq) { + no_of_neighbors = (long int) VECTOR(*outseq)[i]; + } + + if (i >= time_window) { + while ((j = (long int) igraph_dqueue_pop(&history)) != -1) { + VECTOR(degree)[j] -= 1; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, j, pow(VECTOR(degree)[j], power) + zero_appeal)); + } + } + + sum = igraph_psumtree_sum(&sumtree); + for (j = 0; j < no_of_neighbors; j++) { + igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum)); + VECTOR(degree)[to]++; + VECTOR(edges)[edgeptr++] = i; + VECTOR(edges)[edgeptr++] = to; + igraph_dqueue_push(&history, to); + } + igraph_dqueue_push(&history, -1); + + /* update probabilities */ + for (j = 0; j < no_of_neighbors; j++) { + long int nn = (long int) VECTOR(edges)[edgeptr - 2 * j - 1]; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, nn, pow(VECTOR(degree)[nn], power) + zero_appeal)); + } + if (outpref) { + VECTOR(degree)[i] += no_of_neighbors; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, pow(VECTOR(degree)[i], power) + zero_appeal)); + } else { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, zero_appeal)); + } + } + + RNG_END(); + + igraph_dqueue_destroy(&history); + igraph_psumtree_destroy(&sumtree); + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(3); + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_recent_degree_aging_game + * \brief Preferential attachment based on the number of edges gained recently, with aging of vertices. + * + * + * This game is very similar to \ref igraph_barabasi_aging_game(), + * except that instead of the total number of incident edges the + * number of edges gained in the last \p time_window time steps are + * counted. + * + * The degree dependent part of the attractiveness is + * given by k to the power of \p pa_exp plus \p zero_appeal; the age + * dependent part is l to the power to \p aging_exp. + * k is the number of edges gained in the last \p time_window time + * steps, l is the age of the vertex. + * \param graph Pointer to an uninitialized graph object. + * \param nodes The number of vertices in the graph. + * \param m The number of edges to add in each time step. If the \p + * outseq argument is not a null vector or a zero-length vector + * then it is ignored. + * \param outseq Vector giving the number of edges to add in each time + * step. If it is a null pointer or a zero-length vector then + * it is ignored and the \p m argument is used. + * \param outpref Logical constant, if true the edges initiated by a + * vertex are also counted. Normally it is false. + * \param pa_exp The exponent for the preferential attachment. + * \param aging_exp The exponent for the aging, normally it is + * negative: old vertices gain edges with less probability. + * \param aging_bins Integer constant, the number of age bins to use. + * \param time_window The time window to use to count the number of + * incident edges for the vertices. + * \param zero_appeal The degree dependent part of the attractiveness + * for zero degree vertices. + * \param directed Logical constant, whether to create a directed + * graph. + * \return Error code. + * + * Time complexity: O((|V|+|V|/aging_bins)*log(|V|)+|E|). |V| is the number + * of vertices, |E| the number of edges. + */ +int igraph_recent_degree_aging_game(igraph_t *graph, + igraph_integer_t nodes, + igraph_integer_t m, + const igraph_vector_t *outseq, + igraph_bool_t outpref, + igraph_real_t pa_exp, + igraph_real_t aging_exp, + igraph_integer_t aging_bins, + igraph_integer_t time_window, + igraph_real_t zero_appeal, + igraph_bool_t directed) { + + long int no_of_nodes = nodes; + long int no_of_neighbors; + long int binwidth; + long int no_of_edges; + igraph_vector_t edges; + long int i, j, k; + igraph_psumtree_t sumtree; + long int edgeptr = 0; + igraph_vector_t degree; + igraph_dqueue_t history; + igraph_bool_t have_outseq = outseq && igraph_vector_size(outseq) > 0; + + if (no_of_nodes == 0) { + igraph_empty(graph, 0, directed); + return IGRAPH_SUCCESS; + } + if (no_of_nodes < 0) { + IGRAPH_ERRORF("Number of nodes should not be negative, got %ld.", IGRAPH_EINVAL, no_of_nodes); + } + if (have_outseq && igraph_vector_size(outseq) != no_of_nodes) { + IGRAPH_ERRORF("Out-degree sequence is specified, but its length (%ld) does not equal the number of nodes (%ld).", + IGRAPH_EINVAL, (long) igraph_vector_size(outseq), no_of_nodes); + } + if (!have_outseq && m < 0) { + IGRAPH_ERRORF("Numer of edges per step cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, m); + } + if (aging_bins <= 0) { + IGRAPH_ERRORF("Aging bins should be positive, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, aging_bins); + } + if (time_window < 0) { + IGRAPH_ERRORF("Time window cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, time_window); + } + if (zero_appeal < 0) { + IGRAPH_ERRORF("The zero appeal cannot be negative, got %g.", IGRAPH_EINVAL, zero_appeal); + } + + if (!have_outseq) { + no_of_neighbors = m; + no_of_edges = (no_of_nodes - 1) * no_of_neighbors; + } else { + long int outseq_len = igraph_vector_size(outseq); + no_of_edges = 0; + for (i = 1; i < outseq_len; i++) { + no_of_edges += VECTOR(*outseq)[i]; + } + } + + binwidth = nodes / aging_bins + 1; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes)); + IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_dqueue_init(&history, + 1.5 * time_window * no_of_edges / no_of_nodes + 10)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &history); + + RNG_BEGIN(); + + /* first node */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, zero_appeal)); + igraph_dqueue_push(&history, -1); + + /* and the rest */ + for (i = 1; i < no_of_nodes; i++) { + igraph_real_t sum; + long int to; + + if (have_outseq) { + no_of_neighbors = (long int) VECTOR(*outseq)[i]; + } + + if (i >= time_window) { + while ((j = (long int) igraph_dqueue_pop(&history)) != -1) { + long int age = (i - j) / binwidth; + VECTOR(degree)[j] -= 1; + IGRAPH_CHECK(igraph_psumtree_update( + &sumtree, j, + (pow(VECTOR(degree)[j], pa_exp) + zero_appeal) * pow(age + 1, aging_exp) + )); + } + } + + sum = igraph_psumtree_sum(&sumtree); + for (j = 0; j < no_of_neighbors; j++) { + igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum)); + VECTOR(degree)[to]++; + VECTOR(edges)[edgeptr++] = i; + VECTOR(edges)[edgeptr++] = to; + igraph_dqueue_push(&history, to); + } + igraph_dqueue_push(&history, -1); + + /* update probabilities */ + for (j = 0; j < no_of_neighbors; j++) { + long int n = (long int) VECTOR(edges)[edgeptr - 2 * j - 1]; + long int age = (i - n) / binwidth; + IGRAPH_CHECK(igraph_psumtree_update( + &sumtree, n, + (pow(VECTOR(degree)[n], pa_exp) + zero_appeal) * pow(age + 1, aging_exp) + )); + } + if (outpref) { + VECTOR(degree)[i] += no_of_neighbors; + IGRAPH_CHECK(igraph_psumtree_update( + &sumtree, i, + pow(VECTOR(degree)[i], pa_exp) + zero_appeal + )); + } else { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, zero_appeal)); + } + + /* aging */ + for (k = 1; binwidth * k <= i; k++) { + long int shnode = i - binwidth * k; + long int deg = (long int) VECTOR(degree)[shnode]; + long int age = (i - shnode) / binwidth; + IGRAPH_CHECK(igraph_psumtree_update( + &sumtree, shnode, + (pow(deg, pa_exp) + zero_appeal) * pow(age + 2, aging_exp) + )); + } + } + + RNG_END(); + + igraph_dqueue_destroy(&history); + igraph_vector_destroy(°ree); + igraph_psumtree_destroy(&sumtree); + IGRAPH_FINALLY_CLEAN(3); + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} diff --git a/src/rigraph/core/games/sbm.c b/src/rigraph/core/games/sbm.c new file mode 100644 index 0000000..cc95868 --- /dev/null +++ b/src/rigraph/core/games/sbm.c @@ -0,0 +1,621 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph R library. + Copyright (C) 2003-2013 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_interface.h" +#include "igraph_vector.h" +#include "igraph_matrix.h" +#include "igraph_random.h" +#include "igraph_constructors.h" +#include "igraph_games.h" + +#include "../core/interruption.h" + +#include /* for DBL_EPSILON */ +#include /* for sqrt */ + +/** + * \function igraph_sbm_game + * \brief Sample from a stochastic block model. + * + * This function samples graphs from a stochastic block + * model by (doing the equivalent of) Bernoulli + * trials for each potential edge with the probabilities + * given by the Bernoulli rate matrix, \p pref_matrix. + * See Faust, K., & Wasserman, S. (1992a). Blockmodels: + * Interpretation and evaluation. Social Networks, 14, 5-–61. + * + * + * The order of the vertex ids in the generated graph corresponds to + * the \p block_sizes argument. + * + * \param graph The output graph. This should be a pointer to an + * uninitialized graph. + * \param n Number of vertices. + * \param pref_matrix The matrix giving the Bernoulli rates. + * This is a KxK matrix, where K is the number of groups. + * The probability of creating an edge between vertices from + * groups i and j is given by element (i,j). + * \param block_sizes An integer vector giving the number of + * vertices in each group. + * \param directed Boolean, whether to create a directed graph. If + * this argument is false, then \p pref_matrix must be symmetric. + * \param loops Boolean, whether to create self-loops. + * \return Error code. + * + * Time complexity: O(|V|+|E|+K^2), where |V| is the number of + * vertices, |E| is the number of edges, and K is the number of + * groups. + * + * \sa \ref igraph_erdos_renyi_game() for a simple Bernoulli graph. + * + */ + +int igraph_sbm_game(igraph_t *graph, igraph_integer_t n, + const igraph_matrix_t *pref_matrix, + const igraph_vector_int_t *block_sizes, + igraph_bool_t directed, igraph_bool_t loops) { + + long int no_blocks = igraph_matrix_nrow(pref_matrix); + long int from, to, fromoff = 0; + igraph_real_t minp, maxp; + igraph_vector_t edges; + + /* ------------------------------------------------------------ */ + /* Check arguments */ + /* ------------------------------------------------------------ */ + + if (igraph_matrix_ncol(pref_matrix) != no_blocks) { + IGRAPH_ERROR("Preference matrix is not square.", + IGRAPH_NONSQUARE); + } + + if (no_blocks > 0) { + igraph_matrix_minmax(pref_matrix, &minp, &maxp); + if (minp < 0 || maxp > 1) { + IGRAPH_ERROR("Connection probabilities must be in [0,1].", IGRAPH_EINVAL); + } + } + + if (!directed && !igraph_matrix_is_symmetric(pref_matrix)) { + IGRAPH_ERROR("Preference matrix must be symmetric for undirected graphs.", + IGRAPH_EINVAL); + } + + if (igraph_vector_int_size(block_sizes) != no_blocks) { + IGRAPH_ERRORF("Block size vector length (%ld) does not agree with " + "preference matrix size (%ld).", IGRAPH_EINVAL, + igraph_vector_int_size(block_sizes), no_blocks); + } + + if (no_blocks > 0) { + if (igraph_vector_int_min(block_sizes) < 0) { + IGRAPH_ERRORF("Block sizes must be non-negative, but got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, igraph_vector_int_min(block_sizes)); + } + } + + if (igraph_vector_int_sum(block_sizes) != n) { + IGRAPH_ERRORF("Sum of the block sizes (%" IGRAPH_PRId ") must equal the number of vertices (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_sum(block_sizes), n); + } + + /* Since the sum of the block sizes should equal the number of vertices, + * and the block sizes are non-negative, the number of vertices is + * guaranteed to be non-negative. This shouldn't be checked separately. + */ + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + RNG_BEGIN(); + + for (from = 0; from < no_blocks; from++) { + double fromsize = VECTOR(*block_sizes)[from]; + long int start = directed ? 0 : from; + long int i, tooff = 0; + + IGRAPH_ALLOW_INTERRUPTION(); + + for (i = 0; i < start; i++) { + tooff += VECTOR(*block_sizes)[i]; + } + for (to = start; to < no_blocks; to++) { + double tosize = VECTOR(*block_sizes)[to]; + igraph_real_t prob = MATRIX(*pref_matrix, from, to); + double maxedges, last = RNG_GEOM(prob); + if (directed && loops) { + maxedges = fromsize * tosize; + while (last < maxedges) { + long int vto = floor(last / fromsize); + long int vfrom = last - (igraph_real_t)vto * fromsize; + igraph_vector_push_back(&edges, fromoff + vfrom); + igraph_vector_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else if (directed && !loops && from != to) { + maxedges = fromsize * tosize; + while (last < maxedges) { + long int vto = floor(last / fromsize); + long int vfrom = last - (igraph_real_t)vto * fromsize; + igraph_vector_push_back(&edges, fromoff + vfrom); + igraph_vector_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else if (directed && !loops && from == to) { + maxedges = fromsize * (fromsize - 1); + while (last < maxedges) { + long int vto = floor(last / fromsize); + long int vfrom = last - (igraph_real_t)vto * fromsize; + if (vfrom == vto) { + vto = fromsize - 1; + } + igraph_vector_push_back(&edges, fromoff + vfrom); + igraph_vector_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else if (!directed && loops && from != to) { + maxedges = fromsize * tosize; + while (last < maxedges) { + long int vto = floor(last / fromsize); + long int vfrom = last - (igraph_real_t)vto * fromsize; + igraph_vector_push_back(&edges, fromoff + vfrom); + igraph_vector_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else if (!directed && loops && from == to) { + maxedges = fromsize * (fromsize + 1) / 2.0; + while (last < maxedges) { + long int vto = floor((sqrt(8 * last + 1) - 1) / 2); + long int vfrom = last - (((igraph_real_t)vto) * (vto + 1)) / 2; + igraph_vector_push_back(&edges, fromoff + vfrom); + igraph_vector_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else if (!directed && !loops && from != to) { + maxedges = fromsize * tosize; + while (last < maxedges) { + long int vto = floor(last / fromsize); + long int vfrom = last - (igraph_real_t)vto * fromsize; + igraph_vector_push_back(&edges, fromoff + vfrom); + igraph_vector_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else { /*!directed && !loops && from==to */ + maxedges = fromsize * (fromsize - 1) / 2.0; + while (last < maxedges) { + long int vto = floor((sqrt(8 * last + 1) + 1) / 2); + long int vfrom = last - (((igraph_real_t)vto) * (vto - 1)) / 2; + igraph_vector_push_back(&edges, fromoff + vfrom); + igraph_vector_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } + + tooff += tosize; + } + fromoff += fromsize; + } + + RNG_END(); + + igraph_create(graph, &edges, n, directed); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_hsbm_game + * \brief Hierarchical stochastic block model. + * + * The function generates a random graph according to the hierarchical + * stochastic block model. + * + * \param graph The generated graph is stored here. + * \param n The number of vertices in the graph. + * \param m The number of vertices per block. n/m must be integer. + * \param rho The fraction of vertices per cluster, + * within a block. Must sum up to 1, and rho * m must be integer + * for all elements of rho. + * \param C A square, symmetric numeric matrix, the Bernoulli rates for + * the clusters within a block. Its size must mach the size of the + * \code{rho} vector. + * \param p The Bernoulli rate of connections between + * vertices in different blocks. + * \return Error code. + * + * \sa \ref igraph_sbm_game() for the classic stochastic block model, + * \ref igraph_hsbm_list_game() for a more general version. + */ + +int igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, + igraph_integer_t m, const igraph_vector_t *rho, + const igraph_matrix_t *C, igraph_real_t p) { + + int b, i, k = igraph_vector_size(rho); + igraph_vector_t csizes; + igraph_real_t sq_dbl_epsilon = sqrt(DBL_EPSILON); + int no_blocks = n / m; + igraph_vector_t edges; + int offset = 0; + + if (n < 1) { + IGRAPH_ERROR("`n' must be positive for HSBM", IGRAPH_EINVAL); + } + if (m < 1) { + IGRAPH_ERROR("`m' must be positive for HSBM", IGRAPH_EINVAL); + } + if ((long) n % (long) m) { + IGRAPH_ERROR("`n' must be a multiple of `m' for HSBM", IGRAPH_EINVAL); + } + if (!igraph_vector_isininterval(rho, 0, 1)) { + IGRAPH_ERROR("`rho' must be between zero and one for HSBM", + IGRAPH_EINVAL); + } + if (igraph_matrix_min(C) < 0 || igraph_matrix_max(C) > 1) { + IGRAPH_ERROR("`C' must be between zero and one for HSBM", IGRAPH_EINVAL); + } + if (fabs(igraph_vector_sum(rho) - 1.0) > sq_dbl_epsilon) { + IGRAPH_ERROR("`rho' must sum up to 1 for HSBM", IGRAPH_EINVAL); + } + if (igraph_matrix_nrow(C) != k || igraph_matrix_ncol(C) != k) { + IGRAPH_ERROR("`C' dimensions must match `rho' dimensions in HSBM", + IGRAPH_EINVAL); + } + if (!igraph_matrix_is_symmetric(C)) { + IGRAPH_ERROR("`C' must be a symmetric matrix", IGRAPH_EINVAL); + } + if (p < 0 || p > 1) { + IGRAPH_ERROR("`p' must be a probability for HSBM", IGRAPH_EINVAL); + } + for (i = 0; i < k; i++) { + igraph_real_t s = VECTOR(*rho)[i] * m; + if (fabs(round(s) - s) > sq_dbl_epsilon) { + IGRAPH_ERROR("`rho' * `m' is not integer in HSBM", IGRAPH_EINVAL); + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&csizes, k); + for (i = 0; i < k; i++) { + VECTOR(csizes)[i] = round(VECTOR(*rho)[i] * m); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + RNG_BEGIN(); + + /* Block models first */ + + for (b = 0; b < no_blocks; b++) { + int from, to, fromoff = 0; + + for (from = 0; from < k; from++) { + int fromsize = VECTOR(csizes)[from]; + int i, tooff = 0; + for (i = 0; i < from; i++) { + tooff += VECTOR(csizes)[i]; + } + for (to = from; to < k; to++) { + int tosize = VECTOR(csizes)[to]; + igraph_real_t prob = MATRIX(*C, from, to); + igraph_real_t maxedges; + igraph_real_t last = RNG_GEOM(prob); + if (from != to) { + maxedges = fromsize * tosize; + while (last < maxedges) { + int vto = floor(last / fromsize); + int vfrom = last - (igraph_real_t)vto * fromsize; + igraph_vector_push_back(&edges, offset + fromoff + vfrom); + igraph_vector_push_back(&edges, offset + tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else { /* from==to */ + maxedges = fromsize * (fromsize - 1) / 2.0; + while (last < maxedges) { + int vto = floor((sqrt(8 * last + 1) + 1) / 2); + int vfrom = last - (((igraph_real_t)vto) * (vto - 1)) / 2; + igraph_vector_push_back(&edges, offset + fromoff + vfrom); + igraph_vector_push_back(&edges, offset + tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } + + tooff += tosize; + } + fromoff += fromsize; + } + + offset += m; + } + + /* And now the rest, if not a special case */ + + if (p == 1) { + int fromoff = 0, tooff = m; + for (b = 0; b < no_blocks; b++) { + igraph_real_t fromsize = m; + igraph_real_t tosize = n - tooff; + int from, to; + for (from = 0; from < fromsize; from++) { + for (to = 0; to < tosize; to++) { + igraph_vector_push_back(&edges, fromoff + from); + igraph_vector_push_back(&edges, tooff + to); + } + } + fromoff += m; + tooff += m; + } + } else if (p > 0) { + int fromoff = 0, tooff = m; + for (b = 0; b < no_blocks; b++) { + igraph_real_t fromsize = m; + igraph_real_t tosize = n - tooff; + igraph_real_t maxedges = fromsize * tosize; + igraph_real_t last = RNG_GEOM(p); + while (last < maxedges) { + int vto = floor(last / fromsize); + int vfrom = last - (igraph_real_t) vto * fromsize; + igraph_vector_push_back(&edges, fromoff + vfrom); + igraph_vector_push_back(&edges, tooff + vto); + last += RNG_GEOM(p); + last += 1; + } + + fromoff += m; + tooff += m; + } + } + + RNG_END(); + + igraph_create(graph, &edges, n, /*directed=*/ 0); + + igraph_vector_destroy(&edges); + igraph_vector_destroy(&csizes); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \function igraph_hsbm_list_game + * \brief Hierarchical stochastic block model, more general version. + * + * The function generates a random graph according to the hierarchical + * stochastic block model. + * + * \param graph The generated graph is stored here. + * \param n The number of vertices in the graph. + * \param mlist An integer vector of block sizes. + * \param rholist A list of rho vectors (\c igraph_vector_t objects), one + * for each block. + * \param Clist A list of square matrices (\c igraph_matrix_t objects), + * one for each block, giving the Bernoulli rates of connections + * within the block. + * \param p The Bernoulli rate of connections between + * vertices in different blocks. + * \return Error code. + * + * \sa \ref igraph_sbm_game() for the classic stochastic block model, + * \ref igraph_hsbm_game() for a simpler general version. + */ + +int igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, + const igraph_vector_int_t *mlist, + const igraph_vector_ptr_t *rholist, + const igraph_vector_ptr_t *Clist, + igraph_real_t p) { + + int i, no_blocks = igraph_vector_ptr_size(rholist); + igraph_real_t sq_dbl_epsilon = sqrt(DBL_EPSILON); + igraph_vector_t csizes, edges; + int b, offset = 0; + + if (n < 1) { + IGRAPH_ERROR("`n' must be positive for HSBM", IGRAPH_EINVAL); + } + if (no_blocks == 0) { + IGRAPH_ERROR("`rholist' empty for HSBM", IGRAPH_EINVAL); + } + if (igraph_vector_ptr_size(Clist) != no_blocks && + igraph_vector_int_size(mlist) != no_blocks) { + IGRAPH_ERROR("`rholist' must have same length as `Clist' and `m' " + "for HSBM", IGRAPH_EINVAL); + } + if (p < 0 || p > 1) { + IGRAPH_ERROR("`p' must be a probability for HSBM", IGRAPH_EINVAL); + } + /* Checks for m's */ + if (igraph_vector_int_sum(mlist) != n) { + IGRAPH_ERROR("`m' must sum up to `n' for HSBM", IGRAPH_EINVAL); + } + if (igraph_vector_int_min(mlist) < 1) { + IGRAPH_ERROR("`m' must be positive for HSBM", IGRAPH_EINVAL); + } + /* Checks for the rhos */ + for (i = 0; i < no_blocks; i++) { + const igraph_vector_t *rho = VECTOR(*rholist)[i]; + if (!igraph_vector_isininterval(rho, 0, 1)) { + IGRAPH_ERROR("`rho' must be between zero and one for HSBM", + IGRAPH_EINVAL); + } + if (fabs(igraph_vector_sum(rho) - 1.0) > sq_dbl_epsilon) { + IGRAPH_ERROR("`rho' must sum up to 1 for HSBM", IGRAPH_EINVAL); + } + } + /* Checks for the Cs */ + for (i = 0; i < no_blocks; i++) { + const igraph_matrix_t *C = VECTOR(*Clist)[i]; + if (igraph_matrix_min(C) < 0 || igraph_matrix_max(C) > 1) { + IGRAPH_ERROR("`C' must be between zero and one for HSBM", + IGRAPH_EINVAL); + } + if (!igraph_matrix_is_symmetric(C)) { + IGRAPH_ERROR("`C' must be a symmetric matrix", IGRAPH_EINVAL); + } + } + /* Check that C and rho sizes match */ + for (i = 0; i < no_blocks; i++) { + const igraph_vector_t *rho = VECTOR(*rholist)[i]; + const igraph_matrix_t *C = VECTOR(*Clist)[i]; + int k = igraph_vector_size(rho); + if (igraph_matrix_nrow(C) != k || igraph_matrix_ncol(C) != k) { + IGRAPH_ERROR("`C' dimensions must match `rho' dimensions in HSBM", + IGRAPH_EINVAL); + } + } + /* Check that rho * m is integer */ + for (i = 0; i < no_blocks; i++) { + const igraph_vector_t *rho = VECTOR(*rholist)[i]; + igraph_real_t m = VECTOR(*mlist)[i]; + int j, k = igraph_vector_size(rho); + for (j = 0; j < k; j++) { + igraph_real_t s = VECTOR(*rho)[j] * m; + if (fabs(round(s) - s) > sq_dbl_epsilon) { + IGRAPH_ERROR("`rho' * `m' is not integer in HSBM", IGRAPH_EINVAL); + } + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&csizes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + RNG_BEGIN(); + + /* Block models first */ + + for (b = 0; b < no_blocks; b++) { + int from, to, fromoff = 0; + const igraph_vector_t *rho = VECTOR(*rholist)[b]; + const igraph_matrix_t *C = VECTOR(*Clist)[b]; + igraph_real_t m = VECTOR(*mlist)[b]; + int k = igraph_vector_size(rho); + + igraph_vector_resize(&csizes, k); + for (i = 0; i < k; i++) { + VECTOR(csizes)[i] = round(VECTOR(*rho)[i] * m); + } + + for (from = 0; from < k; from++) { + int fromsize = VECTOR(csizes)[from]; + int i, tooff = 0; + for (i = 0; i < from; i++) { + tooff += VECTOR(csizes)[i]; + } + for (to = from; to < k; to++) { + int tosize = VECTOR(csizes)[to]; + igraph_real_t prob = MATRIX(*C, from, to); + igraph_real_t maxedges; + igraph_real_t last = RNG_GEOM(prob); + if (from != to) { + maxedges = fromsize * tosize; + while (last < maxedges) { + int vto = floor(last / fromsize); + int vfrom = last - (igraph_real_t)vto * fromsize; + igraph_vector_push_back(&edges, offset + fromoff + vfrom); + igraph_vector_push_back(&edges, offset + tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else { /* from==to */ + maxedges = fromsize * (fromsize - 1) / 2.0; + while (last < maxedges) { + int vto = floor((sqrt(8 * last + 1) + 1) / 2); + int vfrom = last - (((igraph_real_t)vto) * (vto - 1)) / 2; + igraph_vector_push_back(&edges, offset + fromoff + vfrom); + igraph_vector_push_back(&edges, offset + tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } + + tooff += tosize; + } + fromoff += fromsize; + } + + offset += m; + } + + /* And now the rest, if not a special case */ + + if (p == 1) { + int fromoff = 0, tooff = VECTOR(*mlist)[0]; + for (b = 0; b < no_blocks; b++) { + igraph_real_t fromsize = VECTOR(*mlist)[b]; + igraph_real_t tosize = n - tooff; + int from, to; + for (from = 0; from < fromsize; from++) { + for (to = 0; to < tosize; to++) { + igraph_vector_push_back(&edges, fromoff + from); + igraph_vector_push_back(&edges, tooff + to); + } + } + fromoff += fromsize; + if (b + 1 < no_blocks) { + tooff += VECTOR(*mlist)[b + 1]; + } + } + } else if (p > 0) { + int fromoff = 0, tooff = VECTOR(*mlist)[0]; + for (b = 0; b < no_blocks; b++) { + igraph_real_t fromsize = VECTOR(*mlist)[b]; + igraph_real_t tosize = n - tooff; + igraph_real_t maxedges = fromsize * tosize; + igraph_real_t last = RNG_GEOM(p); + while (last < maxedges) { + int vto = floor(last / fromsize); + int vfrom = last - (igraph_real_t) vto * fromsize; + igraph_vector_push_back(&edges, fromoff + vfrom); + igraph_vector_push_back(&edges, tooff + vto); + last += RNG_GEOM(p); + last += 1; + } + + fromoff += fromsize; + if (b + 1 < no_blocks) { + tooff += VECTOR(*mlist)[b + 1]; + } + } + } + + RNG_END(); + + igraph_create(graph, &edges, n, /*directed=*/ 0); + + igraph_vector_destroy(&edges); + igraph_vector_destroy(&csizes); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} diff --git a/src/rigraph/core/games/static_fitness.c b/src/rigraph/core/games/static_fitness.c new file mode 100644 index 0000000..943149c --- /dev/null +++ b/src/rigraph/core/games/static_fitness.c @@ -0,0 +1,436 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_adjlist.h" +#include "igraph_conversion.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_random.h" + +#include "../core/interruption.h" + +/** + * \ingroup generators + * \function igraph_static_fitness_game + * \brief Non-growing random graph with edge probabilities proportional to node fitness scores. + * + * This game generates a directed or undirected random graph where the + * probability of an edge between vertices i and j depends on the fitness + * scores of the two vertices involved. For undirected graphs, each vertex + * has a single fitness score. For directed graphs, each vertex has an out- + * and an in-fitness, and the probability of an edge from i to j depends on + * the out-fitness of vertex i and the in-fitness of vertex j. + * + * + * The generation process goes as follows. We start from N disconnected nodes + * (where N is given by the length of the fitness vector). Then we randomly + * select two vertices i and j, with probabilities proportional to their + * fitnesses. (When the generated graph is directed, i is selected according to + * the out-fitnesses and j is selected according to the in-fitnesses). If the + * vertices are not connected yet (or if multiple edges are allowed), we + * connect them; otherwise we select a new pair. This is repeated until the + * desired number of links are created. + * + * + * It can be shown that the \em expected degree of each vertex will be + * proportional to its fitness, although the actual, observed degree will not + * be. If you need to generate a graph with an exact degree sequence, consider + * \ref igraph_degree_sequence_game instead. + * + * + * This model is commonly used to generate static scale-free networks. To + * achieve this, you have to draw the fitness scores from the desired power-law + * distribution. Alternatively, you may use \ref igraph_static_power_law_game + * which generates the fitnesses for you with a given exponent. + * + * + * Reference: Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution + * in scale-free networks. Phys Rev Lett 87(27):278701, 2001. + * + * \param graph Pointer to an uninitialized graph object. + * \param fitness_out A numeric vector containing the fitness of each vertex. + * For directed graphs, this specifies the out-fitness + * of each vertex. + * \param fitness_in If \c NULL, the generated graph will be undirected. + * If not \c NULL, this argument specifies the in-fitness + * of each vertex. + * \param no_of_edges The number of edges in the generated graph. + * \param loops Whether to allow loop edges in the generated graph. + * \param multiple Whether to allow multiple edges in the generated graph. + * + * \return Error code: + * \c IGRAPH_EINVAL: invalid parameter + * \c IGRAPH_ENOMEM: there is not enough + * memory for the operation. + * + * Time complexity: O(|V| + |E| log |E|). + */ +int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges, + const igraph_vector_t *fitness_out, const igraph_vector_t *fitness_in, + igraph_bool_t loops, igraph_bool_t multiple) { + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t no_of_nodes; + igraph_integer_t outnodes, innodes, nodes; + igraph_vector_t cum_fitness_in, cum_fitness_out; + igraph_vector_t *p_cum_fitness_in, *p_cum_fitness_out; + igraph_real_t x, max_in, max_out; + igraph_real_t max_no_of_edges; + igraph_bool_t is_directed = (fitness_in != 0); + float num_steps; + igraph_integer_t step_counter = 0; + long int i, from, to, pos; + + if (fitness_out == 0) { + IGRAPH_ERROR("fitness_out must not be null.", IGRAPH_EINVAL); + } + + if (no_of_edges < 0) { + IGRAPH_ERRORF("Number of edges cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, no_of_edges); + } + + no_of_nodes = (igraph_integer_t) igraph_vector_size(fitness_out); + if (no_of_nodes == 0) { + IGRAPH_CHECK(igraph_empty(graph, 0, is_directed)); + return IGRAPH_SUCCESS; + } + + if (is_directed && igraph_vector_size(fitness_in) != no_of_nodes) { + IGRAPH_ERROR("fitness_in must have the same size as fitness_out.", IGRAPH_EINVAL); + } + + /* Sanity checks for the fitnesses */ + if (igraph_vector_min(fitness_out) < 0) { + IGRAPH_ERROR("Fitness scores must be non-negative.", IGRAPH_EINVAL); + } + if (fitness_in != 0 && igraph_vector_min(fitness_in) < 0) { + IGRAPH_ERROR("Fitness scores must be non-negative.", IGRAPH_EINVAL); + } + + /* Avoid getting into an infinite loop when too many edges are requested */ + if (!multiple) { + if (is_directed) { + outnodes = innodes = nodes = 0; + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*fitness_out)[i] != 0) { + outnodes++; + } + if (VECTOR(*fitness_in)[i] != 0) { + innodes++; + } + if (VECTOR(*fitness_out)[i] != 0 && VECTOR(*fitness_in)[i] != 0) { + nodes++; + } + } + max_no_of_edges = ((igraph_real_t) outnodes) * innodes - (loops ? 0 : nodes); + } else { + nodes = 0; + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*fitness_out)[i] != 0) { + nodes++; + } + } + max_no_of_edges = loops + ? nodes * ((igraph_real_t)nodes + 1) / 2 + : nodes * ((igraph_real_t)nodes - 1) / 2; + } + if (no_of_edges > max_no_of_edges) { + IGRAPH_ERROR("Too many edges requested.", IGRAPH_EINVAL); + } + } + + /* Calculate the cumulative fitness scores */ + IGRAPH_VECTOR_INIT_FINALLY(&cum_fitness_out, no_of_nodes); + IGRAPH_CHECK(igraph_vector_cumsum(&cum_fitness_out, fitness_out)); + max_out = igraph_vector_tail(&cum_fitness_out); + p_cum_fitness_out = &cum_fitness_out; + if (is_directed) { + IGRAPH_VECTOR_INIT_FINALLY(&cum_fitness_in, no_of_nodes); + IGRAPH_CHECK(igraph_vector_cumsum(&cum_fitness_in, fitness_in)); + max_in = igraph_vector_tail(&cum_fitness_in); + p_cum_fitness_in = &cum_fitness_in; + } else { + max_in = max_out; + p_cum_fitness_in = &cum_fitness_out; + } + + RNG_BEGIN(); + num_steps = no_of_edges; + if (multiple) { + /* Generating when multiple edges are allowed */ + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * no_of_edges)); + + while (no_of_edges > 0) { + /* Report progress after every 10000 edges */ + if ((step_counter++) % 10000 == 0) { + IGRAPH_PROGRESS("Static fitness game", 100.0 * (1 - no_of_edges / num_steps), NULL); + IGRAPH_ALLOW_INTERRUPTION(); + } + + x = RNG_UNIF(0, max_out); + igraph_vector_binsearch(p_cum_fitness_out, x, &from); + x = RNG_UNIF(0, max_in); + igraph_vector_binsearch(p_cum_fitness_in, x, &to); + + /* Skip if loop edge and loops = false */ + if (!loops && from == to) { + continue; + } + + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + + no_of_edges--; + } + + /* Create the graph */ + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, is_directed)); + + /* Clear the edge list */ + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + } else { + /* Multiple edges are disallowed */ + igraph_adjlist_t al; + igraph_vector_int_t* neis; + + IGRAPH_CHECK(igraph_adjlist_init_empty(&al, no_of_nodes)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + while (no_of_edges > 0) { + /* Report progress after every 10000 edges */ + if ((step_counter++) % 10000 == 0) { + IGRAPH_PROGRESS("Static fitness game", 100.0 * (1 - no_of_edges / num_steps), NULL); + IGRAPH_ALLOW_INTERRUPTION(); + } + + x = RNG_UNIF(0, max_out); + igraph_vector_binsearch(p_cum_fitness_out, x, &from); + x = RNG_UNIF(0, max_in); + igraph_vector_binsearch(p_cum_fitness_in, x, &to); + + /* Skip if loop edge and loops = false */ + if (!loops && from == to) { + continue; + } + + /* For undirected graphs, ensure that from < to */ + if (!is_directed && from > to) { + pos = from; from = to; to = pos; + } + + /* Is there already an edge? If so, try again */ + neis = igraph_adjlist_get(&al, from); + if (igraph_vector_int_binsearch(neis, to, &pos)) { + continue; + } + + /* Insert the edge */ + IGRAPH_CHECK(igraph_vector_int_insert(neis, pos, to)); + + no_of_edges--; + } + + /* Create the graph. We cannot use IGRAPH_ALL here for undirected graphs + * because we did not add edges in both directions in the adjacency list. + * We will use igraph_to_undirected in an extra step. */ + IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, 1)); + if (!is_directed) { + IGRAPH_CHECK(igraph_to_undirected(graph, IGRAPH_TO_UNDIRECTED_EACH, 0)); + } + + /* Clear the adjacency list */ + igraph_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(1); + } + RNG_END(); + + IGRAPH_PROGRESS("Static fitness game", 100.0, NULL); + + /* Cleanup before we create the graph */ + if (is_directed) { + igraph_vector_destroy(&cum_fitness_in); + IGRAPH_FINALLY_CLEAN(1); + } + igraph_vector_destroy(&cum_fitness_out); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup generators + * \function igraph_static_power_law_game + * \brief Generates a non-growing random graph with expected power-law degree distributions. + * + * This game generates a directed or undirected random graph where the + * degrees of vertices follow power-law distributions with prescribed + * exponents. For directed graphs, the exponents of the in- and out-degree + * distributions may be specified separately. + * + * + * The game simply uses \ref igraph_static_fitness_game with appropriately + * constructed fitness vectors. In particular, the fitness of vertex i + * is i-alpha, where alpha = 1/(gamma-1) + * and gamma is the exponent given in the arguments. + * + * + * To remove correlations between in- and out-degrees in case of directed + * graphs, the in-fitness vector will be shuffled after it has been set up + * and before \ref igraph_static_fitness_game is called. + * + * + * Note that significant finite size effects may be observed for exponents + * smaller than 3 in the original formulation of the game. This function + * provides an argument that lets you remove the finite size effects by + * assuming that the fitness of vertex i is + * (i+i0-1)-alpha, + * where i0 is a constant chosen appropriately to ensure that the maximum + * degree is less than the square root of the number of edges times the + * average degree; see the paper of Chung and Lu, and Cho et al for more + * details. + * + * + * References: + * + * + * Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution + * in scale-free networks. Phys Rev Lett 87(27):278701, 2001. + * + * + * Chung F and Lu L: Connected components in a random graph with given + * degree sequences. Annals of Combinatorics 6, 125-145, 2002. + * + * + * Cho YS, Kim JS, Park J, Kahng B, Kim D: Percolation transitions in + * scale-free networks under the Achlioptas process. Phys Rev Lett + * 103:135702, 2009. + * + * \param graph Pointer to an uninitialized graph object. + * \param no_of_nodes The number of nodes in the generated graph. + * \param no_of_edges The number of edges in the generated graph. + * \param exponent_out The power law exponent of the degree distribution. + * For directed graphs, this specifies the exponent of the + * out-degree distribution. It must be greater than or + * equal to 2. If you pass \c IGRAPH_INFINITY here, you + * will get back an Erdos-Renyi random network. + * \param exponent_in If negative, the generated graph will be undirected. + * If greater than or equal to 2, this argument specifies + * the exponent of the in-degree distribution. If + * non-negative but less than 2, an error will be + * generated. + * \param loops Whether to allow loop edges in the generated graph. + * \param multiple Whether to allow multiple edges in the generated graph. + * \param finite_size_correction Whether to use the proposed finite size + * correction of Cho et al. + * + * \return Error code: + * \c IGRAPH_EINVAL: invalid parameter + * \c IGRAPH_ENOMEM: there is not enough + * memory for the operation. + * + * Time complexity: O(|V| + |E| log |E|). + */ +int igraph_static_power_law_game(igraph_t *graph, + igraph_integer_t no_of_nodes, igraph_integer_t no_of_edges, + igraph_real_t exponent_out, igraph_real_t exponent_in, + igraph_bool_t loops, igraph_bool_t multiple, + igraph_bool_t finite_size_correction) { + + igraph_vector_t fitness_out, fitness_in; + igraph_real_t alpha_out = 0.0, alpha_in = 0.0; + long int i; + igraph_real_t j; + + if (no_of_nodes < 0) { + IGRAPH_ERRORF("Number of nodes cannot be negative, got %" IGRAPH_PRId".", IGRAPH_EINVAL, no_of_nodes); + } + + /* Calculate alpha_out */ + if (exponent_out < 2) { + IGRAPH_ERRORF("Out-degree exponent must be >= 2, got %g.", IGRAPH_EINVAL, exponent_out); + } else if (igraph_finite(exponent_out)) { + alpha_out = -1.0 / (exponent_out - 1); + } else { + alpha_out = 0.0; + } + + /* Construct the out-fitnesses */ + IGRAPH_VECTOR_INIT_FINALLY(&fitness_out, no_of_nodes); + j = no_of_nodes; + if (finite_size_correction && alpha_out < -0.5) { + /* See the Cho et al paper, first page first column + footnote 7 */ + j += pow(no_of_nodes, 1 + 0.5 / alpha_out) * + pow(10 * sqrt(2) * (1 + alpha_out), -1.0 / alpha_out) - 1; + } + if (j < no_of_nodes) { + j = no_of_nodes; + } + for (i = 0; i < no_of_nodes; i++, j--) { + VECTOR(fitness_out)[i] = pow(j, alpha_out); + } + + if (exponent_in >= 0) { + if (exponent_in < 2) { + IGRAPH_ERRORF("For directed graphs the in-degree exponent must be >= 2, got %g.", + IGRAPH_EINVAL, exponent_in); + } else if (igraph_finite(exponent_in)) { + alpha_in = -1.0 / (exponent_in - 1); + } else { + alpha_in = 0.0; + } + + IGRAPH_VECTOR_INIT_FINALLY(&fitness_in, no_of_nodes); + j = no_of_nodes; + if (finite_size_correction && alpha_in < -0.5) { + /* See the Cho et al paper, first page first column + footnote 7 */ + j += pow(no_of_nodes, 1 + 0.5 / alpha_in) * + pow(10 * sqrt(2) * (1 + alpha_in), -1.0 / alpha_in) - 1; + } + if (j < no_of_nodes) { + j = no_of_nodes; + } + for (i = 0; i < no_of_nodes; i++, j--) { + VECTOR(fitness_in)[i] = pow(j, alpha_in); + } + IGRAPH_CHECK(igraph_vector_shuffle(&fitness_in)); + + IGRAPH_CHECK(igraph_static_fitness_game(graph, no_of_edges, + &fitness_out, &fitness_in, loops, multiple)); + + igraph_vector_destroy(&fitness_in); + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_CHECK(igraph_static_fitness_game(graph, no_of_edges, + &fitness_out, 0, loops, multiple)); + } + + igraph_vector_destroy(&fitness_out); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/games/tree.c b/src/rigraph/core/games/tree.c new file mode 100644 index 0000000..805ad53 --- /dev/null +++ b/src/rigraph/core/games/tree.c @@ -0,0 +1,196 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_random.h" + +/* Uniform sampling of labelled trees (igraph_tree_game) */ + +/* The following implementation uniformly samples Prufer trees and converts + * them to trees. + */ + +static int igraph_i_tree_game_prufer(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { + igraph_vector_int_t prufer; + long i; + + if (directed) { + IGRAPH_ERROR("The Prufer method for random tree generation does not support directed trees", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_int_init(&prufer, n - 2)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &prufer); + + RNG_BEGIN(); + + for (i = 0; i < n - 2; ++i) { + VECTOR(prufer)[i] = RNG_INTEGER(0, n - 1); + } + + RNG_END(); + + IGRAPH_CHECK(igraph_from_prufer(graph, &prufer)); + + igraph_vector_int_destroy(&prufer); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* The following implementation is based on loop-erased random walks and Wilson's algorithm + * for uniformly sampling spanning trees. We effectively sample spanning trees of the complete + * graph. + */ + +/* swap two elements of a vector_int */ +#define SWAP_INT_ELEM(vec, i, j) \ + { \ + igraph_integer_t temp; \ + temp = VECTOR(vec)[i]; \ + VECTOR(vec)[i] = VECTOR(vec)[j]; \ + VECTOR(vec)[j] = temp; \ + } + +static int igraph_i_tree_game_loop_erased_random_walk(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { + igraph_vector_t edges; + igraph_vector_int_t vertices; + igraph_vector_bool_t visited; + long i, j, k; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * (n - 1)); + + IGRAPH_CHECK(igraph_vector_bool_init(&visited, n)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited); + + /* The vertices vector contains visited vertices between 0..k-1, unvisited ones between k..n-1. */ + IGRAPH_CHECK(igraph_vector_int_init_seq(&vertices, 0, n - 1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &vertices); + + RNG_BEGIN(); + + /* A simple implementation could be as below. This is for illustration only. + * The actually implemented algorithm avoids unnecessary walking on the already visited + * portion of the vertex set. + */ + /* + // pick starting point for the walk + i = RNG_INTEGER(0, n-1); + VECTOR(visited)[i] = 1; + + k=1; + while (k < n) { + // pick next vertex in the walk + j = RNG_INTEGER(0, n-1); + // if it has not been visited before, connect to the previous vertex in the sequence + if (! VECTOR(visited)[j]) { + VECTOR(edges)[2*k - 2] = i; + VECTOR(edges)[2*k - 1] = j; + VECTOR(visited)[j] = 1; + k++; + } + i=j; + } + */ + + i = RNG_INTEGER(0, n - 1); + VECTOR(visited)[i] = 1; + SWAP_INT_ELEM(vertices, 0, i); + + for (k = 1; k < n; ++k) { + j = RNG_INTEGER(0, n - 1); + if (VECTOR(visited)[VECTOR(vertices)[j]]) { + i = VECTOR(vertices)[j]; + j = RNG_INTEGER(k, n - 1); + } + VECTOR(visited)[VECTOR(vertices)[j]] = 1; + SWAP_INT_ELEM(vertices, k, j); + VECTOR(edges)[2 * k - 2] = i; + i = VECTOR(vertices)[k]; + VECTOR(edges)[2 * k - 1] = i; + } + + RNG_END(); + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + + igraph_vector_int_destroy(&vertices); + igraph_vector_bool_destroy(&visited); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +#undef SWAP_INT_ELEM + +/** + * \ingroup generators + * \function igraph_tree_game + * \brief Generates a random tree with the given number of nodes. + * + * This function samples uniformly from the set of labelled trees, + * i.e. it generates each labelled tree with the same probability. + * + * + * Note that for n=0, the null graph is returned, + * which is not considered to be a tree by \ref igraph_is_tree(). + * + * \param graph Pointer to an uninitialized graph object. + * \param n The number of nodes in the tree. + * \param directed Whether to create a directed tree. The edges are oriented away from the root. + * \param method The algorithm to use to generate the tree. Possible values: + * \clist + * \cli IGRAPH_RANDOM_TREE_PRUFER + * This algorithm samples Prüfer sequences uniformly, then converts them to trees. + * Directed trees are not currently supported. + * \cli IGRAPH_RANDOM_LERW + * This algorithm effectively performs a loop-erased random walk on the complete graph + * to uniformly sample its spanning trees (Wilson's algorithm). + * \endclist + * \return Error code: + * \c IGRAPH_ENOMEM: there is not enough + * memory to perform the operation. + * \c IGRAPH_EINVAL: invalid tree size + * + * \sa \ref igraph_from_prufer() + * + */ + +int igraph_tree_game(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_random_tree_t method) { + if (n < 2) { + IGRAPH_CHECK(igraph_empty(graph, n, directed)); + return IGRAPH_SUCCESS; + } + + switch (method) { + case IGRAPH_RANDOM_TREE_PRUFER: + return igraph_i_tree_game_prufer(graph, n, directed); + case IGRAPH_RANDOM_TREE_LERW: + return igraph_i_tree_game_loop_erased_random_walk(graph, n, directed); + default: + IGRAPH_ERROR("Invalid method for random tree construction", IGRAPH_EINVAL); + } +} diff --git a/src/rigraph/core/games/watts_strogatz.c b/src/rigraph/core/games/watts_strogatz.c new file mode 100644 index 0000000..1a6e730 --- /dev/null +++ b/src/rigraph/core/games/watts_strogatz.c @@ -0,0 +1,102 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +/** + * \function igraph_watts_strogatz_game + * \brief The Watts-Strogatz small-world model. + * + * This function generates a graph according to the Watts-Strogatz + * model of small-world networks. The graph is obtained by creating a + * circular undirected lattice and then rewire the edges randomly with + * a constant probability. + * + * See also: Duncan J Watts and Steven H Strogatz: + * Collective dynamics of small world networks, Nature + * 393, 440-442, 1998. + * + * \param graph The graph to initialize. + * \param dim The dimension of the lattice. + * \param size The size of the lattice along each dimension. + * \param nei The size of the neighborhood for each vertex. This is + * the same as the \p nei argument of \ref + * igraph_connect_neighborhood(). + * \param p The rewiring probability. A real number between zero and + * one (inclusive). + * \param loops Logical, whether to generate loop edges. + * \param multiple Logical, whether to allow multiple edges in the + * generated graph. + * \return Error code. + * + * \sa \ref igraph_lattice(), \ref igraph_connect_neighborhood() and + * \ref igraph_rewire_edges() can be used if more flexibility is + * needed, e.g. a different type of lattice. + * + * Time complexity: O(|V|*d^o+|E|), |V| and |E| are the number of + * vertices and edges, d is the average degree, o is the \p nei + * argument. + */ +int igraph_watts_strogatz_game(igraph_t *graph, igraph_integer_t dim, + igraph_integer_t size, igraph_integer_t nei, + igraph_real_t p, igraph_bool_t loops, + igraph_bool_t multiple) { + + igraph_vector_t dimvector; + long int i; + + if (dim < 1) { + IGRAPH_ERROR("WS game: dimension should be at least one", IGRAPH_EINVAL); + } + if (size < 1) { + IGRAPH_ERROR("WS game: lattice size should be at least one", + IGRAPH_EINVAL); + } + if (p < 0 || p > 1) { + IGRAPH_ERROR("WS game: rewiring probability should be between 0 and 1", + IGRAPH_EINVAL); + } + + /* Create the lattice first */ + + IGRAPH_VECTOR_INIT_FINALLY(&dimvector, dim); + for (i = 0; i < dim; i++) { + VECTOR(dimvector)[i] = size; + } + + IGRAPH_CHECK(igraph_lattice(graph, &dimvector, nei, IGRAPH_UNDIRECTED, + 0 /* mutual */, 1 /* circular */)); + igraph_vector_destroy(&dimvector); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, graph); + + /* Rewire the edges then */ + + IGRAPH_CHECK(igraph_rewire_edges(graph, p, loops, multiple)); + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/graph/adjlist.c b/src/rigraph/core/graph/adjlist.c new file mode 100644 index 0000000..9d1911e --- /dev/null +++ b/src/rigraph/core/graph/adjlist.c @@ -0,0 +1,1192 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_adjlist.h" +#include "igraph_memory.h" +#include "igraph_interface.h" + +#include "../core/interruption.h" + +#include /* memset */ +#include + +/** + * Helper function that simplifies a sorted adjacency vector by removing + * duplicate elements and optionally self-loops. + */ +static int igraph_i_simplify_sorted_int_adjacency_vector_in_place( + igraph_vector_int_t *v, igraph_integer_t index, igraph_neimode_t mode, + igraph_loops_t loops, igraph_multiple_t multiple +); + +/** + * Helper function that removes loops from an incidence vector (either both + * occurrences or only one of them). + */ +static int igraph_i_remove_loops_from_incidence_vector_in_place( + igraph_vector_int_t *v, const igraph_t *graph, igraph_loops_t loops +); + +/** + * \section about_adjlists + * + * Sometimes it is easier to work with a graph which is in + * adjacency list format: a list of vectors; each vector contains the + * neighbor vertices or incident edges of a given vertex. Typically, + * this representation is good if we need to iterate over the neighbors + * of all vertices many times. E.g. when finding the shortest paths + * between all pairs of vertices or calculating closeness centrality + * for all the vertices. + * + * The igraph_adjlist_t stores the adjacency lists + * of a graph. After creation it is independent of the original graph, + * it can be modified freely with the usual vector operations, the + * graph is not affected. E.g. the adjacency list can be used to + * rewire the edges of a graph efficiently. If one used the + * straightforward \ref igraph_delete_edges() and \ref + * igraph_add_edges() combination for this that needs O(|V|+|E|) time + * for every single deletion and insertion operation, it is thus very + * slow if many edges are rewired. Extracting the graph into an + * adjacency list, do all the rewiring operations on the vectors of + * the adjacency list and then creating a new graph needs (depending + * on how exactly the rewiring is done) typically O(|V|+|E|) time for + * the whole rewiring process. + * + * Lazy adjacency lists are a bit different. When creating a + * lazy adjacency list, the neighbors of the vertices are not queried, + * only some memory is allocated for the vectors. When \ref + * igraph_lazy_adjlist_get() is called for vertex v the first time, + * the neighbors of v are queried and stored in a vector of the + * adjacency list, so they don't need to be queried again. Lazy + * adjacency lists are handy if you have an at least linear operation + * (because initialization is generally linear in terms of the number of + * vertices), but you don't know how many vertices you will visit + * during the computation. + * + * + * + * \example examples/simple/adjlist.c + * + */ + +/** + * \function igraph_adjlist_init + * \brief Constructs an adjacency list of vertices from a given graph. + * + * Creates a list of vectors containing the neighbors of all vertices + * in a graph. The adjacency list is independent of the graph after + * creation, e.g. the graph can be destroyed and modified, the + * adjacency list contains the state of the graph at the time of its + * initialization. + * + * \param graph The input graph. + * \param al Pointer to an uninitialized igraph_adjlist_t object. + * \param mode Constant specifying whether outgoing + * (IGRAPH_OUT), incoming (IGRAPH_IN), + * or both (IGRAPH_ALL) types of neighbors to include + * in the adjacency list. It is ignored for undirected networks. + * \param loops Specifies how to treat loop edges. IGRAPH_NO_LOOPS + * removes loop edges from the adjacency list. IGRAPH_LOOPS_ONCE + * makes each loop edge appear only once in the adjacency list of the + * corresponding vertex. IGRAPH_LOOPS_TWICE makes loop edges + * appear \em twice in the adjacency list of the corresponding vertex, + * but only if the graph is undirected or mode is set to + * IGRAPH_ALL. + * \param multiple Specifies how to treat multiple (parallel) edges. + * IGRAPH_NO_MULTIPLE collapses parallel edges into a single one; + * IGRAPH_MULTIPLE keeps the multiplicities of parallel edges + * so the same vertex will appear as many times in the adjacency list of + * another vertex as the number of parallel edges going between the two + * vertices. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + */ + +int igraph_adjlist_init(const igraph_t *graph, igraph_adjlist_t *al, + igraph_neimode_t mode, igraph_loops_t loops, + igraph_multiple_t multiple) { + igraph_integer_t i; + igraph_vector_t tmp; + int j, n; + + if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Cannot create adjacency list view", IGRAPH_EINVMODE); + } + + igraph_vector_init(&tmp, 0); + IGRAPH_FINALLY(igraph_vector_destroy, &tmp); + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + al->length = igraph_vcount(graph); + al->adjs = IGRAPH_CALLOC(al->length, igraph_vector_int_t); + if (al->adjs == 0) { + IGRAPH_ERROR("Cannot create adjacency list view", IGRAPH_ENOMEM); + } + + IGRAPH_FINALLY(igraph_adjlist_destroy, al); + + for (i = 0; i < al->length; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_neighbors(graph, &tmp, i, mode)); + + n = igraph_vector_size(&tmp); + IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], n)); + for (j = 0; j < n; j++) { + VECTOR(al->adjs[i])[j] = VECTOR(tmp)[j]; + } + + IGRAPH_CHECK(igraph_i_simplify_sorted_int_adjacency_vector_in_place( + &al->adjs[i], i, mode, loops, multiple + )); + } + + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \function igraph_adjlist_init_empty + * \brief Initializes an empty adjacency list. + * + * Creates a list of vectors, one for each vertex. This is useful when you + * are \em constructing a graph using an adjacency list representation as + * it does not require your graph to exist yet. + * \param no_of_nodes The number of vertices + * \param al Pointer to an uninitialized igraph_adjlist_t object. + * \return Error code. + * + * Time complexity: O(|V|), linear in the number of vertices. + */ +int igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes) { + long int i; + + al->length = no_of_nodes; + al->adjs = IGRAPH_CALLOC(al->length, igraph_vector_int_t); + if (al->adjs == 0) { + IGRAPH_ERROR("Cannot create adjlist view", IGRAPH_ENOMEM); + } + + IGRAPH_FINALLY(igraph_adjlist_destroy, al); + for (i = 0; i < al->length; i++) { + IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], 0)); + } + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_adjlist_init_complementer + * \brief Adjacency lists for the complementer graph. + * + * This function creates adjacency lists for the complementer + * of the input graph. In the complementer graph all edges are present + * which are not present in the original graph. Multiple edges in the + * input graph are ignored. + * \param graph The input graph. + * \param al Pointer to a not yet initialized adjacency list. + * \param mode Constant specifying whether outgoing + * (IGRAPH_OUT), incoming (IGRAPH_IN), + * or both (IGRAPH_ALL) types of neighbors (in the + * complementer graph) to include in the adjacency list. It is + * ignored for undirected networks. + * \param loops Whether to consider loop edges. + * \return Error code. + * + * Time complexity: O(|V|^2+|E|), quadratic in the number of vertices. + */ +int igraph_adjlist_init_complementer(const igraph_t *graph, + igraph_adjlist_t *al, + igraph_neimode_t mode, + igraph_bool_t loops) { + igraph_integer_t i, j, k, n; + igraph_bool_t* seen; + igraph_vector_t vec; + + if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Cannot create complementer adjlist view", IGRAPH_EINVMODE); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + al->length = igraph_vcount(graph); + al->adjs = IGRAPH_CALLOC(al->length, igraph_vector_int_t); + if (al->adjs == 0) { + IGRAPH_ERROR("Cannot create complementer adjlist view", IGRAPH_ENOMEM); + } + + IGRAPH_FINALLY(igraph_adjlist_destroy, al); + + n = al->length; + seen = IGRAPH_CALLOC(n, igraph_bool_t); + if (seen == 0) { + IGRAPH_ERROR("Cannot create complementer adjlist view", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, seen); + + IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); + + for (i = 0; i < al->length; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + igraph_neighbors(graph, &vec, i, mode); + memset(seen, 0, sizeof(igraph_bool_t) * (unsigned) al->length); + n = al->length; + if (!loops) { + seen[i] = 1; + n--; + } + for (j = 0; j < igraph_vector_size(&vec); j++) { + if (! seen [ (long int) VECTOR(vec)[j] ] ) { + n--; + seen[ (long int) VECTOR(vec)[j] ] = 1; + } + } + IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], n)); + for (j = 0, k = 0; k < n; j++) { + if (!seen[j]) { + VECTOR(al->adjs[i])[k++] = j; + } + } + } + + IGRAPH_FREE(seen); + igraph_vector_destroy(&vec); + IGRAPH_FINALLY_CLEAN(3); + return 0; +} + +/** + * \function igraph_adjlist_destroy + * \brief Deallocates an adjacency list. + * + * Free all memory allocated for an adjacency list. + * \param al The adjacency list to destroy. + * + * Time complexity: depends on memory management. + */ +void igraph_adjlist_destroy(igraph_adjlist_t *al) { + long int i; + for (i = 0; i < al->length; i++) { + igraph_vector_int_destroy(&al->adjs[i]); + } + IGRAPH_FREE(al->adjs); +} + +/** + * \function igraph_adjlist_clear + * Removes all edges from an adjacency list. + * + * \param al The adjacency list. + * Time complexity: depends on memory management, typically O(n), where n is + * the total number of elements in the adjacency list. + */ +void igraph_adjlist_clear(igraph_adjlist_t *al) { + long int i; + for (i = 0; i < al->length; i++) { + igraph_vector_int_clear(&al->adjs[i]); + } +} + +/** + * \function igraph_adjlist_size + * \brief Returns the number of vertices in an adjacency list. + * + * \param al The adjacency list. + * \return The number of vertices in the adjacency list. + * + * Time complexity: O(1). + */ +igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al) { + return al->length; +} + +/** + * \function igraph_adjlist_sort + * \brief Sorts each vector in an adjacency list. + * + * Sorts every vector of the adjacency list. + * \param al The adjacency list. + * + * Time complexity: O(n log n), n is the total number of elements in + * the adjacency list. + */ +void igraph_adjlist_sort(igraph_adjlist_t *al) { + long int i; + for (i = 0; i < al->length; i++) { + igraph_vector_int_sort(&al->adjs[i]); + } +} + +/** + * \function igraph_adjlist_simplify + * \brief Simplifies an adjacency list. + * + * Simplifies an adjacency list, i.e. removes loop and multiple edges. + * + * \param al The adjacency list. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of edges and + * vertices. + */ +int igraph_adjlist_simplify(igraph_adjlist_t *al) { + long int i; + long int n = al->length; + igraph_vector_int_t mark; + igraph_vector_int_init(&mark, n); + IGRAPH_FINALLY(igraph_vector_int_destroy, &mark); + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = &al->adjs[i]; + long int j, l = igraph_vector_int_size(v); + VECTOR(mark)[i] = i + 1; + for (j = 0; j < l; /* nothing */) { + long int e = (long int) VECTOR(*v)[j]; + if (VECTOR(mark)[e] != i + 1) { + VECTOR(mark)[e] = i + 1; + j++; + } else { + VECTOR(*v)[j] = igraph_vector_int_tail(v); + igraph_vector_int_pop_back(v); + l--; + } + } + } + + igraph_vector_int_destroy(&mark); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +int igraph_adjlist_remove_duplicate(const igraph_t *graph, + igraph_adjlist_t *al) { + long int i, j, l, n, p; + igraph_vector_int_t *v; + + IGRAPH_WARNING( + "igraph_adjlist_remove_duplicate() is deprecated; use the constructor " + "arguments of igraph_adjlist_init() to specify whether you want loop " + "edges to appear once or twice in the adjacency list." + ); + + IGRAPH_UNUSED(graph); + + n = al->length; + for (i = 0; i < n; i++) { + v = &al->adjs[i]; + l = igraph_vector_int_size(v); + if (l > 0) { + p = 1; + for (j = 1; j < l; j++) { + long int e = (long int) VECTOR(*v)[j]; + /* Non-loop edges, and one end of loop edges are fine. */ + /* We assume that the vector is sorted and we also keep it sorted */ + if (e != i || VECTOR(*v)[j - 1] != e) { + VECTOR(*v)[p++] = e; + } + } + igraph_vector_int_resize(v, p); + } + } + + return 0; +} + +#ifndef USING_R +int igraph_adjlist_print(const igraph_adjlist_t *al) { + long int i; + long int n = al->length; + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = &al->adjs[i]; + igraph_vector_int_print(v); + } + return 0; +} +#endif + +int igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile) { + long int i; + long int n = al->length; + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = &al->adjs[i]; + igraph_vector_int_fprint(v, outfile); + } + return 0; +} + +#define ADJLIST_CANON_EDGE(from, to, directed) \ + do { \ + igraph_integer_t temp; \ + if((!directed) && from < to) { \ + temp = to; \ + to = from; \ + from = temp; \ + } \ + } while(0); + +igraph_bool_t igraph_adjlist_has_edge(igraph_adjlist_t* al, igraph_integer_t from, igraph_integer_t to, igraph_bool_t directed) { + igraph_vector_int_t* fromvec; + ADJLIST_CANON_EDGE(from, to, directed); + fromvec = igraph_adjlist_get(al, from); + return igraph_vector_int_binsearch2(fromvec, to); + +} + +int igraph_adjlist_replace_edge(igraph_adjlist_t* al, igraph_integer_t from, igraph_integer_t oldto, igraph_integer_t newto, igraph_bool_t directed) { + igraph_vector_int_t *oldfromvec, *newfromvec; + int err1, err2; + long int oldpos, newpos; + igraph_integer_t oldfrom = from, newfrom = from; + ADJLIST_CANON_EDGE(oldfrom, oldto, directed); + ADJLIST_CANON_EDGE(newfrom, newto, directed); + + oldfromvec = igraph_adjlist_get(al, oldfrom); + newfromvec = igraph_adjlist_get(al, newfrom); + + + err1 = igraph_vector_int_binsearch(oldfromvec, oldto, &oldpos); + err2 = igraph_vector_int_binsearch(newfromvec, newto, &newpos); + + /* oldfrom -> oldto should exist; newfrom -> newto should not. */ + if ((!err1) || err2) { + return 1; + } + + igraph_vector_int_remove(oldfromvec, oldpos); + if (oldfromvec == newfromvec && oldpos < newpos) { + --newpos; + } + IGRAPH_CHECK(igraph_vector_int_insert(newfromvec, newpos, newto)); + + return 0; + +} + +static int igraph_i_remove_loops_from_incidence_vector_in_place( + igraph_vector_int_t *v, const igraph_t *graph, igraph_loops_t loops +) { + long int i, length, eid, write_ptr; + igraph_vector_int_t *seen_loops = 0; + + /* In this function we make use of the fact that we are dealing with + * _incidence_ lists, and the only way for an edge ID to appear twice + * within an incidence list is if the edge is a loop edge; otherwise each + * element will be unique. + * + * Note that incidence vectors are not sorted by edge ID, so we need to + * look up the edges in the graph to decide whether they are loops or not. + * + * Also, it may be tempting to introduce a boolean in case of IGRAPH_LOOPS_ONCE, + * and flip it every time we see a loop to get rid of half of the occurrences, + * but the problem is that even if the same loop edge ID appears twice in + * the input list, they are not guaranteed to be next to each other; it + * may be the case that there are multiple loop edges, each edge appears + * twice, and we want to keep exactly one of them for each ID. That's why + * we have a "seen_loops" vector. + */ + + if (loops == IGRAPH_LOOPS_TWICE) { + /* Loop edges appear twice by default, nothing to do. */ + return IGRAPH_SUCCESS; + } + + length = igraph_vector_int_size(v); + if (length == 0) { + return IGRAPH_SUCCESS; + } + + if (loops == IGRAPH_LOOPS_ONCE) { + /* We need a helper vector */ + seen_loops = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_FINALLY(igraph_free, seen_loops); + IGRAPH_CHECK(igraph_vector_int_init(seen_loops, 0)); + IGRAPH_FINALLY(igraph_vector_int_destroy, seen_loops); + } else if (loops != IGRAPH_NO_LOOPS) { + IGRAPH_ERROR("Invalid value for 'loops' argument", IGRAPH_EINVAL); + } + + for (i = 0, write_ptr = 0; i < length; i++) { + eid = VECTOR(*v)[i]; + if (IGRAPH_FROM(graph, eid) == IGRAPH_TO(graph, eid)) { + /* Loop edge */ + if (seen_loops && !igraph_vector_int_contains(seen_loops, eid)) { + VECTOR(*v)[write_ptr++] = eid; + IGRAPH_CHECK(igraph_vector_int_push_back(seen_loops, eid)); + } + } else { + /* Not a loop edge */ + VECTOR(*v)[write_ptr++] = eid; + } + } + + /* Always succeeds since we never grow the vector */ + igraph_vector_int_resize(v, write_ptr); + + /* Destroy the helper vector */ + if (seen_loops) { + igraph_vector_int_destroy(seen_loops); + IGRAPH_FREE(seen_loops); + IGRAPH_FINALLY_CLEAN(2); + } + + return IGRAPH_SUCCESS; +} + +int igraph_inclist_remove_duplicate(const igraph_t *graph, igraph_inclist_t *il) { + long int i, n; + + IGRAPH_WARNING( + "igraph_inclist_remove_duplicate() is deprecated; use the constructor " + "arguments of igraph_inclist_init() to specify whether you want loop " + "edges to appear once or twice in the incidence list." + ); + + IGRAPH_UNUSED(graph); + + n = il->length; + for (i = 0; i < n; i++) { + IGRAPH_CHECK( + igraph_i_remove_loops_from_incidence_vector_in_place( + &il->incs[i], graph, IGRAPH_LOOPS_ONCE + ) + ); + } + + return 0; +} + +#ifndef USING_R +int igraph_inclist_print(const igraph_inclist_t *al) { + long int i; + long int n = al->length; + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = &al->incs[i]; + igraph_vector_int_print(v); + } + return 0; +} +#endif + +int igraph_inclist_fprint(const igraph_inclist_t *al, FILE *outfile) { + long int i; + long int n = al->length; + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = &al->incs[i]; + igraph_vector_int_fprint(v, outfile); + } + return 0; +} + +/** + * \function igraph_inclist_init + * \brief Initializes an incidence list. + * + * Creates a list of vectors containing the incident edges for all + * vertices. The incidence list is independent of the graph after + * creation, subsequent changes of the graph object do not update the + * incidence list, and changes to the incidence list do not update the + * graph. + * + * + * When \p mode is \c IGRAPH_IN or \c IGRAPH_OUT, each edge ID will appear + * in the incidence list \em once. When \p mode is \c IGRAPH_ALL, each edge ID + * will appear in the incidence list \em twice, once for the source vertex + * and once for the target edge. It also means that the edge IDs of loop edges + * may potentially appear \em twice for the \em same vertex. Use the \p loops + * argument to control whether this will be the case (\c IGRAPH_LOOPS_TWICE ) + * or not (\c IGRAPH_LOOPS_ONCE or \c IGRAPH_NO_LOOPS). + * + * \param graph The input graph. + * \param il Pointer to an uninitialized incidence list. + * \param mode Constant specifying whether incoming edges + * (IGRAPH_IN), outgoing edges (IGRAPH_OUT) or + * both (IGRAPH_ALL) to include in the incidence lists + * of directed graphs. It is ignored for undirected graphs. + * \param loops Specifies how to treat loop edges. IGRAPH_NO_LOOPS + * removes loop edges from the incidence list. IGRAPH_LOOPS_ONCE + * makes each loop edge appear only once in the incidence list of the + * corresponding vertex. IGRAPH_LOOPS_TWICE makes loop edges + * appear \em twice in the incidence list of the corresponding vertex, + * but only if the graph is undirected or mode is set to + * IGRAPH_ALL. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + */ +int igraph_inclist_init(const igraph_t *graph, + igraph_inclist_t *il, + igraph_neimode_t mode, + igraph_loops_t loops) { + igraph_integer_t i; + igraph_vector_t tmp; + + if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Cannot create incidence list view", IGRAPH_EINVMODE); + } + + igraph_vector_init(&tmp, 0); + IGRAPH_FINALLY(igraph_vector_destroy, &tmp); + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + il->length = igraph_vcount(graph); + il->incs = IGRAPH_CALLOC(il->length, igraph_vector_int_t); + if (il->incs == 0) { + IGRAPH_ERROR("Cannot create incidence list view", IGRAPH_ENOMEM); + } + + IGRAPH_FINALLY(igraph_inclist_destroy, il); + for (i = 0; i < il->length; i++) { + int j, n; + + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_incident(graph, &tmp, i, mode)); + + n = igraph_vector_size(&tmp); + IGRAPH_CHECK(igraph_vector_int_init(&il->incs[i], n)); + + for (j = 0; j < n; j++) { + VECTOR(il->incs[i])[j] = VECTOR(tmp)[j]; + } + + if (loops != IGRAPH_LOOPS_TWICE) { + IGRAPH_CHECK( + igraph_i_remove_loops_from_incidence_vector_in_place(&il->incs[i], graph, loops) + ); + } + } + + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +/** + * \function igraph_inclist_init_empty + * \brief Initializes an incidence list corresponding to an empty graph. + * + * This function essentially creates a list of empty vectors that may + * be treated as an incidence list for a graph with a given number of + * vertices. + * + * \param il Pointer to an uninitialized incidence list. + * \param n The number of vertices in the incidence list. + * \return Error code. + * + * Time complexity: O(|V|), linear in the number of vertices. + */ + +int igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n) { + long int i; + + il->length = n; + il->incs = IGRAPH_CALLOC(il->length, igraph_vector_int_t); + if (il->incs == 0) { + IGRAPH_ERROR("Cannot create incidence list view", IGRAPH_ENOMEM); + } + + IGRAPH_FINALLY(igraph_inclist_destroy, il); + for (i = 0; i < n; i++) { + IGRAPH_CHECK(igraph_vector_int_init(&il->incs[i], 0)); + } + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \function igraph_inclist_destroy + * \brief Frees all memory allocated for an incidence list. + * + * \param eal The incidence list to destroy. + * + * Time complexity: depends on memory management. + */ + +void igraph_inclist_destroy(igraph_inclist_t *il) { + long int i; + for (i = 0; i < il->length; i++) { + /* This works if some igraph_vector_int_t's are 0, + because igraph_vector_destroy can handle this. */ + igraph_vector_int_destroy(&il->incs[i]); + } + IGRAPH_FREE(il->incs); +} + +/** + * \function igraph_inclist_clear + * \brief Removes all edges from an incidence list. + * + * \param il The incidence list. + * + * Time complexity: depends on memory management, typically O(n), where n is + * the total number of elements in the incidence list. + */ +void igraph_inclist_clear(igraph_inclist_t *il) { + long int i; + for (i = 0; i < il->length; i++) { + igraph_vector_int_clear(&il->incs[i]); + } +} + +/** + * \function igraph_inclist_size + * \brief Returns the number of vertices in an incidence list. + * + * \param il The incidence list. + * \return The number of vertices in the incidence list. + * + * Time complexity: O(1). + */ +igraph_integer_t igraph_inclist_size(const igraph_inclist_t *il) { + return il->length; +} + +static int igraph_i_simplify_sorted_int_adjacency_vector_in_place( + igraph_vector_int_t *v, igraph_integer_t index, igraph_neimode_t mode, + igraph_loops_t loops, igraph_multiple_t multiple +) { + long int i, p = 0; + long int n = igraph_vector_int_size(v); + + if ( + multiple == IGRAPH_MULTIPLE && + ( + loops == IGRAPH_LOOPS_TWICE || + (loops == IGRAPH_LOOPS_ONCE && (mode == IGRAPH_IN || mode == IGRAPH_OUT)) + ) + ) { + /* nothing to simplify */ + return IGRAPH_SUCCESS; + } + + if (loops == IGRAPH_NO_LOOPS) { + if (multiple == IGRAPH_NO_MULTIPLE) { + /* We need to get rid of loops and multiple edges completely */ + for (i = 0; i < n; i++) { + if (VECTOR(*v)[i] != index && + (i == n - 1 || VECTOR(*v)[i + 1] != VECTOR(*v)[i])) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + p++; + } + } + } else { + /* We need to get rid of loops but keep multiple edges */ + for (i = 0; i < n; i++) { + if (VECTOR(*v)[i] != index) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + p++; + } + } + } + } else if (loops == IGRAPH_LOOPS_ONCE) { + if (multiple == IGRAPH_NO_MULTIPLE) { + /* We need to get rid of multiple edges completely (including + * multiple loop edges), but keep one edge from each loop edge */ + /* TODO(ntamas): think this through! */ + for (i = 0; i < n; i++) { + if (i == n - 1 || VECTOR(*v)[i + 1] != VECTOR(*v)[i]) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + p++; + } + } + } else { + /* We need to keep one edge from each loop edge and we don't need to + * touch multiple edges. Note that we can get here only if + * mode == IGRAPH_ALL; if mode was IGRAPH_IN or IGRAPH_OUT, we would + * have bailed out earlier */ + for (i = 0; i < n; i++) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + if (VECTOR(*v)[i] == index) { + /* this was a loop edge so if the next element is the same, we + * need to skip that */ + if (i < n-1 && VECTOR(*v)[i + 1] == index) { + i++; + } + } + p++; + } + } + } else if (loops == IGRAPH_LOOPS_TWICE && multiple == IGRAPH_NO_MULTIPLE) { + /* We need to get rid of multiple edges completely (including + * multiple loop edges), but keep both edge from each loop edge */ + /* TODO(ntamas): think this through! */ + for (i = 0; i < n; i++) { + if (i == n - 1 || VECTOR(*v)[i + 1] != VECTOR(*v)[i]) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + p++; + } else { + /* Current item is the same as the next one, but if it is a + * loop edge, then the first one or two items are okay. We need + * to keep one if mode == IGRAPH_IN or mode == IGRAPH_OUT, + * otherwise we need to keep two */ + if (VECTOR(*v)[i] == index) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + p++; + if (mode == IGRAPH_ALL) { + VECTOR(*v)[p] = VECTOR(*v)[i]; + p++; + } + /* skip over all the items corresponding to the loop edges */ + while (i < n && VECTOR(*v)[i] == index) { + i++; + } + i--; /* because the for loop also increases i by 1 */ + } + } + } + } else { + /* TODO; we don't use this combination yet */ + return IGRAPH_UNIMPLEMENTED; + } + + /* always succeeds since we are never growing the vector */ + igraph_vector_int_resize(v, p); + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_lazy_adjlist_init + * \brief Initialized a lazy adjacency list. + * + * Create a lazy adjacency list for vertices. This function only + * allocates some memory for storing the vectors of an adjacency list, + * but the neighbor vertices are not queried, only at the \ref + * igraph_lazy_adjlist_get() calls. + * \param graph The input graph. + * \param al Pointer to an uninitialized adjacency list object. + * \param mode Constant, it gives whether incoming edges + * (IGRAPH_IN), outgoing edges + * (IGRPAH_OUT) or both types of edges + * (IGRAPH_ALL) are considered. It is ignored for + * undirected graphs. + * \param simplify Constant, it gives whether to simplify the vectors + * in the adjacency list (IGRAPH_SIMPLIFY) or not + * (IGRAPH_DONT_SIMPLIFY). + * \return Error code. + * + * Time complexity: O(|V|), the number of vertices, possibly, but + * depends on the underlying memory management too. + */ + +int igraph_lazy_adjlist_init(const igraph_t *graph, + igraph_lazy_adjlist_t *al, + igraph_neimode_t mode, + igraph_loops_t loops, + igraph_multiple_t multiple) { + if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Cannor create lazy adjacency list view", IGRAPH_EINVMODE); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + al->mode = mode; + al->loops = loops; + al->multiple = multiple; + al->graph = graph; + + al->length = igraph_vcount(graph); + al->adjs = IGRAPH_CALLOC(al->length, igraph_vector_int_t*); + + if (al->adjs == 0) { + IGRAPH_ERROR("Cannot create lazy adjacency list view", IGRAPH_ENOMEM); + } + + IGRAPH_FINALLY(igraph_free, al->adjs); + + IGRAPH_CHECK(igraph_vector_init(&al->dummy, 0)); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_lazy_adjlist_destroy + * \brief Deallocate a lazt adjacency list. + * + * Free all allocated memory for a lazy adjacency list. + * \param al The adjacency list to deallocate. + * + * Time complexity: depends on the memory management. + */ + +void igraph_lazy_adjlist_destroy(igraph_lazy_adjlist_t *al) { + igraph_lazy_adjlist_clear(al); + igraph_vector_destroy(&al->dummy); + IGRAPH_FREE(al->adjs); +} + +/** + * \function igraph_lazy_adjlist_clear + * \brief Removes all edges from a lazy adjacency list. + * + * \param al The lazy adjacency list. + * Time complexity: depends on memory management, typically O(n), where n is + * the total number of elements in the adjacency list. + */ +void igraph_lazy_adjlist_clear(igraph_lazy_adjlist_t *al) { + long int i, n = al->length; + for (i = 0; i < n; i++) { + if (al->adjs[i] != 0) { + igraph_vector_int_destroy(al->adjs[i]); + IGRAPH_FREE(al->adjs[i]); + } + } +} + +/** + * \function igraph_lazy_adjlist_size + * \brief Returns the number of vertices in a lazy adjacency list. + * + * \param al The lazy adjacency list. + * \return The number of vertices in the lazy adjacency list. + * + * Time complexity: O(1). + */ +igraph_integer_t igraph_lazy_adjlist_size(const igraph_lazy_adjlist_t *al) { + return al->length; +} + +igraph_vector_int_t *igraph_i_lazy_adjlist_get_real(igraph_lazy_adjlist_t *al, + igraph_integer_t pno) { + igraph_integer_t no = pno; + long int i, n; + int ret; + + if (al->adjs[no] == 0) { + ret = igraph_neighbors(al->graph, &al->dummy, no, al->mode); + if (ret != 0) { + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return 0; + } + + al->adjs[no] = IGRAPH_CALLOC(1, igraph_vector_int_t); + if (al->adjs[no] == 0) { + igraph_error("Lazy adjlist failed", IGRAPH_FILE_BASENAME, __LINE__, + IGRAPH_ENOMEM); + return 0; + } + + n = igraph_vector_size(&al->dummy); + ret = igraph_vector_int_init(al->adjs[no], n); + if (ret != 0) { + IGRAPH_FREE(al->adjs[no]); + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return 0; + } + + for (i = 0; i < n; i++) { + VECTOR(*al->adjs[no])[i] = VECTOR(al->dummy)[i]; + } + + ret = igraph_i_simplify_sorted_int_adjacency_vector_in_place( + al->adjs[no], no, al->mode, al->loops, al->multiple + ); + if (ret != 0) { + igraph_vector_int_destroy(al->adjs[no]); + IGRAPH_FREE(al->adjs[no]); + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return 0; + } + } + + return al->adjs[no]; +} + +/** + * \function igraph_lazy_inclist_init + * \brief Initializes a lazy incidence list of edges. + * + * Create a lazy incidence list for edges. This function only + * allocates some memory for storing the vectors of an incidence list, + * but the incident edges are not queried, only when \ref + * igraph_lazy_inclist_get() is called. + * + * + * When \p mode is \c IGRAPH_IN or \c IGRAPH_OUT, each edge ID will appear + * in the incidence list \em once. When \p mode is \c IGRAPH_ALL, each edge ID + * will appear in the incidence list \em twice, once for the source vertex + * and once for the target edge. It also means that the edge IDs of loop edges + * will appear \em twice for the \em same vertex. + * + * \param graph The input graph. + * \param al Pointer to an uninitialized incidence list. + * \param mode Constant, it gives whether incoming edges + * (IGRAPH_IN), outgoing edges + * (IGRAPH_OUT) or both types of edges + * (IGRAPH_ALL) are considered. It is ignored for + * undirected graphs. + * \param loops Specifies how to treat loop edges. IGRAPH_NO_LOOPS + * removes loop edges from the incidence list. IGRAPH_LOOPS_ONCE + * makes each loop edge appear only once in the incidence list of the + * corresponding vertex. IGRAPH_LOOPS_TWICE makes loop edges + * appear \em twice in the incidence list of the corresponding vertex, + * but only if the graph is undirected or mode is set to + * IGRAPH_ALL. + * \return Error code. + * + * Time complexity: O(|V|), the number of vertices, possibly. But it + * also depends on the underlying memory management. + */ + +int igraph_lazy_inclist_init(const igraph_t *graph, + igraph_lazy_inclist_t *il, + igraph_neimode_t mode, + igraph_loops_t loops) { + + if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Cannot create lazy incidence list view", IGRAPH_EINVMODE); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + il->graph = graph; + il->loops = loops; + il->mode = mode; + + il->length = igraph_vcount(graph); + il->incs = IGRAPH_CALLOC(il->length, igraph_vector_int_t*); + if (il->incs == 0) { + + IGRAPH_ERROR("Cannot create lazy incidence list view", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, il->incs); + + IGRAPH_CHECK(igraph_vector_init(&il->dummy, 0)); + IGRAPH_FINALLY_CLEAN(1); + + return 0; + +} + +/** + * \function igraph_lazy_inclist_destroy + * \brief Deallocates a lazy incidence list. + * + * Frees all allocated memory for a lazy incidence list. + * \param al The incidence list to deallocate. + * + * Time complexity: depends on memory management. + */ + +void igraph_lazy_inclist_destroy(igraph_lazy_inclist_t *il) { + igraph_lazy_inclist_clear(il); + igraph_vector_destroy(&il->dummy); + IGRAPH_FREE(il->incs); +} + +/** + * \function igraph_lazy_inclist_clear + * \brief Removes all edges from a lazy incidence list. + * + * \param il The lazy incidence list. + * + * Time complexity: depends on memory management, typically O(n), where n is + * the total number of elements in the incidence list. + */ +void igraph_lazy_inclist_clear(igraph_lazy_inclist_t *il) { + long int i, n = il->length; + for (i = 0; i < n; i++) { + if (il->incs[i] != 0) { + igraph_vector_int_destroy(il->incs[i]); + IGRAPH_FREE(il->incs[i]); + } + } +} + +/** + * \function igraph_lazy_inclist_size + * \brief Returns the number of vertices in a lazy incidence list. + * + * \param il The lazy incidence list. + * \return The number of vertices in the lazy incidence list. + * + * Time complexity: O(1). + */ +igraph_integer_t igraph_lazy_inclist_size(const igraph_lazy_inclist_t *il) { + return il->length; +} + +igraph_vector_int_t *igraph_i_lazy_inclist_get_real(igraph_lazy_inclist_t *il, + igraph_integer_t pno) { + igraph_integer_t no = pno; + int ret; + long int i, n; + + if (il->incs[no] == 0) { + ret = igraph_incident(il->graph, &il->dummy, no, il->mode); + if (ret != 0) { + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return 0; + } + + il->incs[no] = IGRAPH_CALLOC(1, igraph_vector_int_t); + if (il->incs[no] == 0) { + igraph_error("Lazy incidence list query failed", IGRAPH_FILE_BASENAME, __LINE__, + IGRAPH_ENOMEM); + return 0; + } + + n = igraph_vector_size(&il->dummy); + ret = igraph_vector_int_init(il->incs[no], n); + if (ret != 0) { + IGRAPH_FREE(il->incs[no]); + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return 0; + } + + for (i = 0; i < n; i++) { + VECTOR(*il->incs[no])[i] = VECTOR(il->dummy)[i]; + } + + if (il->loops != IGRAPH_LOOPS_TWICE) { + ret = igraph_i_remove_loops_from_incidence_vector_in_place(il->incs[no], il->graph, il->loops); + if (ret != 0) { + igraph_vector_int_destroy(il->incs[no]); + IGRAPH_FREE(il->incs[no]); + return 0; + } + } + } + + return il->incs[no]; +} diff --git a/src/rigraph/core/graph/attributes.c b/src/rigraph/core/graph/attributes.c new file mode 100644 index 0000000..e1f53d9 --- /dev/null +++ b/src/rigraph/core/graph/attributes.c @@ -0,0 +1,498 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_attributes.h" +#include "igraph_memory.h" + +#include "attributes.h" + +#include "config.h" + +#include +#include + +/* Should you ever want to have a thread-local attribute handler table, prepend + * IGRAPH_THREAD_LOCAL to the following declaration */ +igraph_attribute_table_t *igraph_i_attribute_table = 0; + +int igraph_i_attribute_init(igraph_t *graph, void *attr) { + graph->attr = 0; + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->init(graph, attr); + } else { + return 0; + } +} + +void igraph_i_attribute_destroy(igraph_t *graph) { + if (igraph_i_attribute_table) { + igraph_i_attribute_table->destroy(graph); + } +} + +int igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga, + igraph_bool_t va, igraph_bool_t ea) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->copy(to, from, ga, va, ea); + } else { + return 0; + } +} + +int igraph_i_attribute_add_vertices(igraph_t *graph, long int nv, void *attr) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->add_vertices(graph, nv, attr); + } else { + return 0; + } +} + +int igraph_i_attribute_permute_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx) { + + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->permute_vertices(graph, newgraph, idx); + } else { + return 0; + } +} + +int igraph_i_attribute_combine_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_ptr_t *merges, + const igraph_attribute_combination_t *comb) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->combine_vertices(graph, newgraph, + merges, + comb); + } else { + return 0; + } +} + +int igraph_i_attribute_add_edges(igraph_t *graph, + const igraph_vector_t *edges, void *attr) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->add_edges(graph, edges, attr); + } else { + return 0; + } +} + +int igraph_i_attribute_permute_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->permute_edges(graph, newgraph, idx); + } else { + return 0; + } +} + +int igraph_i_attribute_combine_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_ptr_t *merges, + const igraph_attribute_combination_t *comb) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->combine_edges(graph, newgraph, + merges, + comb); + } else { + return 0; + } +} + +int igraph_i_attribute_get_info(const igraph_t *graph, + igraph_strvector_t *gnames, + igraph_vector_t *gtypes, + igraph_strvector_t *vnames, + igraph_vector_t *vtypes, + igraph_strvector_t *enames, + igraph_vector_t *etypes) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->get_info(graph, gnames, gtypes, + vnames, vtypes, + enames, etypes); + } else { + return 0; + } +} + +igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph, + igraph_attribute_elemtype_t type, + const char *name) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->has_attr(graph, type, name); + } else { + return 0; + } +} + +int igraph_i_attribute_gettype(const igraph_t *graph, + igraph_attribute_type_t *type, + igraph_attribute_elemtype_t elemtype, + const char *name) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->gettype(graph, type, elemtype, name); + } else { + return 0; + } + +} + +int igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_t *value) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->get_numeric_graph_attr(graph, name, value); + } else { + return 0; + } +} + +int igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_t *value) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->get_numeric_vertex_attr(graph, name, vs, value); + } else { + return 0; + } +} + +int igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_t *value) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->get_numeric_edge_attr(graph, name, es, value); + } else { + return 0; + } +} + +int igraph_i_attribute_get_string_graph_attr(const igraph_t *graph, + const char *name, + igraph_strvector_t *value) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->get_string_graph_attr(graph, name, value); + } else { + return 0; + } +} + +int igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_strvector_t *value) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->get_string_vertex_attr(graph, name, vs, value); + } else { + return 0; + } +} + +int igraph_i_attribute_get_string_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_strvector_t *value) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->get_string_edge_attr(graph, name, es, value); + } else { + return 0; + } +} + +int igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_bool_t *value) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->get_bool_graph_attr(graph, name, value); + } else { + return 0; + } +} + +int igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_bool_t *value) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->get_bool_vertex_attr(graph, name, vs, value); + } else { + return 0; + } +} + +int igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_bool_t *value) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->get_bool_edge_attr(graph, name, es, value); + } else { + return 0; + } +} + +/** + * \function igraph_set_attribute_table + * \brief Attach an attribute table. + * + * This function attaches attribute handling code to the igraph library. + * Note that the attribute handler table is \em not thread-local even if + * igraph is compiled in thread-local mode. In the vast majority of cases, + * this is not a significant restriction. + * + * \param table Pointer to an \ref igraph_attribute_table_t object + * containing the functions for attribute manipulation. Supply \c + * NULL here if you don't want attributes. + * \return Pointer to the old attribute handling table. + * + * Time complexity: O(1). + */ + +igraph_attribute_table_t * +igraph_set_attribute_table(const igraph_attribute_table_t * table) { + igraph_attribute_table_t *old = igraph_i_attribute_table; + igraph_i_attribute_table = (igraph_attribute_table_t*) table; + return old; +} + +igraph_attribute_table_t * +igraph_i_set_attribute_table(const igraph_attribute_table_t * table) { + IGRAPH_WARNING("igraph_i_set_attribute_table is deprecated, use igraph_set_attribute_table."); + return igraph_set_attribute_table(table); +} + +igraph_bool_t igraph_has_attribute_table() { + return igraph_i_attribute_table != 0; +} + + +/** + * \function igraph_attribute_combination_init + * \brief Initialize attribute combination list. + * + * \param comb The uninitialized attribute combination list. + * \return Error code. + * + * Time complexity: O(1) + */ +int igraph_attribute_combination_init(igraph_attribute_combination_t *comb) { + IGRAPH_CHECK(igraph_vector_ptr_init(&comb->list, 0)); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_attribute_combination_destroy + * \brief Destroy attribute combination list. + * + * \param comb The attribute combination list. + * + * Time complexity: O(n), where n is the number of records in the + attribute combination list. + */ +void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb) { + long int i, n = igraph_vector_ptr_size(&comb->list); + for (i = 0; i < n; i++) { + igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[i]; + if (rec->name) { + IGRAPH_FREE(rec->name); + } + IGRAPH_FREE(rec); + } + igraph_vector_ptr_destroy(&comb->list); +} + +/** + * \function igraph_attribute_combination_add + * \brief Add combination record to attribute combination list. + * + * \param comb The attribute combination list. + * \param name The name of the attribute. If the name already exists + * the attribute combination record will be replaced. + * Use NULL to add a default combination record for all + * atributes not in the list. + * \param type The type of the attribute combination. See \ref + * igraph_attribute_combination_type_t for the options. + * \param func Function to be used if \p type is + * \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION. + * \return Error code. + * + * Time complexity: O(n), where n is the number of current attribute + * combinations. + */ +int igraph_attribute_combination_add(igraph_attribute_combination_t *comb, + const char *name, + igraph_attribute_combination_type_t type, + igraph_function_pointer_t func) { + long int i, n = igraph_vector_ptr_size(&comb->list); + + /* Search, in case it is already there */ + for (i = 0; i < n; i++) { + igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i]; + const char *n = r->name; + if ( (!name && !n) || + (name && n && !strcmp(n, name)) ) { + r->type = type; + r->func = func; + break; + } + } + + if (i == n) { + /* This is a new attribute name */ + igraph_attribute_combination_record_t *rec = + IGRAPH_CALLOC(1, igraph_attribute_combination_record_t); + + if (!rec) { + IGRAPH_ERROR("Cannot create attribute combination data", + IGRAPH_ENOMEM); + } + if (!name) { + rec->name = NULL; + } else { + rec->name = strdup(name); + } + rec->type = type; + rec->func = func; + + IGRAPH_CHECK(igraph_vector_ptr_push_back(&comb->list, rec)); + + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_attribute_combination_remove + * \brief Remove a record from an attribute combination list. + * + * \param comb The attribute combination list. + * \param name The attribute name of the attribute combination record + * to remove. It will be ignored if the named attribute + * does not exist. It can be NULL to remove the default + * combination record. + * \return Error code. This currently always returns IGRAPH_SUCCESS. + * + * Time complexity: O(n), where n is the number of records in the attribute + combination list. + */ +int igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, + const char *name) { + long int i, n = igraph_vector_ptr_size(&comb->list); + + /* Search, in case it is already there */ + for (i = 0; i < n; i++) { + igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i]; + const char *n = r->name; + if ( (!name && !n) || + (name && n && !strcmp(n, name)) ) { + break; + } + } + + if (i != n) { + igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i]; + if (r->name) { + IGRAPH_FREE(r->name); + } + IGRAPH_FREE(r); + igraph_vector_ptr_remove(&comb->list, i); + } else { + /* It is not there, we don't do anything */ + } + + return IGRAPH_SUCCESS; +} + +int igraph_attribute_combination_query(const igraph_attribute_combination_t *comb, + const char *name, + igraph_attribute_combination_type_t *type, + igraph_function_pointer_t *func) { + long int i, def = -1, len = igraph_vector_ptr_size(&comb->list); + + for (i = 0; i < len; i++) { + igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[i]; + const char *n = rec->name; + if ( (!name && !n) || + (name && n && !strcmp(n, name)) ) { + *type = rec->type; + *func = rec->func; + return 0; + } + if (!n) { + def = i; + } + } + + if (def == -1) { + /* Did not find anything */ + *type = IGRAPH_ATTRIBUTE_COMBINE_DEFAULT; + *func = 0; + } else { + igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[def]; + *type = rec->type; + *func = rec->func; + } + + return 0; +} + +int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...) { + + va_list ap; + + IGRAPH_CHECK(igraph_attribute_combination_init(comb)); + + va_start(ap, comb); + while (1) { + igraph_function_pointer_t func = 0; + igraph_attribute_combination_type_t type; + const char *name; + + name = va_arg(ap, const char *); + + if (name == IGRAPH_NO_MORE_ATTRIBUTES) { + break; + } + + type = (igraph_attribute_combination_type_t)va_arg(ap, int); + if (type == IGRAPH_ATTRIBUTE_COMBINE_FUNCTION) { + func = va_arg(ap, igraph_function_pointer_t); + } + + if (strlen(name) == 0) { + name = 0; + } + + IGRAPH_CHECK(igraph_attribute_combination_add(comb, name, type, func)); + } + + va_end(ap); + + return 0; +} diff --git a/src/rigraph/core/graph/attributes.h b/src/rigraph/core/graph/attributes.h new file mode 100644 index 0000000..d0c8426 --- /dev/null +++ b/src/rigraph/core/graph/attributes.h @@ -0,0 +1,112 @@ +/* + IGraph library. + Copyright (C) 2021 The igraph development team + + 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 2 of the License, or + (at your option) 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 IGRAPH_GRAPH_ATTRIBUTES_H +#define IGRAPH_GRAPH_ATTRIBUTES_H + +#include "igraph_attributes.h" +#include "igraph_strvector.h" +#include "igraph_types.h" +#include "igraph_vector_ptr.h" + +#define IGRAPH_I_ATTRIBUTE_DESTROY(graph) \ + do {if ((graph)->attr) igraph_i_attribute_destroy(graph);} while(0) +#define IGRAPH_I_ATTRIBUTE_COPY(to,from,ga,va,ea) do { \ + int igraph_i_ret2=0; \ + if ((from)->attr) { \ + IGRAPH_CHECK(igraph_i_ret2=igraph_i_attribute_copy((to),(from),(ga),(va),(ea))); \ + } else { \ + (to)->attr = 0; \ + } \ + if (igraph_i_ret2 != 0) { \ + IGRAPH_ERROR("", igraph_i_ret2); \ + } \ + } while(0) + +int igraph_i_attribute_init(igraph_t *graph, void *attr); +void igraph_i_attribute_destroy(igraph_t *graph); +int igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, + igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea); +int igraph_i_attribute_add_vertices(igraph_t *graph, long int nv, void *attr); +int igraph_i_attribute_permute_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx); +int igraph_i_attribute_combine_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_ptr_t *merges, + const igraph_attribute_combination_t *comb); +int igraph_i_attribute_add_edges(igraph_t *graph, + const igraph_vector_t *edges, void *attr); +int igraph_i_attribute_permute_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx); +int igraph_i_attribute_combine_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_ptr_t *merges, + const igraph_attribute_combination_t *comb); + +int igraph_i_attribute_get_info(const igraph_t *graph, + igraph_strvector_t *gnames, + igraph_vector_t *gtypes, + igraph_strvector_t *vnames, + igraph_vector_t *vtypes, + igraph_strvector_t *enames, + igraph_vector_t *etypes); +igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph, + igraph_attribute_elemtype_t type, + const char *name); +int igraph_i_attribute_gettype(const igraph_t *graph, + igraph_attribute_type_t *type, + igraph_attribute_elemtype_t elemtype, + const char *name); + +int igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_t *value); +int igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_t *value); +int igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_t *value); +int igraph_i_attribute_get_string_graph_attr(const igraph_t *graph, + const char *name, + igraph_strvector_t *value); +int igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_strvector_t *value); +int igraph_i_attribute_get_string_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_strvector_t *value); +int igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_bool_t *value); +int igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_bool_t *value); +int igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_bool_t *value); + +#endif /* IGRAPH_GRAPH_ATTRIBUTES_H */ diff --git a/src/rigraph/core/graph/basic_query.c b/src/rigraph/core/graph/basic_query.c new file mode 100644 index 0000000..34c4ef4 --- /dev/null +++ b/src/rigraph/core/graph/basic_query.c @@ -0,0 +1,63 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_datatype.h" +#include "igraph_types.h" +#include "igraph_interface.h" +#include "igraph_structural.h" + +/** + * \ingroup structural + * \function igraph_are_connected + * \brief Decides whether two vertices are connected + * + * \param graph The graph object. + * \param v1 The first vertex. + * \param v2 The second vertex. + * \param res Boolean, \c TRUE if there is an edge from + * \p v1 to \p v2, \c FALSE otherwise. + * \return The error code \c IGRAPH_EINVVID is returned if an invalid + * vertex ID is given. + * + * The function is of course symmetric for undirected graphs. + * + * + * Time complexity: O( min(log(d1), log(d2)) ), + * d1 is the (out-)degree of \p v1 and d2 is the (in-)degree of \p v2. + */ +int igraph_are_connected(const igraph_t *graph, + igraph_integer_t v1, igraph_integer_t v2, + igraph_bool_t *res) { + + long int nov = igraph_vcount(graph); + igraph_integer_t eid = -1; + + if (v1 < 0 || v2 < 0 || v1 > nov - 1 || v2 > nov - 1) { + IGRAPH_ERROR("are connected", IGRAPH_EINVVID); + } + + igraph_get_eid(graph, &eid, v1, v2, /*directed=*/1, /*error=*/ 0); + *res = (eid >= 0); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/graph/cattributes.c b/src/rigraph/core/graph/cattributes.c new file mode 100644 index 0000000..9520a2f --- /dev/null +++ b/src/rigraph/core/graph/cattributes.c @@ -0,0 +1,4241 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_attributes.h" +#include "igraph_memory.h" +#include "../core/math.h" +#include "igraph_interface.h" +#include "igraph_random.h" + +#include + +/* An attribute is either a numeric vector (vector_t) or a string + vector (strvector_t). The attribute itself is stored in a + struct igraph_attribute_record_t, there is one such object for each + attribute. The igraph_t has a pointer to an array of three + vector_ptr_t's which contains pointers to + igraph_i_cattribute_t's. Graph attributes are first, then vertex + and edge attributes. */ + +static igraph_bool_t igraph_i_cattribute_find(const igraph_vector_ptr_t *ptrvec, + const char *name, long int *idx) { + long int i, n = igraph_vector_ptr_size(ptrvec); + igraph_bool_t l = 0; + for (i = 0; !l && i < n; i++) { + igraph_attribute_record_t *rec = VECTOR(*ptrvec)[i]; + l = !strcmp(rec->name, name); + } + if (idx) { + *idx = i - 1; + } + return l; +} + +typedef struct igraph_i_cattributes_t { + igraph_vector_ptr_t gal; + igraph_vector_ptr_t val; + igraph_vector_ptr_t eal; +} igraph_i_cattributes_t; + +static int igraph_i_cattributes_copy_attribute_record(igraph_attribute_record_t **newrec, + const igraph_attribute_record_t *rec) { + igraph_vector_t *num, *newnum; + igraph_strvector_t *str, *newstr; + + *newrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + if (!(*newrec)) { + IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, *newrec); + (*newrec)->type = rec->type; + (*newrec)->name = strdup(rec->name); + if (!(*newrec)->name) { + IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (void*)(*newrec)->name); + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + num = (igraph_vector_t *)rec->value; + newnum = IGRAPH_CALLOC(1, igraph_vector_t); + if (!newnum) { + IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newnum); + IGRAPH_CHECK(igraph_vector_copy(newnum, num)); + IGRAPH_FINALLY(igraph_vector_destroy, newnum); + (*newrec)->value = newnum; + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + str = (igraph_strvector_t*)rec->value; + newstr = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newstr); + IGRAPH_CHECK(igraph_strvector_copy(newstr, str)); + IGRAPH_FINALLY(igraph_strvector_destroy, newstr); + (*newrec)->value = newstr; + } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_vector_bool_t *log = (igraph_vector_bool_t*) rec->value; + igraph_vector_bool_t *newlog = IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (!newlog) { + IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newlog); + IGRAPH_CHECK(igraph_vector_bool_copy(newlog, log)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newlog); + (*newrec)->value = newlog; + } + + IGRAPH_FINALLY_CLEAN(4); + return 0; +} + + +static int igraph_i_cattribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) { + igraph_attribute_record_t *attr_rec; + long int i, n; + igraph_i_cattributes_t *nattr; + + n = attr ? igraph_vector_ptr_size(attr) : 0; + + nattr = IGRAPH_CALLOC(1, igraph_i_cattributes_t); + if (!nattr) { + IGRAPH_ERROR("Can't init attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, nattr); + + IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->gal, n)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &nattr->gal); + IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->val, 0)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &nattr->val); + IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->eal, 0)); + IGRAPH_FINALLY_CLEAN(3); + + for (i = 0; i < n; i++) { + IGRAPH_CHECK(igraph_i_cattributes_copy_attribute_record( + &attr_rec, VECTOR(*attr)[i])); + VECTOR(nattr->gal)[i] = attr_rec; + } + + graph->attr = nattr; + + return 0; +} + +static void igraph_i_cattribute_destroy(igraph_t *graph) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *als[3] = { &attr->gal, &attr->val, &attr->eal }; + long int i, n, a; + igraph_vector_t *num; + igraph_strvector_t *str; + igraph_vector_bool_t *boolvec; + igraph_attribute_record_t *rec; + for (a = 0; a < 3; a++) { + n = igraph_vector_ptr_size(als[a]); + for (i = 0; i < n; i++) { + rec = VECTOR(*als[a])[i]; + if (rec) { + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + num = (igraph_vector_t*)rec->value; + igraph_vector_destroy(num); + igraph_free(num); + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + str = (igraph_strvector_t*)rec->value; + igraph_strvector_destroy(str); + igraph_free(str); + } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { + boolvec = (igraph_vector_bool_t*)rec->value; + igraph_vector_bool_destroy(boolvec); + igraph_free(boolvec); + } + igraph_free((char*)rec->name); + igraph_free(rec); + } + } + } + igraph_vector_ptr_destroy(&attr->gal); + igraph_vector_ptr_destroy(&attr->val); + igraph_vector_ptr_destroy(&attr->eal); + igraph_free(graph->attr); + graph->attr = 0; +} + +/* Almost the same as destroy, but we might have null pointers */ + +static void igraph_i_cattribute_copy_free(igraph_i_cattributes_t *attr) { + igraph_vector_ptr_t *als[3] = { &attr->gal, &attr->val, &attr->eal }; + long int i, n, a; + igraph_vector_t *num; + igraph_strvector_t *str; + igraph_vector_bool_t *boolvec; + igraph_attribute_record_t *rec; + for (a = 0; a < 3; a++) { + n = igraph_vector_ptr_size(als[a]); + for (i = 0; i < n; i++) { + rec = VECTOR(*als[a])[i]; + if (!rec) { + continue; + } + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + num = (igraph_vector_t*)rec->value; + igraph_vector_destroy(num); + igraph_free(num); + } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { + boolvec = (igraph_vector_bool_t*)rec->value; + igraph_vector_bool_destroy(boolvec); + igraph_free(boolvec); + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + str = (igraph_strvector_t*)rec->value; + igraph_strvector_destroy(str); + igraph_free(str); + } + igraph_free((char*)rec->name); + igraph_free(rec); + } + } +} + +/* No reference counting here. If you use attributes in C you should + know what you're doing. */ + +static int igraph_i_cattribute_copy(igraph_t *to, const igraph_t *from, + igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea) { + igraph_i_cattributes_t *attrfrom = from->attr, *attrto; + igraph_vector_ptr_t *alto[3], *alfrom[3] = { &attrfrom->gal, &attrfrom->val, + &attrfrom->eal + }; + long int i, n, a; + igraph_bool_t copy[3] = { ga, va, ea }; + to->attr = attrto = IGRAPH_CALLOC(1, igraph_i_cattributes_t); + if (!attrto) { + IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, attrto); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->gal, 0); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->val, 0); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->eal, 0); + IGRAPH_FINALLY_CLEAN(3); + IGRAPH_FINALLY(igraph_i_cattribute_copy_free, attrto); + + alto[0] = &attrto->gal; alto[1] = &attrto->val; alto[2] = &attrto->eal; + for (a = 0; a < 3; a++) { + if (copy[a]) { + n = igraph_vector_ptr_size(alfrom[a]); + IGRAPH_CHECK(igraph_vector_ptr_resize(alto[a], n)); + igraph_vector_ptr_null(alto[a]); + for (i = 0; i < n; i++) { + igraph_attribute_record_t *newrec; + IGRAPH_CHECK(igraph_i_cattributes_copy_attribute_record(&newrec, + VECTOR(*alfrom[a])[i])); + VECTOR(*alto[a])[i] = newrec; + } + } + } + + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +static int igraph_i_cattribute_add_vertices(igraph_t *graph, long int nv, + igraph_vector_ptr_t *nattr) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int length = igraph_vector_ptr_size(val); + long int nattrno = nattr == NULL ? 0 : igraph_vector_ptr_size(nattr); + long int origlen = igraph_vcount(graph) - nv; + long int newattrs = 0, i; + igraph_vector_t news; + + /* First add the new attributes if any */ + newattrs = 0; + IGRAPH_VECTOR_INIT_FINALLY(&news, 0); + for (i = 0; i < nattrno; i++) { + igraph_attribute_record_t *nattr_entry = VECTOR(*nattr)[i]; + const char *nname = nattr_entry->name; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(val, nname, &j); + if (!l) { + newattrs++; + IGRAPH_CHECK(igraph_vector_push_back(&news, i)); + } else { + /* check types */ + if (nattr_entry->type != + ((igraph_attribute_record_t*)VECTOR(*val)[j])->type) { + IGRAPH_ERROR("You cannot mix attribute types", IGRAPH_EINVAL); + } + } + } + + /* Add NA/empty string vectors for the existing vertices */ + if (newattrs != 0) { + for (i = 0; i < newattrs; i++) { + igraph_attribute_record_t *tmp = VECTOR(*nattr)[(long int)VECTOR(news)[i]]; + igraph_attribute_record_t *newrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_attribute_type_t type = tmp->type; + if (!newrec) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newrec); + newrec->type = type; + newrec->name = strdup(tmp->name); + if (!newrec->name) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)newrec->name); + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *newnum = IGRAPH_CALLOC(1, igraph_vector_t); + if (!newnum) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newnum); + IGRAPH_VECTOR_INIT_FINALLY(newnum, origlen); + newrec->value = newnum; + igraph_vector_fill(newnum, IGRAPH_NAN); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *newstr = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newstr); + IGRAPH_STRVECTOR_INIT_FINALLY(newstr, origlen); + newrec->value = newstr; + } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_vector_bool_t *newbool = IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (!newbool) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newbool); + IGRAPH_CHECK(igraph_vector_bool_init(newbool, origlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool); + newrec->value = newbool; + igraph_vector_bool_fill(newbool, 0); + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, newrec)); + IGRAPH_FINALLY_CLEAN(4); + } + length = igraph_vector_ptr_size(val); + } + + /* Now append the new values */ + for (i = 0; i < length; i++) { + igraph_attribute_record_t *oldrec = VECTOR(*val)[i]; + igraph_attribute_record_t *newrec = 0; + const char *name = oldrec->name; + long int j = -1; + igraph_bool_t l = 0; + if (nattr) { + l = igraph_i_cattribute_find(nattr, name, &j); + } + if (l) { + /* This attribute is present in nattr */ + igraph_vector_t *oldnum, *newnum; + igraph_strvector_t *oldstr, *newstr; + igraph_vector_bool_t *oldbool, *newbool; + newrec = VECTOR(*nattr)[j]; + oldnum = (igraph_vector_t*)oldrec->value; + newnum = (igraph_vector_t*)newrec->value; + oldstr = (igraph_strvector_t*)oldrec->value; + newstr = (igraph_strvector_t*)newrec->value; + oldbool = (igraph_vector_bool_t*)oldrec->value; + newbool = (igraph_vector_bool_t*)newrec->value; + if (oldrec->type != newrec->type) { + IGRAPH_ERROR("Attribute types do not match", IGRAPH_EINVAL); + } + switch (oldrec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + if (nv != igraph_vector_size(newnum)) { + IGRAPH_ERROR("Invalid numeric attribute length", IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_vector_append(oldnum, newnum)); + break; + case IGRAPH_ATTRIBUTE_STRING: + if (nv != igraph_strvector_size(newstr)) { + IGRAPH_ERROR("Invalid string attribute length", IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_strvector_append(oldstr, newstr)); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + if (nv != igraph_vector_bool_size(newbool)) { + IGRAPH_ERROR("Invalid Boolean attribute length", IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_vector_bool_append(oldbool, newbool)); + break; + default: + IGRAPH_WARNING("Invalid attribute type"); + break; + } + } else { + /* No such attribute, append NA's */ + igraph_vector_t *oldnum = (igraph_vector_t *)oldrec->value; + igraph_strvector_t *oldstr = (igraph_strvector_t*)oldrec->value; + igraph_vector_bool_t *oldbool = (igraph_vector_bool_t*)oldrec->value; + switch (oldrec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + IGRAPH_CHECK(igraph_vector_resize(oldnum, origlen + nv)); + for (j = origlen; j < origlen + nv; j++) { + VECTOR(*oldnum)[j] = IGRAPH_NAN; + } + break; + case IGRAPH_ATTRIBUTE_STRING: + IGRAPH_CHECK(igraph_strvector_resize(oldstr, origlen + nv)); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + IGRAPH_CHECK(igraph_vector_bool_resize(oldbool, origlen + nv)); + for (j = origlen; j < origlen + nv; j++) { + VECTOR(*oldbool)[j] = 0; + } + break; + default: + IGRAPH_WARNING("Invalid attribute type"); + break; + } + } + } + + igraph_vector_destroy(&news); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static void igraph_i_cattribute_permute_free(igraph_vector_ptr_t *v) { + long int i, n = igraph_vector_ptr_size(v); + for (i = 0; i < n; i++) { + igraph_attribute_record_t *rec = VECTOR(*v)[i]; + IGRAPH_FREE(rec->name); + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *numv = (igraph_vector_t*) rec->value; + igraph_vector_destroy(numv); + IGRAPH_FREE(numv); + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *strv = (igraph_strvector_t*) rec->value; + igraph_strvector_destroy(strv); + IGRAPH_FREE(strv); + } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_vector_bool_t *boolv = (igraph_vector_bool_t*) rec->value; + igraph_vector_bool_destroy(boolv); + IGRAPH_FREE(boolv); + } + IGRAPH_FREE(rec); + } + igraph_vector_ptr_clear(v); +} + +static int igraph_i_cattribute_permute_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx) { + + if (graph == newgraph) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int valno = igraph_vector_ptr_size(val); + long int i; + + for (i = 0; i < valno; i++) { + igraph_attribute_record_t *oldrec = VECTOR(*val)[i]; + igraph_attribute_type_t type = oldrec->type; + igraph_vector_t *num, *newnum; + igraph_strvector_t *str, *newstr; + igraph_vector_bool_t *oldbool, *newbool; + switch (type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + num = (igraph_vector_t*) oldrec->value; + newnum = IGRAPH_CALLOC(1, igraph_vector_t); + if (!newnum) { + IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); + } + IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); + igraph_vector_index(num, newnum, idx); + oldrec->value = newnum; + igraph_vector_destroy(num); + IGRAPH_FREE(num); + IGRAPH_FINALLY_CLEAN(1); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + oldbool = (igraph_vector_bool_t*) oldrec->value; + newbool = IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (!newbool) { + IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_bool_init(newbool, 0)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool); + igraph_vector_bool_index(oldbool, newbool, idx); + oldrec->value = newbool; + igraph_vector_bool_destroy(oldbool); + IGRAPH_FREE(oldbool); + IGRAPH_FINALLY_CLEAN(1); + break; + case IGRAPH_ATTRIBUTE_STRING: + str = (igraph_strvector_t*)oldrec->value; + newstr = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_strvector_init(newstr, 0)); + IGRAPH_FINALLY(igraph_strvector_destroy, newstr); + igraph_strvector_index(str, newstr, idx); + oldrec->value = newstr; + igraph_strvector_destroy(str); + IGRAPH_FREE(str); + IGRAPH_FINALLY_CLEAN(1); + break; + default: + IGRAPH_WARNING("Unknown edge attribute ignored"); + } + } + + } else { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int valno = igraph_vector_ptr_size(val); + long int i; + + /* New vertex attributes */ + igraph_i_cattributes_t *new_attr = newgraph->attr; + igraph_vector_ptr_t *new_val = &new_attr->val; + if (igraph_vector_ptr_size(new_val) != 0) { + IGRAPH_ERROR("Vertex attributes were already copied", + IGRAPH_EATTRIBUTES); + } + IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, valno)); + + IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_val); + + for (i = 0; i < valno; i++) { + igraph_attribute_record_t *oldrec = VECTOR(*val)[i]; + igraph_attribute_type_t type = oldrec->type; + igraph_vector_t *num, *newnum; + igraph_strvector_t *str, *newstr; + igraph_vector_bool_t *oldbool, *newbool; + + /* The record itself */ + igraph_attribute_record_t *new_rec = + IGRAPH_CALLOC(1, igraph_attribute_record_t); + if (!new_rec) { + IGRAPH_ERROR("Cannot create vertex attributes", IGRAPH_ENOMEM); + } + new_rec->name = strdup(oldrec->name); + new_rec->type = oldrec->type; + VECTOR(*new_val)[i] = new_rec; + + /* The data */ + switch (type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + num = (igraph_vector_t*)oldrec->value; + newnum = IGRAPH_CALLOC(1, igraph_vector_t); + if (!newnum) { + IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); + } + IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); + igraph_vector_index(num, newnum, idx); + new_rec->value = newnum; + IGRAPH_FINALLY_CLEAN(1); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + oldbool = (igraph_vector_bool_t*)oldrec->value; + newbool = IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (!newbool) { + IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_bool_init(newbool, 0)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool); + igraph_vector_bool_index(oldbool, newbool, idx); + new_rec->value = newbool; + IGRAPH_FINALLY_CLEAN(1); + break; + case IGRAPH_ATTRIBUTE_STRING: + str = (igraph_strvector_t*)oldrec->value; + newstr = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_strvector_init(newstr, 0)); + IGRAPH_FINALLY(igraph_strvector_destroy, newstr); + igraph_strvector_index(str, newstr, idx); + new_rec->value = newstr; + IGRAPH_FINALLY_CLEAN(1); + break; + default: + IGRAPH_WARNING("Unknown vertex attribute ignored"); + } + } + } + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +typedef int igraph_cattributes_combine_num_t(const igraph_vector_t *input, + igraph_real_t *output); + +typedef int igraph_cattributes_combine_str_t(const igraph_strvector_t *input, + char **output); + +typedef int igraph_cattributes_combine_bool_t(const igraph_vector_bool_t *input, + igraph_bool_t *output); + +static int igraph_i_cattributes_cn_sum(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_real_t s = 0.0; + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int j, n = igraph_vector_size(idx); + for (j = 0; j < n; j++) { + long int x = (long int) VECTOR(*idx)[j]; + s += VECTOR(*oldv)[x]; + } + VECTOR(*newv)[i] = s; + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cn_prod(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_real_t s = 1.0; + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int j, n = igraph_vector_size(idx); + for (j = 0; j < n; j++) { + long int x = (long int) VECTOR(*idx)[j]; + s *= VECTOR(*oldv)[x]; + } + VECTOR(*newv)[i] = s; + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cn_min(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i; + igraph_real_t nan = IGRAPH_NAN; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int j, n = igraph_vector_size(idx); + igraph_real_t m = n > 0 ? VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ] : nan; + for (j = 1; j < n; j++) { + long int x = (long int) VECTOR(*idx)[j]; + igraph_real_t val = VECTOR(*oldv)[x]; + if (val < m) { + m = val; + } + } + VECTOR(*newv)[i] = m; + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cn_max(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i; + igraph_real_t nan = IGRAPH_NAN; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int j, n = igraph_vector_size(idx); + igraph_real_t m = n > 0 ? VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ] : nan; + for (j = 1; j < n; j++) { + long int x = (long int) VECTOR(*idx)[j]; + igraph_real_t val = VECTOR(*oldv)[x]; + if (val > m) { + m = val; + } + } + VECTOR(*newv)[i] = m; + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cn_random(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i; + igraph_real_t nan = IGRAPH_NAN; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + RNG_BEGIN(); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int n = igraph_vector_size(idx); + if (n == 0) { + VECTOR(*newv)[i] = nan; + } else if (n == 1) { + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ]; + } else { + long int r = RNG_INTEGER(0, n - 1); + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[r] ]; + } + } + + RNG_END(); + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cn_first(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i; + igraph_real_t nan = IGRAPH_NAN; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int n = igraph_vector_size(idx); + if (n == 0) { + VECTOR(*newv)[i] = nan; + } else { + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ]; + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cn_last(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i; + igraph_real_t nan = IGRAPH_NAN; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int n = igraph_vector_size(idx); + if (n == 0) { + VECTOR(*newv)[i] = nan; + } else { + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[n - 1] ]; + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cn_mean(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i; + igraph_real_t nan = IGRAPH_NAN; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int j, n = igraph_vector_size(idx); + igraph_real_t s = n > 0 ? 0.0 : nan; + for (j = 0; j < n; j++) { + long int x = (long int) VECTOR(*idx)[j]; + s += VECTOR(*oldv)[x]; + } + if (n > 0) { + s = s / n; + } + VECTOR(*newv)[i] = s; + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cn_func(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_ptr_t *merges, + igraph_cattributes_combine_num_t *func) { + + const igraph_vector_t *oldv = oldrec->value; + long int newlen = igraph_vector_ptr_size(merges); + long int i; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + igraph_vector_t values; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + IGRAPH_VECTOR_INIT_FINALLY(&values, 0); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int j, n = igraph_vector_size(idx); + igraph_real_t res; + IGRAPH_CHECK(igraph_vector_resize(&values, n)); + for (j = 0; j < n; j++) { + long int x = (long int) VECTOR(*idx)[j]; + VECTOR(values)[j] = VECTOR(*oldv)[x]; + } + IGRAPH_CHECK(func(&values, &res)); + VECTOR(*newv)[i] = res; + } + + igraph_vector_destroy(&values); + IGRAPH_FINALLY_CLEAN(3); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cb_random(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + + const igraph_vector_bool_t *oldv = oldrec->value; + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + + RNG_BEGIN(); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int n = igraph_vector_size(idx); + if (n == 0) { + VECTOR(*newv)[i] = 0; + } else if (n == 1) { + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ]; + } else { + long int r = RNG_INTEGER(0, n - 1); + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[r] ]; + } + } + + RNG_END(); + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cb_first(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + + const igraph_vector_bool_t *oldv = oldrec->value; + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int n = igraph_vector_size(idx); + if (n == 0) { + VECTOR(*newv)[i] = 0; + } else { + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ]; + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cb_last(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + + const igraph_vector_bool_t *oldv = oldrec->value; + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int n = igraph_vector_size(idx); + if (n == 0) { + VECTOR(*newv)[i] = 0; + } else { + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[n - 1] ]; + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cb_all_is_true(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + + const igraph_vector_bool_t *oldv = oldrec->value; + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i, j, n, x; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + n = igraph_vector_size(idx); + VECTOR(*newv)[i] = 1; + for (j = 0; j < n; j++) { + x = (long int) VECTOR(*idx)[j]; + if (!VECTOR(*oldv)[x]) { + VECTOR(*newv)[i] = 0; + break; + } + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cb_any_is_true(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + + const igraph_vector_bool_t *oldv = oldrec->value; + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i, j, n, x; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + n = igraph_vector_size(idx); + VECTOR(*newv)[i] = 0; + for (j = 0; j < n; j++) { + x = (long int) VECTOR(*idx)[j]; + if (VECTOR(*oldv)[x]) { + VECTOR(*newv)[i] = 1; + break; + } + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cb_majority(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_ptr_t *merges) { + + const igraph_vector_bool_t *oldv = oldrec->value; + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + long int newlen = igraph_vector_ptr_size(merges); + long int i, j, n, x, num_trues; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + + RNG_BEGIN(); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + + n = igraph_vector_size(idx); + + num_trues = 0; + for (j = 0; j < n; j++) { + x = (long int) VECTOR(*idx)[j]; + if (VECTOR(*oldv)[x]) { + num_trues++; + } + } + + if (n % 2 != 0) { + VECTOR(*newv)[i] = (num_trues > n / 2); + } else { + if (num_trues == n / 2) { + VECTOR(*newv)[i] = (RNG_UNIF01() < 0.5); + } else { + VECTOR(*newv)[i] = (num_trues > n / 2); + } + } + } + + RNG_END(); + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_cb_func(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_ptr_t *merges, + igraph_cattributes_combine_bool_t *func) { + + const igraph_vector_bool_t *oldv = oldrec->value; + long int newlen = igraph_vector_ptr_size(merges); + long int i; + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + igraph_vector_bool_t values; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + + IGRAPH_CHECK(igraph_vector_bool_init(&values, 0)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int j, n = igraph_vector_size(idx); + igraph_bool_t res; + + IGRAPH_CHECK(igraph_vector_bool_resize(&values, n)); + for (j = 0; j < n; j++) { + long int x = (long int) VECTOR(*idx)[j]; + VECTOR(values)[j] = VECTOR(*oldv)[x]; + } + + IGRAPH_CHECK(func(&values, &res)); + VECTOR(*newv)[i] = res; + } + + igraph_vector_bool_destroy(&values); + IGRAPH_FINALLY_CLEAN(3); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_sn_random(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_ptr_t *merges) { + + const igraph_strvector_t *oldv = oldrec->value; + long int newlen = igraph_vector_ptr_size(merges); + long int i; + igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); + IGRAPH_FINALLY(igraph_strvector_destroy, newv); + + RNG_BEGIN(); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int n = igraph_vector_size(idx); + char *tmp; + if (n == 0) { + IGRAPH_CHECK(igraph_strvector_set(newv, i, "")); + } else if (n == 1) { + igraph_strvector_get(oldv, 0, &tmp); + IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); + } else { + long int r = RNG_INTEGER(0, n - 1); + igraph_strvector_get(oldv, r, &tmp); + IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); + } + } + + RNG_END(); + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_sn_first(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_ptr_t *merges) { + + const igraph_strvector_t *oldv = oldrec->value; + long int i, newlen = igraph_vector_ptr_size(merges); + igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); + IGRAPH_FINALLY(igraph_strvector_destroy, newv); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int n = igraph_vector_size(idx); + if (n == 0) { + IGRAPH_CHECK(igraph_strvector_set(newv, i, "")); + } else { + char *tmp; + igraph_strvector_get(oldv, (long int) VECTOR(*idx)[0], &tmp); + IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_sn_last(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_ptr_t *merges) { + + const igraph_strvector_t *oldv = oldrec->value; + long int i, newlen = igraph_vector_ptr_size(merges); + igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); + IGRAPH_FINALLY(igraph_strvector_destroy, newv); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int n = igraph_vector_size(idx); + if (n == 0) { + IGRAPH_CHECK(igraph_strvector_set(newv, i, "")); + } else { + char *tmp; + igraph_strvector_get(oldv, (long int) VECTOR(*idx)[n - 1], &tmp); + IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_sn_concat(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_ptr_t *merges) { + + const igraph_strvector_t *oldv = oldrec->value; + long int i, newlen = igraph_vector_ptr_size(merges); + igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); + IGRAPH_FINALLY(igraph_strvector_destroy, newv); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int j, n = igraph_vector_size(idx); + size_t len = 0; + char *tmp, *tmp2; + for (j = 0; j < n; j++) { + igraph_strvector_get(oldv, j, &tmp); + len += strlen(tmp); + } + tmp2 = IGRAPH_CALLOC(len + 1, char); + if (!tmp2) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, tmp2); + len = 0; + for (j = 0; j < n; j++) { + igraph_strvector_get(oldv, j, &tmp); + strcpy(tmp2 + len, tmp); + len += strlen(tmp); + } + + IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp2)); + IGRAPH_FREE(tmp2); + IGRAPH_FINALLY_CLEAN(1); + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return 0; +} + +static int igraph_i_cattributes_sn_func(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_ptr_t *merges, + igraph_cattributes_combine_str_t *func) { + + const igraph_strvector_t *oldv = oldrec->value; + long int newlen = igraph_vector_ptr_size(merges); + long int i; + igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); + igraph_strvector_t values; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); + IGRAPH_FINALLY(igraph_strvector_destroy, newv); + + IGRAPH_CHECK(igraph_strvector_init(newv, 0)); + IGRAPH_FINALLY(igraph_strvector_destroy, &values); + + for (i = 0; i < newlen; i++) { + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int j, n = igraph_vector_size(idx); + char *res; + IGRAPH_CHECK(igraph_strvector_resize(&values, n)); + for (j = 0; j < n; j++) { + long int x = (long int) VECTOR(*idx)[j]; + char *elem; + igraph_strvector_get(oldv, x, &elem); + IGRAPH_CHECK(igraph_strvector_set(newv, j, elem)); + } + IGRAPH_CHECK(func(&values, &res)); + IGRAPH_FINALLY(igraph_free, res); + IGRAPH_CHECK(igraph_strvector_set(newv, i, res)); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FREE(res); + } + + igraph_strvector_destroy(&values); + IGRAPH_FINALLY_CLEAN(3); + newrec->value = newv; + + return 0; +} + + +static int igraph_i_cattribute_combine_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_ptr_t *merges, + const igraph_attribute_combination_t *comb) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_i_cattributes_t *toattr = newgraph->attr; + igraph_vector_ptr_t *val = &attr->val; + igraph_vector_ptr_t *new_val = &toattr->val; + long int valno = igraph_vector_ptr_size(val); + long int i, j, keepno = 0; + int *TODO; + igraph_function_pointer_t *funcs; + + TODO = IGRAPH_CALLOC(valno, int); + if (!TODO) { + IGRAPH_ERROR("Cannot combine vertex attributes", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, TODO); + funcs = IGRAPH_CALLOC(valno, igraph_function_pointer_t); + if (!funcs) { + IGRAPH_ERROR("Cannot combine vertex attributes", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, funcs); + + for (i = 0; i < valno; i++) { + igraph_attribute_record_t *oldrec = VECTOR(*val)[i]; + const char *name = oldrec->name; + igraph_attribute_combination_type_t todo; + igraph_function_pointer_t voidfunc; + igraph_attribute_combination_query(comb, name, &todo, &voidfunc); + TODO[i] = todo; + funcs[i] = voidfunc; + if (todo != IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { + keepno++; + } + } + + IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, keepno)); + IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_val); + + for (i = 0, j = 0; i < valno; i++) { + igraph_attribute_record_t *newrec, *oldrec = VECTOR(*val)[i]; + const char *name = oldrec->name; + igraph_attribute_combination_type_t todo = + (igraph_attribute_combination_type_t) (TODO[i]); + igraph_attribute_type_t type = oldrec->type; + igraph_cattributes_combine_num_t *numfunc = + (igraph_cattributes_combine_num_t*) funcs[i]; + igraph_cattributes_combine_str_t *strfunc = + (igraph_cattributes_combine_str_t*) funcs[i]; + igraph_cattributes_combine_bool_t *boolfunc = + (igraph_cattributes_combine_bool_t*) funcs[i]; + + if (todo == IGRAPH_ATTRIBUTE_COMBINE_DEFAULT || + todo == IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { + continue; + } + + newrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + if (!newrec) { + IGRAPH_ERROR("Cannot combine vertex attributes", + IGRAPH_ENOMEM); + } + newrec->name = strdup(name); + newrec->type = type; + VECTOR(*new_val)[j] = newrec; + + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + switch (todo) { + case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: + IGRAPH_CHECK(igraph_i_cattributes_cn_func(oldrec, newrec, merges, + numfunc)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_SUM: + IGRAPH_CHECK(igraph_i_cattributes_cn_sum(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_PROD: + IGRAPH_CHECK(igraph_i_cattributes_cn_prod(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MIN: + IGRAPH_CHECK(igraph_i_cattributes_cn_min(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MAX: + IGRAPH_CHECK(igraph_i_cattributes_cn_max(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_RANDOM: + IGRAPH_CHECK(igraph_i_cattributes_cn_random(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_FIRST: + IGRAPH_CHECK(igraph_i_cattributes_cn_first(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_LAST: + IGRAPH_CHECK(igraph_i_cattributes_cn_last(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MEAN: + IGRAPH_CHECK(igraph_i_cattributes_cn_mean(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN: + IGRAPH_ERROR("Median calculation not implemented", + IGRAPH_UNIMPLEMENTED); + break; + case IGRAPH_ATTRIBUTE_COMBINE_CONCAT: + IGRAPH_ERROR("Cannot concatenate numeric attributes", + IGRAPH_EATTRCOMBINE); + break; + default: + IGRAPH_ERROR("Unknown attribute_combination", + IGRAPH_UNIMPLEMENTED); + break; + } + } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) { + switch (todo) { + case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: + IGRAPH_CHECK(igraph_i_cattributes_cb_func(oldrec, newrec, merges, + boolfunc)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_SUM: + case IGRAPH_ATTRIBUTE_COMBINE_MAX: + IGRAPH_CHECK(igraph_i_cattributes_cb_any_is_true(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_PROD: + case IGRAPH_ATTRIBUTE_COMBINE_MIN: + IGRAPH_CHECK(igraph_i_cattributes_cb_all_is_true(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MEAN: + case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN: + IGRAPH_CHECK(igraph_i_cattributes_cb_majority(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_RANDOM: + IGRAPH_CHECK(igraph_i_cattributes_cb_random(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_FIRST: + IGRAPH_CHECK(igraph_i_cattributes_cb_first(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_LAST: + IGRAPH_CHECK(igraph_i_cattributes_cb_last(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_CONCAT: + IGRAPH_ERROR("Cannot calculate concatenation of Booleans", + IGRAPH_EATTRCOMBINE); + break; + default: + IGRAPH_ERROR("Unknown attribute_combination", + IGRAPH_UNIMPLEMENTED); + break; + } + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + switch (todo) { + case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: + IGRAPH_CHECK(igraph_i_cattributes_sn_func(oldrec, newrec, merges, + strfunc)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_SUM: + IGRAPH_ERROR("Cannot sum strings", IGRAPH_EATTRCOMBINE); + break; + case IGRAPH_ATTRIBUTE_COMBINE_PROD: + IGRAPH_ERROR("Cannot multiply strings", IGRAPH_EATTRCOMBINE); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MIN: + IGRAPH_ERROR("Cannot find minimum of strings", + IGRAPH_EATTRCOMBINE); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MAX: + IGRAPH_ERROR("Cannot find maximum of strings", + IGRAPH_EATTRCOMBINE); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MEAN: + IGRAPH_ERROR("Cannot calculate mean of strings", + IGRAPH_EATTRCOMBINE); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN: + IGRAPH_ERROR("Cannot calculate median of strings", + IGRAPH_EATTRCOMBINE); + break; + case IGRAPH_ATTRIBUTE_COMBINE_RANDOM: + IGRAPH_CHECK(igraph_i_cattributes_sn_random(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_FIRST: + IGRAPH_CHECK(igraph_i_cattributes_sn_first(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_LAST: + IGRAPH_CHECK(igraph_i_cattributes_sn_last(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_CONCAT: + IGRAPH_CHECK(igraph_i_cattributes_sn_concat(oldrec, newrec, merges)); + break; + default: + IGRAPH_ERROR("Unknown attribute_combination", + IGRAPH_UNIMPLEMENTED); + break; + } + } else { + IGRAPH_ERROR("Unknown attribute type, this should not happen", + IGRAPH_UNIMPLEMENTED); + } + + j++; + } + + igraph_free(funcs); + igraph_free(TODO); + igraph_i_cattribute_permute_free(new_val); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/* void igraph_i_cattribute_delete_vertices(igraph_t *graph, */ +/* const igraph_vector_t *eidx, */ +/* const igraph_vector_t *vidx) { */ + +/* igraph_i_cattributes_t *attr=graph->attr; */ +/* igraph_vector_ptr_t *val=&attr->val; */ +/* igraph_vector_ptr_t *eal=&attr->eal; */ +/* long int valno=igraph_vector_ptr_size(val); */ +/* long int ealno=igraph_vector_ptr_size(eal); */ +/* long int i; */ +/* long int origlen, newlen; */ + +/* /\* Vertices *\/ */ +/* origlen=igraph_vector_size(vidx); */ +/* newlen=0; */ +/* for (i=0; i0) { */ +/* newlen++; */ +/* } */ +/* } */ +/* for (i=0; itype; */ +/* igraph_vector_t *num=(igraph_vector_t*)oldrec->value; */ +/* igraph_strvector_t *str=(igraph_strvector_t*)oldrec->value; */ +/* switch (type) { */ +/* case IGRAPH_ATTRIBUTE_NUMERIC: */ +/* igraph_vector_permdelete(num, vidx, origlen-newlen); */ +/* break; */ +/* case IGRAPH_ATTRIBUTE_STRING: */ +/* igraph_strvector_permdelete(str, vidx, origlen-newlen); */ +/* break; */ +/* default: */ +/* IGRAPH_WARNING("Unknown vertex attribute ignored"); */ +/* } */ +/* } */ + +/* /\* Edges *\/ */ +/* origlen=igraph_vector_size(eidx); */ +/* newlen=0; */ +/* for (i=0; i0) { */ +/* newlen++; */ +/* } */ +/* } */ +/* for (i=0; itype; */ +/* igraph_vector_t *num=(igraph_vector_t*)oldrec->value; */ +/* igraph_strvector_t *str=(igraph_strvector_t*)oldrec->value; */ +/* switch (type) { */ +/* case IGRAPH_ATTRIBUTE_NUMERIC: */ +/* igraph_vector_permdelete(num, eidx, origlen-newlen); */ +/* break; */ +/* case IGRAPH_ATTRIBUTE_STRING: */ +/* igraph_strvector_permdelete(str, eidx, origlen-newlen); */ +/* break; */ +/* default: */ +/* IGRAPH_WARNING("Unknown edge attribute ignored"); */ +/* } */ +/* } */ +/* } */ + +static int igraph_i_cattribute_add_edges(igraph_t *graph, const igraph_vector_t *edges, + igraph_vector_ptr_t *nattr) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int ealno = igraph_vector_ptr_size(eal); + long int ne = igraph_vector_size(edges) / 2; + long int origlen = igraph_ecount(graph) - ne; + long int nattrno = nattr == 0 ? 0 : igraph_vector_ptr_size(nattr); + igraph_vector_t news; + long int newattrs, i; + + /* First add the new attributes if any */ + newattrs = 0; + IGRAPH_VECTOR_INIT_FINALLY(&news, 0); + for (i = 0; i < nattrno; i++) { + igraph_attribute_record_t *nattr_entry = VECTOR(*nattr)[i]; + const char *nname = nattr_entry->name; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(eal, nname, &j); + if (!l) { + newattrs++; + IGRAPH_CHECK(igraph_vector_push_back(&news, i)); + } else { + /* check types */ + if (nattr_entry->type != + ((igraph_attribute_record_t*)VECTOR(*eal)[j])->type) { + IGRAPH_ERROR("You cannot mix attribute types", IGRAPH_EINVAL); + } + } + } + + /* Add NA/empty string vectors for the existing vertices */ + if (newattrs != 0) { + for (i = 0; i < newattrs; i++) { + igraph_attribute_record_t *tmp = VECTOR(*nattr)[(long int)VECTOR(news)[i]]; + igraph_attribute_record_t *newrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_attribute_type_t type = tmp->type; + if (!newrec) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newrec); + newrec->type = type; + newrec->name = strdup(tmp->name); + if (!newrec->name) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)newrec->name); + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *newnum = IGRAPH_CALLOC(1, igraph_vector_t); + if (!newnum) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newnum); + IGRAPH_VECTOR_INIT_FINALLY(newnum, origlen); + newrec->value = newnum; + igraph_vector_fill(newnum, IGRAPH_NAN); + } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_vector_bool_t *newbool = IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (!newbool) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newbool); + IGRAPH_CHECK(igraph_vector_bool_init(newbool, origlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool); + newrec->value = newbool; + igraph_vector_bool_fill(newbool, 0); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *newstr = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newstr); + IGRAPH_STRVECTOR_INIT_FINALLY(newstr, origlen); + newrec->value = newstr; + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, newrec)); + IGRAPH_FINALLY_CLEAN(4); + } + ealno = igraph_vector_ptr_size(eal); + } + + /* Now append the new values */ + for (i = 0; i < ealno; i++) { + igraph_attribute_record_t *oldrec = VECTOR(*eal)[i]; + igraph_attribute_record_t *newrec = 0; + const char *name = oldrec->name; + long int j = -1; + igraph_bool_t l = 0; + if (nattr) { + l = igraph_i_cattribute_find(nattr, name, &j); + } + if (l) { + /* This attribute is present in nattr */ + igraph_vector_t *oldnum, *newnum; + igraph_strvector_t *oldstr, *newstr; + igraph_vector_bool_t *oldbool, *newbool; + newrec = VECTOR(*nattr)[j]; + oldnum = (igraph_vector_t*)oldrec->value; + newnum = (igraph_vector_t*)newrec->value; + oldstr = (igraph_strvector_t*)oldrec->value; + newstr = (igraph_strvector_t*)newrec->value; + oldbool = (igraph_vector_bool_t*)oldrec->value; + newbool = (igraph_vector_bool_t*)newrec->value; + if (oldrec->type != newrec->type) { + IGRAPH_ERROR("Attribute types do not match", IGRAPH_EINVAL); + } + switch (oldrec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + if (ne != igraph_vector_size(newnum)) { + IGRAPH_ERROR("Invalid numeric attribute length", IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_vector_append(oldnum, newnum)); + break; + case IGRAPH_ATTRIBUTE_STRING: + if (ne != igraph_strvector_size(newstr)) { + IGRAPH_ERROR("Invalid string attribute length", IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_strvector_append(oldstr, newstr)); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + if (ne != igraph_vector_bool_size(newbool)) { + IGRAPH_ERROR("Invalid Boolean attribute length", IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_vector_bool_append(oldbool, newbool)); + break; + default: + IGRAPH_WARNING("Invalid attribute type"); + break; + } + } else { + /* No such attribute, append NA's */ + igraph_vector_t *oldnum = (igraph_vector_t *)oldrec->value; + igraph_strvector_t *oldstr = (igraph_strvector_t*)oldrec->value; + igraph_vector_bool_t *oldbool = (igraph_vector_bool_t *)oldrec->value; + switch (oldrec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + IGRAPH_CHECK(igraph_vector_resize(oldnum, origlen + ne)); + for (j = origlen; j < origlen + ne; j++) { + VECTOR(*oldnum)[j] = IGRAPH_NAN; + } + break; + case IGRAPH_ATTRIBUTE_STRING: + IGRAPH_CHECK(igraph_strvector_resize(oldstr, origlen + ne)); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + IGRAPH_CHECK(igraph_vector_bool_resize(oldbool, origlen + ne)); + for (j = origlen; j < origlen + ne; j++) { + VECTOR(*oldbool)[j] = 0; + } + break; + default: + IGRAPH_WARNING("Invalid attribute type"); + break; + } + } + } + + igraph_vector_destroy(&news); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/* void igraph_i_cattribute_delete_edges(igraph_t *graph, const igraph_vector_t *idx) { */ + +/* igraph_i_cattributes_t *attr=graph->attr; */ +/* igraph_vector_ptr_t *eal=&attr->eal; */ +/* long int ealno=igraph_vector_ptr_size(eal); */ +/* long int i; */ +/* long int origlen=igraph_vector_size(idx), newlen; */ + +/* newlen=0; */ +/* for (i=0; i0) { */ +/* newlen++; */ +/* } */ +/* } */ +/* for (i=0; itype; */ +/* igraph_vector_t *num=(igraph_vector_t*)oldrec->value; */ +/* igraph_strvector_t *str=(igraph_strvector_t*)oldrec->value; */ +/* switch (type) { */ +/* case IGRAPH_ATTRIBUTE_NUMERIC: */ +/* igraph_vector_permdelete(num, idx, origlen-newlen); */ +/* break; */ +/* case IGRAPH_ATTRIBUTE_STRING: */ +/* igraph_strvector_permdelete(str, idx, origlen-newlen); */ +/* break; */ +/* default: */ +/* IGRAPH_WARNING("Unknown edge attribute ignored"); */ +/* } */ +/* } */ + +/* } */ + +static int igraph_i_cattribute_permute_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx) { + + if (graph == newgraph) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int ealno = igraph_vector_ptr_size(eal); + long int i; + + for (i = 0; i < ealno; i++) { + igraph_attribute_record_t *oldrec = VECTOR(*eal)[i]; + igraph_attribute_type_t type = oldrec->type; + igraph_vector_t *num, *newnum; + igraph_strvector_t *str, *newstr; + igraph_vector_bool_t *oldbool, *newbool; + switch (type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + num = (igraph_vector_t*) oldrec->value; + newnum = IGRAPH_CALLOC(1, igraph_vector_t); + if (!newnum) { + IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM); + } + IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); + igraph_vector_index(num, newnum, idx); + oldrec->value = newnum; + igraph_vector_destroy(num); + IGRAPH_FREE(num); + IGRAPH_FINALLY_CLEAN(1); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + oldbool = (igraph_vector_bool_t*) oldrec->value; + newbool = IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (!newbool) { + IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_bool_init(newbool, 0)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool); + igraph_vector_bool_index(oldbool, newbool, idx); + oldrec->value = newbool; + igraph_vector_bool_destroy(oldbool); + IGRAPH_FREE(oldbool); + IGRAPH_FINALLY_CLEAN(1); + break; + case IGRAPH_ATTRIBUTE_STRING: + str = (igraph_strvector_t*)oldrec->value; + newstr = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_strvector_init(newstr, 0)); + IGRAPH_FINALLY(igraph_strvector_destroy, newstr); + igraph_strvector_index(str, newstr, idx); + oldrec->value = newstr; + igraph_strvector_destroy(str); + IGRAPH_FREE(str); + IGRAPH_FINALLY_CLEAN(1); + break; + default: + IGRAPH_WARNING("Unknown edge attribute ignored"); + } + } + + } else { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int ealno = igraph_vector_ptr_size(eal); + long int i; + + /* New edge attributes */ + igraph_i_cattributes_t *new_attr = newgraph->attr; + igraph_vector_ptr_t *new_eal = &new_attr->eal; + IGRAPH_CHECK(igraph_vector_ptr_resize(new_eal, ealno)); + + IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_eal); + + for (i = 0; i < ealno; i++) { + igraph_attribute_record_t *oldrec = VECTOR(*eal)[i]; + igraph_attribute_type_t type = oldrec->type; + igraph_vector_t *num, *newnum; + igraph_strvector_t *str, *newstr; + igraph_vector_bool_t *oldbool, *newbool; + + /* The record itself */ + igraph_attribute_record_t *new_rec = + IGRAPH_CALLOC(1, igraph_attribute_record_t); + if (!new_rec) { + IGRAPH_ERROR("Cannot create edge attributes", IGRAPH_ENOMEM); + } + new_rec->name = strdup(oldrec->name); + new_rec->type = oldrec->type; + VECTOR(*new_eal)[i] = new_rec; + + switch (type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + num = (igraph_vector_t*) oldrec->value; + newnum = IGRAPH_CALLOC(1, igraph_vector_t); + if (!newnum) { + IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM); + } + IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); + igraph_vector_index(num, newnum, idx); + new_rec->value = newnum; + IGRAPH_FINALLY_CLEAN(1); + break; + case IGRAPH_ATTRIBUTE_STRING: + str = (igraph_strvector_t*)oldrec->value; + newstr = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_strvector_init(newstr, 0)); + IGRAPH_FINALLY(igraph_strvector_destroy, newstr); + igraph_strvector_index(str, newstr, idx); + new_rec->value = newstr; + IGRAPH_FINALLY_CLEAN(1); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + oldbool = (igraph_vector_bool_t*) oldrec->value; + newbool = IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (!newbool) { + IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_bool_init(newbool, 0)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool); + igraph_vector_bool_index(oldbool, newbool, idx); + new_rec->value = newbool; + IGRAPH_FINALLY_CLEAN(1); + break; + default: + IGRAPH_WARNING("Unknown edge attribute ignored"); + } + } + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +static int igraph_i_cattribute_combine_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_ptr_t *merges, + const igraph_attribute_combination_t *comb) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_i_cattributes_t *toattr = newgraph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + igraph_vector_ptr_t *new_eal = &toattr->eal; + long int ealno = igraph_vector_ptr_size(eal); + long int i, j, keepno = 0; + int *TODO; + igraph_function_pointer_t *funcs; + + TODO = IGRAPH_CALLOC(ealno, int); + if (!TODO) { + IGRAPH_ERROR("Cannot combine edge attributes", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, TODO); + funcs = IGRAPH_CALLOC(ealno, igraph_function_pointer_t); + if (!funcs) { + IGRAPH_ERROR("Cannot combine edge attributes", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, funcs); + + for (i = 0; i < ealno; i++) { + igraph_attribute_record_t *oldrec = VECTOR(*eal)[i]; + const char *name = oldrec->name; + igraph_attribute_combination_type_t todo; + igraph_function_pointer_t voidfunc; + igraph_attribute_combination_query(comb, name, &todo, &voidfunc); + TODO[i] = todo; + funcs[i] = voidfunc; + if (todo != IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { + keepno++; + } + } + + IGRAPH_CHECK(igraph_vector_ptr_resize(new_eal, keepno)); + IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_eal); + + for (i = 0, j = 0; i < ealno; i++) { + igraph_attribute_record_t *newrec, *oldrec = VECTOR(*eal)[i]; + const char *name = oldrec->name; + igraph_attribute_combination_type_t todo = + (igraph_attribute_combination_type_t) (TODO[i]); + igraph_attribute_type_t type = oldrec->type; + igraph_cattributes_combine_num_t *numfunc = + (igraph_cattributes_combine_num_t*) funcs[i]; + igraph_cattributes_combine_str_t *strfunc = + (igraph_cattributes_combine_str_t*) funcs[i]; + igraph_cattributes_combine_bool_t *boolfunc = + (igraph_cattributes_combine_bool_t*) funcs[i]; + + if (todo == IGRAPH_ATTRIBUTE_COMBINE_DEFAULT || + todo == IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { + continue; + } + + newrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + if (!newrec) { + IGRAPH_ERROR("Cannot combine edge attributes", + IGRAPH_ENOMEM); + } + newrec->name = strdup(name); + newrec->type = type; + VECTOR(*new_eal)[j] = newrec; + + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + switch (todo) { + case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: + IGRAPH_CHECK(igraph_i_cattributes_cn_func(oldrec, newrec, merges, + numfunc)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_SUM: + IGRAPH_CHECK(igraph_i_cattributes_cn_sum(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_PROD: + IGRAPH_CHECK(igraph_i_cattributes_cn_prod(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MIN: + IGRAPH_CHECK(igraph_i_cattributes_cn_min(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MAX: + IGRAPH_CHECK(igraph_i_cattributes_cn_max(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_RANDOM: + IGRAPH_CHECK(igraph_i_cattributes_cn_random(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_FIRST: + IGRAPH_CHECK(igraph_i_cattributes_cn_first(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_LAST: + IGRAPH_CHECK(igraph_i_cattributes_cn_last(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MEAN: + IGRAPH_CHECK(igraph_i_cattributes_cn_mean(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN: + IGRAPH_ERROR("Median calculation not implemented", + IGRAPH_UNIMPLEMENTED); + break; + case IGRAPH_ATTRIBUTE_COMBINE_CONCAT: + IGRAPH_ERROR("Cannot concatenate numeric attributes", + IGRAPH_EATTRCOMBINE); + break; + default: + IGRAPH_ERROR("Unknown attribute_combination", + IGRAPH_UNIMPLEMENTED); + break; + } + } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) { + switch (todo) { + case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: + IGRAPH_CHECK(igraph_i_cattributes_cb_func(oldrec, newrec, merges, + boolfunc)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_SUM: + case IGRAPH_ATTRIBUTE_COMBINE_MAX: + IGRAPH_CHECK(igraph_i_cattributes_cb_any_is_true(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_PROD: + case IGRAPH_ATTRIBUTE_COMBINE_MIN: + IGRAPH_CHECK(igraph_i_cattributes_cb_all_is_true(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MEAN: + case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN: + IGRAPH_CHECK(igraph_i_cattributes_cb_majority(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_RANDOM: + IGRAPH_CHECK(igraph_i_cattributes_cb_random(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_FIRST: + IGRAPH_CHECK(igraph_i_cattributes_cb_first(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_LAST: + IGRAPH_CHECK(igraph_i_cattributes_cb_last(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_CONCAT: + IGRAPH_ERROR("Cannot calculate concatenation of Booleans", + IGRAPH_EATTRCOMBINE); + break; + default: + IGRAPH_ERROR("Unknown attribute_combination", + IGRAPH_UNIMPLEMENTED); + break; + } + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + switch (todo) { + case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: + IGRAPH_CHECK(igraph_i_cattributes_sn_func(oldrec, newrec, merges, + strfunc)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_SUM: + IGRAPH_ERROR("Cannot sum strings", IGRAPH_EATTRCOMBINE); + break; + case IGRAPH_ATTRIBUTE_COMBINE_PROD: + IGRAPH_ERROR("Cannot multiply strings", IGRAPH_EATTRCOMBINE); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MIN: + IGRAPH_ERROR("Cannot find minimum of strings", + IGRAPH_EATTRCOMBINE); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MAX: + IGRAPH_ERROR("Cannot find maximum of strings", + IGRAPH_EATTRCOMBINE); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MEAN: + IGRAPH_ERROR("Cannot calculate mean of strings", + IGRAPH_EATTRCOMBINE); + break; + case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN: + IGRAPH_ERROR("Cannot calculate median of strings", + IGRAPH_EATTRCOMBINE); + break; + case IGRAPH_ATTRIBUTE_COMBINE_RANDOM: + IGRAPH_CHECK(igraph_i_cattributes_sn_random(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_FIRST: + IGRAPH_CHECK(igraph_i_cattributes_sn_first(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_LAST: + IGRAPH_CHECK(igraph_i_cattributes_sn_last(oldrec, newrec, merges)); + break; + case IGRAPH_ATTRIBUTE_COMBINE_CONCAT: + IGRAPH_CHECK(igraph_i_cattributes_sn_concat(oldrec, newrec, merges)); + break; + default: + IGRAPH_ERROR("Unknown attribute_combination", + IGRAPH_UNIMPLEMENTED); + break; + } + } else { + IGRAPH_ERROR("Unknown attribute type, this should not happen", + IGRAPH_UNIMPLEMENTED); + } + + j++; + } + + igraph_free(funcs); + igraph_free(TODO); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +static int igraph_i_cattribute_get_info(const igraph_t *graph, + igraph_strvector_t *gnames, + igraph_vector_t *gtypes, + igraph_strvector_t *vnames, + igraph_vector_t *vtypes, + igraph_strvector_t *enames, + igraph_vector_t *etypes) { + + igraph_strvector_t *names[3] = { gnames, vnames, enames }; + igraph_vector_t *types[3] = { gtypes, vtypes, etypes }; + igraph_i_cattributes_t *at = graph->attr; + igraph_vector_ptr_t *attr[3] = { &at->gal, &at->val, &at->eal }; + long int i, j; + + for (i = 0; i < 3; i++) { + igraph_strvector_t *n = names[i]; + igraph_vector_t *t = types[i]; + igraph_vector_ptr_t *al = attr[i]; + long int len = igraph_vector_ptr_size(al); + + if (n) { + IGRAPH_CHECK(igraph_strvector_resize(n, len)); + } + if (t) { + IGRAPH_CHECK(igraph_vector_resize(t, len)); + } + + for (j = 0; j < len; j++) { + igraph_attribute_record_t *rec = VECTOR(*al)[j]; + const char *name = rec->name; + igraph_attribute_type_t type = rec->type; + if (n) { + IGRAPH_CHECK(igraph_strvector_set(n, j, name)); + } + if (t) { + VECTOR(*t)[j] = type; + } + } + } + + return 0; +} + +static igraph_bool_t igraph_i_cattribute_has_attr(const igraph_t *graph, + igraph_attribute_elemtype_t type, + const char *name) { + igraph_i_cattributes_t *at = graph->attr; + igraph_vector_ptr_t *attr[3] = { &at->gal, &at->val, &at->eal }; + long int attrnum; + + switch (type) { + case IGRAPH_ATTRIBUTE_GRAPH: + attrnum = 0; + break; + case IGRAPH_ATTRIBUTE_VERTEX: + attrnum = 1; + break; + case IGRAPH_ATTRIBUTE_EDGE: + attrnum = 2; + break; + default: + IGRAPH_ERROR("Unknown attribute element type", IGRAPH_EINVAL); + break; + } + + return igraph_i_cattribute_find(attr[attrnum], name, 0); +} + +static int igraph_i_cattribute_gettype(const igraph_t *graph, + igraph_attribute_type_t *type, + igraph_attribute_elemtype_t elemtype, + const char *name) { + long int attrnum; + igraph_attribute_record_t *rec; + igraph_i_cattributes_t *at = graph->attr; + igraph_vector_ptr_t *attr[3] = { &at->gal, &at->val, &at->eal }; + igraph_vector_ptr_t *al; + long int j; + igraph_bool_t l = 0; + + switch (elemtype) { + case IGRAPH_ATTRIBUTE_GRAPH: + attrnum = 0; + break; + case IGRAPH_ATTRIBUTE_VERTEX: + attrnum = 1; + break; + case IGRAPH_ATTRIBUTE_EDGE: + attrnum = 2; + break; + default: + IGRAPH_ERROR("Unknown attribute element type", IGRAPH_EINVAL); + break; + } + + al = attr[attrnum]; + l = igraph_i_cattribute_find(al, name, &j); + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + rec = VECTOR(*al)[j]; + *type = rec->type; + + return 0; +} + +static int igraph_i_cattribute_get_numeric_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_t *value) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *gal = &attr->gal; + long int j; + igraph_attribute_record_t *rec; + igraph_vector_t *num; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + + rec = VECTOR(*gal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERROR("Numeric graph attribute expected.", IGRAPH_EINVAL); + } + num = (igraph_vector_t*)rec->value; + IGRAPH_CHECK(igraph_vector_resize(value, 1)); + VECTOR(*value)[0] = VECTOR(*num)[0]; + + return 0; +} + +static int igraph_i_cattribute_get_bool_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_bool_t *value) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *gal = &attr->gal; + long int j; + igraph_attribute_record_t *rec; + igraph_vector_bool_t *log; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + + rec = VECTOR(*gal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERROR("Boolean graph attribute expected.", IGRAPH_EINVAL); + } + log = (igraph_vector_bool_t*)rec->value; + IGRAPH_CHECK(igraph_vector_bool_resize(value, 1)); + VECTOR(*value)[0] = VECTOR(*log)[0]; + + return 0; +} + +static int igraph_i_cattribute_get_string_graph_attr(const igraph_t *graph, + const char *name, + igraph_strvector_t *value) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *gal = &attr->gal; + long int j; + igraph_attribute_record_t *rec; + igraph_strvector_t *str; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + + rec = VECTOR(*gal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("String graph attribute expected.", IGRAPH_EINVAL); + } + str = (igraph_strvector_t*)rec->value; + IGRAPH_CHECK(igraph_strvector_resize(value, 1)); + IGRAPH_CHECK(igraph_strvector_set(value, 0, STR(*str, 0))); + + return 0; +} + +static int igraph_i_cattribute_get_numeric_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_t *value) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int j; + igraph_attribute_record_t *rec; + igraph_vector_t *num; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + + rec = VECTOR(*val)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERROR("Numeric vertex attribute expected.", IGRAPH_EINVAL); + } + num = (igraph_vector_t*)rec->value; + if (igraph_vs_is_all(&vs)) { + igraph_vector_clear(value); + IGRAPH_CHECK(igraph_vector_append(value, num)); + } else { + igraph_vit_t it; + long int i = 0; + IGRAPH_CHECK(igraph_vit_create(graph, vs, &it)); + IGRAPH_FINALLY(igraph_vit_destroy, &it); + IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_VIT_SIZE(it))); + for (; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) { + long int v = IGRAPH_VIT_GET(it); + VECTOR(*value)[i] = VECTOR(*num)[v]; + } + igraph_vit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +static int igraph_i_cattribute_get_bool_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_bool_t *value) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + igraph_vit_t it; + long int i, j, v; + igraph_attribute_record_t *rec; + igraph_vector_bool_t *log; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + + rec = VECTOR(*val)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERROR("Boolean vertex attribute expected.", IGRAPH_EINVAL); + } + log = (igraph_vector_bool_t*)rec->value; + if (igraph_vs_is_all(&vs)) { + igraph_vector_bool_clear(value); + IGRAPH_CHECK(igraph_vector_bool_append(value, log)); + } else { + IGRAPH_CHECK(igraph_vit_create(graph, vs, &it)); + IGRAPH_FINALLY(igraph_vit_destroy, &it); + IGRAPH_CHECK(igraph_vector_bool_resize(value, IGRAPH_VIT_SIZE(it))); + for (i = 0; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) { + v = IGRAPH_VIT_GET(it); + VECTOR(*value)[i] = VECTOR(*log)[v]; + } + igraph_vit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +static int igraph_i_cattribute_get_string_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_strvector_t *value) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int j; + igraph_attribute_record_t *rec; + igraph_strvector_t *str; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + + rec = VECTOR(*val)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("String vertex attribute expected.", IGRAPH_EINVAL); + } + str = (igraph_strvector_t*)rec->value; + if (igraph_vs_is_all(&vs)) { + igraph_strvector_resize(value, 0); + IGRAPH_CHECK(igraph_strvector_append(value, str)); + } else { + igraph_vit_t it; + long int i = 0; + IGRAPH_CHECK(igraph_vit_create(graph, vs, &it)); + IGRAPH_FINALLY(igraph_vit_destroy, &it); + IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_VIT_SIZE(it))); + for (; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) { + long int v = IGRAPH_VIT_GET(it); + char *s; + igraph_strvector_get(str, v, &s); + IGRAPH_CHECK(igraph_strvector_set(value, i, s)); + } + igraph_vit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +static int igraph_i_cattribute_get_numeric_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_t *value) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int j; + igraph_attribute_record_t *rec; + igraph_vector_t *num; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + + rec = VECTOR(*eal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERROR("Numeric edge attribute expected.", IGRAPH_EINVAL); + } + num = (igraph_vector_t*)rec->value; + if (igraph_es_is_all(&es)) { + igraph_vector_clear(value); + IGRAPH_CHECK(igraph_vector_append(value, num)); + } else { + igraph_eit_t it; + long int i = 0; + IGRAPH_CHECK(igraph_eit_create(graph, es, &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_EIT_SIZE(it))); + for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) { + long int e = IGRAPH_EIT_GET(it); + VECTOR(*value)[i] = VECTOR(*num)[e]; + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +static int igraph_i_cattribute_get_string_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_strvector_t *value) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int j; + igraph_attribute_record_t *rec; + igraph_strvector_t *str; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + + rec = VECTOR(*eal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("String edge attribute expected.", IGRAPH_EINVAL); + } + str = (igraph_strvector_t*)rec->value; + if (igraph_es_is_all(&es)) { + igraph_strvector_resize(value, 0); + IGRAPH_CHECK(igraph_strvector_append(value, str)); + } else { + igraph_eit_t it; + long int i = 0; + IGRAPH_CHECK(igraph_eit_create(graph, es, &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_EIT_SIZE(it))); + for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) { + long int e = IGRAPH_EIT_GET(it); + char *s; + igraph_strvector_get(str, e, &s); + IGRAPH_CHECK(igraph_strvector_set(value, i, s)); + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +static int igraph_i_cattribute_get_bool_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_bool_t *value) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int j; + igraph_attribute_record_t *rec; + igraph_vector_bool_t *log; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + + rec = VECTOR(*eal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERROR("Boolean edge attribute expected.", IGRAPH_EINVAL); + } + log = (igraph_vector_bool_t*)rec->value; + if (igraph_es_is_all(&es)) { + igraph_vector_bool_clear(value); + IGRAPH_CHECK(igraph_vector_bool_append(value, log)); + } else { + igraph_eit_t it; + long int i = 0; + IGRAPH_CHECK(igraph_eit_create(graph, es, &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + IGRAPH_CHECK(igraph_vector_bool_resize(value, IGRAPH_EIT_SIZE(it))); + for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) { + long int e = IGRAPH_EIT_GET(it); + VECTOR(*value)[i] = VECTOR(*log)[e]; + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/* -------------------------------------- */ + +const igraph_attribute_table_t igraph_cattribute_table = { + &igraph_i_cattribute_init, + &igraph_i_cattribute_destroy, + &igraph_i_cattribute_copy, + &igraph_i_cattribute_add_vertices, + &igraph_i_cattribute_permute_vertices, + &igraph_i_cattribute_combine_vertices, &igraph_i_cattribute_add_edges, + &igraph_i_cattribute_permute_edges, + &igraph_i_cattribute_combine_edges, + &igraph_i_cattribute_get_info, + &igraph_i_cattribute_has_attr, + &igraph_i_cattribute_gettype, + &igraph_i_cattribute_get_numeric_graph_attr, + &igraph_i_cattribute_get_string_graph_attr, + &igraph_i_cattribute_get_bool_graph_attr, + &igraph_i_cattribute_get_numeric_vertex_attr, + &igraph_i_cattribute_get_string_vertex_attr, + &igraph_i_cattribute_get_bool_vertex_attr, + &igraph_i_cattribute_get_numeric_edge_attr, + &igraph_i_cattribute_get_string_edge_attr, + &igraph_i_cattribute_get_bool_edge_attr +}; + +/* -------------------------------------- */ + +/** + * \section cattributes + * There is an experimental attribute handler that can be used + * from C code. In this section we show how this works. This attribute + * handler is by default not attached (the default is no attribute + * handler), so we first need to attach it: + * + * igraph_set_attribute_table(&igraph_cattribute_table); + * + * + * Now the attribute functions are available. Please note that + * the attribute handler must be attached before you call any other + * igraph functions, otherwise you might end up with graphs without + * attributes and an active attribute handler, which might cause + * unexpected program behaviour. The rule is that you attach the + * attribute handler in the beginning of your + * main() and never touch it again. (Detaching + * the attribute handler might lead to memory leaks.) + * + * It is not currently possible to have attribute handlers on a + * per-graph basis. All graphs in an application must be managed with + * the same attribute handler. (Including the default case when there + * is no attribute handler at all. + * + * The C attribute handler supports attaching real numbers and + * character strings as attributes. No vectors are allowed, i.e. every + * vertex might have an attribute called name, but it is + * not possible to have a coords graph (or other) + * attribute which is a vector of numbers. + * + * \example examples/simple/cattributes.c + * \example examples/simple/cattributes2.c + * \example examples/simple/cattributes3.c + * \example examples/simple/cattributes4.c + */ + +/** + * \function igraph_cattribute_GAN + * Query a numeric graph attribute. + * + * Returns the value of the given numeric graph attribute. + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute to query. + * \return The value of the attribute. + * + * \sa \ref GAN for a simpler interface. + * + * Time complexity: O(Ag), the number of graph attributes. + */ +igraph_real_t igraph_cattribute_GAN(const igraph_t *graph, const char *name) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *gal = &attr->gal; + long int j; + igraph_attribute_record_t *rec; + igraph_vector_t *num; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (!l) { + igraph_error("Unknown attribute", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EINVAL); + return 0; + } + + rec = VECTOR(*gal)[j]; + num = (igraph_vector_t*)rec->value; + return VECTOR(*num)[0]; +} + +/** + * \function igraph_cattribute_GAB + * Query a boolean graph attribute. + * + * Returns the value of the given numeric graph attribute. + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute to query. + * \return The value of the attribute. + * + * \sa \ref GAB for a simpler interface. + * + * Time complexity: O(Ag), the number of graph attributes. + */ +igraph_bool_t igraph_cattribute_GAB(const igraph_t *graph, const char *name) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *gal = &attr->gal; + long int j; + igraph_attribute_record_t *rec; + igraph_vector_bool_t *log; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (!l) { + igraph_error("Unknown attribute", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EINVAL); + return 0; + } + + rec = VECTOR(*gal)[j]; + log = (igraph_vector_bool_t*)rec->value; + return VECTOR(*log)[0]; +} + +/** + * \function igraph_cattribute_GAS + * Query a string graph attribute. + * + * Returns a const pointer to the string graph attribute + * specified in \p name. + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute to query. + * \return The value of the attribute. + * + * \sa \ref GAS for a simpler interface. + * + * Time complexity: O(Ag), the number of graph attributes. + */ +const char* igraph_cattribute_GAS(const igraph_t *graph, const char *name) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *gal = &attr->gal; + long int j; + igraph_attribute_record_t *rec; + igraph_strvector_t *str; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (!l) { + igraph_error("Unknown attribute", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EINVAL); + return 0; + } + + rec = VECTOR(*gal)[j]; + str = (igraph_strvector_t*)rec->value; + return STR(*str, 0); +} + +/** + * \function igraph_cattribute_VAN + * Query a numeric vertex attribute. + * + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute. + * \param vid The id of the queried vertex. + * \return The value of the attribute. + * + * \sa \ref VAN macro for a simpler interface. + * + * Time complexity: O(Av), the number of vertex attributes. + */ +igraph_real_t igraph_cattribute_VAN(const igraph_t *graph, const char *name, + igraph_integer_t vid) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int j; + igraph_attribute_record_t *rec; + igraph_vector_t *num; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (!l) { + igraph_error("Unknown attribute", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EINVAL); + return 0; + } + + rec = VECTOR(*val)[j]; + num = (igraph_vector_t*)rec->value; + return VECTOR(*num)[(long int)vid]; +} + +/** + * \function igraph_cattribute_VAB + * Query a boolean vertex attribute. + * + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute. + * \param vid The id of the queried vertex. + * \return The value of the attribute. + * + * \sa \ref VAB macro for a simpler interface. + * + * Time complexity: O(Av), the number of vertex attributes. + */ +igraph_bool_t igraph_cattribute_VAB(const igraph_t *graph, const char *name, + igraph_integer_t vid) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int j; + igraph_attribute_record_t *rec; + igraph_vector_bool_t *log; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (!l) { + igraph_error("Unknown attribute", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EINVAL); + return 0; + } + + rec = VECTOR(*val)[j]; + log = (igraph_vector_bool_t*)rec->value; + return VECTOR(*log)[(long int)vid]; +} + +/** + * \function igraph_cattribute_VAS + * Query a string vertex attribute. + * + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute. + * \param vid The id of the queried vertex. + * \return The value of the attribute. + * + * \sa The macro \ref VAS for a simpler interface. + * + * Time complexity: O(Av), the number of vertex attributes. + */ +const char* igraph_cattribute_VAS(const igraph_t *graph, const char *name, + igraph_integer_t vid) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int j; + igraph_attribute_record_t *rec; + igraph_strvector_t *str; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (!l) { + igraph_error("Unknown attribute", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EINVAL); + return 0; + } + + rec = VECTOR(*val)[j]; + str = (igraph_strvector_t*)rec->value; + return STR(*str, (long int)vid); +} + +/** + * \function igraph_cattribute_EAN + * Query a numeric edge attribute. + * + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute. + * \param eid The id of the queried edge. + * \return The value of the attribute. + * + * \sa \ref EAN for an easier interface. + * + * Time complexity: O(Ae), the number of edge attributes. + */ +igraph_real_t igraph_cattribute_EAN(const igraph_t *graph, const char *name, + igraph_integer_t eid) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int j; + igraph_attribute_record_t *rec; + igraph_vector_t *num; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (!l) { + igraph_error("Unknown attribute", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EINVAL); + return 0; + } + + rec = VECTOR(*eal)[j]; + num = (igraph_vector_t*)rec->value; + return VECTOR(*num)[(long int)eid]; +} + +/** + * \function igraph_cattribute_EAB + * Query a boolean edge attribute. + * + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute. + * \param eid The id of the queried edge. + * \return The value of the attribute. + * + * \sa \ref EAB for an easier interface. + * + * Time complexity: O(Ae), the number of edge attributes. + */ +igraph_bool_t igraph_cattribute_EAB(const igraph_t *graph, const char *name, + igraph_integer_t eid) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int j; + igraph_attribute_record_t *rec; + igraph_vector_bool_t *log; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (!l) { + igraph_error("Unknown attribute", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EINVAL); + return 0; + } + + rec = VECTOR(*eal)[j]; + log = (igraph_vector_bool_t*)rec->value; + return VECTOR(*log)[(long int)eid]; +} + +/** + * \function igraph_cattribute_EAS + * Query a string edge attribute. + * + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute. + * \param eid The id of the queried edge. + * \return The value of the attribute. + * + * \se \ref EAS if you want to type less. + * + * Time complexity: O(Ae), the number of edge attributes. + */ +const char* igraph_cattribute_EAS(const igraph_t *graph, const char *name, + igraph_integer_t eid) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int j; + igraph_attribute_record_t *rec; + igraph_strvector_t *str; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (!l) { + igraph_error("Unknown attribute", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EINVAL); + return 0; + } + + rec = VECTOR(*eal)[j]; + str = (igraph_strvector_t*)rec->value; + return STR(*str, (long int)eid); +} + +/** + * \function igraph_cattribute_VANV + * Query a numeric vertex attribute for many vertices + * + * \param graph The input graph. + * \param name The name of the attribute. + * \param vids The vertices to query. + * \param result Pointer to an initialized vector, the result is + * stored here. It will be resized, if needed. + * \return Error code. + * + * Time complexity: O(v), where v is the number of vertices in 'vids'. + */ + +int igraph_cattribute_VANV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_vector_t *result) { + + return igraph_i_cattribute_get_numeric_vertex_attr(graph, name, vids, + result); +} + +/** + * \function igraph_cattribute_VABV + * Query a boolean vertex attribute for many vertices + * + * \param graph The input graph. + * \param name The name of the attribute. + * \param vids The vertices to query. + * \param result Pointer to an initialized boolean vector, the result is + * stored here. It will be resized, if needed. + * \return Error code. + * + * Time complexity: O(v), where v is the number of vertices in 'vids'. + */ + +int igraph_cattribute_VABV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_vector_bool_t *result) { + + return igraph_i_cattribute_get_bool_vertex_attr(graph, name, vids, + result); +} + +/** + * \function igraph_cattribute_EANV + * Query a numeric edge attribute for many edges + * + * \param graph The input graph. + * \param name The name of the attribute. + * \param eids The edges to query. + * \param result Pointer to an initialized vector, the result is + * stored here. It will be resized, if needed. + * \return Error code. + * + * Time complexity: O(e), where e is the number of edges in 'eids'. + */ + +int igraph_cattribute_EANV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_vector_t *result) { + + return igraph_i_cattribute_get_numeric_edge_attr(graph, name, eids, + result); +} + +/** + * \function igraph_cattribute_EABV + * Query a boolean edge attribute for many edges + * + * \param graph The input graph. + * \param name The name of the attribute. + * \param eids The edges to query. + * \param result Pointer to an initialized boolean vector, the result is + * stored here. It will be resized, if needed. + * \return Error code. + * + * Time complexity: O(e), where e is the number of edges in 'eids'. + */ + +int igraph_cattribute_EABV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_vector_bool_t *result) { + + return igraph_i_cattribute_get_bool_edge_attr(graph, name, eids, + result); +} + +/** + * \function igraph_cattribute_VASV + * Query a string vertex attribute for many vertices + * + * \param graph The input graph. + * \param name The name of the attribute. + * \param vids The vertices to query. + * \param result Pointer to an initialized string vector, the result + * is stored here. It will be resized, if needed. + * \return Error code. + * + * Time complexity: O(v), where v is the number of vertices in 'vids'. + * (We assume that the string attributes have a bounded length.) + */ + +int igraph_cattribute_VASV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_strvector_t *result) { + + return igraph_i_cattribute_get_string_vertex_attr(graph, name, vids, + result); +} + +/** + * \function igraph_cattribute_EASV + * Query a string edge attribute for many edges + * + * \param graph The input graph. + * \param name The name of the attribute. + * \param vids The edges to query. + * \param result Pointer to an initialized string vector, the result + * is stored here. It will be resized, if needed. + * \return Error code. + * + * Time complexity: O(e), where e is the number of edges in + * 'eids'. (We assume that the string attributes have a bounded length.) + */ + +int igraph_cattribute_EASV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_strvector_t *result) { + + return igraph_i_cattribute_get_string_edge_attr(graph, name, eids, + result); +} + +/** + * \function igraph_cattribute_list + * List all attributes + * + * See \ref igraph_attribute_type_t for the various attribute types. + * \param graph The input graph. + * \param gnames String vector, the names of the graph attributes. + * \param gtypes Numeric vector, the types of the graph attributes. + * \param vnames String vector, the names of the vertex attributes. + * \param vtypes Numeric vector, the types of the vertex attributes. + * \param enames String vector, the names of the edge attributes. + * \param etypes Numeric vector, the types of the edge attributes. + * \return Error code. + * + * Naturally, the string vector with the attribute names and the + * numeric vector with the attribute types are in the right order, + * i.e. the first name corresponds to the first type, etc. + * + * Time complexity: O(Ag+Av+Ae), the number of all attributes. + */ +int igraph_cattribute_list(const igraph_t *graph, + igraph_strvector_t *gnames, igraph_vector_t *gtypes, + igraph_strvector_t *vnames, igraph_vector_t *vtypes, + igraph_strvector_t *enames, igraph_vector_t *etypes) { + return igraph_i_cattribute_get_info(graph, gnames, gtypes, vnames, vtypes, + enames, etypes); +} + +/** + * \function igraph_cattribute_has_attr + * Checks whether a (graph, vertex or edge) attribute exists + * + * \param graph The graph. + * \param type The type of the attribute, \c IGRAPH_ATTRIBUTE_GRAPH, + * \c IGRAPH_ATTRIBUTE_VERTEX or \c IGRAPH_ATTRIBUTE_EDGE. + * \param name Character constant, the name of the attribute. + * \return Logical value, TRUE if the attribute exists, FALSE otherwise. + * + * Time complexity: O(A), the number of (graph, vertex or edge) + * attributes, assuming attribute names are not too long. + */ +igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph, + igraph_attribute_elemtype_t type, + const char *name) { + return igraph_i_cattribute_has_attr(graph, type, name); +} + +/** + * \function igraph_cattribute_GAN_set + * Set a numeric graph attribute + * + * \param graph The graph. + * \param name Name of the graph attribute. If there is no such + * attribute yet, then it will be added. + * \param value The (new) value of the graph attribute. + * \return Error code. + * + * \se \ref SETGAN if you want to type less. + * + * Time complexity: O(1). + */ +int igraph_cattribute_GAN_set(igraph_t *graph, const char *name, + igraph_real_t value) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *gal = &attr->gal; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (l) { + igraph_attribute_record_t *rec = VECTOR(*gal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); + } else { + igraph_vector_t *num = (igraph_vector_t *)rec->value; + VECTOR(*num)[0] = value; + } + } else { + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_vector_t *num; + if (!rec) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + rec->type = IGRAPH_ATTRIBUTE_NUMERIC; + num = IGRAPH_CALLOC(1, igraph_vector_t); + if (!num) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, num); + IGRAPH_VECTOR_INIT_FINALLY(num, 1); + VECTOR(*num)[0] = value; + rec->value = num; + IGRAPH_CHECK(igraph_vector_ptr_push_back(gal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_cattribute_GAB_set + * Set a boolean graph attribute + * + * \param graph The graph. + * \param name Name of the graph attribute. If there is no such + * attribute yet, then it will be added. + * \param value The (new) value of the graph attribute. + * \return Error code. + * + * \se \ref SETGAN if you want to type less. + * + * Time complexity: O(1). + */ +int igraph_cattribute_GAB_set(igraph_t *graph, const char *name, + igraph_bool_t value) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *gal = &attr->gal; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (l) { + igraph_attribute_record_t *rec = VECTOR(*gal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); + } else { + igraph_vector_bool_t *log = (igraph_vector_bool_t *)rec->value; + VECTOR(*log)[0] = value; + } + } else { + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_vector_bool_t *log; + if (!rec) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + rec->type = IGRAPH_ATTRIBUTE_BOOLEAN; + log = IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (!log) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, log); + IGRAPH_CHECK(igraph_vector_bool_init(log, 1)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, log); + VECTOR(*log)[0] = value; + rec->value = log; + IGRAPH_CHECK(igraph_vector_ptr_push_back(gal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_cattribute_GAS_set + * Set a string graph attribute. + * + * \param graph The graph. + * \param name Name of the graph attribute. If there is no such + * attribute yet, then it will be added. + * \param value The (new) value of the graph attribute. It will be + * copied. + * \return Error code. + * + * \se \ref SETGAS if you want to type less. + * + * Time complexity: O(1). + */ +int igraph_cattribute_GAS_set(igraph_t *graph, const char *name, + const char *value) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *gal = &attr->gal; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (l) { + igraph_attribute_record_t *rec = VECTOR(*gal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); + } else { + igraph_strvector_t *str = (igraph_strvector_t*)rec->value; + IGRAPH_CHECK(igraph_strvector_set(str, 0, value)); + } + } else { + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_strvector_t *str; + if (!rec) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + rec->type = IGRAPH_ATTRIBUTE_STRING; + str = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!str) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, str); + IGRAPH_STRVECTOR_INIT_FINALLY(str, 1); + IGRAPH_CHECK(igraph_strvector_set(str, 0, value)); + rec->value = str; + IGRAPH_CHECK(igraph_vector_ptr_push_back(gal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_cattribute_VAN_set + * Set a numeric vertex attribute + * + * The attribute will be added if not present already. If present it + * will be overwritten. The same \p value is set for all vertices + * included in \p vid. + * \param graph The graph. + * \param name Name of the attribute. + * \param vid Vertices for which to set the attribute. + * \param value The (new) value of the attribute. + * \return Error code. + * + * \sa \ref SETVAN for a simpler way. + * + * Time complexity: O(n), the number of vertices if the attribute is + * new, O(|vid|) otherwise. + */ +int igraph_cattribute_VAN_set(igraph_t *graph, const char *name, + igraph_integer_t vid, igraph_real_t value) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (l) { + igraph_attribute_record_t *rec = VECTOR(*val)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); + } else { + igraph_vector_t *num = (igraph_vector_t*)rec->value; + VECTOR(*num)[(long int)vid] = value; + } + } else { + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_vector_t *num; + if (!rec) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + rec->type = IGRAPH_ATTRIBUTE_NUMERIC; + num = IGRAPH_CALLOC(1, igraph_vector_t); + if (!num) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, num); + IGRAPH_VECTOR_INIT_FINALLY(num, igraph_vcount(graph)); + igraph_vector_fill(num, IGRAPH_NAN); + VECTOR(*num)[(long int)vid] = value; + rec->value = num; + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_cattribute_VAB_set + * Set a boolean vertex attribute + * + * The attribute will be added if not present already. If present it + * will be overwritten. The same \p value is set for all vertices + * included in \p vid. + * \param graph The graph. + * \param name Name of the attribute. + * \param vid Vertices for which to set the attribute. + * \param value The (new) value of the attribute. + * \return Error code. + * + * \sa \ref SETVAB for a simpler way. + * + * Time complexity: O(n), the number of vertices if the attribute is + * new, O(|vid|) otherwise. + */ +int igraph_cattribute_VAB_set(igraph_t *graph, const char *name, + igraph_integer_t vid, igraph_bool_t value) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (l) { + igraph_attribute_record_t *rec = VECTOR(*val)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); + } else { + igraph_vector_bool_t *log = (igraph_vector_bool_t*)rec->value; + VECTOR(*log)[(long int)vid] = value; + } + } else { + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_vector_bool_t *log; + if (!rec) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + rec->type = IGRAPH_ATTRIBUTE_BOOLEAN; + log = IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (!log) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, log); + IGRAPH_CHECK(igraph_vector_bool_init(log, igraph_vcount(graph))); + IGRAPH_FINALLY(igraph_vector_bool_destroy, log); + igraph_vector_bool_fill(log, 0); + VECTOR(*log)[(long int)vid] = value; + rec->value = log; + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_cattribute_VAS_set + * Set a string vertex attribute + * + * The attribute will be added if not present already. If present it + * will be overwritten. The same \p value is set for all vertices + * included in \p vid. + * \param graph The graph. + * \param name Name of the attribute. + * \param vid Vertices for which to set the attribute. + * \param value The (new) value of the attribute. + * \return Error code. + * + * \sa \ref SETVAS for a simpler way. + * + * Time complexity: O(n*l), n is the number of vertices, l is the + * length of the string to set. If the attribute if not new then only + * O(|vid|*l). + */ +int igraph_cattribute_VAS_set(igraph_t *graph, const char *name, + igraph_integer_t vid, const char *value) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (l) { + igraph_attribute_record_t *rec = VECTOR(*val)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); + } else { + igraph_strvector_t *str = (igraph_strvector_t*)rec->value; + IGRAPH_CHECK(igraph_strvector_set(str, vid, value)); + } + } else { + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_strvector_t *str; + if (!rec) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + rec->type = IGRAPH_ATTRIBUTE_STRING; + str = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!str) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, str); + IGRAPH_STRVECTOR_INIT_FINALLY(str, igraph_vcount(graph)); + IGRAPH_CHECK(igraph_strvector_set(str, vid, value)); + rec->value = str; + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_cattribute_EAN_set + * Set a numeric edge attribute + * + * The attribute will be added if not present already. If present it + * will be overwritten. The same \p value is set for all edges + * included in \p vid. + * \param graph The graph. + * \param name Name of the attribute. + * \param eid Edges for which to set the attribute. + * \param value The (new) value of the attribute. + * \return Error code. + * + * \sa \ref SETEAN for a simpler way. + * + * Time complexity: O(e), the number of edges if the attribute is + * new, O(|eid|) otherwise. + */ +int igraph_cattribute_EAN_set(igraph_t *graph, const char *name, + igraph_integer_t eid, igraph_real_t value) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (l) { + igraph_attribute_record_t *rec = VECTOR(*eal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); + } else { + igraph_vector_t *num = (igraph_vector_t*)rec->value; + VECTOR(*num)[(long int)eid] = value; + } + } else { + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_vector_t *num; + if (!rec) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + rec->type = IGRAPH_ATTRIBUTE_NUMERIC; + num = IGRAPH_CALLOC(1, igraph_vector_t); + if (!num) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, num); + IGRAPH_VECTOR_INIT_FINALLY(num, igraph_ecount(graph)); + igraph_vector_fill(num, IGRAPH_NAN); + VECTOR(*num)[(long int)eid] = value; + rec->value = num; + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_cattribute_EAB_set + * Set a boolean edge attribute + * + * The attribute will be added if not present already. If present it + * will be overwritten. The same \p value is set for all edges + * included in \p vid. + * \param graph The graph. + * \param name Name of the attribute. + * \param eid Edges for which to set the attribute. + * \param value The (new) value of the attribute. + * \return Error code. + * + * \sa \ref SETEAB for a simpler way. + * + * Time complexity: O(e), the number of edges if the attribute is + * new, O(|eid|) otherwise. + */ +int igraph_cattribute_EAB_set(igraph_t *graph, const char *name, + igraph_integer_t eid, igraph_bool_t value) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (l) { + igraph_attribute_record_t *rec = VECTOR(*eal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); + } else { + igraph_vector_bool_t *log = (igraph_vector_bool_t*)rec->value; + VECTOR(*log)[(long int)eid] = value; + } + } else { + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_vector_bool_t *log; + if (!rec) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + rec->type = IGRAPH_ATTRIBUTE_BOOLEAN; + log = IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (!log) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, log); + IGRAPH_CHECK(igraph_vector_bool_init(log, igraph_ecount(graph))); + IGRAPH_FINALLY(igraph_vector_bool_destroy, log); + igraph_vector_bool_fill(log, 0); + VECTOR(*log)[(long int)eid] = value; + rec->value = log; + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_cattribute_EAS_set + * Set a string edge attribute + * + * The attribute will be added if not present already. If present it + * will be overwritten. The same \p value is set for all edges + * included in \p vid. + * \param graph The graph. + * \param name Name of the attribute. + * \param eid Edges for which to set the attribute. + * \param value The (new) value of the attribute. + * \return Error code. + * + * \sa \ref SETEAS for a simpler way. + * + * Time complexity: O(e*l), n is the number of edges, l is the + * length of the string to set. If the attribute if not new then only + * O(|eid|*l). + */ +int igraph_cattribute_EAS_set(igraph_t *graph, const char *name, + igraph_integer_t eid, const char *value) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (l) { + igraph_attribute_record_t *rec = VECTOR(*eal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); + } else { + igraph_strvector_t *str = (igraph_strvector_t*)rec->value; + IGRAPH_CHECK(igraph_strvector_set(str, eid, value)); + } + } else { + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_strvector_t *str; + if (!rec) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + rec->type = IGRAPH_ATTRIBUTE_STRING; + str = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!str) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, str); + IGRAPH_STRVECTOR_INIT_FINALLY(str, igraph_ecount(graph)); + IGRAPH_CHECK(igraph_strvector_set(str, eid, value)); + rec->value = str; + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_cattribute_VAN_setv + * Set a numeric vertex attribute for all vertices. + * + * The attribute will be added if not present yet. + * \param graph The graph. + * \param name Name of the attribute. + * \param v The new attribute values. The length of this vector must + * match the number of vertices. + * \return Error code. + * + * \sa \ref SETVANV for a simpler way. + * + * Time complexity: O(n), the number of vertices. + */ + +int igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, + const igraph_vector_t *v) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + /* Check length first */ + if (igraph_vector_size(v) != igraph_vcount(graph)) { + IGRAPH_ERROR("Invalid vertex attribute vector length", IGRAPH_EINVAL); + } + + if (l) { + /* Already present, check type */ + igraph_attribute_record_t *rec = VECTOR(*val)[j]; + igraph_vector_t *num = (igraph_vector_t *)rec->value; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); + } + igraph_vector_clear(num); + IGRAPH_CHECK(igraph_vector_append(num, v)); + } else { + /* Add it */ + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_vector_t *num; + if (!rec) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->type = IGRAPH_ATTRIBUTE_NUMERIC; + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + num = IGRAPH_CALLOC(1, igraph_vector_t); + if (!num) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, num); + rec->value = num; + IGRAPH_CHECK(igraph_vector_copy(num, v)); + IGRAPH_FINALLY(igraph_vector_destroy, num); + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} +/** + * \function igraph_cattribute_VAB_setv + * Set a boolean vertex attribute for all vertices. + * + * The attribute will be added if not present yet. + * \param graph The graph. + * \param name Name of the attribute. + * \param v The new attribute values. The length of this boolean vector must + * match the number of vertices. + * \return Error code. + * + * \sa \ref SETVANV for a simpler way. + * + * Time complexity: O(n), the number of vertices. + */ + +int igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, + const igraph_vector_bool_t *v) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + /* Check length first */ + if (igraph_vector_bool_size(v) != igraph_vcount(graph)) { + IGRAPH_ERROR("Invalid vertex attribute vector length", IGRAPH_EINVAL); + } + + if (l) { + /* Already present, check type */ + igraph_attribute_record_t *rec = VECTOR(*val)[j]; + igraph_vector_bool_t *log = (igraph_vector_bool_t *)rec->value; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); + } + igraph_vector_bool_clear(log); + IGRAPH_CHECK(igraph_vector_bool_append(log, v)); + } else { + /* Add it */ + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_vector_bool_t *log; + if (!rec) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->type = IGRAPH_ATTRIBUTE_BOOLEAN; + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + log = IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (!log) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, log); + rec->value = log; + IGRAPH_CHECK(igraph_vector_bool_copy(log, v)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, log); + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_cattribute_VAS_setv + * Set a string vertex attribute for all vertices. + * + * The attribute will be added if not present yet. + * \param graph The graph. + * \param name Name of the attribute. + * \param sv String vector, the new attribute values. The length of this vector must + * match the number of vertices. + * \return Error code. + * + * \sa \ref SETVASV for a simpler way. + * + * Time complexity: O(n+l), n is the number of vertices, l is the + * total length of the strings. + */ +int igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, + const igraph_strvector_t *sv) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + /* Check length first */ + if (igraph_strvector_size(sv) != igraph_vcount(graph)) { + IGRAPH_ERROR("Invalid vertex attribute vector length", IGRAPH_EINVAL); + } + + if (l) { + /* Already present, check type */ + igraph_attribute_record_t *rec = VECTOR(*val)[j]; + igraph_strvector_t *str = (igraph_strvector_t *)rec->value; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); + } + igraph_strvector_clear(str); + IGRAPH_CHECK(igraph_strvector_append(str, sv)); + } else { + /* Add it */ + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_strvector_t *str; + if (!rec) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->type = IGRAPH_ATTRIBUTE_STRING; + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + str = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!str) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, str); + rec->value = str; + IGRAPH_CHECK(igraph_strvector_copy(str, sv)); + IGRAPH_FINALLY(igraph_strvector_destroy, str); + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_cattribute_EAN_setv + * Set a numeric edge attribute for all edges. + * + * The attribute will be added if not present yet. + * \param graph The graph. + * \param name Name of the attribute. + * \param v The new attribute values. The length of this vector must + * match the number of edges. + * \return Error code. + * + * \sa \ref SETEANV for a simpler way. + * + * Time complexity: O(e), the number of edges. + */ +int igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, + const igraph_vector_t *v) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + /* Check length first */ + if (igraph_vector_size(v) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid edge attribute vector length", IGRAPH_EINVAL); + } + + if (l) { + /* Already present, check type */ + igraph_attribute_record_t *rec = VECTOR(*eal)[j]; + igraph_vector_t *num = (igraph_vector_t *)rec->value; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); + } + igraph_vector_clear(num); + IGRAPH_CHECK(igraph_vector_append(num, v)); + } else { + /* Add it */ + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_vector_t *num; + if (!rec) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->type = IGRAPH_ATTRIBUTE_NUMERIC; + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + num = IGRAPH_CALLOC(1, igraph_vector_t); + if (!num) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, num); + rec->value = num; + IGRAPH_CHECK(igraph_vector_copy(num, v)); + IGRAPH_FINALLY(igraph_vector_destroy, num); + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_cattribute_EAB_setv + * Set a boolean edge attribute for all edges. + * + * The attribute will be added if not present yet. + * \param graph The graph. + * \param name Name of the attribute. + * \param v The new attribute values. The length of this vector must + * match the number of edges. + * \return Error code. + * + * \sa \ref SETEABV for a simpler way. + * + * Time complexity: O(e), the number of edges. + */ +int igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, + const igraph_vector_bool_t *v) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + /* Check length first */ + if (igraph_vector_bool_size(v) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid edge attribute vector length", IGRAPH_EINVAL); + } + + if (l) { + /* Already present, check type */ + igraph_attribute_record_t *rec = VECTOR(*eal)[j]; + igraph_vector_bool_t *log = (igraph_vector_bool_t *)rec->value; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); + } + igraph_vector_bool_clear(log); + IGRAPH_CHECK(igraph_vector_bool_append(log, v)); + } else { + /* Add it */ + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_vector_bool_t *log; + if (!rec) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->type = IGRAPH_ATTRIBUTE_BOOLEAN; + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + log = IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (!log) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, log); + rec->value = log; + IGRAPH_CHECK(igraph_vector_bool_copy(log, v)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, log); + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_cattribute_EAS_setv + * Set a string edge attribute for all edges. + * + * The attribute will be added if not present yet. + * \param graph The graph. + * \param name Name of the attribute. + * \param sv String vector, the new attribute values. The length of this vector must + * match the number of edges. + * \return Error code. + * + * \sa \ref SETEASV for a simpler way. + * + * Time complexity: O(e+l), e is the number of edges, l is the + * total length of the strings. + */ +int igraph_cattribute_EAS_setv(igraph_t *graph, const char *name, + const igraph_strvector_t *sv) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + /* Check length first */ + if (igraph_strvector_size(sv) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid edge attribute vector length", IGRAPH_EINVAL); + } + + if (l) { + /* Already present, check type */ + igraph_attribute_record_t *rec = VECTOR(*eal)[j]; + igraph_strvector_t *str = (igraph_strvector_t *)rec->value; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); + } + igraph_strvector_clear(str); + IGRAPH_CHECK(igraph_strvector_append(str, sv)); + } else { + /* Add it */ + igraph_attribute_record_t *rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + igraph_strvector_t *str; + if (!rec) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->type = IGRAPH_ATTRIBUTE_STRING; + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + str = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!str) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, str); + rec->value = str; + IGRAPH_CHECK(igraph_strvector_copy(str, sv)); + IGRAPH_FINALLY(igraph_strvector_destroy, str); + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +static void igraph_i_cattribute_free_rec(igraph_attribute_record_t *rec) { + + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *num = (igraph_vector_t*)rec->value; + igraph_vector_destroy(num); + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *str = (igraph_strvector_t*)rec->value; + igraph_strvector_destroy(str); + } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_vector_bool_t *boolvec = (igraph_vector_bool_t*)rec->value; + igraph_vector_bool_destroy(boolvec); + } + IGRAPH_FREE(rec->name); + IGRAPH_FREE(rec->value); + IGRAPH_FREE(rec); +} + +/** + * \function igraph_cattribute_remove_g + * Remove a graph attribute + * + * \param graph The graph object. + * \param name Name of the graph attribute to remove. + * + * \sa \ref DELGA for a simpler way. + * + */ +void igraph_cattribute_remove_g(igraph_t *graph, const char *name) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *gal = &attr->gal; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (l) { + igraph_i_cattribute_free_rec(VECTOR(*gal)[j]); + igraph_vector_ptr_remove(gal, j); + } else { + IGRAPH_WARNING("Cannot remove non-existent graph attribute"); + } +} + +/** + * \function igraph_cattribute_remove_v + * Remove a vertex attribute + * + * \param graph The graph object. + * \param name Name of the vertex attribute to remove. + * + * \sa \ref DELVA for a simpler way. + * + */ +void igraph_cattribute_remove_v(igraph_t *graph, const char *name) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (l) { + igraph_i_cattribute_free_rec(VECTOR(*val)[j]); + igraph_vector_ptr_remove(val, j); + } else { + IGRAPH_WARNING("Cannot remove non-existent graph attribute"); + } +} + +/** + * \function igraph_cattribute_remove_e + * Remove an edge attribute + * + * \param graph The graph object. + * \param name Name of the edge attribute to remove. + * + * \sa \ref DELEA for a simpler way. + * + */ +void igraph_cattribute_remove_e(igraph_t *graph, const char *name) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + long int j; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (l) { + igraph_i_cattribute_free_rec(VECTOR(*eal)[j]); + igraph_vector_ptr_remove(eal, j); + } else { + IGRAPH_WARNING("Cannot remove non-existent graph attribute"); + } +} + +/** + * \function igraph_cattribute_remove_all + * Remove all graph/vertex/edge attributes + * + * \param graph The graph object. + * \param g Boolean, whether to remove graph attributes. + * \param v Boolean, whether to remove vertex attributes. + * \param e Boolean, whether to remove edge attributes. + * + * \sa \ref DELGAS, \ref DELVAS, \ref DELEAS, \ref DELALL for simpler + * ways. + */ +void igraph_cattribute_remove_all(igraph_t *graph, igraph_bool_t g, + igraph_bool_t v, igraph_bool_t e) { + + igraph_i_cattributes_t *attr = graph->attr; + + if (g) { + igraph_vector_ptr_t *gal = &attr->gal; + long int i, n = igraph_vector_ptr_size(gal); + for (i = 0; i < n; i++) { + igraph_i_cattribute_free_rec(VECTOR(*gal)[i]); + } + igraph_vector_ptr_clear(gal); + } + if (v) { + igraph_vector_ptr_t *val = &attr->val; + long int i, n = igraph_vector_ptr_size(val); + for (i = 0; i < n; i++) { + igraph_i_cattribute_free_rec(VECTOR(*val)[i]); + } + igraph_vector_ptr_clear(val); + } + if (e) { + igraph_vector_ptr_t *eal = &attr->eal; + long int i, n = igraph_vector_ptr_size(eal); + for (i = 0; i < n; i++) { + igraph_i_cattribute_free_rec(VECTOR(*eal)[i]); + } + igraph_vector_ptr_clear(eal); + } +} diff --git a/src/rigraph/core/graph/iterators.c b/src/rigraph/core/graph/iterators.c new file mode 100644 index 0000000..5d85fa5 --- /dev/null +++ b/src/rigraph/core/graph/iterators.c @@ -0,0 +1,1932 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_iterators.h" +#include "igraph_memory.h" +#include "igraph_interface.h" + +#include +#include + +/** + * \section about_iterators About selectors, iterators + * + * Everything about vertices and vertex selectors also applies + * to edges and edge selectors unless explicitly noted otherwise. + * + * The vertex (and edge) selector notion was introduced in igraph 0.2. + * It is a way to reference a sequence of vertices or edges + * independently of the graph. + * + * While this might sound quite mysterious, it is actually very + * simple. For example, all vertices of a graph can be selected by + * \ref igraph_vs_all() and the graph independence means that + * \ref igraph_vs_all() is not parametrized by a graph object. That is, + * \ref igraph_vs_all() is the general \em concept of selecting all vertices + * of a graph. A vertex selector is then a way to specify the class of vertices + * to be visited. The selector might specify that all vertices of a graph or + * all the neighbours of a vertex are to be visited. A vertex selector is a + * way of saying that you want to visit a bunch of vertices, as opposed to a + * vertex iterator which is a concrete plan for visiting each of the + * chosen vertices of a specific graph. + * + * To determine the actual vertex IDs implied by a vertex selector, you + * need to apply the concept of selecting vertices to a specific graph object. + * This can be accomplished by instantiating a vertex iterator using a + * specific vertex selection concept and a specific graph object. The notion + * of vertex iterators can be thought of in the following way. Given a + * specific graph object and the class of vertices to be visited, a vertex + * iterator is a road map, plan or route for how to visit the chosen + * vertices. + * + * Some vertex selectors have \em immediate versions. These have the + * prefix \c igraph_vss instead of \c igraph_vs, e.g. \ref igraph_vss_all() + * instead of \ref igraph_vs_all(). The immediate versions are to be used in + * the parameter list of the igraph functions, such as \ref igraph_degree(). + * These functions are not associated with any \type igraph_vs_t object, so + * they have no separate constructors and destructors + * (destroy functions). + */ + +/** + * \section about_vertex_selectors + * + * Vertex selectors are created by vertex selector constructors, + * can be instantiated with \ref igraph_vit_create(), and are + * destroyed with \ref igraph_vs_destroy(). + */ + +/** + * \function igraph_vs_all + * \brief Vertex set, all vertices of a graph. + * + * \param vs Pointer to an uninitialized \type igraph_vs_t object. + * \return Error code. + * \sa \ref igraph_vss_all(), \ref igraph_vs_destroy() + * + * This selector includes all vertices of a given graph in + * increasing vertex id order. + * + * + * Time complexity: O(1). + */ + +int igraph_vs_all(igraph_vs_t *vs) { + vs->type = IGRAPH_VS_ALL; + return 0; +} + +/** + * \function igraph_vss_all + * \brief All vertices of a graph (immediate version). + * + * Immediate vertex selector for all vertices in a graph. It can + * be used conveniently when some vertex property (e.g. betweenness, + * degree, etc.) should be calculated for all vertices. + * + * \return A vertex selector for all vertices in a graph. + * \sa \ref igraph_vs_all() + * + * Time complexity: O(1). + */ + +igraph_vs_t igraph_vss_all(void) { + igraph_vs_t allvs; + allvs.type = IGRAPH_VS_ALL; + return allvs; +} + +/** + * \function igraph_vs_adj + * \brief Adjacent vertices of a vertex. + * + * All neighboring vertices of a given vertex are selected by this + * selector. The \c mode argument controls the type of the neighboring + * vertices to be selected. The vertices are visited in increasing vertex + * ID order, as of igraph version 0.4. + * + * \param vs Pointer to an uninitialized vertex selector object. + * \param vid Vertex ID, the center of the neighborhood. + * \param mode Decides the type of the neighborhood for directed + * graphs. This parameter is ignored for undirected graphs. + * Possible values: + * \clist + * \cli IGRAPH_OUT + * All vertices to which there is a directed edge from \c vid. That + * is, all the out-neighbors of \c vid. + * \cli IGRAPH_IN + * All vertices from which there is a directed edge to \c vid. In + * other words, all the in-neighbors of \c vid. + * \cli IGRAPH_ALL + * All vertices to which or from which there is a directed edge + * from/to \c vid. That is, all the neighbors of \c vid considered + * as if the graph is undirected. + * \endclist + * \return Error code. + * \sa \ref igraph_vs_destroy() + * + * Time complexity: O(1). + */ + +int igraph_vs_adj(igraph_vs_t *vs, + igraph_integer_t vid, igraph_neimode_t mode) { + vs->type = IGRAPH_VS_ADJ; + vs->data.adj.vid = vid; + vs->data.adj.mode = mode; + return 0; +} + +/** + * \function igraph_vs_nonadj + * \brief Non-adjacent vertices of a vertex. + * + * All non-neighboring vertices of a given vertex. The \p mode + * argument controls the type of neighboring vertices \em not to + * select. Instead of selecting immediate neighbors of \c vid as is done by + * \ref igraph_vs_adj(), the current function selects vertices that are \em not + * immediate neighbors of \c vid. + * + * \param vs Pointer to an uninitialized vertex selector object. + * \param vid Vertex ID, the \quote center \endquote of the + * non-neighborhood. + * \param mode The type of neighborhood not to select in directed + * graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * All vertices will be selected except those to which there is a + * directed edge from \c vid. That is, we select all vertices + * excluding the out-neighbors of \c vid. + * \cli IGRAPH_IN + * All vertices will be selected except those from which there is a + * directed edge to \c vid. In other words, we select all vertices + * but the in-neighbors of \c vid. + * \cli IGRAPH_ALL + * All vertices will be selected except those from or to which there + * is a directed edge to or from \c vid. That is, we select all + * vertices of \c vid except for its immediate neighbors. + * \endclist + * \return Error code. + * \sa \ref igraph_vs_destroy() + * + * Time complexity: O(1). + * + * \example examples/simple/igraph_vs_nonadj.c + */ + +int igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, + igraph_neimode_t mode) { + vs->type = IGRAPH_VS_NONADJ; + vs->data.adj.vid = vid; + vs->data.adj.mode = mode; + return 0; +} + +/** + * \function igraph_vs_none + * \brief Empty vertex set. + * + * Creates an empty vertex selector. + * + * \param vs Pointer to an uninitialized vertex selector object. + * \return Error code. + * \sa \ref igraph_vss_none(), \ref igraph_vs_destroy() + * + * Time complexity: O(1). + */ + +int igraph_vs_none(igraph_vs_t *vs) { + vs->type = IGRAPH_VS_NONE; + return 0; +} + +/** + * \function igraph_vss_none + * \brief Empty vertex set (immediate version). + * + * The immediate version of the empty vertex selector. + * + * \return An empty vertex selector. + * \sa \ref igraph_vs_none() + * + * Time complexity: O(1). + */ + +igraph_vs_t igraph_vss_none(void) { + igraph_vs_t nonevs; + nonevs.type = IGRAPH_VS_NONE; + return nonevs; +} + +/** + * \function igraph_vs_1 + * \brief Vertex set with a single vertex. + * + * This vertex selector selects a single vertex. + * + * \param vs Pointer to an uninitialized vertex selector object. + * \param vid The vertex id to be selected. + * \return Error Code. + * \sa \ref igraph_vss_1(), \ref igraph_vs_destroy() + * + * Time complexity: O(1). + */ + +int igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid) { + vs->type = IGRAPH_VS_1; + vs->data.vid = vid; + return 0; +} + +/** + * \function igraph_vss_1 + * \brief Vertex set with a single vertex (immediate version). + * + * The immediate version of the single-vertex selector. + * + * \param vid The vertex to be selected. + * \return A vertex selector containing a single vertex. + * \sa \ref igraph_vs_1() + * + * Time complexity: O(1). + */ + +igraph_vs_t igraph_vss_1(igraph_integer_t vid) { + igraph_vs_t onevs; + onevs.type = IGRAPH_VS_1; + onevs.data.vid = vid; + return onevs; +} + +/** + * \function igraph_vs_vector + * \brief Vertex set based on a vector. + * + * This function makes it possible to handle a \type vector_t + * temporarily as a vertex selector. The vertex selector should be + * thought of like a \em view to the vector. If you make changes to + * the vector that also affects the vertex selector. Destroying the + * vertex selector does not destroy the vector. (Of course.) Do not + * destroy the vector before destroying the vertex selector, or you + * might get strange behavior. + * + * \param vs Pointer to an uninitialized vertex selector. + * \param v Pointer to a \type igraph_vector_t object. + * \return Error code. + * \sa \ref igraph_vss_vector(), \ref igraph_vs_destroy() + * + * Time complexity: O(1). + * + * \example examples/simple/igraph_vs_vector.c + */ + +int igraph_vs_vector(igraph_vs_t *vs, + const igraph_vector_t *v) { + vs->type = IGRAPH_VS_VECTORPTR; + vs->data.vecptr = v; + return 0; +} + +/** + * \function igraph_vss_vector + * \brief Vertex set based on a vector (immediate version). + * + * This is the immediate version of \ref igraph_vs_vector. + * + * \param v Pointer to a \type igraph_vector_t object. + * \return A vertex selector object containing the vertices in the + * vector. + * \sa \ref igraph_vs_vector() + * + * Time complexity: O(1). + */ + +igraph_vs_t igraph_vss_vector(const igraph_vector_t *v) { + igraph_vs_t vecvs; + vecvs.type = IGRAPH_VS_VECTORPTR; + vecvs.data.vecptr = v; + return vecvs; +} + +/** + * \function igraph_vs_vector_small + * \brief Create a vertex set by giving its elements. + * + * This function can be used to create a vertex selector with a couple + * of vertices. Do not forget to include a -1 after the + * last vertex id. The behavior of the function is undefined if you + * don't use a -1 properly. + * + * + * Note that the vertex ids supplied will be parsed as + * int's so you cannot supply arbitrarily large (too + * large for int) vertex ids here. + * + * \param vs Pointer to an uninitialized vertex selector object. + * \param ... Additional parameters, these will be the vertex ids to + * be included in the vertex selector. Supply a -1 + * after the last vertex id. + * \return Error code. + * \sa \ref igraph_vs_destroy() + * + * Time complexity: O(n), the number of vertex ids supplied. + */ + +int igraph_vs_vector_small(igraph_vs_t *vs, ...) { + va_list ap; + long int i, n = 0; + vs->type = IGRAPH_VS_VECTOR; + vs->data.vecptr = IGRAPH_CALLOC(1, igraph_vector_t); + if (vs->data.vecptr == 0) { + IGRAPH_ERROR("Cannot create vertex selector", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)vs->data.vecptr); + + va_start(ap, vs); + while (1) { + int num = va_arg(ap, int); + if (num == -1) { + break; + } + n++; + } + va_end(ap); + + IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)vs->data.vecptr, n); + + va_start(ap, vs); + for (i = 0; i < n; i++) { + VECTOR(*vs->data.vecptr)[i] = (igraph_real_t) va_arg(ap, int); + } + va_end(ap); + + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +/** + * \function igraph_vs_vector_copy + * \brief Vertex set based on a vector, with copying. + * + * This function makes it possible to handle a \type vector_t + * permanently as a vertex selector. The vertex selector creates a + * copy of the original vector, so the vector can safely be destroyed + * after creating the vertex selector. Changing the original vector + * will not affect the vertex selector. The vertex selector is + * responsible for deleting the copy made by itself. + * + * \param vs Pointer to an uninitialized vertex selector. + * \param v Pointer to a \type igraph_vector_t object. + * \return Error code. + * \sa \ref igraph_vs_destroy() + * + * Time complexity: O(1). + */ + +int igraph_vs_vector_copy(igraph_vs_t *vs, + const igraph_vector_t *v) { + vs->type = IGRAPH_VS_VECTOR; + vs->data.vecptr = IGRAPH_CALLOC(1, igraph_vector_t); + if (vs->data.vecptr == 0) { + IGRAPH_ERROR("Cannot create vertex selector", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)vs->data.vecptr); + IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)vs->data.vecptr, v)); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \function igraph_vs_seq + * \brief Vertex set, an interval of vertices. + * + * Creates a vertex selector containing all vertices with vertex id + * equal to or bigger than \c from and equal to or smaller than \c + * to. + * + * \param vs Pointer to an uninitialized vertex selector object. + * \param from The first vertex id to be included in the vertex + * selector. + * \param to The last vertex id to be included in the vertex + * selector. + * \return Error code. + * \sa \ref igraph_vss_seq(), \ref igraph_vs_destroy() + * + * Time complexity: O(1). + * + * \example examples/simple/igraph_vs_seq.c + */ + +int igraph_vs_seq(igraph_vs_t *vs, + igraph_integer_t from, igraph_integer_t to) { + vs->type = IGRAPH_VS_SEQ; + vs->data.seq.from = from; + vs->data.seq.to = to + 1; + return 0; +} + +/** + * \function igraph_vss_seq + * \brief An interval of vertices (immediate version). + * + * The immediate version of \ref igraph_vs_seq(). + * + * \param from The first vertex id to be included in the vertex + * selector. + * \param to The last vertex id to be included in the vertex + * selector. + * \return Error code. + * \sa \ref igraph_vs_seq() + * + * Time complexity: O(1). + */ + +igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to) { + igraph_vs_t vs; + vs.type = IGRAPH_VS_SEQ; + vs.data.seq.from = from; + vs.data.seq.to = to + 1; + return vs; +} + +/** + * \function igraph_vs_destroy + * \brief Destroy a vertex set. + * + * This function should be called for all vertex selectors when they + * are not needed. The memory allocated for the vertex selector will + * be deallocated. Do not call this function on vertex selectors + * created with the immediate versions of the vertex selector + * constructors (starting with igraph_vss). + * + * \param vs Pointer to a vertex selector object. + * + * Time complexity: operating system dependent, usually O(1). + */ + +void igraph_vs_destroy(igraph_vs_t *vs) { + switch (vs->type) { + case IGRAPH_VS_ALL: + case IGRAPH_VS_ADJ: + case IGRAPH_VS_NONE: + case IGRAPH_VS_1: + case IGRAPH_VS_VECTORPTR: + case IGRAPH_VS_SEQ: + case IGRAPH_VS_NONADJ: + break; + case IGRAPH_VS_VECTOR: + igraph_vector_destroy((igraph_vector_t*)vs->data.vecptr); + IGRAPH_FREE(vs->data.vecptr); + break; + default: + break; + } +} + +/** + * \function igraph_vs_is_all + * \brief Check whether all vertices are included. + * + * This function checks whether the vertex selector object was created + * by \ref igraph_vs_all() or \ref igraph_vss_all(). Note that the + * vertex selector might contain all vertices in a given graph but if + * it wasn't created by the two constructors mentioned here the return + * value will be FALSE. + * + * \param vs Pointer to a vertex selector object. + * \return TRUE (1) if the vertex selector contains all vertices and + * FALSE (0) otherwise. + * + * Time complexity: O(1). + */ + +igraph_bool_t igraph_vs_is_all(const igraph_vs_t *vs) { + return vs->type == IGRAPH_VS_ALL; +} + +int igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, + igraph_vector_t *v) { + igraph_vit_t vit; + + IGRAPH_CHECK(igraph_vit_create(graph, vs, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_CHECK(igraph_vit_as_vector(&vit, v)); + + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \function igraph_vs_copy + * \brief Creates a copy of a vertex selector. + * \param src The selector being copied. + * \param dest An uninitialized selector that will contain the copy. + */ +int igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src) { + memcpy(dest, src, sizeof(igraph_vs_t)); + switch (dest->type) { + case IGRAPH_VS_VECTOR: + dest->data.vecptr = IGRAPH_CALLOC(1, igraph_vector_t); + if (!dest->data.vecptr) { + IGRAPH_ERROR("Cannot copy vertex selector", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)dest->data.vecptr, + (igraph_vector_t*)src->data.vecptr)); + break; + } + return 0; +} + +/** + * \function igraph_vs_type + * \brief Returns the type of the vertex selector. + */ +int igraph_vs_type(const igraph_vs_t *vs) { + return vs->type; +} + +/** + * \function igraph_vs_size + * \brief Returns the size of the vertex selector. + * + * The size of the vertex selector is the number of vertices it will + * yield when it is iterated over. + * + * \param graph The graph over which we will iterate. + * \param result The result will be returned here. + */ +int igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, + igraph_integer_t *result) { + igraph_vector_t vec; + igraph_bool_t *seen; + long i; + + switch (vs->type) { + case IGRAPH_VS_NONE: + *result = 0; return 0; + + case IGRAPH_VS_1: + *result = 0; + if (vs->data.vid < igraph_vcount(graph) && vs->data.vid >= 0) { + *result = 1; + } + return 0; + + case IGRAPH_VS_SEQ: + *result = vs->data.seq.to - vs->data.seq.from; + return 0; + + case IGRAPH_VS_ALL: + *result = igraph_vcount(graph); return 0; + + case IGRAPH_VS_ADJ: + IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs->data.adj.vid, vs->data.adj.mode)); + *result = (igraph_integer_t) igraph_vector_size(&vec); + igraph_vector_destroy(&vec); + IGRAPH_FINALLY_CLEAN(1); + return 0; + + case IGRAPH_VS_NONADJ: + IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs->data.adj.vid, vs->data.adj.mode)); + *result = igraph_vcount(graph); + seen = IGRAPH_CALLOC(*result, igraph_bool_t); + if (seen == 0) { + IGRAPH_ERROR("Cannot calculate selector length", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, seen); + for (i = 0; i < igraph_vector_size(&vec); i++) { + if (!seen[(long int)VECTOR(vec)[i]]) { + (*result)--; + seen[(long int)VECTOR(vec)[i]] = 1; + } + } + igraph_free(seen); + igraph_vector_destroy(&vec); + IGRAPH_FINALLY_CLEAN(2); + return 0; + + case IGRAPH_VS_VECTOR: + case IGRAPH_VS_VECTORPTR: + *result = (igraph_integer_t) igraph_vector_size((igraph_vector_t*)vs->data.vecptr); + return 0; + } + + IGRAPH_ERROR("Cannot calculate selector length, invalid selector type", + IGRAPH_EINVAL); +} + +/***************************************************/ + +/** + * \function igraph_vit_create + * \brief Creates a vertex iterator from a vertex selector. + * + * This function instantiates a vertex selector object with a given + * graph. This is the step when the actual vertex ids are created from + * the \em logical notion of the vertex selector based on the graph. + * E.g. a vertex selector created with \ref igraph_vs_all() contains + * knowledge that \em all vertices are included in a (yet indefinite) + * graph. When instantiating it a vertex iterator object is created, + * this contains the actual vertex ids in the graph supplied as a + * parameter. + * + * + * The same vertex selector object can be used to instantiate any + * number vertex iterators. + * + * \param graph An \type igraph_t object, a graph. + * \param vs A vertex selector object. + * \param vit Pointer to an uninitialized vertex iterator object. + * \return Error code. + * \sa \ref igraph_vit_destroy(). + * + * Time complexity: it depends on the vertex selector type. O(1) for + * vertex selectors created with \ref igraph_vs_all(), \ref + * igraph_vs_none(), \ref igraph_vs_1, \ref igraph_vs_vector, \ref + * igraph_vs_seq(), \ref igraph_vs_vector(), \ref + * igraph_vs_vector_small(). O(d) for \ref igraph_vs_adj(), d is the + * number of vertex ids to be included in the iterator. O(|V|) for + * \ref igraph_vs_nonadj(), |V| is the number of vertices in the graph. + */ + +int igraph_vit_create(const igraph_t *graph, + igraph_vs_t vs, igraph_vit_t *vit) { + igraph_vector_t vec; + igraph_bool_t *seen; + long int i, j, n; + + switch (vs.type) { + case IGRAPH_VS_ALL: + vit->type = IGRAPH_VIT_SEQ; + vit->pos = 0; + vit->start = 0; + vit->end = igraph_vcount(graph); + break; + case IGRAPH_VS_ADJ: + vit->type = IGRAPH_VIT_VECTOR; + vit->pos = 0; + vit->start = 0; + vit->vec = IGRAPH_CALLOC(1, igraph_vector_t); + if (vit->vec == 0) { + IGRAPH_ERROR("Cannot create iterator", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) vit->vec); + IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)vit->vec, 0); + IGRAPH_CHECK(igraph_neighbors(graph, (igraph_vector_t*)vit->vec, + vs.data.adj.vid, vs.data.adj.mode)); + vit->end = igraph_vector_size(vit->vec); + IGRAPH_FINALLY_CLEAN(2); + break; + case IGRAPH_VS_NONADJ: + vit->type = IGRAPH_VIT_VECTOR; + vit->pos = 0; + vit->start = 0; + vit->vec = IGRAPH_CALLOC(1, igraph_vector_t); + if (vit->vec == 0) { + IGRAPH_ERROR("Cannot create iterator", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) vit->vec); + IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t *) vit->vec, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &vec, + vs.data.adj.vid, vs.data.adj.mode)); + n = igraph_vcount(graph); + seen = IGRAPH_CALLOC(n, igraph_bool_t); + if (seen == 0) { + IGRAPH_ERROR("Cannot create iterator", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, seen); + for (i = 0; i < igraph_vector_size(&vec); i++) { + if (! seen [ (long int) VECTOR(vec)[i] ] ) { + n--; + seen[ (long int) VECTOR(vec)[i] ] = 1; + } + } + IGRAPH_CHECK(igraph_vector_resize((igraph_vector_t*)vit->vec, n)); + for (i = 0, j = 0; j < n; i++) { + if (!seen[i]) { + VECTOR(*vit->vec)[j++] = i; + } + } + + IGRAPH_FREE(seen); + igraph_vector_destroy(&vec); + vit->end = n; + IGRAPH_FINALLY_CLEAN(4); + break; + case IGRAPH_VS_NONE: + vit->type = IGRAPH_VIT_SEQ; + vit->pos = 0; + vit->start = 0; + vit->end = 0; + break; + case IGRAPH_VS_1: + vit->type = IGRAPH_VIT_SEQ; + vit->pos = vs.data.vid; + vit->start = vs.data.vid; + vit->end = vs.data.vid + 1; + if (vit->pos >= igraph_vcount(graph)) { + IGRAPH_ERROR("Cannot create iterator, invalid vertex id", IGRAPH_EINVVID); + } + break; + case IGRAPH_VS_VECTORPTR: + case IGRAPH_VS_VECTOR: + vit->type = IGRAPH_VIT_VECTORPTR; + vit->pos = 0; + vit->start = 0; + vit->vec = vs.data.vecptr; + vit->end = igraph_vector_size(vit->vec); + if (!igraph_vector_isininterval(vit->vec, 0, igraph_vcount(graph) - 1)) { + IGRAPH_ERROR("Cannot create iterator, invalid vertex id", IGRAPH_EINVVID); + } + break; + case IGRAPH_VS_SEQ: + vit->type = IGRAPH_VIT_SEQ; + vit->pos = vs.data.seq.from; + vit->start = vs.data.seq.from; + vit->end = vs.data.seq.to; + break; + default: + IGRAPH_ERROR("Cannot create iterator, invalid selector", IGRAPH_EINVAL); + break; + } + return 0; +} + +/** + * \function igraph_vit_destroy + * \brief Destroys a vertex iterator. + * + * + * Deallocates memory allocated for a vertex iterator. + * + * \param vit Pointer to an initialized vertex iterator object. + * \sa \ref igraph_vit_create() + * + * Time complexity: operating system dependent, usually O(1). + */ + +void igraph_vit_destroy(const igraph_vit_t *vit) { + switch (vit->type) { + case IGRAPH_VIT_SEQ: + case IGRAPH_VIT_VECTORPTR: + break; + case IGRAPH_VIT_VECTOR: + igraph_vector_destroy((igraph_vector_t*)vit->vec); + igraph_free((igraph_vector_t*)vit->vec); + break; + default: + /* IGRAPH_ERROR("Cannot destroy iterator, unknown type", IGRAPH_EINVAL); */ + break; + } +} + +int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v) { + + long int i; + + IGRAPH_CHECK(igraph_vector_resize(v, IGRAPH_VIT_SIZE(*vit))); + + switch (vit->type) { + case IGRAPH_VIT_SEQ: + for (i = 0; i < IGRAPH_VIT_SIZE(*vit); i++) { + VECTOR(*v)[i] = vit->start + i; + } + break; + case IGRAPH_VIT_VECTOR: + case IGRAPH_VIT_VECTORPTR: + for (i = 0; i < IGRAPH_VIT_SIZE(*vit); i++) { + VECTOR(*v)[i] = VECTOR(*vit->vec)[i]; + } + break; + default: + IGRAPH_ERROR("Cannot convert to vector, unknown iterator type", + IGRAPH_EINVAL); + break; + } + + return 0; +} + +/*******************************************************/ + +/** + * \function igraph_es_all + * \brief Edge set, all edges. + * + * \param es Pointer to an uninitialized edge selector object. + * \param order Constant giving the order in which the edges will be + * included in the selector. Possible values: + * \c IGRAPH_EDGEORDER_ID, edge id order. + * \c IGRAPH_EDGEORDER_FROM, vertex id order, the id of the + * \em source vertex counts for directed graphs. The order + * of the incident edges of a given vertex is arbitrary. + * \c IGRAPH_EDGEORDER_TO, vertex id order, the id of the \em + * target vertex counts for directed graphs. The order + * of the incident edges of a given vertex is arbitrary. + * For undirected graph the latter two is the same. + * \return Error code. + * \sa \ref igraph_ess_all(), \ref igraph_es_destroy() + * + * Time complexity: O(1). + */ + +int igraph_es_all(igraph_es_t *es, + igraph_edgeorder_type_t order) { + switch (order) { + case IGRAPH_EDGEORDER_ID: + es->type = IGRAPH_ES_ALL; + break; + case IGRAPH_EDGEORDER_FROM: + es->type = IGRAPH_ES_ALLFROM; + break; + case IGRAPH_EDGEORDER_TO: + es->type = IGRAPH_ES_ALLTO; + break; + default: + IGRAPH_ERROR("Invalid edge order, cannot create selector", IGRAPH_EINVAL); + break; + } + return 0; +} + +/** + * \function igraph_ess_all + * \brief Edge set, all edges (immediate version) + * + * The immediate version of the all-edges selector. + * + * \param order Constant giving the order of the edges in the edge + * selector. See \ref igraph_es_all() for the possible values. + * \return The edge selector. + * \sa \ref igraph_es_all() + * + * Time complexity: O(1). + */ + +igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order) { + igraph_es_t es; + igraph_es_all(&es, order); /* cannot fail */ + return es; +} + +/** + * \function igraph_es_incident + * \brief Edges incident on a given vertex. + * + * \param es Pointer to an uninitialized edge selector object. + * \param vid Vertex id, of which the incident edges will be + * selected. + * \param mode Constant giving the type of the incident edges to + * select. This is ignored for undirected graphs. Possible values: + * \c IGRAPH_OUT, outgoing edges; + * \c IGRAPH_IN, incoming edges; + * \c IGRAPH_ALL, all edges. + * \return Error code. + * \sa \ref igraph_es_destroy() + * + * Time complexity: O(1). + */ + +int igraph_es_incident(igraph_es_t *es, + igraph_integer_t vid, igraph_neimode_t mode) { + es->type = IGRAPH_ES_INCIDENT; + es->data.incident.vid = vid; + es->data.incident.mode = mode; + return 0; +} + +/** + * \function igraph_es_none + * \brief Empty edge selector. + * + * \param es Pointer to an uninitialized edge selector object to + * initialize. + * \return Error code. + * \sa \ref igraph_ess_none(), \ref igraph_es_destroy() + * + * Time complexity: O(1). + */ + +int igraph_es_none(igraph_es_t *es) { + es->type = IGRAPH_ES_NONE; + return 0; +} + +/** + * \function igraph_ess_none + * \brief Immediate empty edge selector. + * + * + * Immediate version of the empty edge selector. + * + * \return Initialized empty edge selector. + * \sa \ref igraph_es_none() + * + * Time complexity: O(1). + */ + +igraph_es_t igraph_ess_none(void) { + igraph_es_t es; + es.type = IGRAPH_ES_NONE; + return es; +} + +/** + * \function igraph_es_1 + * \brief Edge selector containing a single edge. + * + * \param es Pointer to an uninitialized edge selector object. + * \param eid Edge id of the edge to select. + * \return Error code. + * \sa \ref igraph_ess_1(), \ref igraph_es_destroy() + * + * Time complexity: O(1). + */ + +int igraph_es_1(igraph_es_t *es, igraph_integer_t eid) { + es->type = IGRAPH_ES_1; + es->data.eid = eid; + return 0; +} + +/** + * \function igraph_ess_1 + * \brief Immediate version of the single edge edge selector. + * + * \param eid The id of the edge. + * \return The edge selector. + * \sa \ref igraph_es_1() + * + * Time complexity: O(1). + */ + +igraph_es_t igraph_ess_1(igraph_integer_t eid) { + igraph_es_t es; + es.type = IGRAPH_ES_1; + es.data.eid = eid; + return es; +} + +/** + * \function igraph_es_vector + * \brief Handle a vector as an edge selector. + * + * + * Creates an edge selector which serves as a view to a vector + * containing edge ids. Do not destroy the vector before destroying + * the view. + * + * Many views can be created to the same vector. + * + * \param es Pointer to an uninitialized edge selector. + * \param v Vector containing edge ids. + * \return Error code. + * \sa \ref igraph_ess_vector(), \ref igraph_es_destroy() + * + * Time complexity: O(1). + */ + +int igraph_es_vector(igraph_es_t *es, + const igraph_vector_t *v) { + es->type = IGRAPH_ES_VECTORPTR; + es->data.vecptr = v; + return 0; +} + +/** + * \function igraph_es_vector_copy + * \brief Edge set, based on a vector, with copying. + * + * + * This function makes it possible to handle a \type vector_t + * permanently as an edge selector. The edge selector creates a + * copy of the original vector, so the vector can safely be destroyed + * after creating the edge selector. Changing the original vector + * will not affect the edge selector. The edge selector is + * responsible for deleting the copy made by itself. + * + * \param es Pointer to an uninitialized edge selector. + * \param v Pointer to a \type igraph_vector_t object. + * \return Error code. + * \sa \ref igraph_es_destroy() + * + * Time complexity: O(1). + */ + +int igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_t *v) { + es->type = IGRAPH_ES_VECTOR; + es->data.vecptr = IGRAPH_CALLOC(1, igraph_vector_t); + if (es->data.vecptr == 0) { + IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)es->data.vecptr); + IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)es->data.vecptr, v)); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \function igraph_ess_vector + * \brief Immediate vector view edge selector. + * + * + * This is the immediate version of the vector of edge ids edge + * selector. + * + * \param v The vector of edge ids. + * \return Edge selector, initialized. + * \sa \ref igraph_es_vector() + * + * Time complexity: O(1). + */ + +igraph_es_t igraph_ess_vector(const igraph_vector_t *v) { + igraph_es_t es; + es.type = IGRAPH_ES_VECTORPTR; + es.data.vecptr = v; + return es; +} + +/** + * \function igraph_es_fromto + * \brief Edge selector, all edges between two vertex sets. + * + * + * This function is not implemented yet. + * + * \param es Pointer to an uninitialized edge selector. + * \param from Vertex selector, their outgoing edges will be + * selected. + * \param to Vertex selector, their incoming edges will be selected + * from the previous selection. + * \return Error code. + * \sa \ref igraph_es_destroy() + * + * Time complexity: O(1). + */ + +int igraph_es_fromto(igraph_es_t *es, + igraph_vs_t from, igraph_vs_t to) { + + IGRAPH_UNUSED(es); IGRAPH_UNUSED(from); IGRAPH_UNUSED(to); + IGRAPH_ERROR("igraph_es_fromto not implemented yet", IGRAPH_UNIMPLEMENTED); + /* TODO */ +} + +/** + * \function igraph_es_seq + * \brief Edge selector, a sequence of edge ids. + * + * All edge ids between from and to will be + * included in the edge selection. This includes from and + * excludes to. + * + * \param es Pointer to an uninitialized edge selector object. + * \param from The first edge id to be included. + * \param to The last edge id to be included. + * \return Error code. + * \sa \ref igraph_ess_seq(), \ref igraph_es_destroy() + * + * Time complexity: O(1). + */ + +int igraph_es_seq(igraph_es_t *es, + igraph_integer_t from, igraph_integer_t to) { + es->type = IGRAPH_ES_SEQ; + es->data.seq.from = from; + es->data.seq.to = to; + return 0; +} + +/** + * \function igraph_ess_seq + * \brief Immediate version of the sequence edge selector. + * + * \param from The first edge id to include. + * \param to The last edge id to include. + * \return The initialized edge selector. + * \sa \ref igraph_es_seq() + * + * Time complexity: O(1). + */ + +igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to) { + igraph_es_t es; + es.type = IGRAPH_ES_SEQ; + es.data.seq.from = from; + es.data.seq.to = to; + return es; +} + +/** + * \function igraph_es_pairs + * \brief Edge selector, multiple edges defined by their endpoints in a vector. + * + * The edges between the given pairs of vertices will be included in the + * edge selection. The vertex pairs must be defined in the vector v, + * the first element of the vector is the first vertex of the first edge + * to be selected, the second element is the second vertex of the first + * edge, the third element is the first vertex of the second edge and + * so on. + * + * \param es Pointer to an uninitialized edge selector object. + * \param v The vector containing the endpoints of the edges. + * \param directed Whether the graph is directed or not. + * \return Error code. + * \sa \ref igraph_es_pairs_small(), \ref igraph_es_destroy() + * + * Time complexity: O(n), the number of edges being selected. + * + * \example examples/simple/igraph_es_pairs.c + */ + +int igraph_es_pairs(igraph_es_t *es, const igraph_vector_t *v, + igraph_bool_t directed) { + es->type = IGRAPH_ES_PAIRS; + es->data.path.mode = directed; + es->data.path.ptr = IGRAPH_CALLOC(1, igraph_vector_t); + if (es->data.path.ptr == 0) { + IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) es->data.path.ptr); + + IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*) es->data.path.ptr, v)); + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \function igraph_es_pairs_small + * \brief Edge selector, multiple edges defined by their endpoints as arguments. + * + * The edges between the given pairs of vertices will be included in the + * edge selection. The vertex pairs must be given as the arguments of the + * function call, the third argument is the first vertex of the first edge, + * the fourth argument is the second vertex of the first edge, the fifth + * is the first vertex of the second edge and so on. The last element of the + * argument list must be -1 to denote the end of the argument list. + * + * \param es Pointer to an uninitialized edge selector object. + * \param directed Whether the graph is directed or not. + * \return Error code. + * \sa \ref igraph_es_pairs(), \ref igraph_es_destroy() + * + * Time complexity: O(n), the number of edges being selected. + */ + +int igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, ...) { + va_list ap; + long int i, n = 0; + es->type = IGRAPH_ES_PAIRS; + es->data.path.mode = directed; + es->data.path.ptr = IGRAPH_CALLOC(1, igraph_vector_t); + if (es->data.path.ptr == 0) { + IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)es->data.path.ptr); + + va_start(ap, directed); + while (1) { + int num = va_arg(ap, int); + if (num == -1) { + break; + } + n++; + } + va_end(ap); + + IGRAPH_VECTOR_INIT_FINALLY( (igraph_vector_t*) es->data.path.ptr, n); + + va_start(ap, directed); + for (i = 0; i < n; i++) { + VECTOR(*es->data.path.ptr)[i] = (igraph_real_t) va_arg(ap, int); + } + va_end(ap); + + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +int igraph_es_multipairs(igraph_es_t *es, const igraph_vector_t *v, + igraph_bool_t directed) { + es->type = IGRAPH_ES_MULTIPAIRS; + es->data.path.mode = directed; + es->data.path.ptr = IGRAPH_CALLOC(1, igraph_vector_t); + if (es->data.path.ptr == 0) { + IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) es->data.path.ptr); + + IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*) es->data.path.ptr, v)); + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \function igraph_es_path + * \brief Edge selector, edge ids on a path. + * + * This function takes a vector of vertices and creates a selector of + * edges between those vertices. Vector {0, 3, 4, 7} will select edges + * (0 -> 3), (3 -> 4), (4 -> 7). If these edges don't exist then trying + * to create an iterator using this selector will fail. + * + * \param es Pointer to an uninitialized edge selector object. + * \param v Pointer to a vector of vertex id's along the path. + * \param directed If edge directions should be taken into account. This + * will be ignored if the graph to select from is undirected. + * \return Error code. + * \sa \ref igraph_es_destroy() + * + * Time complexity: O(n), the number of vertices. + */ +int igraph_es_path(igraph_es_t *es, const igraph_vector_t *v, + igraph_bool_t directed) { + es->type = IGRAPH_ES_PATH; + es->data.path.mode = directed; + es->data.path.ptr = IGRAPH_CALLOC(1, igraph_vector_t); + if (es->data.path.ptr == 0) { + IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) es->data.path.ptr); + + IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*) es->data.path.ptr, v)); + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +int igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, ...) { + va_list ap; + long int i, n = 0; + es->type = IGRAPH_ES_PATH; + es->data.path.mode = directed; + es->data.path.ptr = IGRAPH_CALLOC(1, igraph_vector_t); + if (es->data.path.ptr == 0) { + IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)es->data.path.ptr); + + va_start(ap, directed); + while (1) { + int num = va_arg(ap, int); + if (num == -1) { + break; + } + n++; + } + va_end(ap); + + IGRAPH_VECTOR_INIT_FINALLY( (igraph_vector_t*) es->data.path.ptr, n); + + va_start(ap, directed); + for (i = 0; i < n; i++) { + VECTOR(*es->data.path.ptr)[i] = (igraph_real_t) va_arg(ap, int); + } + va_end(ap); + + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +/** + * \function igraph_es_destroy + * \brief Destroys an edge selector object. + * + * + * Call this function on an edge selector when it is not needed any + * more. Do \em not call this function on edge selectors created by + * immediate constructors, those don't need to be destroyed. + * + * \param es Pointer to an edge selector object. + * + * Time complexity: operating system dependent, usually O(1). + */ + +void igraph_es_destroy(igraph_es_t *es) { + switch (es->type) { + case IGRAPH_ES_ALL: + case IGRAPH_ES_ALLFROM: + case IGRAPH_ES_ALLTO: + case IGRAPH_ES_INCIDENT: + case IGRAPH_ES_NONE: + case IGRAPH_ES_1: + case IGRAPH_ES_VECTORPTR: + case IGRAPH_ES_SEQ: + break; + case IGRAPH_ES_VECTOR: + igraph_vector_destroy((igraph_vector_t*)es->data.vecptr); + IGRAPH_FREE(es->data.vecptr); + break; + case IGRAPH_ES_PAIRS: + case IGRAPH_ES_PATH: + case IGRAPH_ES_MULTIPAIRS: + igraph_vector_destroy((igraph_vector_t*)es->data.path.ptr); + IGRAPH_FREE(es->data.path.ptr); + break; + default: + break; + } +} + +/** + * \function igraph_es_is_all + * \brief Check whether an edge selector includes all edges. + * + * \param es Pointer to an edge selector object. + * \return TRUE (1) if es was created with \ref + * igraph_es_all() or \ref igraph_ess_all(), and FALSE (0) otherwise. + * + * Time complexity: O(1). + */ + +igraph_bool_t igraph_es_is_all(const igraph_es_t *es) { + return es->type == IGRAPH_ES_ALL; +} + +/** + * \function igraph_es_copy + * \brief Creates a copy of an edge selector. + * \param src The selector being copied. + * \param dest An uninitialized selector that will contain the copy. + * \sa \ref igraph_es_destroy() + */ +int igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src) { + memcpy(dest, src, sizeof(igraph_es_t)); + switch (dest->type) { + case IGRAPH_ES_VECTOR: + dest->data.vecptr = IGRAPH_CALLOC(1, igraph_vector_t); + if (!dest->data.vecptr) { + IGRAPH_ERROR("Cannot copy edge selector", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)dest->data.vecptr, + (igraph_vector_t*)src->data.vecptr)); + break; + case IGRAPH_ES_PATH: + case IGRAPH_ES_PAIRS: + case IGRAPH_ES_MULTIPAIRS: + dest->data.path.ptr = IGRAPH_CALLOC(1, igraph_vector_t); + if (!dest->data.path.ptr) { + IGRAPH_ERROR("Cannot copy edge selector", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)dest->data.path.ptr, + (igraph_vector_t*)src->data.path.ptr)); + break; + } + return 0; +} + +/** + * \function igraph_es_as_vector + * \brief Transform edge selector into vector. + * + * + * Call this function on an edge selector to transform it into a vector. + * This is only implemented for sequence and vector selectors. If the + * edges do not exist in the graph, this will result in an error. + * + * \param graph Pointer to a graph to check if the edges in the selector exist. + * \param es An edge selector object. + * \param v Pointer to initialized vector. The result will be stored here. + * + * Time complexity: O(n), the number of edges in the selector. + */ +int igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, + igraph_vector_t *v) { + igraph_eit_t eit; + + IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + IGRAPH_CHECK(igraph_eit_as_vector(&eit, v)); + + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \function igraph_es_type + * \brief Returns the type of the edge selector. + */ +int igraph_es_type(const igraph_es_t *es) { + return es->type; +} + +static int igraph_i_es_pairs_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result); +static int igraph_i_es_path_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result); +static int igraph_i_es_multipairs_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result); + +/** + * \function igraph_es_size + * \brief Returns the size of the edge selector. + * + * The size of the edge selector is the number of edges it will + * yield when it is iterated over. + * + * \param graph The graph over which we will iterate. + * \param result The result will be returned here. + */ +int igraph_es_size(const igraph_t *graph, const igraph_es_t *es, + igraph_integer_t *result) { + igraph_vector_t v; + + switch (es->type) { + case IGRAPH_ES_ALL: + *result = igraph_ecount(graph); + return 0; + + case IGRAPH_ES_ALLFROM: + *result = igraph_ecount(graph); + return 0; + + case IGRAPH_ES_ALLTO: + *result = igraph_ecount(graph); + return 0; + + case IGRAPH_ES_INCIDENT: + IGRAPH_VECTOR_INIT_FINALLY(&v, 0); + IGRAPH_CHECK(igraph_incident(graph, &v, + es->data.incident.vid, es->data.incident.mode)); + *result = (igraph_integer_t) igraph_vector_size(&v); + igraph_vector_destroy(&v); + IGRAPH_FINALLY_CLEAN(1); + return 0; + + case IGRAPH_ES_NONE: + *result = 0; + return 0; + + case IGRAPH_ES_1: + if (es->data.eid < igraph_ecount(graph) && es->data.eid >= 0) { + *result = 1; + } else { + *result = 0; + } + return 0; + + case IGRAPH_ES_VECTOR: + case IGRAPH_ES_VECTORPTR: + *result = (igraph_integer_t) igraph_vector_size((igraph_vector_t*)es->data.vecptr); + return 0; + + case IGRAPH_ES_SEQ: + *result = es->data.seq.to - es->data.seq.from; + return 0; + + case IGRAPH_ES_PAIRS: + IGRAPH_CHECK(igraph_i_es_pairs_size(graph, es, result)); + return 0; + + case IGRAPH_ES_PATH: + IGRAPH_CHECK(igraph_i_es_path_size(graph, es, result)); + return 0; + + case IGRAPH_ES_MULTIPAIRS: + IGRAPH_CHECK(igraph_i_es_multipairs_size(graph, es, result)); + return 0; + + default: + IGRAPH_ERROR("Cannot calculate selector length, invalid selector type", + IGRAPH_EINVAL); + } +} + +static int igraph_i_es_pairs_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result) { + long int n = igraph_vector_size(es->data.path.ptr); + long int no_of_nodes = igraph_vcount(graph); + long int i; + + if (n % 2 != 0) { + IGRAPH_ERROR("Cannot calculate edge selector length from odd number of vertices", + IGRAPH_EINVAL); + } + if (!igraph_vector_isininterval(es->data.path.ptr, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Cannot calculate edge selector length", IGRAPH_EINVVID); + } + + *result = (igraph_integer_t) (n / 2); + /* Check for the existence of all edges */ + for (i = 0; i < *result; i++) { + long int from = (long int) VECTOR(*es->data.path.ptr)[2 * i]; + long int to = (long int) VECTOR(*es->data.path.ptr)[2 * i + 1]; + igraph_integer_t eid; + IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) from, + (igraph_integer_t) to, es->data.path.mode, + /*error=*/ 1)); + } + + return 0; +} + +static int igraph_i_es_path_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result) { + long int n = igraph_vector_size(es->data.path.ptr); + long int no_of_nodes = igraph_vcount(graph); + long int i; + + if (!igraph_vector_isininterval(es->data.path.ptr, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Cannot calculate selector length", IGRAPH_EINVVID); + } + + if (n <= 1) { + *result = 0; + } else { + *result = (igraph_integer_t) (n - 1); + } + for (i = 0; i < *result; i++) { + long int from = (long int) VECTOR(*es->data.path.ptr)[i]; + long int to = (long int) VECTOR(*es->data.path.ptr)[i + 1]; + igraph_integer_t eid; + IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) from, + (igraph_integer_t) to, es->data.path.mode, + /*error=*/ 1)); + } + + return 0; +} + +static int igraph_i_es_multipairs_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result) { + IGRAPH_UNUSED(graph); IGRAPH_UNUSED(es); IGRAPH_UNUSED(result); + IGRAPH_ERROR("Cannot calculate edge selector length", IGRAPH_UNIMPLEMENTED); +} + +/**************************************************/ + +static int igraph_i_eit_create_allfromto(const igraph_t *graph, + igraph_eit_t *eit, + igraph_neimode_t mode); +static int igraph_i_eit_pairs(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit); +static int igraph_i_eit_multipairs(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit); +static int igraph_i_eit_path(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit); + +static int igraph_i_eit_create_allfromto(const igraph_t *graph, + igraph_eit_t *eit, + igraph_neimode_t mode) { + igraph_vector_t *vec; + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int i; + + vec = IGRAPH_CALLOC(1, igraph_vector_t); + if (vec == 0) { + IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_VECTOR_INIT_FINALLY(vec, 0); + IGRAPH_CHECK(igraph_vector_reserve(vec, no_of_edges)); + + if (igraph_is_directed(graph)) { + igraph_vector_t adj; + IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); + for (i = 0; i < no_of_nodes; i++) { + igraph_incident(graph, &adj, (igraph_integer_t) i, mode); + igraph_vector_append(vec, &adj); + } + igraph_vector_destroy(&adj); + IGRAPH_FINALLY_CLEAN(1); + + } else { + + igraph_vector_t adj; + igraph_bool_t *added; + long int j; + IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); + added = IGRAPH_CALLOC(no_of_edges, igraph_bool_t); + if (added == 0) { + IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added); + for (i = 0; i < no_of_nodes; i++) { + igraph_incident(graph, &adj, (igraph_integer_t) i, IGRAPH_ALL); + for (j = 0; j < igraph_vector_size(&adj); j++) { + if (!added[ (long int)VECTOR(adj)[j] ]) { + igraph_vector_push_back(vec, VECTOR(adj)[j]); + added[ (long int)VECTOR(adj)[j] ] += 1; + } + } + } + igraph_vector_destroy(&adj); + IGRAPH_FREE(added); + IGRAPH_FINALLY_CLEAN(2); + } + + eit->type = IGRAPH_EIT_VECTOR; + eit->pos = 0; + eit->start = 0; + eit->vec = vec; + eit->end = igraph_vector_size(eit->vec); + + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +static int igraph_i_eit_pairs(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit) { + long int n = igraph_vector_size(es.data.path.ptr); + long int no_of_nodes = igraph_vcount(graph); + long int i; + + if (n % 2 != 0) { + IGRAPH_ERROR("Cannot create edge iterator from odd number of vertices", + IGRAPH_EINVAL); + } + if (!igraph_vector_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_EINVVID); + } + + eit->type = IGRAPH_EIT_VECTOR; + eit->pos = 0; + eit->start = 0; + eit->end = n / 2; + eit->vec = IGRAPH_CALLOC(1, igraph_vector_t); + if (eit->vec == 0) { + IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)eit->vec); + IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)eit->vec, n / 2); + + for (i = 0; i < igraph_vector_size(eit->vec); i++) { + long int from = (long int) VECTOR(*es.data.path.ptr)[2 * i]; + long int to = (long int) VECTOR(*es.data.path.ptr)[2 * i + 1]; + igraph_integer_t eid; + IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) from, + (igraph_integer_t) to, es.data.path.mode, + /*error=*/ 1)); + VECTOR(*eit->vec)[i] = eid; + } + + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +static int igraph_i_eit_multipairs(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit) { + long int n = igraph_vector_size(es.data.path.ptr); + long int no_of_nodes = igraph_vcount(graph); + + if (n % 2 != 0) { + IGRAPH_ERROR("Cannot create edge iterator from odd number of vertices", + IGRAPH_EINVAL); + } + if (!igraph_vector_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_EINVVID); + } + + eit->type = IGRAPH_EIT_VECTOR; + eit->pos = 0; + eit->start = 0; + eit->end = n / 2; + eit->vec = IGRAPH_CALLOC(1, igraph_vector_t); + if (eit->vec == 0) { + IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)eit->vec); + IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)eit->vec, n / 2); + + IGRAPH_CHECK(igraph_get_eids_multi(graph, (igraph_vector_t *) eit->vec, + /*pairs=*/ es.data.path.ptr, /*path=*/ 0, + es.data.path.mode, /*error=*/ 1)); + + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +static int igraph_i_eit_path(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit) { + long int n = igraph_vector_size(es.data.path.ptr); + long int no_of_nodes = igraph_vcount(graph); + long int i, len; + + if (!igraph_vector_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Cannot create edge iterator.", IGRAPH_EINVVID); + } + + if (n <= 1) { + len = 0; + } else { + len = n - 1; + } + + eit->type = IGRAPH_EIT_VECTOR; + eit->pos = 0; + eit->start = 0; + eit->end = len; + eit->vec = IGRAPH_CALLOC(1, igraph_vector_t); + if (eit->vec == 0) { + IGRAPH_ERROR("Cannot create edge iterator.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)eit->vec); + + IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t *)eit->vec, len); + + for (i = 0; i < len; i++) { + long int from = (long int) VECTOR(*es.data.path.ptr)[i]; + long int to = (long int) VECTOR(*es.data.path.ptr)[i + 1]; + igraph_integer_t eid; + IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) from, + (igraph_integer_t) to, es.data.path.mode, + /*error=*/ 1)); + VECTOR(*eit->vec)[i] = eid; + } + + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +/** + * \function igraph_eit_create + * \brief Creates an edge iterator from an edge selector. + * + * + * This function creates an edge iterator based on an edge selector + * and a graph. + * + * + * The same edge selector can be used to create many edge iterators, + * also for different graphs. + * + * \param graph An \type igraph_t object for which the edge selector + * will be instantiated. + * \param es The edge selector to instantiate. + * \param eit Pointer to an uninitialized edge iterator. + * \return Error code. + * \sa \ref igraph_eit_destroy() + * + * Time complexity: depends on the type of the edge selector. For edge + * selectors created by \ref igraph_es_all(), \ref igraph_es_none(), + * \ref igraph_es_1(), igraph_es_vector(), igraph_es_seq() it is + * O(1). For \ref igraph_es_incident() it is O(d) where d is the number of + * incident edges of the vertex. + */ + +int igraph_eit_create(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit) { + switch (es.type) { + case IGRAPH_ES_ALL: + eit->type = IGRAPH_EIT_SEQ; + eit->pos = 0; + eit->start = 0; + eit->end = igraph_ecount(graph); + break; + case IGRAPH_ES_ALLFROM: + IGRAPH_CHECK(igraph_i_eit_create_allfromto(graph, eit, IGRAPH_OUT)); + break; + case IGRAPH_ES_ALLTO: + IGRAPH_CHECK(igraph_i_eit_create_allfromto(graph, eit, IGRAPH_IN)); + break; + case IGRAPH_ES_INCIDENT: + eit->type = IGRAPH_EIT_VECTOR; + eit->pos = 0; + eit->start = 0; + eit->vec = IGRAPH_CALLOC(1, igraph_vector_t); + if (eit->vec == 0) { + IGRAPH_ERROR("Cannot create iterator.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) eit->vec); + IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)eit->vec, 0); + IGRAPH_CHECK(igraph_incident(graph, (igraph_vector_t*)eit->vec, + es.data.incident.vid, es.data.incident.mode)); + eit->end = igraph_vector_size(eit->vec); + IGRAPH_FINALLY_CLEAN(2); + break; + case IGRAPH_ES_NONE: + eit->type = IGRAPH_EIT_SEQ; + eit->pos = 0; + eit->start = 0; + eit->end = 0; + break; + case IGRAPH_ES_1: + eit->type = IGRAPH_EIT_SEQ; + eit->pos = es.data.eid; + eit->start = es.data.eid; + eit->end = es.data.eid + 1; + if (eit->pos >= igraph_ecount(graph)) { + IGRAPH_ERROR("Cannot create iterator, invalid edge id.", IGRAPH_EINVAL); + } + break; + case IGRAPH_ES_VECTOR: + case IGRAPH_ES_VECTORPTR: + eit->type = IGRAPH_EIT_VECTORPTR; + eit->pos = 0; + eit->start = 0; + eit->vec = es.data.vecptr; + eit->end = igraph_vector_size(eit->vec); + if (!igraph_vector_isininterval(eit->vec, 0, igraph_ecount(graph) - 1)) { + IGRAPH_ERROR("Cannot create iterator, invalid edge id.", IGRAPH_EINVAL); + } + break; + case IGRAPH_ES_SEQ: + eit->type = IGRAPH_EIT_SEQ; + eit->pos = es.data.seq.from; + eit->start = es.data.seq.from; + eit->end = es.data.seq.to; + if (eit->start < 0) { + IGRAPH_ERROR("Cannot create iterator, invalid edge id.", IGRAPH_EINVAL); + } + if (eit->end < 0) { + IGRAPH_ERROR("Cannot create iterator, invalid edge id.", IGRAPH_EINVAL); + } + if (eit->start >= igraph_ecount(graph)) { + IGRAPH_ERROR("Cannot create iterator, starting edge greater than number of edges.", IGRAPH_EINVAL); + } + break; + case IGRAPH_ES_PAIRS: + IGRAPH_CHECK(igraph_i_eit_pairs(graph, es, eit)); + break; + case IGRAPH_ES_MULTIPAIRS: + IGRAPH_CHECK(igraph_i_eit_multipairs(graph, es, eit)); + break; + case IGRAPH_ES_PATH: + IGRAPH_CHECK(igraph_i_eit_path(graph, es, eit)); + break; + default: + IGRAPH_ERROR("Cannot create iterator, invalid selector.", IGRAPH_EINVAL); + break; + } + return 0; +} + +/** + * \function igraph_eit_destroy + * \brief Destroys an edge iterator. + * + * \param eit Pointer to an edge iterator to destroy. + * \sa \ref igraph_eit_create() + * + * Time complexity: operating system dependent, usually O(1). + */ + +void igraph_eit_destroy(const igraph_eit_t *eit) { + switch (eit->type) { + case IGRAPH_EIT_SEQ: + case IGRAPH_EIT_VECTORPTR: + break; + case IGRAPH_EIT_VECTOR: + igraph_vector_destroy((igraph_vector_t*)eit->vec); + igraph_free((igraph_vector_t*)eit->vec); + break; + default: + /* IGRAPH_ERROR("Cannot destroy iterator, unknown type", IGRAPH_EINVAL); */ + break; + } +} + +int igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_t *v) { + + long int i; + + IGRAPH_CHECK(igraph_vector_resize(v, IGRAPH_EIT_SIZE(*eit))); + + switch (eit->type) { + case IGRAPH_EIT_SEQ: + for (i = 0; i < IGRAPH_EIT_SIZE(*eit); i++) { + VECTOR(*v)[i] = eit->start + i; + } + break; + case IGRAPH_EIT_VECTOR: + case IGRAPH_EIT_VECTORPTR: + for (i = 0; i < IGRAPH_EIT_SIZE(*eit); i++) { + VECTOR(*v)[i] = VECTOR(*eit->vec)[i]; + } + break; + default: + IGRAPH_ERROR("Cannot convert to vector, unknown iterator type", + IGRAPH_EINVAL); + break; + } + + return 0; +} diff --git a/src/rigraph/core/graph/neighbors.h b/src/rigraph/core/graph/neighbors.h new file mode 100644 index 0000000..88255b3 --- /dev/null +++ b/src/rigraph/core/graph/neighbors.h @@ -0,0 +1,42 @@ +/* + IGraph library. + Copyright (C) 2021 The igraph development team + + 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 2 of the License, or + (at your option) 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 IGRAPH_NEIGHBORS_H +#define IGRAPH_NEIGHBORS_H + +#include "igraph_constants.h" + +__BEGIN_DECLS + +IGRAPH_PRIVATE_EXPORT int igraph_i_neighbors(const igraph_t *graph, + igraph_vector_t *neis, + igraph_integer_t pnode, + igraph_neimode_t mode, + igraph_loops_t loops, + igraph_multiple_t multiple); + +IGRAPH_PRIVATE_EXPORT int igraph_i_incident(const igraph_t *graph, + igraph_vector_t *eids, + igraph_integer_t pnode, + igraph_neimode_t mode, + igraph_loops_t loops, + igraph_multiple_t multiple); + +__END_DECLS + +#endif /* IGRAPH_NEIGHBORS_H */ diff --git a/src/rigraph/core/graph/type_indexededgelist.c b/src/rigraph/core/graph/type_indexededgelist.c new file mode 100644 index 0000000..9494408 --- /dev/null +++ b/src/rigraph/core/graph/type_indexededgelist.c @@ -0,0 +1,1964 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_datatype.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "attributes.h" +#include "neighbors.h" + +/* Internal functions */ + +static int igraph_i_create_start( + igraph_vector_t *res, igraph_vector_t *el, + igraph_vector_t *index, igraph_integer_t nodes); + +/** + * \section about_basic_interface + * + * This is the very minimal API in \a igraph. All the other + * functions use this minimal set for creating and manipulating + * graphs. + * + * This is a very important principle since it makes possible to + * implement other data representations by implementing only this + * minimal set. + */ + +/** + * \ingroup interface + * \function igraph_empty + * \brief Creates an empty graph with some vertices and no edges. + * + * + * The most basic constructor, all the other constructors should call + * this to create a minimal graph object. Our use of the term "empty graph" + * in the above description should be distinguished from the mathematical + * definition of the empty or null graph. Strictly speaking, the empty or null + * graph in graph theory is the graph with no vertices and no edges. However + * by "empty graph" as used in \c igraph we mean a graph having zero or more + * vertices, but no edges. + * \param graph Pointer to a not-yet initialized graph object. + * \param n The number of vertices in the graph, a non-negative + * integer number is expected. + * \param directed Boolean; whether the graph is directed or not. Supported + * values are: + * \clist + * \cli IGRAPH_DIRECTED + * The graph will be \em directed. + * \cli IGRAPH_UNDIRECTED + * The graph will be \em undirected. + * \endclist + * \return Error code: + * \c IGRAPH_EINVAL: invalid number of vertices. + * + * Time complexity: O(|V|) for a graph with + * |V| vertices (and no edges). + * + * \example examples/simple/igraph_empty.c + */ +int igraph_empty(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { + return igraph_empty_attrs(graph, n, directed, 0); +} + + +/** + * \ingroup interface + * \function igraph_empty_attrs + * \brief Creates an empty graph with some vertices, no edges and some graph attributes. + * + * + * Use this instead of \ref igraph_empty() if you wish to add some graph + * attributes right after initialization. This function is currently + * not very interesting for the ordinary user. Just supply 0 here or + * use \ref igraph_empty(). + * \param graph Pointer to a not-yet initialized graph object. + * \param n The number of vertices in the graph; a non-negative + * integer number is expected. + * \param directed Boolean; whether the graph is directed or not. Supported + * values are: + * \clist + * \cli IGRAPH_DIRECTED + * Create a \em directed graph. + * \cli IGRAPH_UNDIRECTED + * Create an \em undirected graph. + * \endclist + * \param attr The attributes. + * \return Error code: + * \c IGRAPH_EINVAL: invalid number of vertices. + * + * Time complexity: O(|V|) for a graph with + * |V| vertices (and no edges). + */ +int igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void* attr) { + + if (n < 0) { + IGRAPH_ERROR("cannot create empty graph with negative number of vertices", + IGRAPH_EINVAL); + } + + if (!IGRAPH_FINITE(n)) { + IGRAPH_ERROR("number of vertices is not finite (NA, NaN or Inf)", IGRAPH_EINVAL); + } + + graph->n = 0; + graph->directed = directed; + IGRAPH_VECTOR_INIT_FINALLY(&graph->from, 0); + IGRAPH_VECTOR_INIT_FINALLY(&graph->to, 0); + IGRAPH_VECTOR_INIT_FINALLY(&graph->oi, 0); + IGRAPH_VECTOR_INIT_FINALLY(&graph->ii, 0); + IGRAPH_VECTOR_INIT_FINALLY(&graph->os, 1); + IGRAPH_VECTOR_INIT_FINALLY(&graph->is, 1); + + VECTOR(graph->os)[0] = 0; + VECTOR(graph->is)[0] = 0; + + /* init attributes */ + graph->attr = 0; + IGRAPH_CHECK(igraph_i_attribute_init(graph, attr)); + + /* add the vertices */ + IGRAPH_CHECK(igraph_add_vertices(graph, n, 0)); + + IGRAPH_FINALLY_CLEAN(6); + return 0; +} + +/** + * \ingroup interface + * \function igraph_destroy + * \brief Frees the memory allocated for a graph object. + * + * + * This function should be called for every graph object exactly once. + * + * + * This function invalidates all iterators (of course), but the + * iterators of a graph should be destroyed before the graph itself + * anyway. + * \param graph Pointer to the graph to free. + * + * Time complexity: operating system specific. + */ +void igraph_destroy(igraph_t *graph) { + + IGRAPH_I_ATTRIBUTE_DESTROY(graph); + + igraph_vector_destroy(&graph->from); + igraph_vector_destroy(&graph->to); + igraph_vector_destroy(&graph->oi); + igraph_vector_destroy(&graph->ii); + igraph_vector_destroy(&graph->os); + igraph_vector_destroy(&graph->is); +} + +/** + * \ingroup interface + * \function igraph_copy + * \brief Creates an exact (deep) copy of a graph. + * + * + * This function deeply copies a graph object to create an exact + * replica of it. The new replica should be destroyed by calling + * \ref igraph_destroy() on it when not needed any more. + * + * + * You can also create a shallow copy of a graph by simply using the + * standard assignment operator, but be careful and do \em not + * destroy a shallow replica. To avoid this mistake, creating shallow + * copies is not recommended. + * \param to Pointer to an uninitialized graph object. + * \param from Pointer to the graph object to copy. + * \return Error code. + * + * Time complexity: O(|V|+|E|) for a + * graph with |V| vertices and + * |E| edges. + * + * \example examples/simple/igraph_copy.c + */ + +int igraph_copy(igraph_t *to, const igraph_t *from) { + to->n = from->n; + to->directed = from->directed; + IGRAPH_CHECK(igraph_vector_copy(&to->from, &from->from)); + IGRAPH_FINALLY(igraph_vector_destroy, &to->from); + IGRAPH_CHECK(igraph_vector_copy(&to->to, &from->to)); + IGRAPH_FINALLY(igraph_vector_destroy, &to->to); + IGRAPH_CHECK(igraph_vector_copy(&to->oi, &from->oi)); + IGRAPH_FINALLY(igraph_vector_destroy, &to->oi); + IGRAPH_CHECK(igraph_vector_copy(&to->ii, &from->ii)); + IGRAPH_FINALLY(igraph_vector_destroy, &to->ii); + IGRAPH_CHECK(igraph_vector_copy(&to->os, &from->os)); + IGRAPH_FINALLY(igraph_vector_destroy, &to->os); + IGRAPH_CHECK(igraph_vector_copy(&to->is, &from->is)); + IGRAPH_FINALLY(igraph_vector_destroy, &to->is); + + IGRAPH_I_ATTRIBUTE_COPY(to, from, 1, 1, 1); /* does IGRAPH_CHECK */ + + IGRAPH_FINALLY_CLEAN(6); + return 0; +} + +/** + * \ingroup interface + * \function igraph_add_edges + * \brief Adds edges to a graph object. + * + * + * The edges are given in a vector, the + * first two elements define the first edge (the order is + * from, to for directed + * graphs). The vector + * should contain even number of integer numbers between zero and the + * number of vertices in the graph minus one (inclusive). If you also + * want to add new vertices, call igraph_add_vertices() first. + * \param graph The graph to which the edges will be added. + * \param edges The edges themselves. + * \param attr The attributes of the new edges, only used by high level + * interfaces currently, you can supply 0 here. + * \return Error code: + * \c IGRAPH_EINVEVECTOR: invalid (odd) + * edges vector length, \c IGRAPH_EINVVID: + * invalid vertex id in edges vector. + * + * This function invalidates all iterators. + * + * + * Time complexity: O(|V|+|E|) where + * |V| is the number of vertices and + * |E| is the number of + * edges in the \em new, extended graph. + * + * \example examples/simple/igraph_add_edges.c + */ +int igraph_add_edges(igraph_t *graph, const igraph_vector_t *edges, + void *attr) { + long int no_of_edges = igraph_vector_size(&graph->from); + long int edges_to_add = igraph_vector_size(edges) / 2; + long int i = 0; + igraph_error_handler_t *oldhandler; + int ret1, ret2; + igraph_vector_t newoi, newii; + igraph_bool_t directed = igraph_is_directed(graph); + + if (igraph_vector_size(edges) % 2 != 0) { + IGRAPH_ERROR("invalid (odd) length of edges vector", IGRAPH_EINVEVECTOR); + } + if (!igraph_vector_isininterval(edges, 0, igraph_vcount(graph) - 1)) { + IGRAPH_ERROR("cannot add edges", IGRAPH_EINVVID); + } + + /* from & to */ + IGRAPH_CHECK(igraph_vector_reserve(&graph->from, no_of_edges + edges_to_add)); + IGRAPH_CHECK(igraph_vector_reserve(&graph->to, no_of_edges + edges_to_add)); + + while (i < edges_to_add * 2) { + if (directed || VECTOR(*edges)[i] > VECTOR(*edges)[i + 1]) { + igraph_vector_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */ + igraph_vector_push_back(&graph->to, VECTOR(*edges)[i++]); /* reserved */ + } else { + igraph_vector_push_back(&graph->to, VECTOR(*edges)[i++]); /* reserved */ + igraph_vector_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */ + } + } + + /* disable the error handler temporarily */ + oldhandler = igraph_set_error_handler(igraph_error_handler_ignore); + + /* oi & ii */ + ret1 = igraph_vector_init(&newoi, no_of_edges); + ret2 = igraph_vector_init(&newii, no_of_edges); + if (ret1 != 0 || ret2 != 0) { + igraph_vector_resize(&graph->from, no_of_edges); /* gets smaller */ + igraph_vector_resize(&graph->to, no_of_edges); /* gets smaller */ + igraph_set_error_handler(oldhandler); + IGRAPH_ERROR("cannot add edges", IGRAPH_ERROR_SELECT_2(ret1, ret2)); + } + ret1 = igraph_vector_order(&graph->from, &graph->to, &newoi, graph->n); + ret2 = igraph_vector_order(&graph->to, &graph->from, &newii, graph->n); + if (ret1 != 0 || ret2 != 0) { + igraph_vector_resize(&graph->from, no_of_edges); + igraph_vector_resize(&graph->to, no_of_edges); + igraph_vector_destroy(&newoi); + igraph_vector_destroy(&newii); + igraph_set_error_handler(oldhandler); + IGRAPH_ERROR("cannot add edges", IGRAPH_ERROR_SELECT_2(ret1, ret2)); + } + + /* Attributes */ + if (graph->attr) { + igraph_set_error_handler(oldhandler); + ret1 = igraph_i_attribute_add_edges(graph, edges, attr); + igraph_set_error_handler(igraph_error_handler_ignore); + if (ret1 != 0) { + igraph_vector_resize(&graph->from, no_of_edges); + igraph_vector_resize(&graph->to, no_of_edges); + igraph_vector_destroy(&newoi); + igraph_vector_destroy(&newii); + igraph_set_error_handler(oldhandler); + IGRAPH_ERROR("cannot add edges", ret1); + } + } + + /* os & is, its length does not change, error safe */ + igraph_i_create_start(&graph->os, &graph->from, &newoi, graph->n); + igraph_i_create_start(&graph->is, &graph->to, &newii, graph->n); + + /* everything went fine */ + igraph_vector_destroy(&graph->oi); + igraph_vector_destroy(&graph->ii); + graph->oi = newoi; + graph->ii = newii; + igraph_set_error_handler(oldhandler); + + return 0; +} + +/** + * \ingroup interface + * \function igraph_add_vertices + * \brief Adds vertices to a graph. + * + * + * This function invalidates all iterators. + * + * \param graph The graph object to extend. + * \param nv Non-negative integer giving the number of + * vertices to add. + * \param attr The attributes of the new vertices, only used by + * high level interfaces, you can supply 0 here. + * \return Error code: + * \c IGRAPH_EINVAL: invalid number of new + * vertices. + * + * Time complexity: O(|V|) where + * |V| is + * the number of vertices in the \em new, extended graph. + * + * \example examples/simple/igraph_add_vertices.c + */ +int igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr) { + long int ec = igraph_ecount(graph); + long int i; + + if (nv < 0) { + IGRAPH_ERROR("cannot add negative number of vertices", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_reserve(&graph->os, graph->n + nv + 1)); + IGRAPH_CHECK(igraph_vector_reserve(&graph->is, graph->n + nv + 1)); + + igraph_vector_resize(&graph->os, graph->n + nv + 1); /* reserved */ + igraph_vector_resize(&graph->is, graph->n + nv + 1); /* reserved */ + for (i = graph->n + 1; i < graph->n + nv + 1; i++) { + VECTOR(graph->os)[i] = ec; + VECTOR(graph->is)[i] = ec; + } + + graph->n += nv; + + if (graph->attr) { + IGRAPH_CHECK(igraph_i_attribute_add_vertices(graph, nv, attr)); + } + + return 0; +} + +/** + * \ingroup interface + * \function igraph_delete_edges + * \brief Removes edges from a graph. + * + * + * The edges to remove are given as an edge selector. + * + * + * This function cannot remove vertices, they will be kept, even if + * they lose all their edges. + * + * + * This function invalidates all iterators. + * \param graph The graph to work on. + * \param edges The edges to remove. + * \return Error code. + * + * Time complexity: O(|V|+|E|) where + * |V| + * and |E| are the number of vertices + * and edges in the \em original graph, respectively. + * + * \example examples/simple/igraph_delete_edges.c + */ +int igraph_delete_edges(igraph_t *graph, igraph_es_t edges) { + long int no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + long int edges_to_remove = 0; + long int remaining_edges; + igraph_eit_t eit; + + igraph_vector_t newfrom, newto, newoi; + + int *mark; + long int i, j; + + mark = IGRAPH_CALLOC(no_of_edges, int); + if (mark == 0) { + IGRAPH_ERROR("Cannot delete edges", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, mark); + + IGRAPH_CHECK(igraph_eit_create(graph, edges, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + for (IGRAPH_EIT_RESET(eit); !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { + long int e = IGRAPH_EIT_GET(eit); + if (mark[e] == 0) { + edges_to_remove++; + mark[e]++; + } + } + remaining_edges = no_of_edges - edges_to_remove; + + /* We don't need the iterator any more */ + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_VECTOR_INIT_FINALLY(&newfrom, remaining_edges); + IGRAPH_VECTOR_INIT_FINALLY(&newto, remaining_edges); + + /* Actually remove the edges, move from pos i to pos j in newfrom/newto */ + for (i = 0, j = 0; j < remaining_edges; i++) { + if (mark[i] == 0) { + VECTOR(newfrom)[j] = VECTOR(graph->from)[i]; + VECTOR(newto)[j] = VECTOR(graph->to)[i]; + j++; + } + } + + /* Create index, this might require additional memory */ + IGRAPH_VECTOR_INIT_FINALLY(&newoi, remaining_edges); + IGRAPH_CHECK(igraph_vector_order(&newfrom, &newto, &newoi, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_order(&newto, &newfrom, &graph->ii, no_of_nodes)); + + /* Edge attributes, we need an index that gives the ids of the + original edges for every new edge. + */ + if (graph->attr) { + igraph_vector_t idx; + IGRAPH_VECTOR_INIT_FINALLY(&idx, remaining_edges); + for (i = 0, j = 0; i < no_of_edges; i++) { + if (mark[i] == 0) { + VECTOR(idx)[j++] = i; + } + } + IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, graph, &idx)); + igraph_vector_destroy(&idx); + IGRAPH_FINALLY_CLEAN(1); + } + + /* Ok, we've all memory needed, free the old structure */ + igraph_vector_destroy(&graph->from); + igraph_vector_destroy(&graph->to); + igraph_vector_destroy(&graph->oi); + graph->from = newfrom; + graph->to = newto; + graph->oi = newoi; + IGRAPH_FINALLY_CLEAN(3); + + IGRAPH_FREE(mark); + IGRAPH_FINALLY_CLEAN(1); + + /* Create start vectors, no memory is needed for this */ + igraph_i_create_start(&graph->os, &graph->from, &graph->oi, + (igraph_integer_t) no_of_nodes); + igraph_i_create_start(&graph->is, &graph->to, &graph->ii, + (igraph_integer_t) no_of_nodes); + + /* Nothing to deallocate... */ + return 0; +} + +/** + * \ingroup interface + * \function igraph_delete_vertices + * \brief Removes vertices (with all their edges) from the graph. + * + * + * This function changes the ids of the vertices (except in some very + * special cases, but these should not be relied on anyway). + * + * + * This function invalidates all iterators. + * + * \param graph The graph to work on. + * \param vertices The ids of the vertices to remove in a + * vector. The vector may contain the same id more + * than once. + * \return Error code: + * \c IGRAPH_EINVVID: invalid vertex id. + * + * Time complexity: O(|V|+|E|), + * |V| and + * |E| are the number of vertices and + * edges in the original graph. + * + * \example examples/simple/igraph_delete_vertices.c + */ +int igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices) { + return igraph_delete_vertices_idx(graph, vertices, /* idx= */ 0, + /* invidx= */ 0); +} + +int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, + igraph_vector_t *idx, + igraph_vector_t *invidx) { + + long int no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t edge_recoding, vertex_recoding; + igraph_vector_t *my_vertex_recoding = &vertex_recoding; + igraph_vit_t vit; + igraph_t newgraph; + long int i, j; + long int remaining_vertices, remaining_edges; + + if (idx) { + my_vertex_recoding = idx; + IGRAPH_CHECK(igraph_vector_resize(idx, no_of_nodes)); + igraph_vector_null(idx); + } else { + IGRAPH_VECTOR_INIT_FINALLY(&vertex_recoding, no_of_nodes); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edge_recoding, no_of_edges); + + IGRAPH_CHECK(igraph_vit_create(graph, vertices, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + /* mark the vertices to delete */ + for (; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit) ) { + long int vertex = IGRAPH_VIT_GET(vit); + if (vertex < 0 || vertex >= no_of_nodes) { + IGRAPH_ERROR("Cannot delete vertices", IGRAPH_EINVVID); + } + VECTOR(*my_vertex_recoding)[vertex] = 1; + } + /* create vertex recoding vector */ + for (remaining_vertices = 0, i = 0; i < no_of_nodes; i++) { + if (VECTOR(*my_vertex_recoding)[i] == 0) { + VECTOR(*my_vertex_recoding)[i] = remaining_vertices + 1; + remaining_vertices++; + } else { + VECTOR(*my_vertex_recoding)[i] = 0; + } + } + /* create edge recoding vector */ + for (remaining_edges = 0, i = 0; i < no_of_edges; i++) { + long int from = (long int) VECTOR(graph->from)[i]; + long int to = (long int) VECTOR(graph->to)[i]; + if (VECTOR(*my_vertex_recoding)[from] != 0 && + VECTOR(*my_vertex_recoding)[to ] != 0) { + VECTOR(edge_recoding)[i] = remaining_edges + 1; + remaining_edges++; + } + } + + /* start creating the graph */ + newgraph.n = (igraph_integer_t) remaining_vertices; + newgraph.directed = graph->directed; + + /* allocate vectors */ + IGRAPH_VECTOR_INIT_FINALLY(&newgraph.from, remaining_edges); + IGRAPH_VECTOR_INIT_FINALLY(&newgraph.to, remaining_edges); + IGRAPH_VECTOR_INIT_FINALLY(&newgraph.oi, remaining_edges); + IGRAPH_VECTOR_INIT_FINALLY(&newgraph.ii, remaining_edges); + IGRAPH_VECTOR_INIT_FINALLY(&newgraph.os, remaining_vertices + 1); + IGRAPH_VECTOR_INIT_FINALLY(&newgraph.is, remaining_vertices + 1); + + /* Add the edges */ + for (i = 0, j = 0; j < remaining_edges; i++) { + if (VECTOR(edge_recoding)[i] > 0) { + long int from = (long int) VECTOR(graph->from)[i]; + long int to = (long int) VECTOR(graph->to )[i]; + VECTOR(newgraph.from)[j] = VECTOR(*my_vertex_recoding)[from] - 1; + VECTOR(newgraph.to )[j] = VECTOR(*my_vertex_recoding)[to] - 1; + j++; + } + } + /* update oi & ii */ + IGRAPH_CHECK(igraph_vector_order(&newgraph.from, &newgraph.to, &newgraph.oi, + remaining_vertices)); + IGRAPH_CHECK(igraph_vector_order(&newgraph.to, &newgraph.from, &newgraph.ii, + remaining_vertices)); + + IGRAPH_CHECK(igraph_i_create_start(&newgraph.os, &newgraph.from, + &newgraph.oi, (igraph_integer_t) + remaining_vertices)); + IGRAPH_CHECK(igraph_i_create_start(&newgraph.is, &newgraph.to, + &newgraph.ii, (igraph_integer_t) + remaining_vertices)); + + /* attributes */ + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, + /*graph=*/ 1, /*vertex=*/0, /*edge=*/0); + IGRAPH_FINALLY_CLEAN(6); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + + if (newgraph.attr) { + igraph_vector_t iidx; + IGRAPH_VECTOR_INIT_FINALLY(&iidx, remaining_vertices); + for (i = 0; i < no_of_nodes; i++) { + long int jj = (long int) VECTOR(*my_vertex_recoding)[i]; + if (jj != 0) { + VECTOR(iidx)[ jj - 1 ] = i; + } + } + IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, + &newgraph, + &iidx)); + IGRAPH_CHECK(igraph_vector_resize(&iidx, remaining_edges)); + for (i = 0; i < no_of_edges; i++) { + long int jj = (long int) VECTOR(edge_recoding)[i]; + if (jj != 0) { + VECTOR(iidx)[ jj - 1 ] = i; + } + } + IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, &newgraph, &iidx)); + igraph_vector_destroy(&iidx); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vit_destroy(&vit); + igraph_vector_destroy(&edge_recoding); + igraph_destroy(graph); + *graph = newgraph; + + IGRAPH_FINALLY_CLEAN(3); + + /* TODO: this is duplicate */ + if (invidx) { + IGRAPH_CHECK(igraph_vector_resize(invidx, remaining_vertices)); + for (i = 0; i < no_of_nodes; i++) { + long int newid = (long int) VECTOR(*my_vertex_recoding)[i]; + if (newid != 0) { + VECTOR(*invidx)[newid - 1] = i; + } + } + } + + if (!idx) { + igraph_vector_destroy(my_vertex_recoding); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \ingroup interface + * \function igraph_vcount + * \brief The number of vertices in a graph. + * + * \param graph The graph. + * \return Number of vertices. + * + * Time complexity: O(1) + */ +igraph_integer_t igraph_vcount(const igraph_t *graph) { + return graph->n; +} + +/** + * \ingroup interface + * \function igraph_ecount + * \brief The number of edges in a graph. + * + * \param graph The graph. + * \return Number of edges. + * + * Time complexity: O(1) + */ +igraph_integer_t igraph_ecount(const igraph_t *graph) { + return (igraph_integer_t) igraph_vector_size(&graph->from); +} + +/** + * \ingroup interface + * \function igraph_neighbors + * \brief Adjacent vertices to a vertex. + * + * \param graph The graph to work on. + * \param neis This vector will contain the result. The vector should + * be initialized beforehand and will be resized. Starting from igraph + * version 0.4 this vector is always sorted, the vertex ids are + * in increasing order. + * \param pnode The id of the node for which the adjacent vertices are + * to be searched. + * \param mode Defines the way adjacent vertices are searched in + * directed graphs. It can have the following values: + * \c IGRAPH_OUT, vertices reachable by an + * edge from the specified vertex are searched; + * \c IGRAPH_IN, vertices from which the + * specified vertex is reachable are searched; + * \c IGRAPH_ALL, both kinds of vertices are + * searched. + * This parameter is ignored for undirected graphs. + * \return Error code: + * \c IGRAPH_EINVVID: invalid vertex id. + * \c IGRAPH_EINVMODE: invalid mode argument. + * \c IGRAPH_ENOMEM: not enough memory. + * + * Time complexity: O(d), + * d is the number + * of adjacent vertices to the queried vertex. + * + * \example examples/simple/igraph_neighbors.c + */ +int igraph_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_integer_t pnode, + igraph_neimode_t mode) { + if (!igraph_is_directed(graph) || mode == IGRAPH_ALL) { + return igraph_i_neighbors(graph, neis, pnode, mode, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + } else { + return igraph_i_neighbors(graph, neis, pnode, mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + } +} + +int igraph_i_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_integer_t pnode, + igraph_neimode_t mode, igraph_loops_t loops, igraph_multiple_t multiple) { +#define DEDUPLICATE_IF_NEEDED(vertex, n) \ + if (should_filter_duplicates) { \ + if ((loops == IGRAPH_NO_LOOPS && vertex == pnode) || \ + (loops == IGRAPH_LOOPS_ONCE && vertex == pnode && last_added == pnode) || \ + (multiple == IGRAPH_NO_MULTIPLE && vertex == last_added)) { \ + length -= n; \ + continue; \ + } else { \ + last_added = vertex; \ + } \ + } + + long int length = 0, idx = 0; + long int i, j; + + long int node = pnode; + igraph_real_t last_added = -1; + igraph_bool_t should_filter_duplicates; + + if (node < 0 || node > igraph_vcount(graph) - 1) { + IGRAPH_ERROR("Given vertex is not in the graph.", IGRAPH_EINVVID); + } + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && + mode != IGRAPH_ALL) { + IGRAPH_ERROR("Mode should be either IGRAPH_OUT, IGRAPH_IN or IGRAPH_ALL.", IGRAPH_EINVMODE); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (mode != IGRAPH_ALL && loops == IGRAPH_LOOPS_TWICE) { + IGRAPH_ERROR("For a directed graph (with directions not ignored), " + "IGRAPH_LOOPS_TWICE does not make sense.\n", IGRAPH_EINVAL); + } + /* Calculate needed space first & allocate it */ + /* Note that 'mode' is treated as a bit field here; it's okay because + * IGRAPH_ALL = IGRAPH_IN | IGRAPH_OUT, bit-wise */ + if (mode & IGRAPH_OUT) { + length += (VECTOR(graph->os)[node + 1] - VECTOR(graph->os)[node]); + } + if (mode & IGRAPH_IN) { + length += (VECTOR(graph->is)[node + 1] - VECTOR(graph->is)[node]); + } + + IGRAPH_CHECK(igraph_vector_resize(neis, length)); + + /* The loops below produce an ordering what is consistent with the + * ordering returned by igraph_neighbors(), and this should be preserved. + * We are dealing with two sorted lists; one for the successors and one + * for the predecessors. If we have requested only one of them, we have + * an easy job. If we have requested both, we need to merge the two lists + * to ensure that the output is sorted by the vertex IDs of the "other" + * endpoint of the affected edges. We don't need to merge if the graph + * is undirected, because in that case the data structure guarantees that + * the "out-edges" contain only (u, v) pairs where u <= v and the + * "in-edges" contains the rest, so the result is sorted even without + * merging. */ + if (!igraph_is_directed(graph) || mode != IGRAPH_ALL) { + /* graph is undirected or we did not ask for both directions in a + * directed graph; this is the easy case */ + + should_filter_duplicates = !(multiple == IGRAPH_MULTIPLE && + ((!igraph_is_directed(graph) && loops == IGRAPH_LOOPS_TWICE) || + (igraph_is_directed(graph) && loops != IGRAPH_NO_LOOPS))); + + if (mode & IGRAPH_OUT) { + j = (long int) VECTOR(graph->os)[node + 1]; + for (i = (long int) VECTOR(graph->os)[node]; i < j; i++) { + igraph_real_t to = VECTOR(graph->to)[ (long int)VECTOR(graph->oi)[i] ]; + DEDUPLICATE_IF_NEEDED(to, 1); + VECTOR(*neis)[idx++] = to; + } + } + if (mode & IGRAPH_IN) { + j = (long int) VECTOR(graph->is)[node + 1]; + for (i = (long int) VECTOR(graph->is)[node]; i < j; i++) { + igraph_real_t from = VECTOR(graph->from)[ (long int)VECTOR(graph->ii)[i] ]; + DEDUPLICATE_IF_NEEDED(from, 1); + VECTOR(*neis)[idx++] = from; + } + } + } else { + /* Both in- and out- neighbors in a directed graph, + we need to merge the two 'vectors' so the result is + correctly ordered. */ + long int j1 = (long int) VECTOR(graph->os)[node + 1]; + long int j2 = (long int) VECTOR(graph->is)[node + 1]; + long int i1 = (long int) VECTOR(graph->os)[node]; + long int i2 = (long int) VECTOR(graph->is)[node]; + long int eid1, eid2; + long int n1, n2; + + should_filter_duplicates = !(multiple == IGRAPH_MULTIPLE && + loops == IGRAPH_LOOPS_TWICE); + + while (i1 < j1 && i2 < j2) { + eid1 = (long int) VECTOR(graph->oi)[i1]; + eid2 = (long int) VECTOR(graph->ii)[i2]; + n1 = (long int) VECTOR(graph->to)[eid1]; + n2 = (long int) VECTOR(graph->from)[eid2]; + if (n1 < n2) { + i1++; + DEDUPLICATE_IF_NEEDED(n1, 1); + VECTOR(*neis)[idx++] = n1; + } else if (n1 > n2) { + i2++; + DEDUPLICATE_IF_NEEDED(n2, 1); + VECTOR(*neis)[idx++] = n2; + } else { + i1++; + i2++; + DEDUPLICATE_IF_NEEDED(n1, 2); + VECTOR(*neis)[idx++] = n1; + if (should_filter_duplicates && ((loops == IGRAPH_LOOPS_ONCE && n1 == pnode && last_added == pnode) || + (multiple == IGRAPH_NO_MULTIPLE))) { + length--; + continue; + } + VECTOR(*neis)[idx++] = n2; + } + } + + while (i1 < j1) { + eid1 = (long int) VECTOR(graph->oi)[i1++]; + igraph_real_t to = (long int) VECTOR(graph->to)[eid1]; + DEDUPLICATE_IF_NEEDED(to, 1); + VECTOR(*neis)[idx++] = to; + } + + while (i2 < j2) { + eid2 = (long int) VECTOR(graph->ii)[i2++]; + igraph_real_t from = (long int) VECTOR(graph->from)[eid2]; + DEDUPLICATE_IF_NEEDED(from, 1); + VECTOR(*neis)[idx++] = from; + } + + } + IGRAPH_CHECK(igraph_vector_resize(neis, length)); + + return IGRAPH_SUCCESS; +#undef DEDUPLICATE_IF_NEEDED +} +/** + * \ingroup internal + * + */ + +static int igraph_i_create_start( + igraph_vector_t *res, igraph_vector_t *el, + igraph_vector_t *iindex, igraph_integer_t nodes) { + +# define EDGE(i) (VECTOR(*el)[ (long int) VECTOR(*iindex)[(i)] ]) + + long int no_of_nodes; + long int no_of_edges; + long int i, j, idx; + + no_of_nodes = nodes; + no_of_edges = igraph_vector_size(el); + + /* result */ + + IGRAPH_CHECK(igraph_vector_resize(res, nodes + 1)); + + /* create the index */ + + if (igraph_vector_size(el) == 0) { + /* empty graph */ + igraph_vector_null(res); + } else { + idx = -1; + for (i = 0; i <= EDGE(0); i++) { + idx++; VECTOR(*res)[idx] = 0; + } + for (i = 1; i < no_of_edges; i++) { + long int n = (long int) (EDGE(i) - EDGE((long int)VECTOR(*res)[idx])); + for (j = 0; j < n; j++) { + idx++; VECTOR(*res)[idx] = i; + } + } + j = (long int) EDGE((long int)VECTOR(*res)[idx]); + for (i = 0; i < no_of_nodes - j; i++) { + idx++; VECTOR(*res)[idx] = no_of_edges; + } + } + + /* clean */ + +# undef EDGE + return 0; +} + +/** + * \ingroup interface + * \function igraph_is_directed + * \brief Is this a directed graph? + * + * \param graph The graph. + * \return Logical value, TRUE if the graph is directed, + * FALSE otherwise. + * + * Time complexity: O(1) + * + * \example examples/simple/igraph_is_directed.c + */ + +igraph_bool_t igraph_is_directed(const igraph_t *graph) { + return graph->directed; +} + +/** + * \ingroup interface + * \function igraph_degree + * \brief The degree of some vertices in a graph. + * + * + * This function calculates the in-, out- or total degree of the + * specified vertices. + * \param graph The graph. + * \param res Vector, this will contain the result. It should be + * initialized and will be resized to be the appropriate size. + * \param vids Vector, giving the vertex ids of which the degree will + * be calculated. + * \param mode Defines the type of the degree. Valid modes are: + * \c IGRAPH_OUT, out-degree; + * \c IGRAPH_IN, in-degree; + * \c IGRAPH_ALL, total degree (sum of the + * in- and out-degree). + * This parameter is ignored for undirected graphs. + * \param loops Boolean, gives whether the self-loops should be + * counted. + * \return Error code: + * \c IGRAPH_EINVVID: invalid vertex id. + * \c IGRAPH_EINVMODE: invalid mode argument. + * + * Time complexity: O(v) if + * loops is + * TRUE, and + * O(v*d) + * otherwise. v is the number of + * vertices for which the degree will be calculated, and + * d is their (average) degree. + * + * \sa \ref igraph_strength() for the version that takes into account + * edge weights. + * + * \example examples/simple/igraph_degree.c + */ +int igraph_degree(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, + igraph_neimode_t mode, igraph_bool_t loops) { + + long int nodes_to_calc; + long int i, j; + igraph_vit_t vit; + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { + IGRAPH_ERROR("degree calculation failed", IGRAPH_EINVMODE); + } + + nodes_to_calc = IGRAPH_VIT_SIZE(vit); + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); + igraph_vector_null(res); + + if (loops) { + if (mode & IGRAPH_OUT) { + for (IGRAPH_VIT_RESET(vit), i = 0; + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) { + long int vid = IGRAPH_VIT_GET(vit); + VECTOR(*res)[i] += (VECTOR(graph->os)[vid + 1] - VECTOR(graph->os)[vid]); + } + } + if (mode & IGRAPH_IN) { + for (IGRAPH_VIT_RESET(vit), i = 0; + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) { + long int vid = IGRAPH_VIT_GET(vit); + VECTOR(*res)[i] += (VECTOR(graph->is)[vid + 1] - VECTOR(graph->is)[vid]); + } + } + } else { /* no loops */ + if (mode & IGRAPH_OUT) { + for (IGRAPH_VIT_RESET(vit), i = 0; + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) { + long int vid = IGRAPH_VIT_GET(vit); + VECTOR(*res)[i] += (VECTOR(graph->os)[vid + 1] - VECTOR(graph->os)[vid]); + for (j = (long int) VECTOR(graph->os)[vid]; + j < VECTOR(graph->os)[vid + 1]; j++) { + if (VECTOR(graph->to)[ (long int)VECTOR(graph->oi)[j] ] == vid) { + VECTOR(*res)[i] -= 1; + } + } + } + } + if (mode & IGRAPH_IN) { + for (IGRAPH_VIT_RESET(vit), i = 0; + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) { + long int vid = IGRAPH_VIT_GET(vit); + VECTOR(*res)[i] += (VECTOR(graph->is)[vid + 1] - VECTOR(graph->is)[vid]); + for (j = (long int) VECTOR(graph->is)[vid]; + j < VECTOR(graph->is)[vid + 1]; j++) { + if (VECTOR(graph->from)[ (long int)VECTOR(graph->ii)[j] ] == vid) { + VECTOR(*res)[i] -= 1; + } + } + } + } + } /* loops */ + + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_edge + * \brief Gives the head and tail vertices of an edge. + * + * \param graph The graph object. + * \param eid The edge id. + * \param from Pointer to an \type igraph_integer_t. The tail (head) of + * the edge will be placed here for undirected (directed) graphs. + * \param to Pointer to an \type igraph_integer_t. The head (tail) of the + * edge will be placed here for undirected (directed) graphs. + * \return Error code. The current implementation always returns with + * success. + * \sa \ref igraph_get_eid() for the opposite operation; + * \ref igraph_edges() to get the endpoints of several edges; + * \ref IGRAPH_TO(), \ref IGRAPH_FROM() and \ref IGRAPH_OTHER() for + * a faster but non-error-checked version. + * + * Added in version 0.2. + * + * Time complexity: O(1). + */ + +int igraph_edge(const igraph_t *graph, igraph_integer_t eid, + igraph_integer_t *from, igraph_integer_t *to) { + + if (igraph_is_directed(graph)) { + *from = IGRAPH_FROM(graph, eid); + *to = IGRAPH_TO(graph, eid); + } else { + *from = IGRAPH_TO(graph, eid); + *to = IGRAPH_FROM(graph, eid); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_edges + * \brief Gives the head and tail vertices of a series of edges. + * + * \param graph The graph object. + * \param eids Edge selector, the series of edges. + * \param edges Pointer to an initialized vector. The start and endpoints of + * each edge will be placed here. + * \return Error code. + * \sa \ref igraph_get_edgelist() to get the endpoints of all edges; + * \ref igraph_get_eids() and \ref igraph_get_eids_multi() + * for the opposite operation; + * \ref igraph_edge() for getting the endpoints of a single edge; + * \ref IGRAPH_TO(), \ref IGRAPH_FROM() and \ref IGRAPH_OTHER() for + * a faster but non-error-checked method. + * + * Time complexity: O(k) where k is the number of edges in the selector. + */ + +int igraph_edges(const igraph_t *graph, igraph_es_t eids, + igraph_vector_t *edges) { + + igraph_eit_t eit; + long int n, ptr = 0; + + IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + n = IGRAPH_EIT_SIZE(eit); + IGRAPH_CHECK(igraph_vector_resize(edges, n * 2)); + if (igraph_is_directed(graph)) { + for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { + long int e = IGRAPH_EIT_GET(eit); + VECTOR(*edges)[ptr++] = IGRAPH_FROM(graph, e); + VECTOR(*edges)[ptr++] = IGRAPH_TO(graph, e); + } + } else { + for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { + long int e = IGRAPH_EIT_GET(eit); + VECTOR(*edges)[ptr++] = IGRAPH_TO(graph, e); + VECTOR(*edges)[ptr++] = IGRAPH_FROM(graph, e); + } + } + + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/* This is an unsafe macro. Only supply variable names, i.e. no + expressions as parameters, otherwise nasty things can happen */ + +#define BINSEARCH(start,end,value,iindex,edgelist,N,pos) \ + do { \ + while ((start) < (end)) { \ + long int mid=(start)+((end)-(start))/2; \ + long int e=(long int) VECTOR((iindex))[mid]; \ + if (VECTOR((edgelist))[e] < (value)) { \ + (start)=mid+1; \ + } else { \ + (end)=mid; \ + } \ + } \ + if ((start)<(N)) { \ + long int e=(long int) VECTOR((iindex))[(start)]; \ + if (VECTOR((edgelist))[e] == (value)) { \ + *(pos)=(igraph_integer_t) e; \ + } \ + } } while(0) + +#define FIND_DIRECTED_EDGE(graph,xfrom,xto,eid) \ + do { \ + long int start=(long int) VECTOR(graph->os)[xfrom]; \ + long int end=(long int) VECTOR(graph->os)[xfrom+1]; \ + long int N=end; \ + long int start2=(long int) VECTOR(graph->is)[xto]; \ + long int end2=(long int) VECTOR(graph->is)[xto+1]; \ + long int N2=end2; \ + if (end-startoi,graph->to,N,eid); \ + } else { \ + BINSEARCH(start2,end2,xfrom,graph->ii,graph->from,N2,eid); \ + } \ + } while (0) + +#define FIND_UNDIRECTED_EDGE(graph,from,to,eid) \ + do { \ + long int xfrom1= from > to ? from : to; \ + long int xto1= from > to ? to : from; \ + FIND_DIRECTED_EDGE(graph,xfrom1,xto1,eid); \ + } while (0) + +/** + * \function igraph_get_eid + * \brief Get the edge id from the end points of an edge. + * + * For undirected graphs \c pfrom and \c pto are exchangeable. + * + * \param graph The graph object. + * \param eid Pointer to an integer, the edge id will be stored here. + * \param pfrom The starting point of the edge. + * \param pto The end point of the edge. + * \param directed Logical constant, whether to search for directed + * edges in a directed graph. Ignored for undirected graphs. + * \param error Logical scalar, whether to report an error if the edge + * was not found. If it is false, then -1 will be assigned to \p eid. + * \return Error code. + * \sa \ref igraph_edge() for the opposite operation. + * + * Time complexity: O(log (d)), where d is smaller of the out-degree + * of \c pfrom and in-degree of \c pto if \p directed is true. If \p directed + * is false, then it is O(log(d)+log(d2)), where d is the same as before and + * d2 is the minimum of the out-degree of \c pto and the in-degree of \c pfrom. + * + * \example examples/simple/igraph_get_eid.c + * + * Added in version 0.2. + */ + +int igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid, + igraph_integer_t pfrom, igraph_integer_t pto, + igraph_bool_t directed, igraph_bool_t error) { + + long int from = pfrom, to = pto; + long int nov = igraph_vcount(graph); + + if (from < 0 || to < 0 || from > nov - 1 || to > nov - 1) { + IGRAPH_ERROR("cannot get edge id", IGRAPH_EINVVID); + } + + *eid = -1; + if (igraph_is_directed(graph)) { + + /* Directed graph */ + FIND_DIRECTED_EDGE(graph, from, to, eid); + if (!directed && *eid < 0) { + FIND_DIRECTED_EDGE(graph, to, from, eid); + } + + } else { + + /* Undirected graph, they only have one mode */ + FIND_UNDIRECTED_EDGE(graph, from, to, eid); + + } + + if (*eid < 0) { + if (error) { + IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); + } + } + + return IGRAPH_SUCCESS; +} + +int igraph_get_eids_pairs(const igraph_t *graph, igraph_vector_t *eids, + const igraph_vector_t *pairs, + igraph_bool_t directed, igraph_bool_t error); + +int igraph_get_eids_path(const igraph_t *graph, igraph_vector_t *eids, + const igraph_vector_t *path, + igraph_bool_t directed, igraph_bool_t error); + +int igraph_get_eids_pairs(const igraph_t *graph, igraph_vector_t *eids, + const igraph_vector_t *pairs, + igraph_bool_t directed, igraph_bool_t error) { + long int n = igraph_vector_size(pairs); + long int no_of_nodes = igraph_vcount(graph); + long int i; + igraph_integer_t eid = -1; + + if (n % 2 != 0) { + IGRAPH_ERROR("Cannot get edge ids, invalid length of edge ids", + IGRAPH_EINVAL); + } + if (!igraph_vector_isininterval(pairs, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Cannot get edge ids, invalid vertex id", IGRAPH_EINVVID); + } + + IGRAPH_CHECK(igraph_vector_resize(eids, n / 2)); + + if (igraph_is_directed(graph)) { + for (i = 0; i < n / 2; i++) { + long int from = (long int) VECTOR(*pairs)[2 * i]; + long int to = (long int) VECTOR(*pairs)[2 * i + 1]; + + eid = -1; + FIND_DIRECTED_EDGE(graph, from, to, &eid); + if (!directed && eid < 0) { + FIND_DIRECTED_EDGE(graph, to, from, &eid); + } + + VECTOR(*eids)[i] = eid; + if (eid < 0 && error) { + IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); + } + } + } else { + for (i = 0; i < n / 2; i++) { + long int from = (long int) VECTOR(*pairs)[2 * i]; + long int to = (long int) VECTOR(*pairs)[2 * i + 1]; + + eid = -1; + FIND_UNDIRECTED_EDGE(graph, from, to, &eid); + VECTOR(*eids)[i] = eid; + if (eid < 0 && error) { + IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); + } + } + } + + return 0; +} + +int igraph_get_eids_path(const igraph_t *graph, igraph_vector_t *eids, + const igraph_vector_t *path, + igraph_bool_t directed, igraph_bool_t error) { + + long int n = igraph_vector_size(path); + long int no_of_nodes = igraph_vcount(graph); + long int i; + igraph_integer_t eid = -1; + + if (!igraph_vector_isininterval(path, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Cannot get edge ids, invalid vertex id", IGRAPH_EINVVID); + } + + IGRAPH_CHECK(igraph_vector_resize(eids, n == 0 ? 0 : n - 1)); + + if (igraph_is_directed(graph)) { + for (i = 0; i < n - 1; i++) { + long int from = (long int) VECTOR(*path)[i]; + long int to = (long int) VECTOR(*path)[i + 1]; + + eid = -1; + FIND_DIRECTED_EDGE(graph, from, to, &eid); + if (!directed && eid < 0) { + FIND_DIRECTED_EDGE(graph, to, from, &eid); + } + + VECTOR(*eids)[i] = eid; + if (eid < 0 && error) { + IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); + } + } + } else { + for (i = 0; i < n - 1; i++) { + long int from = (long int) VECTOR(*path)[i]; + long int to = (long int) VECTOR(*path)[i + 1]; + + eid = -1; + FIND_UNDIRECTED_EDGE(graph, from, to, &eid); + VECTOR(*eids)[i] = eid; + if (eid < 0 && error) { + IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); + } + } + } + + return 0; +} + +/** + * \function igraph_get_eids + * Return edge ids based on the adjacent vertices. + * + * This function operates in two modes. If the \c pairs argument is + * not a null pointer, but the \c path argument is, then it searches + * for the edge ids of all pairs of vertices given in \c pairs. The + * pairs of vertex ids are taken consecutively from the vector, + * i.e. VECTOR(pairs)[0] and + * VECTOR(pairs)[1] give the first + * pair, VECTOR(pairs)[2] and + * VECTOR(pairs)[3] the second pair, etc. + * + * + * If the \c pairs argument is a null pointer, and \c path is not a + * null pointer, then the \c path is interpreted as a path given by + * vertex ids and the edges along the path are returned. + * + * + * If neither \c pairs nor \c path are null pointers, then both are + * considered (first \c pairs and then \c path), and the results are + * concatenated. + * + * + * If the \c error argument is true, then it is an error to give pairs + * of vertices that are not connected. Otherwise -1 is + * reported for not connected vertices. + * + * + * If there are multiple edges in the graph, then these are ignored; + * i.e. for a given pair of vertex ids, always the same edge id is + * returned, even if the pair is given multiple time in \c pairs or in + * \c path. See \ref igraph_get_eids_multi() for a similar function + * that works differently in case of multiple edges. + * + * \param graph The input graph. + * \param eids Pointer to an initialized vector, the result is stored + * here. It will be resized as needed. + * \param pairs Vector giving pairs of vertices, or a null pointer. + * \param path Vector giving vertex ids along a path, or a null + * pointer. + * \param directed Logical scalar, whether to consider edge directions + * in directed graphs. This is ignored for undirected graphs. + * \param error Logical scalar, whether it is an error to supply + * non-connected vertices. If false, then -1 is + * returned for non-connected pairs. + * \return Error code. + * + * Time complexity: O(n log(d)), where n is the number of queried + * edges and d is the average degree of the vertices. + * + * \sa \ref igraph_get_eid() for a single edge, \ref + * igraph_get_eids_multi() for a version that handles multiple edges + * better (at a cost). + * + * \example examples/simple/igraph_get_eids.c + */ + +int igraph_get_eids(const igraph_t *graph, igraph_vector_t *eids, + const igraph_vector_t *pairs, + const igraph_vector_t *path, + igraph_bool_t directed, igraph_bool_t error) { + + if (!pairs && !path) { + igraph_vector_clear(eids); + return 0; + } else if (pairs && !path) { + return igraph_get_eids_pairs(graph, eids, pairs, directed, error); + } else if (!pairs && path) { + return igraph_get_eids_path(graph, eids, path, directed, error); + } else { + /* both */ + igraph_vector_t tmp; + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + IGRAPH_CHECK(igraph_get_eids_pairs(graph, eids, pairs, directed, error)); + IGRAPH_CHECK(igraph_get_eids_path(graph, &tmp, path, directed, error)); + IGRAPH_CHECK(igraph_vector_append(eids, &tmp)); + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + return 0; + } +} + +#undef BINSEARCH +#undef FIND_DIRECTED_EDGE +#undef FIND_UNDIRECTED_EDGE + +#define BINSEARCH(start,end,value,iindex,edgelist,N,pos,seen) \ + do { \ + while ((start) < (end)) { \ + long int mid=(start)+((end)-(start))/2; \ + long int e=(long int) VECTOR((iindex))[mid]; \ + if (VECTOR((edgelist))[e] < (value)) { \ + (start)=mid+1; \ + } else { \ + (end)=mid; \ + } \ + } \ + if ((start)<(N)) { \ + long int e=(long int) VECTOR((iindex))[(start)]; \ + while ((start)<(N) && seen[e] && VECTOR(edgelist)[e] == (value)) { \ + (start)++; \ + e=(long int) VECTOR(iindex)[(start)]; \ + } \ + if ((start)<(N) && !(seen[e]) && VECTOR(edgelist)[e] == (value)) { \ + *(pos)=(igraph_integer_t) e; \ + } \ + } } while(0) + +#define FIND_DIRECTED_EDGE(graph,xfrom,xto,eid,seen) \ + do { \ + long int start=(long int) VECTOR(graph->os)[xfrom]; \ + long int end=(long int) VECTOR(graph->os)[xfrom+1]; \ + long int N=end; \ + long int start2=(long int) VECTOR(graph->is)[xto]; \ + long int end2=(long int) VECTOR(graph->is)[xto+1]; \ + long int N2=end2; \ + if (end-startoi,graph->to,N,eid,seen); \ + } else { \ + BINSEARCH(start2,end2,xfrom,graph->ii,graph->from,N2,eid,seen); \ + } \ + } while (0) + +#define FIND_UNDIRECTED_EDGE(graph,from,to,eid,seen) \ + do { \ + long int xfrom1= from > to ? from : to; \ + long int xto1= from > to ? to : from; \ + FIND_DIRECTED_EDGE(graph,xfrom1,xto1,eid,seen); \ + } while (0) + + +int igraph_get_eids_multipairs(const igraph_t *graph, igraph_vector_t *eids, + const igraph_vector_t *pairs, + igraph_bool_t directed, igraph_bool_t error); + +int igraph_get_eids_multipath(const igraph_t *graph, igraph_vector_t *eids, + const igraph_vector_t *path, + igraph_bool_t directed, igraph_bool_t error); + +int igraph_get_eids_multipairs(const igraph_t *graph, igraph_vector_t *eids, + const igraph_vector_t *pairs, + igraph_bool_t directed, igraph_bool_t error) { + + long int n = igraph_vector_size(pairs); + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_bool_t *seen; + long int i; + igraph_integer_t eid = -1; + + if (n % 2 != 0) { + IGRAPH_ERROR("Cannot get edge ids, invalid length of edge ids", + IGRAPH_EINVAL); + } + if (!igraph_vector_isininterval(pairs, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Cannot get edge ids, invalid vertex id", IGRAPH_EINVVID); + } + + seen = IGRAPH_CALLOC(no_of_edges, igraph_bool_t); + if (seen == 0) { + IGRAPH_ERROR("Cannot get edge ids", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, seen); + IGRAPH_CHECK(igraph_vector_resize(eids, n / 2)); + + if (igraph_is_directed(graph)) { + for (i = 0; i < n / 2; i++) { + long int from = (long int) VECTOR(*pairs)[2 * i]; + long int to = (long int) VECTOR(*pairs)[2 * i + 1]; + + eid = -1; + FIND_DIRECTED_EDGE(graph, from, to, &eid, seen); + if (!directed && eid < 0) { + FIND_DIRECTED_EDGE(graph, to, from, &eid, seen); + } + + VECTOR(*eids)[i] = eid; + if (eid >= 0) { + seen[(long int)(eid)] = 1; + } else if (error) { + IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); + } + } + } else { + for (i = 0; i < n / 2; i++) { + long int from = (long int) VECTOR(*pairs)[2 * i]; + long int to = (long int) VECTOR(*pairs)[2 * i + 1]; + + eid = -1; + FIND_UNDIRECTED_EDGE(graph, from, to, &eid, seen); + VECTOR(*eids)[i] = eid; + if (eid >= 0) { + seen[(long int)(eid)] = 1; + } else if (error) { + IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); + } + } + } + + IGRAPH_FREE(seen); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +int igraph_get_eids_multipath(const igraph_t *graph, igraph_vector_t *eids, + const igraph_vector_t *path, + igraph_bool_t directed, igraph_bool_t error) { + + long int n = igraph_vector_size(path); + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_bool_t *seen; + long int i; + igraph_integer_t eid = -1; + + if (!igraph_vector_isininterval(path, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Cannot get edge ids, invalid vertex id", IGRAPH_EINVVID); + } + + seen = IGRAPH_CALLOC(no_of_edges, igraph_bool_t); + if (!seen) { + IGRAPH_ERROR("Cannot get edge ids", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, seen); + IGRAPH_CHECK(igraph_vector_resize(eids, n == 0 ? 0 : n - 1)); + + if (igraph_is_directed(graph)) { + for (i = 0; i < n - 1; i++) { + long int from = (long int) VECTOR(*path)[i]; + long int to = (long int) VECTOR(*path)[i + 1]; + + eid = -1; + FIND_DIRECTED_EDGE(graph, from, to, &eid, seen); + if (!directed && eid < 0) { + FIND_DIRECTED_EDGE(graph, to, from, &eid, seen); + } + + VECTOR(*eids)[i] = eid; + if (eid >= 0) { + seen[(long int)(eid)] = 1; + } else if (error) { + IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); + } + } + } else { + for (i = 0; i < n - 1; i++) { + long int from = (long int) VECTOR(*path)[i]; + long int to = (long int) VECTOR(*path)[i + 1]; + + eid = -1; + FIND_UNDIRECTED_EDGE(graph, from, to, &eid, seen); + VECTOR(*eids)[i] = eid; + if (eid >= 0) { + seen[(long int)(eid)] = 1; + } else if (error) { + IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); + } + } + } + + IGRAPH_FREE(seen); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +#undef BINSEARCH +#undef FIND_DIRECTED_EDGE +#undef FIND_UNDIRECTED_EDGE + +/** + * \function igraph_get_eids_multi + * \brief Query edge ids based on their adjacent vertices, handle multiple edges. + * + * This function operates in two modes. If the \c pairs argument is + * not a null pointer, but the \c path argument is, then it searches + * for the edge ids of all pairs of vertices given in \c pairs. The + * pairs of vertex ids are taken consecutively from the vector, + * i.e. VECTOR(pairs)[0] and + * VECTOR(pairs)[1] give the first pair, + * VECTOR(pairs)[2] and VECTOR(pairs)[3] the + * second pair, etc. + * + * + * If the \c pairs argument is a null pointer, and \c path is not a + * null pointer, then the \c path is interpreted as a path given by + * vertex ids and the edges along the path are returned. + * + * + * If the \c error argument is true, then it is an error to give pairs of + * vertices that are not connected. Otherwise -1 is + * returned for not connected vertex pairs. + * + * + * An error is triggered if both \c pairs and \c path are non-null + * pointers. + * + * + * This function handles multiple edges properly, i.e. if the same + * pair is given multiple times and they are indeed connected by + * multiple edges, then each time a different edge id is reported. + * + * \param graph The input graph. + * \param eids Pointer to an initialized vector, the result is stored + * here. It will be resized as needed. + * \param pairs Vector giving pairs of vertices, or a null pointer. + * \param path Vector giving vertex ids along a path, or a null + * pointer. + * \param directed Logical scalar, whether to consider edge directions + * in directed graphs. This is ignored for undirected graphs. + * \param error Logical scalar, whether to report an error if + * non-connected vertices are specified. If false, then -1 + * is returned for non-connected vertex pairs. + * \return Error code. + * + * Time complexity: O(|E|+n log(d)), where |E| is the number of edges + * in the graph, n is the number of queried edges and d is the average + * degree of the vertices. + * + * \sa \ref igraph_get_eid() for a single edge, \ref + * igraph_get_eids() for a faster version that does not handle + * multiple edges. + */ + +int igraph_get_eids_multi(const igraph_t *graph, igraph_vector_t *eids, + const igraph_vector_t *pairs, + const igraph_vector_t *path, + igraph_bool_t directed, igraph_bool_t error) { + + if (!pairs && !path) { + igraph_vector_clear(eids); + return 0; + } else if (pairs && !path) { + return igraph_get_eids_multipairs(graph, eids, pairs, directed, error); + } else if (!pairs && path) { + return igraph_get_eids_multipath(graph, eids, path, directed, error); + } else { /* both */ + IGRAPH_ERROR("Give `pairs' or `path' but not both", IGRAPH_EINVAL); + } +} + +/** + * \function igraph_incident + * \brief Gives the incident edges of a vertex. + * + * \param graph The graph object. + * \param eids An initialized \type vector_t object. It will be resized + * to hold the result. + * \param pnode A vertex id. + * \param mode Specifies what kind of edges to include for directed + * graphs. \c IGRAPH_OUT means only outgoing edges, \c IGRAPH_IN only + * incoming edges, \c IGRAPH_ALL both. This parameter is ignored for + * undirected graphs. + * \return Error code. \c IGRAPH_EINVVID: invalid \p pnode argument, + * \c IGRAPH_EINVMODE: invalid \p mode argument. + * + * Added in version 0.2. + * + * Time complexity: O(d), the number of incident edges to \p pnode. + */ + +int igraph_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t pnode, + igraph_neimode_t mode) { + if (!igraph_is_directed(graph) || mode == IGRAPH_ALL) { + return igraph_i_incident(graph, eids, pnode, mode, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + } else { + return igraph_i_incident(graph, eids, pnode, mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + } +} + +int igraph_i_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t pnode, + igraph_neimode_t mode, igraph_loops_t loops, igraph_multiple_t multiple) { +#define DEDUPLICATE_IF_NEEDED(vertex, n) \ + if (should_filter_duplicates) { \ + if ((loops == IGRAPH_NO_LOOPS && vertex == pnode) || \ + (loops == IGRAPH_LOOPS_ONCE && vertex == pnode && last_added == pnode) || \ + (multiple == IGRAPH_NO_MULTIPLE && vertex == last_added)) { \ + length -= n; \ + continue; \ + } else { \ + last_added = vertex; \ + } \ + } + long int length = 0, idx = 0; + long int i, j; + + long int node = pnode; + igraph_real_t last_added = -1; + + igraph_bool_t should_filter_duplicates; + + if (node < 0 || node > igraph_vcount(graph) - 1) { + IGRAPH_ERROR("Given vertex is not in the graph.", IGRAPH_EINVVID); + } + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && + mode != IGRAPH_ALL) { + IGRAPH_ERROR("Mode should be either IGRAPH_OUT, IGRAPH_IN or IGRAPH_ALL.", IGRAPH_EINVMODE); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (mode != IGRAPH_ALL && loops == IGRAPH_LOOPS_TWICE) { + IGRAPH_ERROR("For a directed graph (with directions not ignored), " + "IGRAPH_LOOPS_TWICE does not make sense.\n", IGRAPH_EINVAL); + } + + /* Calculate needed space first & allocate it */ + /* Note that 'mode' is treated as a bit field here; it's okay because + * IGRAPH_ALL = IGRAPH_IN | IGRAPH_OUT, bit-wise */ + if (mode & IGRAPH_OUT) { + length += (VECTOR(graph->os)[node + 1] - VECTOR(graph->os)[node]); + } + if (mode & IGRAPH_IN) { + length += (VECTOR(graph->is)[node + 1] - VECTOR(graph->is)[node]); + } + + IGRAPH_CHECK(igraph_vector_resize(eids, length)); + + /* The loops below produce an ordering what is consistent with the + * ordering returned by igraph_neighbors(), and this should be preserved. + * We are dealing with two sorted lists; one for the successors and one + * for the predecessors. If we have requested only one of them, we have + * an easy job. If we have requested both, we need to merge the two lists + * to ensure that the output is sorted by the vertex IDs of the "other" + * endpoint of the affected edges */ + if (!igraph_is_directed(graph) || mode != IGRAPH_ALL) { + /* We did not ask for both directions; this is the easy case */ + + should_filter_duplicates = !(multiple == IGRAPH_MULTIPLE && + ((!igraph_is_directed(graph) && loops == IGRAPH_LOOPS_TWICE) || + (igraph_is_directed(graph) && loops != IGRAPH_NO_LOOPS))); + + if (mode & IGRAPH_OUT) { + j = (long int) VECTOR(graph->os)[node + 1]; + for (i = (long int) VECTOR(graph->os)[node]; i < j; i++) { + long int edge = VECTOR(graph->oi)[i]; + igraph_real_t other = VECTOR(graph->to)[edge]; + DEDUPLICATE_IF_NEEDED(other, 1); + VECTOR(*eids)[idx++] = edge; + } + } + if (mode & IGRAPH_IN) { + j = (long int) VECTOR(graph->is)[node + 1]; + for (i = (long int) VECTOR(graph->is)[node]; i < j; i++) { + long int edge = VECTOR(graph->ii)[i]; + igraph_real_t other = VECTOR(graph->from)[edge]; + DEDUPLICATE_IF_NEEDED(other, 1); + VECTOR(*eids)[idx++] = edge; + } + } + } else { + /* both in- and out- neighbors in a directed graph, + we need to merge the two 'vectors' */ + long int j1 = (long int) VECTOR(graph->os)[node + 1]; + long int j2 = (long int) VECTOR(graph->is)[node + 1]; + long int i1 = (long int) VECTOR(graph->os)[node]; + long int i2 = (long int) VECTOR(graph->is)[node]; + long int eid1, eid2; + long int n1, n2; + + should_filter_duplicates = !(multiple == IGRAPH_MULTIPLE && + loops == IGRAPH_LOOPS_TWICE); + + while (i1 < j1 && i2 < j2) { + eid1 = (long int) VECTOR(graph->oi)[i1]; + eid2 = (long int) VECTOR(graph->ii)[i2]; + n1 = (long int) VECTOR(graph->to)[eid1]; + n2 = (long int) VECTOR(graph->from)[eid2]; + if (n1 < n2) { + i1++; + DEDUPLICATE_IF_NEEDED(n1, 1); + VECTOR(*eids)[idx++] = eid1; + } else if (n1 > n2) { + i2++; + DEDUPLICATE_IF_NEEDED(n2, 1); + VECTOR(*eids)[idx++] = eid2; + } else { + i1++; + i2++; + DEDUPLICATE_IF_NEEDED(n2, 2); + VECTOR(*eids)[idx++] = eid1; + if (should_filter_duplicates && ((loops == IGRAPH_LOOPS_ONCE && n1 == pnode && last_added == pnode) || + (multiple == IGRAPH_NO_MULTIPLE))) { + length--; + continue; + } + VECTOR(*eids)[idx++] = eid2; + } + } + + while (i1 < j1) { + eid1 = VECTOR(graph->oi)[i1++]; + igraph_real_t to = VECTOR(graph->to)[eid1]; + DEDUPLICATE_IF_NEEDED(to, 1); + VECTOR(*eids)[idx++] = eid1; + } + + while (i2 < j2) { + eid2 = VECTOR(graph->ii)[i2++]; + igraph_real_t from = VECTOR(graph->from)[eid2]; + DEDUPLICATE_IF_NEEDED(from, 1); + VECTOR(*eids)[idx++] = eid2; + } + } + IGRAPH_CHECK(igraph_vector_resize(eids, length)); + return IGRAPH_SUCCESS; +#undef DEDUPLICATE_IF_NEEDED +} + + +/** + * \function igraph_is_same_graph + * \brief Are two graphs identical as labelled graphs? + * + * Two graphs are considered to be the same if they have the same vertex and edge sets. + * Graphs which are the same may have multiple different representations in igraph, + * hence the need for this function. + * + * + * This function verifies that the two graphs have the same directedness, the same + * number of vertices, and that they contain precisely the same edges (regardless of their ordering) + * when written in terms of vertex indices. Graph attributes are not taken into account. + * + * + * This concept is different from isomorphism. For example, the graphs + * 0-1, 2-1 and 1-2, 0-1 are considered the same + * because they only differ in the ordering of their edge lists and the ordering + * of vertices in an undirected edge. However, they are not the same as + * 0-2, 1-2, even though they are isomorphic to it. + * Note that this latter graph contains the edge 0-2 + * while the former two do not — thus their edge sets differ. + * + * \param graph1 The first graph object. + * \param graph2 The second graph object. + * \param res The result will be stored here. + * \return Error code. + * + * Time complexity: O(E), the number of edges in the graphs. + * + * \sa igraph_isomorphic() to test if two graphs are isomorphic. + */ + +int igraph_is_same_graph(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *res) { + long int nv1 = igraph_vcount(graph1); + long int nv2 = igraph_vcount(graph2); + long int ne1 = igraph_ecount(graph1); + long int ne2 = igraph_ecount(graph2); + long int i, eid1, eid2; + + *res = 0; /* Assume that the graphs differ */ + + /* Check for same number of vertices/edges */ + if ((nv1 != nv2) || (ne1 != ne2)) { + return IGRAPH_SUCCESS; + } + + /* Check for same directedness */ + if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) { + return IGRAPH_SUCCESS; + } + + /* Vertices have no names, so they must be 0 to nv - 1 */ + + /* Edges are double sorted in the current representations ii/oi of + * igraph_t (ii: by incoming, then outgoing, oi: vice versa), so + * we just need to check them one by one. If that representation + * changes, this part will need to change too. + * + * Furthermore, in the current representation the "source" of undirected + * edges always has a vertex index that is no larger than that of the + * "target". + */ + for (i = 0; i < ne1; i++) { + eid1 = (long int) VECTOR(graph1->ii)[i]; + eid2 = (long int) VECTOR(graph2->ii)[i]; + + /* Check they have the same source */ + if (IGRAPH_FROM(graph1, eid1) != IGRAPH_FROM(graph2, eid2)) { + return IGRAPH_SUCCESS; + } + + /* Check they have the same target */ + if (IGRAPH_TO(graph1, eid1) != IGRAPH_TO(graph2, eid2)) { + return IGRAPH_SUCCESS; + } + } + + *res = 1; /* No difference was found, graphs are the same */ + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/graph/visitors.c b/src/rigraph/core/graph/visitors.c new file mode 100644 index 0000000..82997c7 --- /dev/null +++ b/src/rigraph/core/graph/visitors.c @@ -0,0 +1,648 @@ +/* -*- mode: C -*- */ +/* + IGraph R package. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_visitor.h" +#include "igraph_memory.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_dqueue.h" +#include "igraph_stack.h" + +/** + * \function igraph_bfs + * Breadth-first search + * + * A simple breadth-first search, with a lot of different results and + * the possibility to call a callback whenever a vertex is visited. + * It is allowed to supply null pointers as the output arguments the + * user is not interested in, in this case they will be ignored. + * + * + * If not all vertices can be reached from the supplied root vertex, + * then additional root vertices will be used, in the order of their + * vertex ids. + * + * + * Consider using \ref igraph_bfs_simple instead if you set most of the output + * arguments provided by this function to a null pointer. + * + * \param graph The input graph. + * \param root The id of the root vertex. It is ignored if the \c + * roots argument is not a null pointer. + * \param roots Pointer to an initialized vector, or a null + * pointer. If not a null pointer, then it is a vector + * containing root vertices to start the BFS from. The vertices + * are considered in the order they appear. If a root vertex + * was already found while searching from another one, then no + * search is conducted from it. + * \param mode For directed graphs, it defines which edges to follow. + * \c IGRAPH_OUT means following the direction of the edges, + * \c IGRAPH_IN means the opposite, and + * \c IGRAPH_ALL ignores the direction of the edges. + * This parameter is ignored for undirected graphs. + * \param unreachable Logical scalar, whether the search should visit + * the vertices that are unreachable from the given root + * node(s). If true, then additional searches are performed + * until all vertices are visited. + * \param restricted If not a null pointer, then it must be a pointer + * to a vector containing vertex ids. The BFS is carried out + * only on these vertices. + * \param order If not null pointer, then the vertex ids of the graph are + * stored here, in the same order as they were visited. + * \param rank If not a null pointer, then the rank of each vertex is + * stored here. + * \param father If not a null pointer, then the id of the father of + * each vertex is stored here. + * \param pred If not a null pointer, then the id of vertex that was + * visited before the current one is stored here. If there is + * no such vertex (the current vertex is the root of a search + * tree), then -1 is stored. + * \param succ If not a null pointer, then the id of the vertex that + * was visited after the current one is stored here. If there + * is no such vertex (the current one is the last in a search + * tree), then -1 is stored. + * \param dist If not a null pointer, then the distance from the root of + * the current search tree is stored here. + * \param callback If not null, then it should be a pointer to a + * function of type \ref igraph_bfshandler_t. This function + * will be called, whenever a new vertex is visited. + * \param extra Extra argument to pass to the callback function. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + * + * \example examples/simple/igraph_bfs.c + * \example examples/simple/igraph_bfs_callback.c + */ +int igraph_bfs(const igraph_t *graph, + igraph_integer_t root, const igraph_vector_t *roots, + igraph_neimode_t mode, igraph_bool_t unreachable, + const igraph_vector_t *restricted, + igraph_vector_t *order, igraph_vector_t *rank, + igraph_vector_t *father, + igraph_vector_t *pred, igraph_vector_t *succ, + igraph_vector_t *dist, igraph_bfshandler_t *callback, + void *extra) { + + igraph_dqueue_t Q; + long int no_of_nodes = igraph_vcount(graph); + long int actroot = 0; + igraph_vector_char_t added; + + igraph_lazy_adjlist_t adjlist; + + long int act_rank = 0; + long int pred_vec = -1; + + long int rootpos = 0; + long int noroots = roots ? igraph_vector_size(roots) : 1; + + if (!roots && (root < 0 || root >= no_of_nodes)) { + IGRAPH_ERROR("Invalid root vertex in BFS", IGRAPH_EINVAL); + } + + if (roots && noroots > 0) { + igraph_real_t min, max; + igraph_vector_minmax(roots, &min, &max); + if (min < 0 || max >= no_of_nodes) { + IGRAPH_ERROR("Invalid root vertex in BFS", IGRAPH_EINVAL); + } + } + + if (restricted && igraph_vector_size(restricted) > 0) { + igraph_real_t min, max; + igraph_vector_minmax(restricted, &min, &max); + if (min < 0 || max >= no_of_nodes) { + IGRAPH_ERROR("Invalid vertex id in restricted set", IGRAPH_EINVAL); + } + } + + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && + mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + IGRAPH_CHECK(igraph_vector_char_init(&added, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_char_destroy, &added); + IGRAPH_CHECK(igraph_dqueue_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &Q); + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + + /* Mark the vertices that are not in the restricted set, as already + found. Special care must be taken for vertices that are not in + the restricted set, but are to be used as 'root' vertices. */ + if (restricted) { + long int i, n = igraph_vector_size(restricted); + igraph_vector_char_fill(&added, 1); + for (i = 0; i < n; i++) { + long int v = (long int) VECTOR(*restricted)[i]; + VECTOR(added)[v] = 0; + } + } + + /* Resize result vectors, and fill them with IGRAPH_NAN */ + +# define VINIT(v) if (v) { \ + igraph_vector_resize((v), no_of_nodes); \ + igraph_vector_fill((v), IGRAPH_NAN); } + + VINIT(order); + VINIT(rank); + VINIT(father); + VINIT(pred); + VINIT(succ); + VINIT(dist); +# undef VINIT + + while (1) { + + /* Get the next root vertex, if any */ + + if (roots && rootpos < noroots) { + /* We are still going through the 'roots' vector */ + actroot = (long int) VECTOR(*roots)[rootpos++]; + } else if (!roots && rootpos == 0) { + /* We have a single root vertex given, and start now */ + actroot = root; + rootpos++; + } else if (rootpos == noroots && unreachable) { + /* We finished the given root(s), but other vertices are also + tried as root */ + actroot = 0; + rootpos++; + } else if (unreachable && actroot + 1 < no_of_nodes) { + /* We are already doing the other vertices, take the next one */ + actroot++; + } else { + /* No more root nodes to do */ + break; + } + + /* OK, we have a new root, start BFS */ + if (VECTOR(added)[actroot]) { + continue; + } + IGRAPH_CHECK(igraph_dqueue_push(&Q, actroot)); + IGRAPH_CHECK(igraph_dqueue_push(&Q, 0)); + VECTOR(added)[actroot] = 1; + if (father) { + VECTOR(*father)[actroot] = -1; + } + + pred_vec = -1; + + while (!igraph_dqueue_empty(&Q)) { + long int actvect = (long int) igraph_dqueue_pop(&Q); + long int actdist = (long int) igraph_dqueue_pop(&Q); + long int succ_vec; + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, + (igraph_integer_t) actvect); + long int i, n = igraph_vector_int_size(neis); + + if (pred) { + VECTOR(*pred)[actvect] = pred_vec; + } + if (rank) { + VECTOR(*rank) [actvect] = act_rank; + } + if (order) { + VECTOR(*order)[act_rank++] = actvect; + } + if (dist) { + VECTOR(*dist)[actvect] = actdist; + } + + for (i = 0; i < n; i++) { + long int nei = (long int) VECTOR(*neis)[i]; + if (! VECTOR(added)[nei]) { + VECTOR(added)[nei] = 1; + IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(&Q, actdist + 1)); + if (father) { + VECTOR(*father)[nei] = actvect; + } + } + } + + succ_vec = igraph_dqueue_empty(&Q) ? -1L : + (long int) igraph_dqueue_head(&Q); + if (callback) { + igraph_bool_t terminate = + callback(graph, (igraph_integer_t) actvect, (igraph_integer_t) + pred_vec, (igraph_integer_t) succ_vec, + (igraph_integer_t) act_rank - 1, (igraph_integer_t) actdist, + extra); + if (terminate) { + igraph_lazy_adjlist_destroy(&adjlist); + igraph_dqueue_destroy(&Q); + igraph_vector_char_destroy(&added); + IGRAPH_FINALLY_CLEAN(3); + return 0; + } + } + + if (succ) { + VECTOR(*succ)[actvect] = succ_vec; + } + pred_vec = actvect; + + } /* while Q !empty */ + + } /* for actroot < no_of_nodes */ + + igraph_lazy_adjlist_destroy(&adjlist); + igraph_dqueue_destroy(&Q); + igraph_vector_char_destroy(&added); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \function igraph_bfs_simple + * Breadth-first search, single-source version + * + * An alternative breadth-first search implementation to cater for the + * simpler use-cases when only a single breadth-first search has to be conducted + * from a source node and most of the output arguments from \ref igraph_bfs + * are not needed. It is allowed to supply null pointers as + * the output arguments the user is not interested in, in this case they will + * be ignored. + * + * \param graph The input graph. + * \param vid The id of the root vertex. + * \param mode For directed graphs, it defines which edges to follow. + * \c IGRAPH_OUT means following the direction of the edges, + * \c IGRAPH_IN means the opposite, and + * \c IGRAPH_ALL ignores the direction of the edges. + * This parameter is ignored for undirected graphs. + * \param vids If not a null pointer, then an initialized vector must be passed + * here. The ids of the vertices visited during the traversal will be + * stored here, in the same order as they were visited. + * \param layers If not a null pointer, then an initialized vector must be + * passed here. The i-th element of the vector will contain the index + * into \c vids where the vertices that are at distance i from the root + * are stored. In other words, if you are interested in the vertices that + * are at distance i from the root, you need to look in the \c vids + * vector from \c layers[i] to \c layers[i+1]. + * \param parents If not a null pointer, then an initialized vector must be + * passed here. The vector will be resized so its length is equal to the + * number of nodes, and it will contain the index of the parent node for + * each \em visited node. The values in the vector are undefined for + * vertices that were \em not visited. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + * + * \example examples/simple/igraph_bfs_simple.c + */ +int igraph_bfs_simple(igraph_t *graph, igraph_integer_t vid, igraph_neimode_t mode, + igraph_vector_t *vids, igraph_vector_t *layers, + igraph_vector_t *parents) { + + igraph_dqueue_t q; + long int num_visited = 0; + igraph_vector_t neis; + long int no_of_nodes = igraph_vcount(graph); + long int i; + char *added; + long int lastlayer = -1; + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && + mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); + } + + /* temporary storage */ + added = IGRAPH_CALLOC(no_of_nodes, char); + if (added == 0) { + IGRAPH_ERROR("Cannot calculate BFS", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_init(&q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &q); + + /* results */ + if (vids) { + igraph_vector_clear(vids); + } + if (layers) { + igraph_vector_clear(layers); + } + if (parents) { + IGRAPH_CHECK(igraph_vector_resize(parents, no_of_nodes)); + } + + /* ok start with vid */ + IGRAPH_CHECK(igraph_dqueue_push(&q, vid)); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + if (layers) { + IGRAPH_CHECK(igraph_vector_push_back(layers, num_visited)); + } + if (vids) { + IGRAPH_CHECK(igraph_vector_push_back(vids, vid)); + } + if (parents) { + VECTOR(*parents)[(long int)vid] = vid; + } + num_visited++; + added[(long int)vid] = 1; + + while (!igraph_dqueue_empty(&q)) { + long int actvect = (long int) igraph_dqueue_pop(&q); + long int actdist = (long int) igraph_dqueue_pop(&q); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actvect, + mode)); + for (i = 0; i < igraph_vector_size(&neis); i++) { + long int neighbor = (long int) VECTOR(neis)[i]; + if (added[neighbor] == 0) { + added[neighbor] = 1; + if (parents) { + VECTOR(*parents)[neighbor] = actvect; + } + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + if (layers && lastlayer != actdist + 1) { + IGRAPH_CHECK(igraph_vector_push_back(layers, num_visited)); + } + if (vids) { + IGRAPH_CHECK(igraph_vector_push_back(vids, neighbor)); + } + num_visited++; + lastlayer = actdist + 1; + } + } /* for i in neis */ + } /* while ! dqueue_empty */ + + if (layers) { + IGRAPH_CHECK(igraph_vector_push_back(layers, num_visited)); + } + + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&q); + IGRAPH_FREE(added); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \function igraph_dfs + * Depth-first search + * + * A simple depth-first search, with + * the possibility to call a callback whenever a vertex is discovered + * and/or whenever a subtree is finished. + * It is allowed to supply null pointers as the output arguments the + * user is not interested in, in this case they will be ignored. + * + * + * If not all vertices can be reached from the supplied root vertex, + * then additional root vertices will be used, in the order of their + * vertex ids. + * + * \param graph The input graph. + * \param root The id of the root vertex. + * \param mode For directed graphs, it defines which edges to follow. + * \c IGRAPH_OUT means following the direction of the edges, + * \c IGRAPH_IN means the opposite, and + * \c IGRAPH_ALL ignores the direction of the edges. + * This parameter is ignored for undirected graphs. + * \param unreachable Logical scalar, whether the search should visit + * the vertices that are unreachable from the given root + * node(s). If true, then additional searches are performed + * until all vertices are visited. + * \param order If not null pointer, then the vertex ids of the graph are + * stored here, in the same order as they were discovered. + * \param order_out If not a null pointer, then the vertex ids of the + * graphs are stored here, in the order of the completion of + * their subtree. + * \param father If not a null pointer, then the id of the father of + * each vertex is stored here. + * \param dist If not a null pointer, then the distance from the root of + * the current search tree is stored here. + * \param in_callback If not null, then it should be a pointer to a + * function of type \ref igraph_dfshandler_t. This function + * will be called, whenever a new vertex is discovered. + * \param out_callback If not null, then it should be a pointer to a + * function of type \ref igraph_dfshandler_t. This function + * will be called, whenever the subtree of a vertex is completed. + * \param extra Extra argument to pass to the callback function(s). + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + */ + +int igraph_dfs(const igraph_t *graph, igraph_integer_t root, + igraph_neimode_t mode, igraph_bool_t unreachable, + igraph_vector_t *order, + igraph_vector_t *order_out, igraph_vector_t *father, + igraph_vector_t *dist, igraph_dfshandler_t *in_callback, + igraph_dfshandler_t *out_callback, + void *extra) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_lazy_adjlist_t adjlist; + igraph_stack_t stack; + igraph_vector_char_t added; + igraph_vector_long_t nptr; + long int actroot; + long int act_rank = 0; + long int rank_out = 0; + long int act_dist = 0; + + if (root < 0 || root >= no_of_nodes) { + IGRAPH_ERROR("Invalid root vertex for DFS", IGRAPH_EINVAL); + } + + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && + mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + IGRAPH_CHECK(igraph_vector_char_init(&added, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_char_destroy, &added); + IGRAPH_CHECK(igraph_stack_init(&stack, 100)); + IGRAPH_FINALLY(igraph_stack_destroy, &stack); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + IGRAPH_CHECK(igraph_vector_long_init(&nptr, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &nptr); + +# define FREE_ALL() do { \ + igraph_vector_long_destroy(&nptr); \ + igraph_lazy_adjlist_destroy(&adjlist); \ + igraph_stack_destroy(&stack); \ + igraph_vector_char_destroy(&added); \ + IGRAPH_FINALLY_CLEAN(4); } while (0) + + /* Resize result vectors and fill them with IGRAPH_NAN */ + +# define VINIT(v) if (v) { \ + igraph_vector_resize(v, no_of_nodes); \ + igraph_vector_fill(v, IGRAPH_NAN); } + + VINIT(order); + VINIT(order_out); + VINIT(father); + VINIT(dist); + +# undef VINIT + + IGRAPH_CHECK(igraph_stack_push(&stack, root)); + VECTOR(added)[(long int)root] = 1; + if (father) { + VECTOR(*father)[(long int)root] = -1; + } + if (order) { + VECTOR(*order)[act_rank++] = root; + } + if (dist) { + VECTOR(*dist)[(long int)root] = 0; + } + if (in_callback) { + igraph_bool_t terminate = in_callback(graph, root, 0, extra); + if (terminate) { + FREE_ALL(); + return 0; + } + } + + for (actroot = 0; actroot < no_of_nodes; ) { + + /* 'root' first, then all other vertices */ + if (igraph_stack_empty(&stack)) { + if (!unreachable) { + break; + } + if (VECTOR(added)[actroot]) { + actroot++; + continue; + } + IGRAPH_CHECK(igraph_stack_push(&stack, actroot)); + VECTOR(added)[actroot] = 1; + if (father) { + VECTOR(*father)[actroot] = -1; + } + if (order) { + VECTOR(*order)[act_rank++] = actroot; + } + if (dist) { + VECTOR(*dist)[actroot] = 0; + } + + if (in_callback) { + igraph_bool_t terminate = in_callback(graph, (igraph_integer_t) actroot, + 0, extra); + if (terminate) { + FREE_ALL(); + return 0; + } + } + actroot++; + } + + while (!igraph_stack_empty(&stack)) { + long int actvect = (long int) igraph_stack_top(&stack); + igraph_vector_int_t *neis = + igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) actvect); + long int n = igraph_vector_int_size(neis); + long int *ptr = igraph_vector_long_e_ptr(&nptr, actvect); + + /* Search for a neighbor that was not yet visited */ + igraph_bool_t any = 0; + long int nei = 0; + while (!any && (*ptr) < n) { + nei = (long int) VECTOR(*neis)[(*ptr)]; + any = !VECTOR(added)[nei]; + (*ptr) ++; + } + if (any) { + /* There is such a neighbor, add it */ + IGRAPH_CHECK(igraph_stack_push(&stack, nei)); + VECTOR(added)[nei] = 1; + if (father) { + VECTOR(*father)[ nei ] = actvect; + } + if (order) { + VECTOR(*order)[act_rank++] = nei; + } + act_dist++; + if (dist) { + VECTOR(*dist)[nei] = act_dist; + } + + if (in_callback) { + igraph_bool_t terminate = in_callback(graph, (igraph_integer_t) nei, + (igraph_integer_t) act_dist, + extra); + if (terminate) { + FREE_ALL(); + return 0; + } + } + + } else { + /* There is no such neighbor, finished with the subtree */ + igraph_stack_pop(&stack); + if (order_out) { + VECTOR(*order_out)[rank_out++] = actvect; + } + act_dist--; + + if (out_callback) { + igraph_bool_t terminate = out_callback(graph, (igraph_integer_t) + actvect, (igraph_integer_t) + act_dist, extra); + if (terminate) { + FREE_ALL(); + return 0; + } + } + } + } + } + + FREE_ALL(); +# undef FREE_ALL + + return 0; +} diff --git a/src/rigraph/core/hrg/dendro.h b/src/rigraph/core/hrg/dendro.h new file mode 100644 index 0000000..869801d --- /dev/null +++ b/src/rigraph/core/hrg/dendro.h @@ -0,0 +1,313 @@ +/* -*- mode: C++ -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +// **************************************************************************************************** +// *** COPYRIGHT NOTICE ******************************************************************************* +// dendro_eq.h - hierarchical random graph (hrg) data structure +// Copyright (C) 2006-2008 Aaron Clauset +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.gnu.org/licenses/gpl.txt for more details. +// +// **************************************************************************************************** +// Author : Aaron Clauset ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ ) +// Collaborators: Cristopher Moore and Mark E.J. Newman +// Project : Hierarchical Random Graphs +// Location : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute +// Created : 19 April 2006 +// Modified : 19 May 2007 +// : 19 May 2008 (cleaned up for public consumption) +// +// **************************************************************************************************** +// +// Maximum likelihood dendrogram data structure. This is the heart of the HRG algorithm: all +// manipulations are done here and all data is stored here. The data structure uses the separate +// graph data structure to store the basic adjacency information (in a dangerously mutable way). +// +// Note: This version (dendro_eq.h) differs from other versions because it includes methods for +// doing the consensus dendrogram calculation. +// +// **************************************************************************************************** + +#ifndef IGRAPH_HRG_DENDRO +#define IGRAPH_HRG_DENDRO + +#include "graph.h" +#include "rbtree.h" +#include "splittree_eq.h" + +#include "igraph_hrg.h" + +#include +#include + +using namespace fitHRG; + +namespace fitHRG { + +// *********************************************************************** +// ******** Basic Structures ********************************************* + +#ifndef IGRAPH_HRG_LIST +#define IGRAPH_HRG_LIST + +class list { +public: + int x; // stored elementd in linked-list + list* next; // pointer to next elementd + list::list(): x(-1), next(0) { } + list::~list() { } +}; +#endif + +enum {DENDRO, GRAPH, LEFT, RIGHT}; +struct block { + double x; + int y; +}; +struct ipair { + int x; + int y; + short int t; + std::string sp; +}; +struct child { + int index; + short int type; + child* next; +}; + +// *********************************************************************** +// ******** Cnode Class ************************************************** + +#ifndef IGRAPH_HRG_CNODE +#define IGRAPH_HRG_CNODE +class cnode { +public: + int index; // array index of this node + int degree; // number of children in list + int parent; // index of parent node + double weight; // sampled posterior weight + child* children; // list of children (and their types) + child* lastChild; // pointer to last child in list + cnode(): index(-1), degree(0), parent(-1), weight(0.0), + children(0), lastChild(0) { } + ~cnode() { + child *curr, *prev; + curr = children; + while (curr != NULL) { + prev = curr; + curr = curr->next; + delete prev; + prev = NULL; + } + lastChild = NULL; + } +}; +#endif + +// *********************************************************************** +// ******** Split Class ************************************************** + +class split { +public: + std::string s; // partition assignment of leaf vertices + split(): s("") { } + ~split() { } + void initializeSplit(const int n) { + s = ""; + for (int i = 0; i < n; i++) { + s += "-"; + } + } + bool checkSplit() { + if (s.empty() || s.find("-", 0) != std::string::npos) { + return false; + } else { + return true; + } + } +}; + +// *********************************************************************** +// ******** Internal Edge Class ****************************************** +// The usefulness of this data structure is to provide an easy to way +// maintain the set of internal edges, and the corresponding splits, +// in the dendrogram D. It allows for the selection of a random +// internal edge in O(1) time, and it takes O(1) time to update its +// structure given an internal move. This structure does not provide +// any means to directly manipulate the splits, but does allow them to +// be replaced. A split has the form "int.int...int#int.int...int", +// where all ints on the left side of the # are in the left partition +// and all ints on the right side of the # marker are in the right +// partition defined by the split. + +class interns { +private: + ipair* edgelist; // list of internal edges represented + std::string* splitlist; // split representation of the internal edges + int** indexLUT; // table of indices of internal edges in edgelist + int q; // number of internal edges + int count; // (for adding edges) edgelist index of new edge to add +public: + interns(const int); + ~interns(); + + // add an internal edge, O(1) + bool addEdge(const int, const int, const short int); + // returns the ith edge of edgelist, O(1) + ipair* getEdge(const int); + // returns a uniformly random internal edge, O(1) + ipair* getRandomEdge(); + // returns the ith split of the splitlist, O(1) + std::string getSplit(const int); + // replace an existing split, O(1) + bool replaceSplit(const int, const std::string); + // swaps two edges, O(1) + bool swapEdges(const int, const int, const short int, const int, + const int, const short int); +}; + +// *********************************************************************** +// ******** Tree elementd Class ****************************************** + +class elementd { +public: + short int type; // either DENDRO or GRAPH + double logL; // log-likelihood contribution of this internal node + double p; // probability p_i that an edge exists between L and + // R subtrees + int e; // number of edges between L and R subtrees + int n; // number of leafs in subtree rooted here + int label; // subtree label: smallest leaf index + int index; // index in containing array + + elementd *M; // pointer to parent node + elementd *L; // pointer for L subtree + elementd *R; // pointer for R subtree + + elementd(): type(DENDRO), logL(0.0), p(0.0), e(0), n(0), + label(-1), index(-1), M(0), L(0), R(0) { } + ~elementd() { } +}; + +// *********************************************************************** +// ******** Dendrogram Class ********************************************* + +class dendro { +private: + elementd* root; // root of the dendrogram + elementd* internal; // array of n-1 internal vertices (the dendrogram D) + elementd* leaf; // array of n leaf vertices (the graph G) + int n; // number of leaf vertices to allocate + interns* d; // list of internal edges of dendrogram D + splittree* splithist; // histogram of cumulative split weights + list** paths; // array of path-lists from root to leaf + double L; // log-likelihood of graph G given dendrogram D + rbtree subtreeL, subtreeR; // trees for computeEdgeCount() function + cnode* ctree; // (consensus tree) array of internal tree nodes + int* cancestor; // (consensus tree) oldest ancetor's index for + // each leaf + + // insert node i according to binary search property + void binarySearchInsert(elementd*, elementd*); + // return path to root from leaf + list* binarySearchFind(const double); + // build split for this internal edge + std::string buildSplit(elementd*); + // compute number of edges between two internal subtrees + int computeEdgeCount(const int, const short int, const int, + const short int); + // (consensus tree) counts children + int countChildren(const std::string); + // find internal node of D that is common ancestor of i,j + elementd* findCommonAncestor(list**, const int, const int); + // return reverse of path to leaf from root + list* reversePathToRoot(const int); +// quicksort functions + void QsortMain(block*, int, int); + int QsortPartition(block*, int, int, int); + +public: + // underlying G (dangerously accessible) + graph* g; + + // constructor / destructor + dendro(); ~dendro(); + // build dendrogram from g + void buildDendrogram(); + // delete dendrograph in prep for importDendrogramStructure + void clearDendrograph(); + // read dendrogram structure from HRG structure + bool importDendrogramStructure(const igraph_hrg_t *hrg); + // (consensus tree) delete splits with less than 0.5 weight + void cullSplitHist(); + // return size of consensus split + int getConsensusSize(); + // return split tree with consensus splits + splittree* getConsensusSplits(); + // return likelihood of G given D + double getLikelihood(); + // store splits in this splittree + void getSplitList(splittree*); + // return total weight of splittree + double getSplitTotalWeight(); + // make random G from D + void makeRandomGraph(); + // make single MCMC move + bool monteCarloMove(double&, bool&, const double); + // record consensus tree from splithist + void recordConsensusTree(igraph_vector_t *parents, + igraph_vector_t *weights); + // record D structure + void recordDendrogramStructure(igraph_hrg_t *hrg); + // record G structure to igraph graph + void recordGraphStructure(igraph_t *graph); + // force refresh of log-likelihood value + void refreshLikelihood(); + // sample dendrogram edge likelihoods and update edge histograms + void sampleAdjacencyLikelihoods(); + // reset the dendrograph structures + void resetDendrograph(); + // sample dendrogram's splits and update the split histogram + bool sampleSplitLikelihoods(int&); + // reset splits histogram + void resetAllSplits(); +}; + +} // namespace fitHRG + +#endif diff --git a/src/rigraph/core/hrg/graph.h b/src/rigraph/core/hrg/graph.h new file mode 100644 index 0000000..4eee422 --- /dev/null +++ b/src/rigraph/core/hrg/graph.h @@ -0,0 +1,167 @@ +/* -*- mode: C++ -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +// **************************************************************************************************** +// *** COPYRIGHT NOTICE ******************************************************************************* +// graph.h - graph data structure for hierarchical random graphs +// Copyright (C) 2005-2008 Aaron Clauset +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.gnu.org/licenses/gpl.txt for more details. +// +// **************************************************************************************************** +// Author : Aaron Clauset ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ ) +// Collaborators: Cristopher Moore and Mark E.J. Newman +// Project : Hierarchical Random Graphs +// Location : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute +// Created : 8 November 2005 +// Modified : 23 December 2007 (cleaned up for public consumption) +// +// **************************************************************************************************** +// +// Graph data structure for hierarchical random graphs. The basic structure is an adjacency list of +// edges; however, many additional pieces of metadata are stored as well. Each node stores its +// external name, its degree and (if assigned) its group index. +// +// **************************************************************************************************** + +#ifndef IGRAPH_HRG_GRAPH +#define IGRAPH_HRG_GRAPH + +#include "rbtree.h" + +#include +#include +#include + +namespace fitHRG { + +// ******** Basic Structures ********************************************* + +#ifndef IGRAPH_HRG_EDGE +#define IGRAPH_HRG_EDGE +class edge { +public: + int x; // stored integer value (edge terminator) + double* h; // (histogram) weights of edge existence + double total_weight; // (histogram) total weight observed + int obs_count; // number of observations in histogram + edge* next; // pointer to next elementd + edge(): x(-1), h(0), total_weight(0.0), obs_count(0), next(0) { } + ~edge() { + if (h != NULL) { + delete [] h; + } + h = NULL; + } +}; +#endif + +#ifndef IGRAPH_HRG_VERT +#define IGRAPH_HRG_VERT +class vert { +public: + std::string name; // (external) name of vertex + int degree; // degree of this vertex + + vert(): name(""), degree(0) { } + ~vert() { } +}; +#endif + +// ******** Graph Class with Edge Statistics ***************************** + +class graph { +public: + graph(const int, bool predict = false); + ~graph(); + + // add (i,j) to graph + bool addLink(const int, const int); + // add weight to (i,j)'s histogram + bool addAdjacencyObs(const int, const int, const double, const double); + // add to obs_count and total_weight + void addAdjacencyEnd(); + // true if (i,j) is already in graph + bool doesLinkExist(const int, const int); + // returns degree of vertex i + int getDegree(const int); + // returns name of vertex i + std::string getName(const int); + // returns edge list of vertex i + edge* getNeighborList(const int); + // return ptr to histogram of edge (i,j) + double* getAdjacencyHist(const int, const int); + // return average value of adjacency A(i,j) + double getAdjacencyAverage(const int, const int); + // returns bin_resolution + double getBinResolution(); + // returns num_bins + int getNumBins(); + // returns m + int numLinks(); + // returns n + int numNodes(); + // returns total_weight + double getTotalWeight(); + // reset edge (i,j)'s histogram + void resetAdjacencyHistogram(const int, const int); + // reset all edge histograms + void resetAllAdjacencies(); + // clear all links from graph + void resetLinks(); + // allocate edge histograms + void setAdjacencyHistograms(const int); + // set name of vertex i + bool setName(const int, const std::string); + +private: + bool predict; // do we need prediction? + vert* nodes; // list of nodes + edge** nodeLink; // linked list of neighbors to vertex + edge** nodeLinkTail; // pointers to tail of neighbor list + double*** A; // stochastic adjacency matrix for this graph + int obs_count; // number of observations in A + double total_weight; // total weight added to A + int n; // number of vertices + int m; // number of directed edges + int num_bins; // number of bins in edge histograms + double bin_resolution; // width of histogram bin +}; + +} // namespace fitHRG + +#endif diff --git a/src/rigraph/core/hrg/graph_simp.h b/src/rigraph/core/hrg/graph_simp.h new file mode 100644 index 0000000..3859543 --- /dev/null +++ b/src/rigraph/core/hrg/graph_simp.h @@ -0,0 +1,160 @@ +/* -*- mode: C++ -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +// **************************************************************************************************** +// *** COPYRIGHT NOTICE ******************************************************************************* +// graph_simp.h - graph data structure +// Copyright (C) 2006-2008 Aaron Clauset +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.gnu.org/licenses/gpl.txt for more details. +// +// **************************************************************************************************** +// Author : Aaron Clauset ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ ) +// Collaborators: Cristopher Moore and Mark E.J. Newman +// Project : Hierarchical Random Graphs +// Location : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute +// Created : 21 June 2006 +// Modified : 23 December 2007 (cleaned up for public consumption) +// +// ************************************************************************ +// +// Simple graph data structure. The basic structure is an adjacency +// list of edges, along with degree information for the vertices. +// +// ************************************************************************ + +#ifndef IGRAPH_HRG_SIMPLEGRAPH +#define IGRAPH_HRG_SIMPLEGRAPH + +#include "rbtree.h" +#include "dendro.h" + +#include +#include + +namespace fitHRG { + +// ******** Basic Structures ********************************************* + +#ifndef IGRAPH_HRG_SIMPLEEDGE +#define IGRAPH_HRG_SIMPLEEDGE +class simpleEdge { +public: + int x; // index of edge terminator + simpleEdge* next; // pointer to next elementd + + simpleEdge(): x(-1), next(0) { } + ~simpleEdge() { } +}; +#endif + +#ifndef IGRAPH_HRG_SIMPLEVERT +#define IGRAPH_HRG_SIMPLEVERT +class simpleVert { +public: + std::string name; // (external) name of vertex + int degree; // degree of this vertex + int group_true; // index of vertex's true group + + simpleVert(): name(""), degree(0), group_true(-1) { } + ~simpleVert() { } +}; +#endif + +#ifndef IGRAPH_HRG_TWOEDGE +#define IGRAPH_HRG_TWOEDGE +class twoEdge { +public: + int o; // index of edge originator + int x; // index of edge terminator + + twoEdge(): o(-1), x(-1) { } + ~twoEdge() { } +}; +#endif + +// ******** Graph Class with Edge Statistics ***************************** + +class simpleGraph { +public: + simpleGraph(const int); ~simpleGraph(); + + // add group label to vertex i + bool addGroup(const int, const int); + // add (i,j) to graph + bool addLink(const int, const int); + // true if (i,j) is already in graph + bool doesLinkExist(const int, const int); + // returns A(i,j) + double getAdjacency(const int, const int); + // returns degree of vertex i + int getDegree(const int); + // returns group label of vertex i + int getGroupLabel(const int); + // returns name of vertex i + std::string getName(const int); + // returns edge list of vertex i + simpleEdge* getNeighborList(const int); + // return pointer to a node + simpleVert* getNode(const int); + // returns num_groups + int getNumGroups(); + // returns m + int getNumLinks(); + // returns n + int getNumNodes(); + // set name of vertex i + bool setName(const int, const std::string); + +private: + simpleVert* nodes; // list of nodes + simpleEdge** nodeLink; // linked list of neighbors to vertex + simpleEdge** nodeLinkTail; // pointers to tail of neighbor list + double** A; // adjacency matrix for this graph + twoEdge* E; // list of all edges (array) + int n; // number of vertices + int m; // number of directed edges + int num_groups; // number of bins in node histograms + + // quicksort functions + void QsortMain(block*, int, int); + int QsortPartition(block*, int, int, int); +}; + +} // namespace fitHRG + +#endif diff --git a/src/rigraph/core/hrg/hrg.cc b/src/rigraph/core/hrg/hrg.cc new file mode 100644 index 0000000..d56e54b --- /dev/null +++ b/src/rigraph/core/hrg/hrg.cc @@ -0,0 +1,1071 @@ +/* -*- mode: C++ -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_attributes.h" +#include "igraph_hrg.h" +#include "igraph_random.h" + +#include "dendro.h" +#include "graph.h" +#include "graph_simp.h" + +using namespace fitHRG; + +/** + * \section hrg_intro Introduction + * + * A hierarchical random graph is an ensemble of undirected + * graphs with \c n vertices. It is defined via a binary tree with \c + * n leaf and \c n-1 internal vertices, where the + * internal vertices are labeled with probabilities. + * The probability that two vertices are connected in the random graph + * is given by the probability label at their closest common + * ancestor. + * + * + * Please read the following two articles for more about + * hierarchical random graphs: A. Clauset, C. Moore, and M.E.J. Newman. + * Hierarchical structure and the prediction of missing links in networks. + * Nature 453, 98 - 101 (2008); and A. Clauset, C. Moore, and M.E.J. Newman. + * Structural Inference of Hierarchies in Networks. In E. M. Airoldi + * et al. (Eds.): ICML 2006 Ws, Lecture Notes in Computer Science + * 4503, 1-13. Springer-Verlag, Berlin Heidelberg (2007). + * + * + * + * igraph contains functions for fitting HRG models to a given network + * (\ref igraph_hrg_fit), for generating networks from a given HRG + * ensemble (\ref igraph_hrg_game, \ref igraph_hrg_sample), converting + * an igraph graph to a HRG and back (\ref igraph_hrg_create, \ref + * igraph_hrg_dendrogram), for calculating a consensus tree from a + * set of sampled HRGs (\ref igraph_hrg_consensus) and for predicting + * missing edges in a network based on its HRG models (\ref + * igraph_hrg_predict). + * + * + * The igraph HRG implementation is heavily based on the code + * published by Aaron Clauset, at his website, + * http://tuvalu.santafe.edu/~aaronc/hierarchy/ + * + */ + +namespace fitHRG { +struct pblock { + double L; + int i; + int j; +}; +} + +static int markovChainMonteCarlo(dendro *d, unsigned int period, + igraph_hrg_t *hrg) { + + igraph_real_t bestL = d->getLikelihood(); + double dL; + bool flag_taken; + + // Because moves in the dendrogram space are chosen (Monte + // Carlo) so that we sample dendrograms with probability + // proportional to their likelihood, a likelihood-proportional + // sampling of the dendrogram models would be equivalent to a + // uniform sampling of the walk itself. We would still have to + // decide how often to sample the walk (at most once every n + // steps is recommended) but for simplicity, the code here + // simply runs the MCMC itself. To actually compute something + // over the set of sampled dendrogram models (in a Bayesian + // model averaging sense), you'll need to code that yourself. + + // do 'period' MCMC moves before doing anything else + for (unsigned int i = 0; i < period; i++) { + + // make a MCMC move + IGRAPH_CHECK(! d->monteCarloMove(dL, flag_taken, 1.0)); + + // get likelihood of this D given G + igraph_real_t cl = d->getLikelihood(); + if (cl > bestL) { + // store the current best likelihood + bestL = cl; + // record the HRG structure + d->recordDendrogramStructure(hrg); + } + } + // corrects floating-point errors O(n) + d->refreshLikelihood(); + + return 0; +} + +static int markovChainMonteCarlo2(dendro *d, int num_samples) { + bool flag_taken; + double dL, ptest = 1.0 / (50.0 * (double)(d->g->numNodes())); + int sample_num = 0, t = 1, thresh = 200 * d->g->numNodes(); + + // Since we're sampling uniformly at random over the equilibrium + // walk, we just need to do a bunch of MCMC moves and let the + // sampling happen on its own. + while (sample_num < num_samples) { + // Make a single MCMC move + d->monteCarloMove(dL, flag_taken, 1.0); + + // We sample the dendrogram space once every n MCMC moves (on + // average). Depending on the flags on the command line, we sample + // different aspects of the dendrograph structure. + if (t > thresh && RNG_UNIF01() < ptest) { + sample_num++; + d->sampleSplitLikelihoods(sample_num); + } + + t++; + + // correct floating-point errors O(n) + d->refreshLikelihood(); // TODO: less frequently + } + + return 0; +} + +static int MCMCEquilibrium_Find(dendro *d, igraph_hrg_t *hrg) { + + // We want to run the MCMC until we've found equilibrium; we + // use the heuristic of the average log-likelihood (which is + // exactly the entropy) over X steps being very close to the + // average log-likelihood (entropy) over the X steps that + // preceded those. In other words, we look for an apparent + // local convergence of the entropy measure of the MCMC. + + bool flag_taken; + igraph_real_t dL, Likeli; + igraph_real_t oldMeanL; + igraph_real_t newMeanL = -1e-49; + + while (1) { + oldMeanL = newMeanL; + newMeanL = 0.0; + for (int i = 0; i < 65536; i++) { + IGRAPH_CHECK(! d->monteCarloMove(dL, flag_taken, 1.0)); + Likeli = d->getLikelihood(); + newMeanL += Likeli; + } + // corrects floating-point errors O(n) + d->refreshLikelihood(); + if (fabs(newMeanL - oldMeanL) / 65536.0 < 1.0) { + break; + } + } + + // Record the result + if (hrg) { + d->recordDendrogramStructure(hrg); + } + + return 0; +} + +static int igraph_i_hrg_getgraph(const igraph_t *igraph, + dendro *d) { + + int no_of_nodes = igraph_vcount(igraph); + int no_of_edges = igraph_ecount(igraph); + int i; + + // Create graph + d->g = new graph(no_of_nodes); + + // Add edges + for (i = 0; i < no_of_edges; i++) { + int from = IGRAPH_FROM(igraph, i); + int to = IGRAPH_TO(igraph, i); + if (from == to) { + continue; + } + if (!d->g->doesLinkExist(from, to)) { + d->g->addLink(from, to); + } + if (!d->g->doesLinkExist(to, from)) { + d->g->addLink(to, from); + } + } + + d->buildDendrogram(); + + return 0; +} + +static int igraph_i_hrg_getsimplegraph(const igraph_t *igraph, + dendro *d, simpleGraph **sg, + int num_bins) { + + int no_of_nodes = igraph_vcount(igraph); + int no_of_edges = igraph_ecount(igraph); + int i; + + // Create graphs + d->g = new graph(no_of_nodes, true); + d->g->setAdjacencyHistograms(num_bins); + (*sg) = new simpleGraph(no_of_nodes); + + for (i = 0; i < no_of_edges; i++) { + int from = IGRAPH_FROM(igraph, i); + int to = IGRAPH_TO(igraph, i); + if (from == to) { + continue; + } + if (!d->g->doesLinkExist(from, to)) { + d->g->addLink(from, to); + } + if (!d->g->doesLinkExist(to, from)) { + d->g->addLink(to, from); + } + if (!(*sg)->doesLinkExist(from, to)) { + (*sg)->addLink(from, to); + } + if (!(*sg)->doesLinkExist(to, from)) { + (*sg)->addLink(to, from); + } + } + + d->buildDendrogram(); + + return 0; +} + +/** + * \function igraph_hrg_init + * Allocate memory for a HRG. + * + * This function must be called before passing an \ref igraph_hrg_t to + * an igraph function. + * \param hrg Pointer to the HRG data structure to initialize. + * \param n The number of vertices in the graph that is modeled by + * this HRG. It can be zero, if this is not yet known. + * \return Error code. + * + * Time complexity: O(n), the number of vertices in the graph. + */ + +int igraph_hrg_init(igraph_hrg_t *hrg, int n) { + IGRAPH_VECTOR_INIT_FINALLY(&hrg->left, n - 1); + IGRAPH_VECTOR_INIT_FINALLY(&hrg->right, n - 1); + IGRAPH_VECTOR_INIT_FINALLY(&hrg->prob, n - 1); + IGRAPH_VECTOR_INIT_FINALLY(&hrg->edges, n - 1); + IGRAPH_VECTOR_INIT_FINALLY(&hrg->vertices, n - 1); + IGRAPH_FINALLY_CLEAN(5); + return 0; +} + +/** + * \function igraph_hrg_destroy + * Deallocate memory for an HRG. + * + * The HRG data structure can be reinitialized again with an \ref + * igraph_hrg_destroy call. + * \param hrg Pointer to the HRG data structure to deallocate. + * + * Time complexity: operating system dependent. + */ + +void igraph_hrg_destroy(igraph_hrg_t *hrg) { + igraph_vector_destroy(&hrg->left); + igraph_vector_destroy(&hrg->right); + igraph_vector_destroy(&hrg->prob); + igraph_vector_destroy(&hrg->edges); + igraph_vector_destroy(&hrg->vertices); +} + +/** + * \function igraph_hrg_size + * Returns the size of the HRG, the number of leaf nodes. + * + * \param hrg Pointer to the HRG. + * \return The number of leaf nodes in the HRG. + * + * Time complexity: O(1). + */ + +int igraph_hrg_size(const igraph_hrg_t *hrg) { + return igraph_vector_size(&hrg->left) + 1; +} + +/** + * \function igraph_hrg_resize + * Resize a HRG. + * + * \param hrg Pointer to an initialized (see \ref igraph_hrg_init) + * HRG. + * \param newsize The new size, i.e. the number of leaf nodes. + * \return Error code. + * + * Time complexity: O(n), n is the new size. + */ + +int igraph_hrg_resize(igraph_hrg_t *hrg, int newsize) { + int origsize = igraph_hrg_size(hrg); + int ret = 0; + igraph_error_handler_t *oldhandler = + igraph_set_error_handler(igraph_error_handler_ignore); + + ret = igraph_vector_resize(&hrg->left, newsize - 1); + ret |= igraph_vector_resize(&hrg->right, newsize - 1); + ret |= igraph_vector_resize(&hrg->prob, newsize - 1); + ret |= igraph_vector_resize(&hrg->edges, newsize - 1); + ret |= igraph_vector_resize(&hrg->vertices, newsize - 1); + + igraph_set_error_handler(oldhandler); + + if (ret) { + igraph_vector_resize(&hrg->left, origsize); + igraph_vector_resize(&hrg->right, origsize); + igraph_vector_resize(&hrg->prob, origsize); + igraph_vector_resize(&hrg->edges, origsize); + igraph_vector_resize(&hrg->vertices, origsize); + IGRAPH_ERROR("Cannot resize HRG", ret); + } + + return 0; +} + +/** + * \function igraph_hrg_fit + * Fit a hierarchical random graph model to a network + * + * \param graph The igraph graph to fit the model to. Edge directions + * are ignored in directed graphs. + * \param hrg Pointer to an initialized HRG, the result of the fitting + * is stored here. It can also be used to pass a HRG to the + * function, that can be used as the starting point of the Markov + * Chain Monte Carlo fitting, if the \c start argument is true. + * \param start Logical, whether to start the fitting from the given + * HRG. + * \param steps Integer, the number of MCMC steps to take in the + * fitting procedure. If this is zero, then the fitting stop is a + * convergence criteria is fulfilled. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_hrg_fit(const igraph_t *graph, + igraph_hrg_t *hrg, + igraph_bool_t start, + int steps) { + + int no_of_nodes = igraph_vcount(graph); + dendro *d; + + RNG_BEGIN(); + + d = new dendro; + + // If we want to start from HRG + if (start) { + d->clearDendrograph(); + if (igraph_hrg_size(hrg) != no_of_nodes) { + delete d; + IGRAPH_ERROR("Invalid HRG to start from", IGRAPH_EINVAL); + } + // Convert the igraph graph + IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d)); + d->importDendrogramStructure(hrg); + } else { + // Convert the igraph graph + IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d)); + IGRAPH_CHECK(igraph_hrg_resize(hrg, no_of_nodes)); + } + + // Run fixed number of steps, or until convergence + if (steps > 0) { + IGRAPH_CHECK(markovChainMonteCarlo(d, steps, hrg)); + } else { + IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg)); + } + + delete d; + + RNG_END(); + + return 0; + +} + +/** + * \function igraph_hrg_sample + * Sample from a hierarchical random graph model + * + * Sample from a hierarchical random graph ensemble. The ensemble can + * be given as a graph (\c input_graph), or as a HRG object (\c hrg). + * If a graph is given, then first an MCMC optimization is performed + * to find the optimal fitting model; then the MCMC is used to sample + * the graph(s). + * \param input_graph An igraph graph, or a null pointer. If not a + * null pointer, then a HRG is first fitted to the graph, possibly + * starting from the given HRG, if the \c start argument is true. If + * is is a null pointer, then the given HRG is used as a starting + * point, to find the optimum of the Markov chain, before the + * sampling. + * \param sample Pointer to an uninitialized graph, or a null + * pointer. If only one sample is requested, and it is not a null + * pointer, then the sample is stored here. + * \param samples An initialized vector of pointers. If more than one + * samples are requested, then they are stored here. Note that to + * free this data structure, you need to call \ref igraph_destroy() on + * each graph first, then \ref igraph_free() on all pointers, and finally + * \ref igraph_vector_ptr_destroy. + * \param no_samples The number of samples to generate. + * \param hrg A HRG. It is modified during the sampling. + * \param start Logical, whether to start the MCMC from the given + * HRG. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_hrg_sample(const igraph_t *input_graph, + igraph_t *sample, + igraph_vector_ptr_t *samples, + igraph_integer_t no_samples, + igraph_hrg_t *hrg, + igraph_bool_t start) { + + int i; + dendro *d; + + if (no_samples < 0) { + IGRAPH_ERROR("Number of samples must be non-negative", IGRAPH_EINVAL); + } + + if (!sample && !samples) { + IGRAPH_ERROR("Give at least one of `sample' and `samples'", + IGRAPH_EINVAL); + } + + if (no_samples != 1 && sample) { + IGRAPH_ERROR("Number of samples should be one if `sample' is given", + IGRAPH_EINVAL); + } + + if (no_samples > 1 && !samples) { + IGRAPH_ERROR("`samples' must be non-null if number of samples " + "is larger than 1", IGRAPH_EINVAL); + } + + if (!start && !input_graph) { + IGRAPH_ERROR("Input graph must be given if initial HRG is not used", + IGRAPH_EINVAL); + } + + if (!start) { + IGRAPH_CHECK(igraph_hrg_resize(hrg, igraph_vcount(input_graph))); + } + + if (input_graph && igraph_hrg_size(hrg) != igraph_vcount(input_graph)) { + IGRAPH_ERROR("Invalid HRG size, should match number of nodes", + IGRAPH_EINVAL); + } + + RNG_BEGIN(); + + d = new dendro; + + // Need to find equilibrium first? + if (start) { + d->clearDendrograph(); + d->importDendrogramStructure(hrg); + } else { + IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg)); + } + + // TODO: free on error + + if (sample) { + // A single graph + d->makeRandomGraph(); + d->recordGraphStructure(sample); + if (samples) { + igraph_t *G = IGRAPH_CALLOC(1, igraph_t); + if (!G) { + IGRAPH_ERROR("Cannot sample HRG graphs", IGRAPH_ENOMEM); + } + d->recordGraphStructure(G); + IGRAPH_CHECK(igraph_vector_ptr_resize(samples, 1)); + VECTOR(*samples)[0] = G; + } + } else { + // Sample many + IGRAPH_CHECK(igraph_vector_ptr_resize(samples, no_samples)); + for (i = 0; i < no_samples; i++) { + igraph_t *G = IGRAPH_CALLOC(1, igraph_t); + if (!G) { + IGRAPH_ERROR("Cannot sample HRG graphs", IGRAPH_ENOMEM); + } + d->makeRandomGraph(); + d->recordGraphStructure(G); + VECTOR(*samples)[i] = G; + } + } + + delete d; + + RNG_END(); + + return 0; +} + +/** + * \function igraph_hrg_game + * Generate a hierarchical random graph + * + * This function is a simple shortcut to \ref igraph_hrg_sample. + * It creates a single graph, from the given HRG. + * \param graph Pointer to an uninitialized graph, the new graph is + * created here. + * \param hrg The hierarchical random graph model to sample from. It + * is modified during the MCMC process. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_hrg_game(igraph_t *graph, + const igraph_hrg_t *hrg) { + return igraph_hrg_sample(/* input_graph= */ 0, /* sample= */ graph, + /* samples= */ 0, /* no_samples=*/ 1, + /* hrg= */ (igraph_hrg_t*) hrg, + /* start= */ 1); +} + +/** + * \function igraph_hrg_dendrogram + * Create a dendrogram from a hierarchical random graph. + * + * Creates the igraph graph equivalent of an \ref igraph_hrg_t data + * structure. + * \param graph Pointer to an uninitialized graph, the result is + * stored here. + * \param hrg The hierarchical random graph to convert. + * \return Error code. + * + * Time complexity: O(n), the number of vertices in the graph. + */ + +int igraph_hrg_dendrogram(igraph_t *graph, + const igraph_hrg_t *hrg) { + + int orig_nodes = igraph_hrg_size(hrg); + int no_of_nodes = orig_nodes * 2 - 1; + int no_of_edges = no_of_nodes > 0 ? no_of_nodes - 1 : 0; + igraph_vector_t edges; + int i, idx = 0; + igraph_vector_ptr_t vattrs; + igraph_vector_t prob; + igraph_attribute_record_t rec = { "probability", + IGRAPH_ATTRIBUTE_NUMERIC, + &prob + }; + + // Probability labels, for leaf nodes they are IGRAPH_NAN + IGRAPH_VECTOR_INIT_FINALLY(&prob, no_of_nodes); + for (i = 0; i < orig_nodes; i++) { + VECTOR(prob)[i] = IGRAPH_NAN; + } + for (i = 0; i < orig_nodes - 1; i++) { + VECTOR(prob)[orig_nodes + i] = VECTOR(hrg->prob)[i]; + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_CHECK(igraph_vector_ptr_init(&vattrs, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vattrs); + VECTOR(vattrs)[0] = &rec; + + for (i = 0; i < orig_nodes - 1; i++) { + int left = VECTOR(hrg->left)[i]; + int right = VECTOR(hrg->right)[i]; + + VECTOR(edges)[idx++] = orig_nodes + i; + VECTOR(edges)[idx++] = left < 0 ? orig_nodes - left - 1 : left; + VECTOR(edges)[idx++] = orig_nodes + i; + VECTOR(edges)[idx++] = right < 0 ? orig_nodes - right - 1 : right; + } + + IGRAPH_CHECK(igraph_empty(graph, 0, IGRAPH_DIRECTED)); + IGRAPH_FINALLY(igraph_destroy, graph); + IGRAPH_CHECK(igraph_add_vertices(graph, no_of_nodes, &vattrs)); + IGRAPH_CHECK(igraph_add_edges(graph, &edges, 0)); + + igraph_vector_ptr_destroy(&vattrs); + igraph_vector_destroy(&edges); + igraph_vector_destroy(&prob); + IGRAPH_FINALLY_CLEAN(4); // + 1 for graph + + return 0; +} + +/** + * \function igraph_hrg_consensus + * Calculate a consensus tree for a HRG. + * + * The calculation can be started from the given HRG (\c hrg), or (if + * \c start is false), a HRG is first fitted to the given graph. + * + * \param graph The input graph. + * \param parents An initialized vector, the results are stored + * here. For each vertex, the id of its parent vertex is stored, or + * -1, if the vertex is the root vertex in the tree. The first n + * vertex ids (from 0) refer to the original vertices of the graph, + * the other ids refer to vertex groups. + * \param weights Numeric vector, counts the number of times a given + * tree split occured in the generated network samples, for each + * internal vertices. The order is the same as in \c parents. + * \param hrg A hierarchical random graph. It is used as a starting + * point for the sampling, if the \c start argument is true. It is + * modified along the MCMC. + * \param start Logical, whether to use the supplied HRG (in \c hrg) + * as a starting point for the MCMC. + * \param num_samples The number of samples to generate for creating + * the consensus tree. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_hrg_consensus(const igraph_t *graph, + igraph_vector_t *parents, + igraph_vector_t *weights, + igraph_hrg_t *hrg, + igraph_bool_t start, + int num_samples) { + + dendro *d; + + if (start && !hrg) { + IGRAPH_ERROR("`hrg' must be given is `start' is true", IGRAPH_EINVAL); + } + + RNG_BEGIN(); + + d = new dendro; + + if (start) { + d->clearDendrograph(); + IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d)); + d->importDendrogramStructure(hrg); + } else { + IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d)); + if (hrg) { + igraph_hrg_resize(hrg, igraph_vcount(graph)); + } + IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg)); + } + + IGRAPH_CHECK(markovChainMonteCarlo2(d, num_samples)); + + d->recordConsensusTree(parents, weights); + + delete d; + + RNG_END(); + + return 0; +} + +static int MCMCEquilibrium_Sample(dendro *d, int num_samples) { + + // Because moves in the dendrogram space are chosen (Monte + // Carlo) so that we sample dendrograms with probability + // proportional to their likelihood, a likelihood-proportional + // sampling of the dendrogram models would be equivalent to a + // uniform sampling of the walk itself. We would still have to + // decide how often to sample the walk (at most once every n steps + // is recommended) but for simplicity, the code here simply runs the + // MCMC itself. To actually compute something over the set of + // sampled dendrogram models (in a Bayesian model averaging sense), + // you'll need to code that yourself. + + double dL; + bool flag_taken; + int sample_num = 0; + int t = 1, thresh = 100 * d->g->numNodes(); + double ptest = 1.0 / 10.0 / d->g->numNodes(); + + while (sample_num < num_samples) { + d->monteCarloMove(dL, flag_taken, 1.0); + if (t > thresh && RNG_UNIF01() < ptest) { + sample_num++; + d->sampleAdjacencyLikelihoods(); + } + d->refreshLikelihood(); // TODO: less frequently + t++; + } + + return 0; +} + +static int QsortPartition (pblock* array, int left, int right, int index) { + pblock p_value, temp; + p_value.L = array[index].L; + p_value.i = array[index].i; + p_value.j = array[index].j; + + // swap(array[p_value], array[right]) + temp.L = array[right].L; + temp.i = array[right].i; + temp.j = array[right].j; + array[right].L = array[index].L; + array[right].i = array[index].i; + array[right].j = array[index].j; + array[index].L = temp.L; + array[index].i = temp.i; + array[index].j = temp.j; + + int stored = left; + for (int i = left; i < right; i++) { + if (array[i].L <= p_value.L) { + // swap(array[stored], array[i]) + temp.L = array[i].L; + temp.i = array[i].i; + temp.j = array[i].j; + array[i].L = array[stored].L; + array[i].i = array[stored].i; + array[i].j = array[stored].j; + array[stored].L = temp.L; + array[stored].i = temp.i; + array[stored].j = temp.j; + stored++; + } + } + // swap(array[right], array[stored]) + temp.L = array[stored].L; + temp.i = array[stored].i; + temp.j = array[stored].j; + array[stored].L = array[right].L; + array[stored].i = array[right].i; + array[stored].j = array[right].j; + array[right].L = temp.L; + array[right].i = temp.i; + array[right].j = temp.j; + + return stored; +} + +static void QsortMain (pblock* array, int left, int right) { + if (right > left) { + int pivot = left; + int part = QsortPartition(array, left, right, pivot); + QsortMain(array, left, part - 1); + QsortMain(array, part + 1, right ); + } + return; +} + +static int rankCandidatesByProbability(simpleGraph *sg, dendro *d, + pblock *br_list, int mk) { + int mkk = 0; + int n = sg->getNumNodes(); + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (sg->getAdjacency(i, j) < 0.5) { + double temp = d->g->getAdjacencyAverage(i, j); + br_list[mkk].L = temp * (1.0 + RNG_UNIF01() / 1000.0); + br_list[mkk].i = i; + br_list[mkk].j = j; + mkk++; + } + } + } + + // Sort the candidates by their average probability + QsortMain(br_list, 0, mk - 1); + + return 0; +} + +static int recordPredictions(pblock *br_list, igraph_vector_t *edges, + igraph_vector_t *prob, int mk) { + + IGRAPH_CHECK(igraph_vector_resize(edges, mk * 2)); + IGRAPH_CHECK(igraph_vector_resize(prob, mk)); + + for (int i = mk - 1, idx = 0, idx2 = 0; i >= 0; i--) { + VECTOR(*edges)[idx++] = br_list[i].i; + VECTOR(*edges)[idx++] = br_list[i].j; + VECTOR(*prob)[idx2++] = br_list[i].L; + } + + return 0; +} + +/** + * \function igraph_hrg_predict + * Predict missing edges in a graph, based on HRG models + * + * Samples HRG models for a network, and estimated the probability + * that an edge was falsely observed as non-existent in the network. + * \param graph The input graph. + * \param edges The list of missing edges is stored here, the first + * two elements are the first edge, the next two the second edge, + * etc. + * \param prob Vector of probabilies for the existence of missing + * edges, in the order corresponding to \c edges. + * \param hrg A HRG, it is used as a starting point if \c start is + * true. It is also modified during the MCMC sampling. + * \param start Logical, whether to start the MCMC from the given HRG. + * \param num_samples The number of samples to generate. + * \param num_bins Controls the resolution of the edge + * probabilities. Higher numbers result higher resolution. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_hrg_predict(const igraph_t *graph, + igraph_vector_t *edges, + igraph_vector_t *prob, + igraph_hrg_t *hrg, + igraph_bool_t start, + int num_samples, + int num_bins) { + + dendro *d; + pblock *br_list; + int mk; + simpleGraph *sg; + + if (start && !hrg) { + IGRAPH_ERROR("`hrg' must be given is `start' is true", IGRAPH_EINVAL); + } + + RNG_BEGIN(); + + d = new dendro; + + IGRAPH_CHECK(igraph_i_hrg_getsimplegraph(graph, d, &sg, num_bins)); + + mk = sg->getNumNodes() * (sg->getNumNodes() - 1) / 2 - sg->getNumLinks() / 2; + br_list = new pblock[mk]; + for (int i = 0; i < mk; i++) { + br_list[i].L = 0.0; + br_list[i].i = -1; + br_list[i].j = -1; + } + + if (start) { + d->clearDendrograph(); + // this has cleared the graph as well.... bug? + IGRAPH_CHECK(igraph_i_hrg_getsimplegraph(graph, d, &sg, num_bins)); + d->importDendrogramStructure(hrg); + } else { + if (hrg) { + igraph_hrg_resize(hrg, igraph_vcount(graph)); + } + IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg)); + } + + IGRAPH_CHECK(MCMCEquilibrium_Sample(d, num_samples)); + IGRAPH_CHECK(rankCandidatesByProbability(sg, d, br_list, mk)); + IGRAPH_CHECK(recordPredictions(br_list, edges, prob, mk)); + + delete d; + delete sg; + delete [] br_list; + + RNG_END(); + + return 0; +} + +/** + * \function igraph_hrg_create + * Create a HRG from an igraph graph. + * + * \param hrg Pointer to an initialized \ref igraph_hrg_t. The result + * is stored here. + * \param graph The igraph graph to convert. It must be a directed + * binary tree, with n-1 internal and n leaf vertices. The root + * vertex must have in-degree zero. + * \param prob The vector of probabilities, this is used to label the + * internal nodes of the hierarchical random graph. The values + * corresponding to the leaves are ignored. + * \return Error code. + * + * Time complexity: O(n), the number of vertices in the tree. + */ + +int igraph_hrg_create(igraph_hrg_t *hrg, + const igraph_t *graph, + const igraph_vector_t *prob) { + + int no_of_nodes = igraph_vcount(graph); + int no_of_internal = no_of_nodes > 0 ? (no_of_nodes - 1) / 2 : 0; + igraph_vector_t deg, idx; + int root = 0; + int d0 = 0, d1 = 0, d2 = 0; + int ii = 0, il = 0; + igraph_vector_t neis; + igraph_vector_t path; + + // -------------------------------------------------------- + // CHECKS + // -------------------------------------------------------- + + // At least three vertices are required + if (no_of_nodes < 3) { + IGRAPH_ERROR("HRG tree must have at least three vertices", + IGRAPH_EINVAL); + } + + // Prob vector was given + if (!prob) { + IGRAPH_ERROR("Probability vector must be given for HRG", + IGRAPH_EINVAL); + } + + // Length of prob vector + if (igraph_vector_size(prob) != no_of_nodes) { + IGRAPH_ERROR("HRG probability vector of wrong size", IGRAPH_EINVAL); + } + + // Must be a directed graph + if (!igraph_is_directed(graph)) { + IGRAPH_ERROR("HRG graph must be directed", IGRAPH_EINVAL); + } + + // Number of nodes must be odd + if (no_of_nodes % 2 == 0) { + IGRAPH_ERROR("Complete HRG graph must have odd number of vertices", + IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(°, 0); + + // Every vertex, except for the root must have in-degree one. + IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), IGRAPH_IN, + IGRAPH_LOOPS)); + for (int i = 0; i < no_of_nodes; i++) { + int d = VECTOR(deg)[i]; + switch (d) { + case 0: d0++; root = i; break; + case 1: d1++; break; + default: + IGRAPH_ERROR("HRG nodes must have in-degree one, except for the " + "root vertex", IGRAPH_EINVAL); + } + } + if (d1 != no_of_nodes - 1 || d0 != 1) { + IGRAPH_ERROR("HRG nodes must have in-degree one, except for the " + "root vertex", IGRAPH_EINVAL); + } + + // Every internal vertex must have out-degree two, + // leaves out-degree zero + d0 = d1 = d2 = 0; + IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), IGRAPH_OUT, + IGRAPH_LOOPS)); + for (int i = 0; i < no_of_nodes; i++) { + int d = VECTOR(deg)[i]; + switch (d) { + case 0: d0++; break; + case 2: d2++; break; + default: + IGRAPH_ERROR("HRG nodes must have out-degree 2 (internal nodes) or " + "degree 0 (leaves)", IGRAPH_EINVAL); + } + } + + // Number of internal and external nodes is correct + // This basically checks that the graph has one component + if (d0 != d2 + 1) { + IGRAPH_ERROR("HRG degrees are incorrect, maybe multiple components?", + IGRAPH_EINVAL); + } + + // -------------------------------------------------------- + // Graph is good, do the conversion + // -------------------------------------------------------- + + // Create an index, that maps the root node as first, then + // the internal nodes, then the leaf nodes + IGRAPH_VECTOR_INIT_FINALLY(&idx, no_of_nodes); + VECTOR(idx)[root] = - (ii++) - 1; + for (int i = 0; i < no_of_nodes; i++) { + int d = VECTOR(deg)[i]; + if (i == root) { + continue; + } + if (d == 2) { + VECTOR(idx)[i] = - (ii++) - 1; + } + if (d == 0) { + VECTOR(idx)[i] = (il++); + } + } + + igraph_hrg_resize(hrg, no_of_internal + 1); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + for (int i = 0; i < no_of_nodes; i++) { + int ri = VECTOR(idx)[i]; + if (ri >= 0) { + continue; + } + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, IGRAPH_OUT)); + VECTOR(hrg->left )[-ri - 1] = VECTOR(idx)[ (int) VECTOR(neis)[0] ]; + VECTOR(hrg->right)[-ri - 1] = VECTOR(idx)[ (int) VECTOR(neis)[1] ]; + VECTOR(hrg->prob )[-ri - 1] = VECTOR(*prob)[i]; + } + + // Calculate the number of vertices and edges in each subtree + igraph_vector_null(&hrg->edges); + igraph_vector_null(&hrg->vertices); + IGRAPH_VECTOR_INIT_FINALLY(&path, 0); + IGRAPH_CHECK(igraph_vector_push_back(&path, VECTOR(idx)[root])); + while (!igraph_vector_empty(&path)) { + int ri = igraph_vector_tail(&path); + int lc = VECTOR(hrg->left)[-ri - 1]; + int rc = VECTOR(hrg->right)[-ri - 1]; + if (lc < 0 && VECTOR(hrg->vertices)[-lc - 1] == 0) { + // Go left + IGRAPH_CHECK(igraph_vector_push_back(&path, lc)); + } else if (rc < 0 && VECTOR(hrg->vertices)[-rc - 1] == 0) { + // Go right + IGRAPH_CHECK(igraph_vector_push_back(&path, rc)); + } else { + // Subtrees are done, update node and go up + VECTOR(hrg->vertices)[-ri - 1] += + lc < 0 ? VECTOR(hrg->vertices)[-lc - 1] : 1; + VECTOR(hrg->vertices)[-ri - 1] += + rc < 0 ? VECTOR(hrg->vertices)[-rc - 1] : 1; + VECTOR(hrg->edges)[-ri - 1] += lc < 0 ? VECTOR(hrg->edges)[-lc - 1] + 1 : 1; + VECTOR(hrg->edges)[-ri - 1] += rc < 0 ? VECTOR(hrg->edges)[-rc - 1] + 1 : 1; + igraph_vector_pop_back(&path); + } + } + + igraph_vector_destroy(&path); + igraph_vector_destroy(&neis); + igraph_vector_destroy(&idx); + igraph_vector_destroy(°); + IGRAPH_FINALLY_CLEAN(4); + + return 0; +} diff --git a/src/rigraph/core/hrg/hrg_types.cc b/src/rigraph/core/hrg/hrg_types.cc new file mode 100644 index 0000000..33e7271 --- /dev/null +++ b/src/rigraph/core/hrg/hrg_types.cc @@ -0,0 +1,3725 @@ +// *********************************************************************** +// *** COPYRIGHT NOTICE ************************************************** +// rbtree - red-black tree (self-balancing binary tree data structure) +// Copyright (C) 2004 Aaron Clauset +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.gnu.org/licenses/gpl.txt for more details. +// +// *********************************************************************** +// Author : Aaron Clauset ( aaronc@santafe.edu | +// http://www.santafe.edu/~aaronc/ ) +// Collaborators: Cristopher Moore and Mark Newman +// Project : Hierarchical Random Graphs +// Location : University of New Mexico, Dept. of Computer Science +// AND Santa Fe Institute +// Created : Spring 2004 +// Modified : many, many times +// +// *********************************************************************** + +#include "rbtree.h" +#include "dendro.h" +#include "graph.h" +#include "splittree_eq.h" +#include "graph_simp.h" + +#include "igraph_hrg.h" +#include "igraph_constructors.h" +#include "igraph_random.h" + +using namespace std; +using namespace fitHRG; + +// ******** Red-Black Tree Methods *************************************** + +rbtree::rbtree() { + root = new elementrb; + leaf = new elementrb; + + leaf->parent = root; + + root->left = leaf; + root->right = leaf; + support = 0; +} + +rbtree::~rbtree() { + if (root != NULL && + (root->left != leaf || root->right != leaf)) { + deleteSubTree(root); + } + if (root) { + delete root; + } + delete leaf; + support = 0; + root = 0; + leaf = 0; +} + +void rbtree::deleteTree() { + if (root != NULL) { + deleteSubTree(root); + } +} // does not leak memory + +void rbtree::deleteSubTree(elementrb *z) { + if (z->left != leaf) { + deleteSubTree(z->left); + } + if (z->right != leaf) { + deleteSubTree(z->right); + } + delete z; +} + +// ******** Search Functions ********************************************* +// public search function - if there exists a elementrb in the tree +// with key=searchKey, it returns TRUE and foundNode is set to point +// to the found node; otherwise, it sets foundNode=NULL and returns +// FALSE +elementrb* rbtree::findItem(const int searchKey) { + elementrb *current = root; + + // empty tree; bail out + if (current->key == -1) { + return NULL; + } + + while (current != leaf) { + // left-or-right? + if (searchKey < current->key) { + // try moving down-left + if (current->left != leaf) { + current = current->left; + } else { + // failure; bail out + return NULL; + } + } else { + // left-or-right? + if (searchKey > current->key) { + // try moving down-left + if (current->right != leaf) { + current = current->right; + } else { + // failure; bail out + return NULL; + } + } else { + // found (searchKey==current->key) + return current; + } + } + } + return NULL; +} + +int rbtree::returnValue(const int searchKey) { + elementrb* test = findItem(searchKey); + if (!test) { + return 0; + } else { + return test->value; + } +} + + +// ******** Return Item Functions **************************************** + +int* rbtree::returnArrayOfKeys() { + int* array; + array = new int [support]; + bool flag_go = true; + int index = 0; + elementrb *curr; + + if (support == 1) { + array[0] = root->key; + } else if (support == 2) { + array[0] = root->key; + if (root->left == leaf) { + array[1] = root->right->key; + } else { + array[1] = root->left->key; + } + } else { + for (int i = 0; i < support; i++) { + array[i] = -1; + } + // non-recursive traversal of tree structure + curr = root; + curr->mark = 1; + while (flag_go) { + // - is it time, and is left child the leaf node? + if (curr->mark == 1 && curr->left == leaf) { + curr->mark = 2; + } + // - is it time, and is right child the leaf node? + if (curr->mark == 2 && curr->right == leaf) { + curr->mark = 3; + } + if (curr->mark == 1) { + // - go left + curr->mark = 2; + curr = curr->left; + curr->mark = 1; + } else if (curr->mark == 2) { + // - else go right + curr->mark = 3; + curr = curr->right; + curr->mark = 1; + } else { + // - else go up a level + curr->mark = 0; + array[index++] = curr->key; + curr = curr->parent; + if (curr == NULL) { + flag_go = false; + } + } + } + } + + return array; +} + +list* rbtree::returnListOfKeys() { + keyValuePair *curr, *prev; + list *head = 0, *tail = 0, *newlist; + + curr = returnTreeAsList(); + while (curr != NULL) { + newlist = new list; + newlist->x = curr->x; + if (head == NULL) { + head = newlist; tail = head; + } else { + tail->next = newlist; tail = newlist; + } + prev = curr; + curr = curr->next; + delete prev; + prev = NULL; + } + return head; +} + +keyValuePair* rbtree::returnTreeAsList() { + // pre-order traversal + keyValuePair *head, *tail; + + head = new keyValuePair; + head->x = root->key; + head->y = root->value; + tail = head; + + if (root->left != leaf) { + tail = returnSubtreeAsList(root->left, tail); + } + if (root->right != leaf) { + tail = returnSubtreeAsList(root->right, tail); + } + + if (head->x == -1) { + return NULL; /* empty tree */ + } else { + return head; + } +} + +keyValuePair* rbtree::returnSubtreeAsList(elementrb *z, keyValuePair *head) { + keyValuePair *newnode, *tail; + + newnode = new keyValuePair; + newnode->x = z->key; + newnode->y = z->value; + head->next = newnode; + tail = newnode; + + if (z->left != leaf) { + tail = returnSubtreeAsList(z->left, tail); + } + if (z->right != leaf) { + tail = returnSubtreeAsList(z->right, tail); + } + + return tail; +} + +keyValuePair rbtree::returnMaxKey() { + keyValuePair themax; + elementrb *current; + current = root; + + // search to bottom-right corner of tree + while (current->right != leaf) { + current = current->right; + } + themax.x = current->key; + themax.y = current->value; + + return themax; +} + +keyValuePair rbtree::returnMinKey() { + keyValuePair themin; + elementrb *current; + current = root; + // search to bottom-left corner of tree + while (current->left != leaf) { + current = current->left; + } + themin.x = current->key; + themin.y = current->value; + + return themin; +} + +// private functions for deleteItem() (although these could easily be +// made public, I suppose) +elementrb* rbtree::returnMinKey(elementrb *z) { + elementrb *current; + + current = z; + // search to bottom-right corner of tree + while (current->left != leaf) { + current = current->left; + } + return current; +} + +elementrb* rbtree::returnSuccessor(elementrb *z) { + elementrb *current, *w; + + w = z; + // if right-subtree exists, return min of it + if (w->right != leaf) { + return returnMinKey(w->right); + } + // else search up in tree + current = w->parent; + while ((current != NULL) && (w == current->right)) { + w = current; + // move up in tree until find a non-right-child + current = current->parent; + } + return current; +} + +int rbtree::returnNodecount() { + return support; +} + +// ******** Insert Functions ********************************************* +// public insert function +void rbtree::insertItem(int newKey, int newValue) { + + // first we check to see if newKey is already present in the tree; + // if so, we do nothing; if not, we must find where to insert the + // key + elementrb *newNode, *current; + + // find newKey in tree; return pointer to it O(log k) + current = findItem(newKey); + if (current == NULL) { + newNode = new elementrb; // elementrb for the rbtree + newNode->key = newKey; + newNode->value = newValue; + newNode->color = true; // new nodes are always RED + newNode->parent = NULL; // new node initially has no parent + newNode->left = leaf; // left leaf + newNode->right = leaf; // right leaf + support++; // increment node count in rbtree + + // must now search for where to insert newNode, i.e., find the + // correct parent and set the parent and child to point to each + // other properly + current = root; + if (current->key == -1) { // insert as root + delete root; // delete old root + root = newNode; // set root to newNode + leaf->parent = newNode; // set leaf's parent + current = leaf; // skip next loop + } + + // search for insertion point + while (current != leaf) { + // left-or-right? + if (newKey < current->key) { + // try moving down-left + if (current->left != leaf) { + current = current->left; + } else { + // else found new parent + newNode->parent = current; // set parent + current->left = newNode; // set child + current = leaf; // exit search + } + } else { + // try moving down-right + if (current->right != leaf) { + current = current->right; + } else { + // else found new parent + newNode->parent = current; // set parent + current->right = newNode; // set child + current = leaf; // exit search + } + } + } + + // now do the house-keeping necessary to preserve the red-black + // properties + insertCleanup(newNode); + } + return; +} + +// private house-keeping function for insertion +void rbtree::insertCleanup(elementrb *z) { + + // fix now if z is root + if (z->parent == NULL) { + z->color = false; + return; + } + + elementrb *temp; + + // while z is not root and z's parent is RED + while (z->parent != NULL && z->parent->color) { + if (z->parent == z->parent->parent->left) { + + // z's parent is LEFT-CHILD + + temp = z->parent->parent->right; // grab z's uncle + if (temp->color) { + z->parent->color = false; // color z's parent BLACK (Case 1) + temp->color = false; // color z's uncle BLACK (Case 1) + z->parent->parent->color = true; // color z's grandpar. RED (Case 1) + z = z->parent->parent; // set z = z's grandparent (Case 1) + } else { + if (z == z->parent->right) { + // z is RIGHT-CHILD + z = z->parent; // set z = z's parent (Case 2) + rotateLeft(z); // perform left-rotation (Case 2) + } + z->parent->color = false; // color z's parent BLACK (Case 3) + z->parent->parent->color = true; // color z's grandpar. RED (Case 3) + rotateRight(z->parent->parent); // perform right-rotation (Case 3) + } + } else { + + // z's parent is RIGHT-CHILD + + temp = z->parent->parent->left; // grab z's uncle + if (temp->color) { + z->parent->color = false; // color z's parent BLACK (Case 1) + temp->color = false; // color z's uncle BLACK (Case 1) + z->parent->parent->color = true; // color z's grandpar. RED (Case 1) + z = z->parent->parent; // set z = z's grandparent (Case 1) + } else { + if (z == z->parent->left) { + // z is LEFT-CHILD + z = z->parent; // set z = z's parent (Case 2) + rotateRight(z); // perform right-rotation (Case 2) + } + z->parent->color = false; // color z's parent BLACK (Case 3) + z->parent->parent->color = true; // color z's grandpar. RED (Case 3) + rotateLeft(z->parent->parent); // perform left-rotation (Case 3) + } + } + } + + root->color = false; // color the root BLACK + return; +} + +// ******** Delete +// ******** Functions ********************************************* + +void rbtree::replaceItem(int key, int newValue) { + elementrb* ptr; + ptr = findItem(key); + ptr->value = newValue; + return; +} + +void rbtree::incrementValue(int key) { + elementrb* ptr; + ptr = findItem(key); + ptr->value = 1 + ptr->value; + return; +} + +// public delete function +void rbtree::deleteItem(int killKey) { + elementrb *x, *y, *z; + + z = findItem(killKey); + if (z == NULL) { + return; // item not present; bail out + } + + if (support == 1) { // attempt to delete the root + root->key = -1; // restore root node to default state + root->value = -1; + root->color = false; + root->parent = NULL; + root->left = leaf; + root->right = leaf; + support--; // set support to zero + return; // exit - no more work to do + } + + if (z != NULL) { + support--; // decrement node count + if ((z->left == leaf) || (z->right == leaf)) { + y = z; // case of less than two children, + // set y to be z + } else { + y = returnSuccessor(z); // set y to be z's key-successor + } + + if (y->left != leaf) { + x = y->left; // pick y's one child (left-child) + } else { + x = y->right; // (right-child) + } + x->parent = y->parent; // make y's child's parent be y's parent + + if (y->parent == NULL) { + root = x; // if y is the root, x is now root + } else { + if (y == y->parent->left) { // decide y's relationship with y's parent + y->parent->left = x; // replace x as y's parent's left child + } else { + y->parent->right = x; // replace x as y's parent's left child + } + } + + if (y != z) { // insert y into z's spot + z->key = y->key; // copy y data into z + z->value = y->value; + } + + // do house-keeping to maintain balance + if (y->color == false) { + deleteCleanup(x); + } + + delete y; + y = NULL; + } + + return; +} + +void rbtree::deleteCleanup(elementrb *x) { + elementrb *w, *t; + + // until x is the root, or x is RED + while ((x != root) && (x->color == false)) { + if (x == x->parent->left) { // branch on x being a LEFT-CHILD + w = x->parent->right; // grab x's sibling + if (w->color == true) { // if x's sibling is RED + w->color = false; // color w BLACK (case 1) + x->parent->color = true; // color x's parent RED (case 1) + rotateLeft(x->parent); // left rotation on x's parent (case 1) + w = x->parent->right; // make w be x's right sibling (case 1) + } + if ((w->left->color == false) && (w->right->color == false)) { + w->color = true; // color w RED (case 2) + x = x->parent; // examine x's parent (case 2) + } else { + if (w->right->color == false) { + w->left->color = false; // color w's left child BLACK (case 3) + w->color = true; // color w RED (case 3) + t = x->parent; // store x's parent (case 3) + rotateRight(w); // right rotation on w (case 3) + x->parent = t; // restore x's parent (case 3) + w = x->parent->right; // make w be x's right sibling (case 3) + } + w->color = x->parent->color; // w's color := x's parent's (case 4) + x->parent->color = false; // color x's parent BLACK (case 4) + w->right->color = false; // color w's right child BLACK (case 4) + rotateLeft(x->parent); // left rotation on x's parent (case 4) + x = root; // finished work. bail out (case 4) + } + } else { // x is RIGHT-CHILD + w = x->parent->left; // grab x's sibling + if (w->color == true) { // if x's sibling is RED + w->color = false; // color w BLACK (case 1) + x->parent->color = true; // color x's parent RED (case 1) + rotateRight(x->parent); // right rotation on x's parent (case 1) + w = x->parent->left; // make w be x's left sibling (case 1) + } + if ((w->right->color == false) && (w->left->color == false)) { + w->color = true; // color w RED (case 2) + x = x->parent; // examine x's parent (case 2) + } else { + if (w->left->color == false) { + w->right->color = false; // color w's right child BLACK (case 3) + w->color = true; // color w RED (case 3) + t = x->parent; // store x's parent (case 3) + rotateLeft(w); // left rotation on w (case 3) + x->parent = t; // restore x's parent (case 3) + w = x->parent->left; // make w be x's left sibling (case 3) + } + w->color = x->parent->color; // w's color := x's parent's (case 4) + x->parent->color = false; // color x's parent BLACK (case 4) + w->left->color = false; // color w's left child BLACK (case 4) + rotateRight(x->parent); // right rotation on x's parent (case 4) + x = root; // x is now the root (case 4) + } + } + } + x->color = false; // color x (the root) BLACK (exit) + + return; +} + +// ******** Rotation Functions ****************************************** + +void rbtree::rotateLeft(elementrb *x) { + elementrb *y; + // do pointer-swapping operations for left-rotation + y = x->right; // grab right child + x->right = y->left; // make x's RIGHT-CHILD be y's LEFT-CHILD + y->left->parent = x; // make x be y's LEFT-CHILD's parent + y->parent = x->parent; // make y's new parent be x's old parent + + if (x->parent == NULL) { + root = y; // if x was root, make y root + } else { + // if x is LEFT-CHILD, make y be x's parent's + if (x == x->parent->left) { + x->parent->left = y; // left-child + } else { + x->parent->right = y; // right-child + } + } + y->left = x; // make x be y's LEFT-CHILD + x->parent = y; // make y be x's parent + + return; +} + +void rbtree::rotateRight(elementrb *y) { + elementrb *x; + // do pointer-swapping operations for right-rotation + x = y->left; // grab left child + y->left = x->right; // replace left child yith x's right subtree + x->right->parent = y; // replace y as x's right subtree's parent + + x->parent = y->parent; // make x's new parent be y's old parent + + // if y was root, make x root + if (y->parent == NULL) { + root = x; + } else { + // if y is RIGHT-CHILD, make x be y's parent's + if (y == y->parent->right) { + // right-child + y->parent->right = x; + } else { + // left-child + y->parent->left = x; + } + } + x->right = y; // make y be x's RIGHT-CHILD + y->parent = x; // make x be y's parent + + return; +} + +// *********************************************************************** +// *** COPYRIGHT NOTICE ************************************************** +// dendro.h - hierarchical random graph (hrg) data structure +// Copyright (C) 2005-2009 Aaron Clauset +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.gnu.org/licenses/gpl.txt for more details. +// +// *********************************************************************** +// Author : Aaron Clauset ( aaronc@santafe.edu | +// http://www.santafe.edu/~aaronc/ ) +// Collaborators: Cristopher Moore and Mark E.J. Newman +// Project : Hierarchical Random Graphs +// Location : University of New Mexico, Dept. of Computer Science +// AND Santa Fe Institute +// Created : 26 October 2005 - 7 December 2005 +// Modified : 23 December 2007 (cleaned up for public consumption) +// +// *********************************************************************** +// +// Maximum likelihood dendrogram data structure. This is the heart of +// the HRG algorithm: all manipulations are done here and all data is +// stored here. The data structure uses the separate graph data +// structure to store the basic adjacency information (in a +// dangerously mutable way). +// +// *********************************************************************** + +// ******** Dendrogram Methods ******************************************* + +dendro::dendro(): root(0), internal(0), leaf(0), d(0), splithist(0), + paths(0), ctree(0), cancestor(0), g(0) { } +dendro::~dendro() { + list *curr, *prev; + + if (g) { + delete g; // O(m) + g = 0; + } + if (internal) { + delete [] internal; // O(n) + internal = 0; + } + if (leaf) { + delete [] leaf; // O(n) + leaf = 0; + } + if (d) { + delete d; // O(n) + d = 0; + } + if (splithist) { + delete splithist; // potentially long + splithist = 0; + } + + if (paths) { + for (int i = 0; i < n; i++) { + curr = paths[i]; + while (curr) { + prev = curr; + curr = curr->next; + delete prev; + prev = 0; + } + paths[i] = 0; + } + delete [] paths; + } + paths = 0; + + if (ctree) { + delete [] ctree; // O(n) + ctree = 0; + } + if (cancestor) { + delete [] cancestor; // O(n) + cancestor = 0; + } +} + +// ********************************************************************* + +void dendro::binarySearchInsert(elementd* x, elementd* y) { + if (y->p < x->p) { // go to left subtree + if (x->L == NULL) { // check if left subtree is empty + x->L = y; // make x left child + y->M = x; // make y parent of child + return; + } else { + binarySearchInsert(x->L, y); + } + } else { // go to right subtree + if (x->R == NULL) { // check if right subtree is empty + x->R = y; // make x right child + y->M = x; // make y parent of child + return; + } else { + binarySearchInsert(x->R, y); + } + } + return; +} + +// ********************************************************************** + +list* dendro::binarySearchFind(const double v) { + list *head = NULL, *tail = NULL, *newlist; + elementd *current = root; + bool flag_stopSearch = false; + + while (!flag_stopSearch) { // continue until we're finished + newlist = new list; // add this node to the path + newlist->x = current->label; + if (current == root) { + head = newlist; tail = head; + } else { + tail->next = newlist; tail = newlist; + } + if (v < current->p) { // now try left subtree + if (current->L->type == GRAPH) { + flag_stopSearch = true; + } else { + current = current->L; + } + } else { // else try right subtree + if (current->R->type == GRAPH) { + flag_stopSearch = true; + } else { + current = current->R; + } + } + } + return head; +} + +// *********************************************************************** + +string dendro::buildSplit(elementd* thisNode) { + // A "split" is defined as the bipartition of vertices into the sets + // of leaves below the internal vertex in the tree (denoted by "C"), + // and those above it (denoted as "M"). For simplicity, we represent + // this bipartition as a character string of length n, where the ith + // character denotes the partition membership (C,M) of the ith leaf + // node. + + bool flag_go = true; + const short int k = 1 + DENDRO + GRAPH; + elementd* curr; + split sp; + + sp.initializeSplit(n); // default split string O(n) + + curr = thisNode; // - set start node as top this sub-tree + curr->type = k + 1; // - initialize in-order tree traversal + while (flag_go) { + + // - is it time, and is left child a graph node? + if (curr->type == k + 1 && curr->L->type == GRAPH) { + sp.s[curr->L->index] = 'C'; // - mark this leaf + curr->type = k + 2; + } + + // - is it time, and is right child a graph node? + if (curr->type == k + 2 && curr->R->type == GRAPH) { + sp.s[curr->R->index] = 'C'; // - mark this leaf + curr->type = k + 3; + } + if (curr->type == k + 1) { // - go left + curr->type = k + 2; + curr = curr->L; + curr->type = k + 1; + } else if (curr->type == k + 2) { // - else go right + curr->type = k + 3; + curr = curr->R; + curr->type = k + 1; + } else { // - else go up a level + curr->type = DENDRO; + if (curr->index == thisNode->index || curr->M == NULL) { + flag_go = false; curr = NULL; + } else { + curr = curr->M; + } + } + } + + // any leaf that was not already marked must be in the remainder of + // the tree + for (int i = 0; i < n; i++) { + if (sp.s[i] != 'C') { + sp.s[i] = 'M'; + } + } + + return sp.s; +} + +// ********************************************************************** + +void dendro::buildDendrogram() { + + /* the initialization of the dendrogram structure goes like this: + * 1) we allocate space for the n-1 internal nodes of the + * dendrogram, and then the n leaf nodes + * 2) we build a random binary tree structure out of the internal + * nodes by assigning each a uniformly random value over [0,1] and + * then inserting it into the tree according to the + * binary-search rule. + * 3) next, we make a random permutation of the n leaf nodes and add + * them to the dendrogram D by replacing the emptpy spots in-order + * 4) then, we compute the path from the root to each leaf and store + * that in each leaf (this is prep work for the next step) + * 5) finally, we compute the values for nL, nR, e (and thus p) and + * the label for each internal node by allocating each of the m + * edges in g to the appropriate internal node + */ + + // --- Initialization and memory allocation for data structures + // After allocating the memory for D and G, we need to mark the + // nodes for G as being non-internal vertices, and then insert them + // into a random binary tree structure. For simplicity, we make the + // first internal node in the array the root. + + n = g->numNodes(); // size of graph + leaf = new elementd [n]; // allocate memory for G, O(n) + internal = new elementd [n - 1]; // allocate memory for D, O(n) + d = new interns(n - 2); // allocate memory for internal + // edges of D, O(n) + for (int i = 0; i < n; i++) { // initialize leaf nodes + leaf[i].type = GRAPH; + leaf[i].label = i; + leaf[i].index = i; + leaf[i].n = 1; + } + +// initialize internal nodes + root = &internal[0]; + root->label = 0; + root->index = 0; + root->p = RNG_UNIF01(); + + // insert remaining internal vertices, O(n log n) + for (int i = 1; i < (n - 1); i++) { + internal[i].label = i; + internal[i].index = i; + internal[i].p = RNG_UNIF01(); + binarySearchInsert(root, &internal[i]); + } + + // --- Hang leaf nodes off end of dendrogram O(n log n) + // To impose this random hierarchical relationship on G, we first + // take a random permutation of the leaf vertices and then replace + // the NULLs at the bottom of the tree in-order with the leafs. As a + // hack to ensure that we can find the leafs later using a binary + // search, we assign each of them the p value of their parent, + // perturbed slightly so as to preserve the binary search property. + + block* array; array = new block [n]; + for (int i = 0; i < n; i++) { + array[i].x = RNG_UNIF01(); + array[i].y = i; + } + QsortMain(array, 0, n - 1); + + int k = 0; // replace NULLs with leaf nodes, and + for (int i = 0; i < (n - 1); i++) { // maintain binary search property, O(n) + if (internal[i].L == NULL) { + internal[i].L = &leaf[array[k].y]; + leaf[array[k].y].M = &internal[i]; + leaf[array[k++].y].p = internal[i].p - 0.0000000000001; + } + if (internal[i].R == NULL) { + internal[i].R = &leaf[array[k].y]; + leaf[array[k].y].M = &internal[i]; + leaf[array[k++].y].p = internal[i].p + 0.0000000000001; + } + } + delete [] array; + + // --- Compute the path from root -> leaf for each leaf O(n log n) + // Using the binary search property, we can find each leaf node in + // O(log n) time. The binarySearchFind() function returns the list + // of internal node indices that the search crossed, in the order of + // root -> ... -> leaf, for use in the subsequent few operations. + + if (paths != NULL) { + list *curr, *prev; + for (int i = 0; i < n; i++) { + curr = paths[i]; + while (curr != NULL) { + prev = curr; + curr = curr->next; + delete prev; + prev = NULL; + } + paths[i] = NULL; + } + delete [] paths; + } + paths = NULL; + paths = new list* [n]; + for (int i = 0; i < n; i++) { + paths[i] = binarySearchFind(leaf[i].p); + } + + // --- Count e for each internal node O(m) + // To count the number of edges that span the L and R subtrees for + // each internal node, we use the path information we just + // computed. Then, we loop over all edges in G and find the common + // ancestor in D of the two endpoints and increment that internal + // node's e count. This process takes O(m) time because in a roughly + // balanced binary tree (given by our random dendrogram), the vast + // majority of vertices take basically constant time to find their + // common ancestor. Note that because our adjacency list is + // symmetric, we overcount each e by a factor of 2, so we need to + // correct this after. + + elementd* ancestor; edge* curr; + for (int i = 0; i < (n - 1); i++) { + internal[i].e = 0; + internal[i].label = -1; + } + for (int i = 0; i < n; i++) { + curr = g->getNeighborList(i); + while (curr != NULL) { + ancestor = findCommonAncestor(paths, i, curr->x); + ancestor->e += 1; + curr = curr->next; + } + } + for (int i = 0; i < (n - 1); i++) { + internal[i].e /= 2; + } + + // --- Count n for each internal node O(n log n) + // To tabulate the number of leafs in each subtree rooted at an + // internal node, we use the path information computed above. + for (int i = 0; i < n; i++) { + ancestor = &leaf[i]; + ancestor = ancestor->M; + while (ancestor != NULL) { + ancestor->n++; + ancestor = ancestor->M; + } + } + + // --- Label all internal vertices O(n log n) + // We want to label each internal vertex with the smallest leaf + // index of its children. This will allow us to collapse many + // leaf-orderings into a single dendrogram structure that is + // independent of child-exhanges (since these have no impact on the + // likelihood of the hierarchical structure). To do this, we loop + // over the leaf vertices from smallest to largest and walk along + // that leaf's path from the root. If we find an unlabeled internal + // node, then we mark it with this leaf's index. + + for (int i = 0; i < n; i++) { + ancestor = &leaf[i]; + while (ancestor != NULL) { + if (ancestor->label == -1 || ancestor->label > leaf[i].label) { + ancestor->label = leaf[i].label; + } + ancestor = ancestor->M; + } + } + + // --- Exchange children to enforce order-property O(n) + // We state that the order-property requires that an internal node's + // label is the smallest index of its left subtree. The dendrogram + // so far doesn't reflect this, so we need to step through each + // internal vertex and make that adjustment (swapping nL and nR if + // we make a change). + + elementd *tempe; + for (int i = 0; i < (n - 1); i++) { + if (internal[i].L->label > internal[i].label) { + tempe = internal[i].L; + internal[i].L = internal[i].R; + internal[i].R = tempe; + } + } + + // --- Tabulate internal dendrogram edges O(n^2) + // For the MCMC moves later on, we'll need to be able to choose, + // uniformly at random, an internal edge of the dendrogram to + // manipulate. There are always n-2 of them, and we can find them + // simply by scanning across the internal vertices and observing + // which have children that are also internal vertices. Note: very + // important that the order property be enforced before this step is + // taken; otherwise, the internal edges wont reflect the actual + // dendrogram structure. + + for (int i = 0; i < (n - 1); i++) { + if (internal[i].L->type == DENDRO) { + d->addEdge(i, internal[i].L->index, LEFT); + } + if (internal[i].R->type == DENDRO) { + d->addEdge(i, internal[i].R->index, RIGHT); + } + } + + // --- Clear memory for paths O(n log n) + // Now that we're finished using the paths, we need to deallocate + // them manually. + + list *current, *previous; + for (int i = 0; i < n; i++) { + current = paths[i]; + while (current) { + previous = current; + current = current->next; + delete previous; + previous = NULL; + } + paths[i] = NULL; + } + delete [] paths; + paths = NULL; + + // --- Compute p_i for each internal node O(n) + // Each internal node's p_i = e_i / (nL_i*nR_i), and now that we + // have each of those pieces, we may calculate this value for each + // internal node. Given these, we can then calculate the + // log-likelihood of the entire dendrogram structure \log(L) = + // \sum_{i=1}^{n} ( ( e_i \log[p_i] ) + ( (nL_i*nR_i - e_i) + // \log[1-p_i] ) ) + + L = 0.0; double dL; + int nL_nR, ei; + for (int i = 0; i < (n - 1); i++) { + nL_nR = internal[i].L->n * internal[i].R->n; + ei = internal[i].e; + internal[i].p = (double)(ei) / (double)(nL_nR); + if (ei == 0 || ei == nL_nR) { + dL = 0.0; + } else { + dL = ei * log(internal[i].p) + (nL_nR - ei) * log(1.0 - internal[i].p); + } + internal[i].logL = dL; + L += dL; + } + + for (int i = 0; i < (n - 1); i++) { + if (internal[i].label > internal[i].L->label) { + tempe = internal[i].L; + internal[i].L = internal[i].R; + internal[i].R = tempe; + } + } + + // Dendrogram is now built + + return; +} + +// *********************************************************************** + +void dendro::clearDendrograph() { + // Clear out the memory and references used by the dendrograph + // structure - this is intended to be called just before an + // importDendrogramStructure call so as to avoid memory leaks and + // overwriting the references therein. + + if (g != NULL) { + delete g; // O(m) + g = NULL; + } + if (leaf != NULL) { + delete [] leaf; // O(n) + leaf = NULL; + } + if (internal != NULL) { + delete [] internal; // O(n) + internal = NULL; + } + if (d != NULL) { + delete d; // O(n) + d = NULL; + } + root = NULL; + + return; +} + +// ********************************************************************** + +int dendro::computeEdgeCount(const int a, const short int atype, + const int b, const short int btype) { + // This function computes the number of edges that cross between the + // subtree internal[a] and the subtree internal[b]. To do this, we + // use an array A[1..n] integers which take values -1 if A[i] is in + // the subtree defined by internal[a], +1 if A[i] is in the subtree + // internal[b], and 0 otherwise. Taking the smaller of the two sets, + // we then scan over the edges attached to that set of vertices and + // count the number of endpoints we see in the other set. + + bool flag_go = true; + int nA, nB; + int count = 0; + const short int k = 1 + DENDRO + GRAPH; + + elementd* curr; + + // First, we push the leaf nodes in the L and R subtrees into + // balanced binary tree structures so that we can search them + // quickly later on. + + if (atype == GRAPH) { + // default case, subtree A is size 1 + // insert single node as member of left subtree + subtreeL.insertItem(a, -1); + nA = 1; // + } else { + // explore subtree A, O(|A|) + curr = &internal[a]; + curr->type = k + 1; + nA = 0; + while (flag_go) { + if (curr->index == internal[a].M->index) { + internal[a].type = DENDRO; + flag_go = false; + } else { + // - is it time, and is left child a graph node? + if (curr->type == k + 1 && curr->L->type == GRAPH) { + subtreeL.insertItem(curr->L->index, -1); + curr->type = k + 2; + nA++; + } + // - is it time, and is right child a graph node? + if (curr->type == k + 2 && curr->R->type == GRAPH) { + subtreeL.insertItem(curr->R->index, -1); + curr->type = k + 3; + nA++; + } + if (curr->type == k + 1) { // - go left + curr->type = k + 2; + curr = curr->L; + curr->type = k + 1; + } else if (curr->type == k + 2) { // - else go right + curr->type = k + 3; + curr = curr->R; + curr->type = k + 1; + } else { // - else go up a level + curr->type = DENDRO; + curr = curr->M; + if (curr == NULL) { + flag_go = false; + } + } + } + } + } + + if (btype == GRAPH) { + // default case, subtree A is size 1 + // insert node as single member of right subtree + subtreeR.insertItem(b, 1); + nB = 1; + } else { + flag_go = true; + // explore subtree B, O(|B|) + curr = &internal[b]; + curr->type = k + 1; + nB = 0; + while (flag_go) { + if (curr->index == internal[b].M->index) { + internal[b].type = DENDRO; + flag_go = false; + } else { + // - is it time, and is left child a graph node? + if (curr->type == k + 1 && curr->L->type == GRAPH) { + subtreeR.insertItem(curr->L->index, 1); + curr->type = k + 2; + nB++; + } + // - is it time, and is right child a graph node? + if (curr->type == k + 2 && curr->R->type == GRAPH) { + subtreeR.insertItem(curr->R->index, 1); + curr->type = k + 3; + nB++; + } + if (curr->type == k + 1) { // - look left + curr->type = k + 2; + curr = curr->L; + curr->type = k + 1; + } else if (curr->type == k + 2) { // - look right + curr->type = k + 3; + curr = curr->R; + curr->type = k + 1; + } else { // - else go up a level + curr->type = DENDRO; + curr = curr->M; + if (curr == NULL) { + flag_go = false; + } + } + } + } + } + + // Now, we take the smaller subtree and ask how many of its + // emerging edges have their partner in the other subtree. O(|A| log + // |A|) time + + edge* current; + int* treeList; + if (nA < nB) { + // subtreeL is smaller + treeList = subtreeL.returnArrayOfKeys(); + for (int i = 0; i < nA; i++) { + current = g->getNeighborList(treeList[i]); + // loop over each of its neighbors v_j + while (current != NULL) { + // to see if v_j is in A + if (subtreeR.findItem(current->x) != NULL) { + count++; + } + current = current->next; + } + subtreeL.deleteItem(treeList[i]); + } + delete [] treeList; + treeList = subtreeR.returnArrayOfKeys(); + for (int i = 0; i < nB; i++) { + subtreeR.deleteItem(treeList[i]); + } + delete [] treeList; + } else { + // subtreeR is smaller + treeList = subtreeR.returnArrayOfKeys(); + for (int i = 0; i < nB; i++) { + current = g->getNeighborList(treeList[i]); + // loop over each of its neighbors v_j + while (current != NULL) { + // to see if v_j is in B + if (subtreeL.findItem(current->x) != NULL) { + count++; + } + current = current->next; + } + subtreeR.deleteItem(treeList[i]); + } + delete [] treeList; + treeList = subtreeL.returnArrayOfKeys(); + for (int i = 0; i < nA; i++) { + subtreeL.deleteItem(treeList[i]); + } + delete [] treeList; + } + + return count; +} + +// *********************************************************************** + +int dendro::countChildren(const string s) { + int len = s.size(); + int numC = 0; + for (int i = 0; i < len; i++) { + if (s[i] == 'C') { + numC++; + } + } + return numC; +} + +// *********************************************************************** + +void dendro::cullSplitHist() { + string* array; + int tot, leng; + + array = splithist->returnArrayOfKeys(); + tot = splithist->returnTotal(); + leng = splithist->returnNodecount(); + for (int i = 0; i < leng; i++) { + if ((splithist->returnValue(array[i]) / tot) < 0.5) { + splithist->deleteItem(array[i]); + } + } + delete [] array; array = NULL; + + return; +} + +// ********************************************************************** + +elementd* dendro::findCommonAncestor(list** paths_, const int i, const int j) { + list* headOne = paths_[i]; + list* headTwo = paths_[j]; + elementd* lastStep = NULL; + while (headOne->x == headTwo->x) { + lastStep = &internal[headOne->x]; + headOne = headOne->next; + headTwo = headTwo->next; + if (headOne == NULL || headTwo == NULL) { + break; + } + } + return lastStep; // Returns address of an internal node; do not deallocate +} + +// ********************************************************************** + +int dendro::getConsensusSize() { + string *array; + double value, tot; + int numSplits, numCons; + numSplits = splithist->returnNodecount(); + array = splithist->returnArrayOfKeys(); + tot = splithist->returnTotal(); + numCons = 0; + for (int i = 0; i < numSplits; i++) { + value = splithist->returnValue(array[i]); + if (value / tot > 0.5) { + numCons++; + } + } + delete [] array; array = NULL; + return numCons; +} + +// ********************************************************************** + +splittree* dendro::getConsensusSplits() { + string *array; + splittree *consensusTree; + double value, tot; + consensusTree = new splittree; + int numSplits; + + // We look at all of the splits in our split histogram and add any + // one that's in the majority to our consensusTree, which we then + // return (note that consensusTree needs to be deallocated by the + // user). + numSplits = splithist->returnNodecount(); + array = splithist->returnArrayOfKeys(); + tot = splithist->returnTotal(); + for (int i = 0; i < numSplits; i++) { + value = splithist->returnValue(array[i]); + if (value / tot > 0.5) { + consensusTree->insertItem(array[i], value / tot); + } + } + delete [] array; array = NULL; + return consensusTree; +} + +// *********************************************************************** + +double dendro::getLikelihood() { + return L; +} + +// *********************************************************************** + +void dendro::getSplitList(splittree* split_tree) { + string sp; + for (int i = 0; i < (n - 1); i++) { + sp = d->getSplit(i); + if (!sp.empty() && sp[1] != '-') { + split_tree->insertItem(sp, 0.0); + } + } + return; +} + +// *********************************************************************** + +double dendro::getSplitTotalWeight() { + if (splithist) { + return splithist->returnTotal(); + } else { + return 0; + } +} + +// *********************************************************************** + +bool dendro::importDendrogramStructure(const igraph_hrg_t *hrg) { + n = igraph_hrg_size(hrg); + + // allocate memory for G, O(n) + leaf = new elementd[n]; + // allocate memory for D, O(n) + internal = new elementd[n - 1]; + // allocate memory for internal edges of D, O(n) + d = new interns(n - 2); + + // initialize leaf nodes + for (int i = 0; i < n; i++) { + leaf[i].type = GRAPH; + leaf[i].label = i; + leaf[i].index = i; + leaf[i].n = 1; + } + + // initialize internal nodes + root = &internal[0]; + root->label = 0; + for (int i = 1; i < n - 1; i++) { + internal[i].index = i; + internal[i].label = -1; + } + + // import basic structure from hrg object, O(n) + for (int i = 0; i < n - 1; i++) { + int left_index = VECTOR(hrg->left)[i]; + int right_index = VECTOR(hrg->right)[i]; + + if (left_index < 0) { + internal[i].L = &internal[-left_index - 1]; + internal[-left_index - 1].M = &internal[i]; + } else { + internal[i].L = &leaf[left_index]; + leaf[left_index].M = &internal[i]; + } + + if (right_index < 0) { + internal[i].R = &internal[-right_index - 1]; + internal[-right_index - 1].M = &internal[i]; + } else { + internal[i].R = &leaf[right_index]; + leaf[right_index].M = &internal[i]; + } + + internal[i].p = VECTOR(hrg->prob)[i]; + internal[i].e = VECTOR(hrg->edges)[i]; + internal[i].n = VECTOR(hrg->vertices)[i]; + internal[i].index = i; + } + + // --- Label all internal vertices O(n log n) + elementd *curr; + for (int i = 0; i < n; i++) { + curr = &leaf[i]; + while (curr) { + if (curr->label == -1 || curr->label > leaf[i].label) { + curr->label = leaf[i].label; + } + curr = curr -> M; + } + } + + // --- Exchange children to enforce order-property O(n) + elementd *tempe; + for (int i = 0; i < n - 1; i++) { + if (internal[i].L->label > internal[i].label) { + tempe = internal[i].L; + internal[i].L = internal[i].R; + internal[i].R = tempe; + } + } + + // --- Tabulate internal dendrogram edges O(n) + for (int i = 0; i < (n - 1); i++) { + if (internal[i].L->type == DENDRO) { + d->addEdge(i, internal[i].L->index, LEFT); + } + if (internal[i].R->type == DENDRO) { + d->addEdge(i, internal[i].R->index, RIGHT); + } + } + + // --- Compute p_i for each internal node O(n) + // Each internal node's p_i = e_i / (nL_i*nR_i), and now that we + // have each of those pieces, we may calculate this value for each + // internal node. Given these, we can then calculate the + // log-likelihood of the entire dendrogram structure + // \log(L) = \sum_{i=1}^{n} ( ( e_i \log[p_i] ) + + // ( (nL_i*nR_i - e_i) \log[1-p_i] ) ) + L = 0.0; double dL; + int nL_nR, ei; + for (int i = 0; i < (n - 1); i++) { + nL_nR = internal[i].L->n * internal[i].R->n; + ei = internal[i].e; + if (ei == 0 || ei == nL_nR) { + dL = 0.0; + } else { + dL = (double)(ei) * log(internal[i].p) + + (double)(nL_nR - ei) * log(1.0 - internal[i].p); + } + internal[i].logL = dL; + L += dL; + } + + return true; +} + +// *********************************************************************** + +void dendro::makeRandomGraph() { + if (g != NULL) { + delete g; + } g = NULL; g = new graph(n); + + list *curr, *prev; + if (paths) { + for (int i = 0; i < n; i++) { + curr = paths[i]; + while (curr != NULL) { + prev = curr; + curr = curr->next; + delete prev; + prev = NULL; + } + paths[i] = NULL; + } + delete [] paths; + } +// build paths from root O(n d) + paths = new list* [n]; + for (int i = 0; i < n; i++) { + paths[i] = reversePathToRoot(i); + } + + elementd* commonAncestor; +// O((h+d)*n^2) - h: height of D; d: average degree in G + for (int i = 0; i < n; i++) { + // decide neighbors of v_i + for (int j = (i + 1); j < n; j++) { + commonAncestor = findCommonAncestor(paths, i, j); + if (RNG_UNIF01() < commonAncestor->p) { + if (!(g->doesLinkExist(i, j))) { + g->addLink(i, j); + } + if (!(g->doesLinkExist(j, i))) { + g->addLink(j, i); + } + } + } + } + + for (int i = 0; i < n; i++) { + curr = paths[i]; + while (curr != NULL) { + prev = curr; + curr = curr->next; + delete prev; + prev = NULL; + } + paths[i] = NULL; + } + delete [] paths; // delete paths data structure O(n log n) + paths = NULL; + + return; +} + +// ********************************************************************** + +bool dendro::monteCarloMove(double& delta, bool& ftaken, const double T) { + // A single MC move begins with the selection of a random internal + // edge (a,b) of the dendrogram. This also determines the three + // subtrees i, j, k that we will rearrange, and we choose uniformly + // from among the options. + // + // If (a,b) is a left-edge, then we have ((i,j),k), and moves + // ((i,j),k) -> ((i,k),j) (alpha move) + // -> (i,(j,k)) + enforce order-property for (j,k) (beta move) + // + // If (a,b) is a right-edge, then we have (i,(j,k)), and moves + // (i,(j,k)) -> ((i,k),j) (alpha move) + // -> ((i,j),k) (beta move) + // + // For each of these moves, we need to know what the change in + // likelihood will be, so that we can determine with what + // probability we execute the move. + + elementd *temp; + ipair *tempPair; + int x, y, e_x, e_y, n_i, n_j, n_k, n_x, n_y; + short int t; + double p_x, p_y, L_x, L_y, dLogL; + string new_split; + + // The remainder of the code executes a single MCMC move, where we + // sample the dendrograms proportionally to their likelihoods (i.e., + // temperature=1, if you're comparing it to the usual MCMC + // framework). + + delta = 0.0; + ftaken = false; + tempPair = d->getRandomEdge(); // returns address; no need to deallocate + x = tempPair->x; // copy contents of referenced random edge + y = tempPair->y; // into local variables + t = tempPair->t; + + if (t == LEFT) { + if (RNG_UNIF01() < 0.5) { // ## LEFT ALPHA move: ((i,j),k) -> ((i,k),j) + // We need to calculate the change in the likelihood (dLogL) + // that would result from this move. Most of the information + // needed to do this is already available, the exception being + // e_ik, the number of edges that span the i and k subtrees. I + // use a slow algorithm O(n) to do this, since I don't know of a + // better way at this point. (After several attempts to find a + // faster method, no luck.) + + n_i = internal[y].L->n; + n_j = internal[y].R->n; + n_k = internal[x].R->n; + + n_y = n_i * n_k; + e_y = computeEdgeCount(internal[y].L->index, internal[y].L->type, + internal[x].R->index, internal[x].R->type); + p_y = (double)(e_y) / (double)(n_y); + if (e_y == 0 || e_y == n_y) { + L_y = 0.0; + } else { + L_y = (double)(e_y) * log(p_y) + (double)(n_y - e_y) * log(1.0 - p_y); + } + + n_x = (n_i + n_k) * n_j; + e_x = internal[x].e + internal[y].e - e_y; // e_yj + p_x = (double)(e_x) / (double)(n_x); + if (e_x == 0 || e_x == n_x) { + L_x = 0.0; + } else { + L_x = (double)(e_x) * log(p_x) + (double)(n_x - e_x) * log(1.0 - p_x); + } + + dLogL = (L_x - internal[x].logL) + (L_y - internal[y].logL); + if ((dLogL > 0.0) || (RNG_UNIF01() < exp(T * dLogL))) { + + // make LEFT ALPHA move + + ftaken = true; + d->swapEdges(x, internal[x].R->index, RIGHT, y, + internal[y].R->index, RIGHT); + temp = internal[x].R; // - swap j and k + internal[x].R = internal[y].R; + internal[y].R = temp; + internal[x].R->M = &internal[x]; // - adjust parent pointers + internal[y].R->M = &internal[y]; + internal[y].n = n_i + n_k; // - update n for [y] + internal[x].e = e_x; // - update e_i for [x] and [y] + internal[y].e = e_y; + internal[x].p = p_x; // - update p_i for [x] and [y] + internal[y].p = p_y; + internal[x].logL = L_x; // - update L_i for [x] and [y] + internal[y].logL = L_y; + // - order-property maintained + L += dLogL; // - update LogL + delta = dLogL; + + } + } else { + + // ## LEFT BETA move: ((i,j),k) -> (i,(j,k)) + + n_i = internal[y].L->n; + n_j = internal[y].R->n; + n_k = internal[x].R->n; + + n_y = n_j * n_k; + e_y = computeEdgeCount(internal[y].R->index, internal[y].R->type, + internal[x].R->index, internal[x].R->type); + p_y = (double)(e_y) / (double)(n_y); + if (e_y == 0 || e_y == n_y) { + L_y = 0.0; + } else { + L_y = (double)(e_y) * log(p_y) + + (double)(n_y - e_y) * log(1.0 - p_y); + } + + n_x = (n_j + n_k) * n_i; + e_x = internal[x].e + internal[y].e - e_y; // e_yj + p_x = (double)(e_x) / (double)(n_x); + if (e_x == 0 || e_x == n_x) { + L_x = 0.0; + } else { + L_x = (double)(e_x) * log(p_x) + (double)(n_x - e_x) * log(1.0 - p_x); + } + + dLogL = (L_x - internal[x].logL) + (L_y - internal[y].logL); + if ((dLogL > 0.0) || (RNG_UNIF01() < exp(T * dLogL))) { + + // make LEFT BETA move + + ftaken = true; + d->swapEdges(y, internal[y].L->index, LEFT, y, + internal[y].R->index, RIGHT); + temp = internal[y].L; // - swap L and R of [y] + internal[y].L = internal[y].R; + internal[y].R = temp; + d->swapEdges(x, internal[x].R->index, RIGHT, + y, internal[y].R->index, RIGHT); + temp = internal[x].R; // - swap i and k + internal[x].R = internal[y].R; + internal[y].R = temp; + internal[x].R->M = &internal[x]; // - adjust parent pointers + internal[y].R->M = &internal[y]; + d->swapEdges(x, internal[x].L->index, LEFT, + x, internal[x].R->index, RIGHT); + temp = internal[x].L; // - swap L and R of [x] + internal[x].L = internal[x].R; + internal[x].R = temp; + internal[y].n = n_j + n_k; // - update n + internal[x].e = e_x; // - update e_i + internal[y].e = e_y; + internal[x].p = p_x; // - update p_i + internal[y].p = p_y; + internal[x].logL = L_x; // - update logL_i + internal[y].logL = L_y; + if (internal[y].R->label < internal[y].L->label) { + // - enforce order-property if necessary + d->swapEdges(y, internal[y].L->index, LEFT, + y, internal[y].R->index, RIGHT); + temp = internal[y].L; + internal[y].L = internal[y].R; + internal[y].R = temp; + } // + internal[y].label = internal[y].L->label; + L += dLogL; // - update LogL + delta = dLogL; + } + } + } else { + + // right-edge: t == RIGHT + + if (RNG_UNIF01() < 0.5) { + + // alpha move: (i,(j,k)) -> ((i,k),j) + + n_i = internal[x].L->n; + n_j = internal[y].L->n; + n_k = internal[y].R->n; + + n_y = n_i * n_k; + e_y = computeEdgeCount(internal[x].L->index, internal[x].L->type, + internal[y].R->index, internal[y].R->type); + p_y = (double)(e_y) / (double)(n_y); + if (e_y == 0 || e_y == n_y) { + L_y = 0.0; + } else { + L_y = (double)(e_y) * log(p_y) + (double)(n_y - e_y) * log(1.0 - p_y); + } + + n_x = (n_i + n_k) * n_j; + e_x = internal[x].e + internal[y].e - e_y; // e_yj + p_x = (double)(e_x) / (double)(n_x); + if (e_x == 0 || e_x == n_x) { + L_x = 0.0; + } else { + L_x = (double)(e_x) * log(p_x) + (double)(n_x - e_x) * log(1.0 - p_x); + } + + dLogL = (L_x - internal[x].logL) + (L_y - internal[y].logL); + if ((dLogL > 0.0) || (RNG_UNIF01() < exp(T * dLogL))) { + + // make RIGHT ALPHA move + + ftaken = true; + d->swapEdges(x, internal[x].L->index, LEFT, + x, internal[x].R->index, RIGHT); + temp = internal[x].L; // - swap L and R of [x] + internal[x].L = internal[x].R; + internal[x].R = temp; + d->swapEdges(y, internal[y].L->index, LEFT, + x, internal[x].R->index, RIGHT); + temp = internal[y].L; // - swap i and j + internal[y].L = internal[x].R; + internal[x].R = temp; + internal[x].R->M = &internal[x]; // - adjust parent pointers + internal[y].L->M = &internal[y]; + internal[y].n = n_i + n_k; // - update n + internal[x].e = e_x; // - update e_i + internal[y].e = e_y; + internal[x].p = p_x; // - update p_i + internal[y].p = p_y; + internal[x].logL = L_x; // - update logL_i + internal[y].logL = L_y; + internal[y].label = internal[x].label; // - update order property + L += dLogL; // - update LogL + delta = dLogL; + } + } else { + + // beta move: (i,(j,k)) -> ((i,j),k) + + n_i = internal[x].L->n; + n_j = internal[y].L->n; + n_k = internal[y].R->n; + + n_y = n_i * n_j; + e_y = computeEdgeCount(internal[x].L->index, internal[x].L->type, + internal[y].L->index, internal[y].L->type); + p_y = (double)(e_y) / (double)(n_y); + if (e_y == 0 || e_y == n_y) { + L_y = 0.0; + } else { + L_y = (double)(e_y) * log(p_y) + (double)(n_y - e_y) * log(1.0 - p_y); + } + + n_x = (n_i + n_j) * n_k; + e_x = internal[x].e + internal[y].e - e_y; // e_yk + p_x = (double)(e_x) / (double)(n_x); + if (e_x == 0 || e_x == n_x) { + L_x = 0.0; + } else { + L_x = (double)(e_x) * log(p_x) + (double)(n_x - e_x) * log(1.0 - p_x); + } + + dLogL = (L_x - internal[x].logL) + (L_y - internal[y].logL); + if ((dLogL > 0.0) || (RNG_UNIF01() < exp(T * dLogL))) { + + // make RIGHT BETA move + + ftaken = true; + d->swapEdges(x, internal[x].L->index, LEFT, + x, internal[x].R->index, RIGHT); + temp = internal[x].L; // - swap L and R of [x] + internal[x].L = internal[x].R; + internal[x].R = temp; + d->swapEdges(x, internal[x].R->index, RIGHT, + y, internal[y].R->index, RIGHT); + temp = internal[x].R; // - swap i and k + internal[x].R = internal[y].R; + internal[y].R = temp; + internal[x].R->M = &internal[x]; // - adjust parent pointers + internal[y].R->M = &internal[y]; + d->swapEdges(y, internal[y].L->index, LEFT, + y, internal[y].R->index, RIGHT); + temp = internal[y].L; // - swap L and R of [y] + internal[y].L = internal[y].R; + internal[y].R = temp; + internal[y].n = n_i + n_j; // - update n + internal[x].e = e_x; // - update e_i + internal[y].e = e_y; + internal[x].p = p_x; // - update p_i + internal[y].p = p_y; + internal[x].logL = L_x; // - update logL_i + internal[y].logL = L_y; + internal[y].label = internal[x].label; // - order-property + L += dLogL; // - update LogL + delta = dLogL; + } + } + } + return true; +} + +// ********************************************************************** + +void dendro::refreshLikelihood() { + // recalculates the log-likelihood of the dendrogram structure + L = 0.0; double dL; + int nL_nR, ei; + for (int i = 0; i < (n - 1); i++) { + nL_nR = internal[i].L->n * internal[i].R->n; + ei = internal[i].e; + internal[i].p = (double)(ei) / (double)(nL_nR); + if (ei == 0 || ei == nL_nR) { + dL = 0.0; + } else { + dL = ei * log(internal[i].p) + (nL_nR - ei) * log(1.0 - internal[i].p); + } + internal[i].logL = dL; + L += dL; + } + return; +} + +// ********************************************************************** + +void dendro::QsortMain (block* array, int left, int right) { + if (right > left) { + int pivot = left; + int part = QsortPartition(array, left, right, pivot); + QsortMain(array, left, part - 1); + QsortMain(array, part + 1, right ); + } + return; +} + +int dendro::QsortPartition (block* array, int left, int right, int index) { + block p_value, temp; + p_value.x = array[index].x; + p_value.y = array[index].y; + + // swap(array[p_value], array[right]) + temp.x = array[right].x; + temp.y = array[right].y; + array[right].x = array[index].x; + array[right].y = array[index].y; + array[index].x = temp.x; + array[index].y = temp.y; + + int stored = left; + for (int i = left; i < right; i++) { + if (array[i].x <= p_value.x) { + // swap(array[stored], array[i]) + temp.x = array[i].x; + temp.y = array[i].y; + array[i].x = array[stored].x; + array[i].y = array[stored].y; + array[stored].x = temp.x; + array[stored].y = temp.y; + stored++; + } + } + // swap(array[right], array[stored]) + temp.x = array[stored].x; + temp.y = array[stored].y; + array[stored].x = array[right].x; + array[stored].y = array[right].y; + array[right].x = temp.x; + array[right].y = temp.y; + + return stored; +} + +void dendro::recordConsensusTree(igraph_vector_t *parents, + igraph_vector_t *weights) { + + keyValuePairSplit *curr, *prev; + child *newChild; + int orig_nodes = g->numNodes(); + + // First, cull the split hist so that only splits with weight >= 0.5 + // remain + cullSplitHist(); + int treesize = splithist->returnNodecount(); + + // Now, initialize the various arrays we use to keep track of the + // internal structure of the consensus tree. + ctree = new cnode[treesize]; + cancestor = new int[n]; + for (int i = 0; i < treesize; i++) { + ctree[i].index = i; + } + for (int i = 0; i < n; i++) { + cancestor[i] = -1; + } + int ii = 0; + + // To build the majority consensus tree, we do the following: For + // each possible number of Ms in the split string (a number that + // ranges from n-2 down to 0), and for each split with that number + // of Ms, we create a new internal node of the tree, and connect the + // oldest ancestor of each C to that node (at most once). Then, we + // update our list of oldest ancestors to reflect this new join, and + // proceed. + for (int i = n - 2; i >= 0; i--) { + // First, we get a list of all the splits with this exactly i Ms + curr = splithist->returnTheseSplits(i); + + // Now we loop over that list + while (curr != NULL) { + splithist->deleteItem(curr->x); + // add weight to this internal node + ctree[ii].weight = curr->y; + // examine each letter of this split + for (int j = 0; j < n; j++) { + if (curr->x[j] == 'C') { + // - node is child of this internal node + if (cancestor[j] == -1) { + // - first time this leaf has ever been seen + newChild = new child; + newChild->type = GRAPH; + newChild->index = j; + newChild->next = NULL; + // - attach child to list + if (ctree[ii].lastChild == NULL) { + ctree[ii].children = newChild; + ctree[ii].lastChild = newChild; + ctree[ii].degree = 1; + } else { + ctree[ii].lastChild->next = newChild; + ctree[ii].lastChild = newChild; + ctree[ii].degree += 1; + } + } else { + // - this leaf has been seen before + // If the parent of the ancestor of this leaf is the + // current internal node then this leaf is already a + // descendant of this internal node, and we can move on; + // otherwise, we need to add that ancestor to this + // internal node's child list, and update various + // relations + if (ctree[cancestor[j]].parent != ii) { + ctree[cancestor[j]].parent = ii; + newChild = new child; + newChild->type = DENDRO; + newChild->index = cancestor[j]; + newChild->next = NULL; + // - attach child to list + if (ctree[ii].lastChild == NULL) { + ctree[ii].children = newChild; + ctree[ii].lastChild = newChild; + ctree[ii].degree = 1; + } else { + ctree[ii].lastChild->next = newChild; + ctree[ii].lastChild = newChild; + ctree[ii].degree += 1; + } + } + } + // note new ancestry for this leaf + cancestor[j] = ii; + } + } + // update internal node index + ii++; + prev = curr; + curr = curr->next; + delete prev; + } + } + + // Return the consensus tree + igraph_vector_resize(parents, ii + orig_nodes); + if (weights) { + igraph_vector_resize(weights, ii); + } + + for (int i = 0; i < ii; i++) { + child *sat, *sit = ctree[i].children; + while (sit) { + VECTOR(*parents)[orig_nodes + i] = + ctree[i].parent < 0 ? -1 : orig_nodes + ctree[i].parent; + if (sit->type == GRAPH) { + VECTOR(*parents)[sit->index] = orig_nodes + i; + } + sat = sit; + sit = sit->next; + delete sat; + } + if (weights) { + VECTOR(*weights)[i] = ctree[i].weight; + } + ctree[i].children = 0; + } + + // Plus the isolate nodes + for (int i = 0; i < n; i++) { + if (cancestor[i] == -1) { + VECTOR(*parents)[i] = -1; + } + } + + +} + +// ********************************************************************** + +void dendro::recordDendrogramStructure(igraph_hrg_t *hrg) { + for (int i = 0; i < n - 1; i++) { + int li = internal[i].L->index; + int ri = internal[i].R->index; + VECTOR(hrg->left )[i] = internal[i].L->type == DENDRO ? -li - 1 : li; + VECTOR(hrg->right)[i] = internal[i].R->type == DENDRO ? -ri - 1 : ri; + VECTOR(hrg->prob )[i] = internal[i].p; + VECTOR(hrg->edges)[i] = internal[i].e; + VECTOR(hrg->vertices)[i] = internal[i].n; + } +} + +void dendro::recordGraphStructure(igraph_t *graph) { + igraph_vector_t edges; + int no_of_nodes = g->numNodes(); + int no_of_edges = g->numLinks() / 2; + int idx = 0; + + igraph_vector_init(&edges, no_of_edges * 2); + IGRAPH_FINALLY(igraph_vector_destroy, &edges); + + for (int i = 0; i < n; i++) { + edge *curr = g->getNeighborList(i); + while (curr) { + if (i < curr->x) { + VECTOR(edges)[idx++] = i; + VECTOR(edges)[idx++] = curr->x; + } + curr = curr->next; + } + } + + igraph_create(graph, &edges, no_of_nodes, /* directed= */ 0); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); +} + +// ********************************************************************** + +list* dendro::reversePathToRoot(const int leafIndex) { + list *head, *subhead, *newlist; + head = subhead = newlist = NULL; + elementd *current = &leaf[leafIndex]; + + // continue until we're finished + while (current != NULL) { + // add this node to the path + newlist = new list; + newlist->x = current->index; + newlist->next = NULL; + if (head == NULL) { + head = newlist; + } else { + subhead = head; + head = newlist; + head->next = subhead; + } + current = current->M; + } + return head; +} + +// *********************************************************************** + +bool dendro::sampleSplitLikelihoods(int &sample_num) { + // In order to compute the majority agreement dendrogram at + // equilibrium, we need to calculate the leaf partition defined by + // each split (internal edge) of the tree. Because splits are only + // defined on a Cayley tree, the buildSplit() function returns the + // default "--...--" string for the root and the root's left + // child. When tabulating the frequency of splits, one of these + // needs to be excluded. + + IGRAPH_UNUSED(sample_num); + + string* array; + int k; + double tot; + + string new_split; + // To decompose the tree into its splits, we simply loop over all + // the internal nodes and replace the old split for the ith internal + // node with its new split. This is a bit time consuming to do + // O(n^2), so try not to do this very often. Once the decomposition + // is had, we insert them into the split histogram, which tracks the + // cumulative weight for each respective split observed. + + if (splithist == NULL) { + splithist = new splittree; + } + for (int i = 0; i < (n - 1); i++) { + new_split = buildSplit(&internal[i]); + d->replaceSplit(i, new_split); + if (!new_split.empty() && new_split[1] != '-') { + if (!splithist->insertItem(new_split, 1.0)) { + return false; + } + } + } + splithist->finishedThisRound(); + + // For large graphs, the split histogram can get extremely large, so + // we need to employ some measures to prevent it from swamping the + // available memory. When the number of splits exceeds a threshold + // (say, a million), we progressively delete splits that have a + // weight less than a rising (k*0.001 of the total weight) fraction + // of the splits, on the assumption that losing such weight is + // unlikely to effect the ultimate split statistics. This deletion + // procedure is slow O(m lg m), but should only happen very rarely. + + int split_max = n * 500; + int leng; + if (splithist->returnNodecount() > split_max) { + k = 1; + while (splithist->returnNodecount() > split_max) { + array = splithist->returnArrayOfKeys(); + tot = splithist->returnTotal(); + leng = splithist->returnNodecount(); + for (int i = 0; i < leng; i++) { + if ((splithist->returnValue(array[i]) / tot) < k * 0.001) { + splithist->deleteItem(array[i]); + } + } + delete [] array; array = NULL; + k++; + } + } + + return true; +} + +void dendro::sampleAdjacencyLikelihoods() { + // Here, we sample the probability values associated with every + // adjacency in A, weighted by their likelihood. The weighted + // histogram is stored in the graph data structure, so we simply + // need to add an observation to each node-pair that corresponds to + // the associated branch point's probability and the dendrogram's + // overall likelihood. + + double nn; + double norm = ((double)(n) * (double)(n)) / 4.0; + + if (L > 0.0) { + L = 0.0; + } + elementd* ancestor; + list *currL, *prevL; + if (paths != NULL) { + for (int i = 0; i < n; i++) { + currL = paths[i]; + while (currL != NULL) { + prevL = currL; + currL = currL->next; + delete prevL; + prevL = NULL; + } + paths[i] = NULL; + } + delete [] paths; + } + paths = NULL; + paths = new list* [n]; + for (int i = 0; i < n; i++) { + // construct paths from root, O(n^2) at worst + paths[i] = reversePathToRoot(i); + } + + // add obs for every node-pair, always O(n^2) + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + // find internal node, O(n) at worst + ancestor = findCommonAncestor(paths, i, j); + nn = ((double)(ancestor->L->n) * (double)(ancestor->R->n)) / norm; + // add obs of ->p to (i,j) histogram, and + g->addAdjacencyObs(i, j, ancestor->p, nn); + // add obs of ->p to (j,i) histogram + g->addAdjacencyObs(j, i, ancestor->p, nn); + } + } + + // finish-up: upate total weight in histograms + g->addAdjacencyEnd(); + + return; +} + +void dendro::resetDendrograph() { + // Reset the dendrograph structure for the next trial + if (leaf != NULL) { + delete [] leaf; // O(n) + leaf = NULL; + } + if (internal != NULL) { + delete [] internal; // O(n) + internal = NULL; + } + if (d != NULL) { + delete d; // O(n) + d = NULL; + } + root = NULL; + if (paths != NULL) { + list *curr, *prev; + for (int i = 0; i < n; i++) { + curr = paths[i]; + while (curr != NULL) { + prev = curr; + curr = curr->next; + delete prev; + prev = NULL; + } + paths[i] = NULL; + } + delete [] paths; + } + paths = NULL; + L = 1.0; + + return; +} + +// ********************************************************************** +// *** COPYRIGHT NOTICE ************************************************* +// graph.h - graph data structure for hierarchical random graphs +// Copyright (C) 2005-2008 Aaron Clauset +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.gnu.org/licenses/gpl.txt for more details. +// +// ********************************************************************** +// Author : Aaron Clauset ( aaronc@santafe.edu | +// http://www.santafe.edu/~aaronc/ ) +// Collaborators: Cristopher Moore and Mark E.J. Newman +// Project : Hierarchical Random Graphs +// Location : University of New Mexico, Dept. of Computer Science +// AND Santa Fe Institute +// Created : 8 November 2005 +// Modified : 23 December 2007 (cleaned up for public consumption) +// +// *********************************************************************** +// +// Graph data structure for hierarchical random graphs. The basic +// structure is an adjacency list of edges; however, many additional +// pieces of metadata are stored as well. Each node stores its +// external name, its degree and (if assigned) its group index. +// +// *********************************************************************** + +// ******** Constructor / Destructor ************************************* + +graph::graph(const int size, bool predict) : predict(predict) { + n = size; + m = 0; + nodes = new vert [n]; + nodeLink = new edge* [n]; + nodeLinkTail = new edge* [n]; + for (int i = 0; i < n; i++) { + nodeLink[i] = NULL; + nodeLinkTail[i] = NULL; + } + if (predict) { + A = new double** [n]; + for (int i = 0; i < n; i++) { + A[i] = new double* [n]; + } + obs_count = 0; + total_weight = 0.0; + bin_resolution = 0.0; + num_bins = 0; + } +} + +graph::~graph() { + edge *curr, *prev; + for (int i = 0; i < n; i++) { + curr = nodeLink[i]; + while (curr != NULL) { + prev = curr; + curr = curr->next; + delete prev; + } + } + delete [] nodeLink; nodeLink = NULL; + delete [] nodeLinkTail; nodeLinkTail = NULL; + delete [] nodes; nodes = NULL; + + if (predict) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + delete [] A[i][j]; + } + delete [] A[i]; + } + delete [] A; A = NULL; + } +} + +// ********************************************************************** + +bool graph::addLink(const int i, const int j) { + // Adds the directed edge (i,j) to the adjacency list for v_i + edge* newedge; + if (i >= 0 && i < n && j >= 0 && j < n) { + newedge = new edge; + newedge->x = j; + if (nodeLink[i] == NULL) { + // first neighbor + nodeLink[i] = newedge; + nodeLinkTail[i] = newedge; + nodes[i].degree = 1; + } else { + // subsequent neighbor + nodeLinkTail[i]->next = newedge; + nodeLinkTail[i] = newedge; + nodes[i].degree++; + } + // increment edge count + m++; + return true; + } else { + return false; + } +} + +// *********************************************************************** + +bool graph::addAdjacencyObs(const int i, const int j, + const double probability, const double size) { + // Adds the observation obs to the histogram of the edge (i,j) + // Note: user must manually add observation to edge (j,i) by calling + // this function with that argument + if (bin_resolution > 0.0 && probability >= 0.0 && probability <= 1.0 + && size >= 0.0 && size <= 1.0 + && i >= 0 && i < n && j >= 0 && j < n) { + int index = (int)(probability / bin_resolution + 0.5); + if (index < 0) { + index = 0; + } else if (index > num_bins) { + index = num_bins; + } + + // Add the weight to the proper probability bin + if (A[i][j][index] < 0.5) { + A[i][j][index] = 1.0; + } else { + A[i][j][index] += 1.0; + } + return true; + } + return false; +} + +// ********************************************************************** + +void graph::addAdjacencyEnd() { + // We need to also keep a running total of how much weight has been added + // to the histogram, and the number of observations in the histogram. + if (obs_count == 0) { + total_weight = 1.0; obs_count = 1; + } else { + total_weight += 1.0; obs_count++; + } + return; +} + +bool graph::doesLinkExist(const int i, const int j) { + // This function determines if the edge (i,j) already exists in the + // adjacency list of v_i + edge* curr; + if (i >= 0 && i < n && j >= 0 && j < n) { + curr = nodeLink[i]; + while (curr != NULL) { + if (curr->x == j) { + return true; + } + curr = curr->next; + } + } + return false; +} + +// ********************************************************************** + +int graph::getDegree(const int i) { + if (i >= 0 && i < n) { + return nodes[i].degree; + } else { + return -1; + } +} + +string graph::getName(const int i) { + if (i >= 0 && i < n) { + return nodes[i].name; + } else { + return ""; + } +} + +// NOTE: Returns address; deallocation of returned object is dangerous +edge* graph::getNeighborList(const int i) { + if (i >= 0 && i < n) { + return nodeLink[i]; + } else { + return NULL; + } +} + +double* graph::getAdjacencyHist(const int i, const int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + return A[i][j]; + } else { + return NULL; + } +} + +// ********************************************************************** + +double graph::getAdjacencyAverage(const int i, const int j) { + double average = 0.0; + if (i != j) { + for (int k = 0; k < num_bins; k++) { + if (A[i][j][k] > 0.0) { + average += (A[i][j][k] / total_weight) * ((double)(k) * bin_resolution); + } + } + } + return average; +} + +int graph::numLinks() { + return m; +} + +int graph::numNodes() { + return n; +} + +double graph::getBinResolution() { + return bin_resolution; +} + +int graph::getNumBins() { + return num_bins; +} + +double graph::getTotalWeight() { + return total_weight; +} + +// *********************************************************************** + +void graph::resetAllAdjacencies() { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + for (int k = 0; k < num_bins; k++) { + A[i][j][k] = 0.0; + } + } + } + obs_count = 0; + total_weight = 0.0; + return; +} + +// ********************************************************************** + +void graph::resetAdjacencyHistogram(const int i, const int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + for (int k = 0; k < num_bins; k++) { + A[i][j][k] = 0.0; + } + } + return; +} + +// ********************************************************************** + +void graph::resetLinks() { + edge *curr, *prev; + for (int i = 0; i < n; i++) { + curr = nodeLink[i]; + while (curr != NULL) { + prev = curr; + curr = curr->next; + delete prev; + } + nodeLink[i] = NULL; + nodeLinkTail[i] = NULL; + nodes[i].degree = 0; + } + m = 0; + return; +} + +// ********************************************************************** + +void graph::setAdjacencyHistograms(const int bin_count) { + // For all possible adjacencies, setup an edge histograms + num_bins = bin_count + 1; + bin_resolution = 1.0 / (double)(bin_count); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = new double [num_bins]; + for (int k = 0; k < num_bins; k++) { + A[i][j][k] = 0.0; + } + } + } + return; +} + +bool graph::setName(const int i, const string text) { + if (i >= 0 && i < n) { + nodes[i].name = text; + return true; + } else { + return false; + } +} + +// ********************************************************************** + +interns::interns(const int n) { + q = n; + count = 0; + edgelist = new ipair [q]; + splitlist = new string [q + 1]; + indexLUT = new int* [q + 1]; + for (int i = 0; i < (q + 1); i++) { + indexLUT[i] = new int [2]; + indexLUT[i][0] = indexLUT[i][1] = -1; + } +} +interns::~interns() { + delete [] edgelist; + delete [] splitlist; + for (int i = 0; i < (q + 1); i++) { + delete [] indexLUT[i]; + } + delete [] indexLUT; +} + +// *********************************************************************** + +// NOTE: Returns an address to another object -- do not deallocate +ipair* interns::getEdge(const int i) { + return &edgelist[i]; +} + +// *********************************************************************** + +// NOTE: Returns an address to another object -- do not deallocate +ipair* interns::getRandomEdge() { + return &edgelist[(int)(floor((double)(q) * RNG_UNIF01()))]; +} + +// *********************************************************************** + +string interns::getSplit(const int i) { + if (i >= 0 && i <= q) { + return splitlist[i]; + } else { + return ""; + } +} + +// ********************************************************************** + +bool interns::addEdge(const int new_x, const int new_y, + const short int new_type) { + // This function adds a new edge (i,j,t,sp) to the list of internal + // edges. After checking that the inputs fall in the appropriate + // range of values, it records the new edgelist index in the + // indexLUT and then puts the input values into that edgelist + // location. + + if (count < q && new_x >= 0 && new_x < (q + 1) && new_y >= 0 && + new_y < (q + 2) && (new_type == LEFT || new_type == RIGHT)) { + if (new_type == LEFT) { + indexLUT[new_x][0] = count; + } else { + indexLUT[new_x][1] = count; + } + edgelist[count].x = new_x; + edgelist[count].y = new_y; + edgelist[count].t = new_type; + count++; + return true; + } else { + return false; + } +} + +// ********************************************************************** + +bool interns::replaceSplit(const int i, const string sp) { + // When an internal edge is changed, its split must be replaced as + // well. This function provides that access; it stores the split + // defined by an internal edge (x,y) at the location [y], which + // is unique. + + if (i >= 0 && i <= q) { + splitlist[i] = sp; + return true; + } + return false; +} + +// *********************************************************************** + +bool interns::swapEdges(const int one_x, const int one_y, + const short int one_type, const int two_x, + const int two_y, const short int two_type) { + // The moves on the dendrogram always swap edges, either of which + // (or both, or neither) can by internal edges. So, this function + // mirrors that operation for the internal edgelist and indexLUT. + + int index, jndex, temp; + bool one_isInternal = false; + bool two_isInternal = false; + + if (one_x >= 0 && one_x < (q + 1) && two_x >= 0 && two_x < (q + 1) && + (two_type == LEFT || two_type == RIGHT) && + one_y >= 0 && one_y < (q + 2) && two_y >= 0 && + two_y < (q + 2) && (one_type == LEFT || one_type == RIGHT)) { + + if (one_type == LEFT) { + temp = 0; + } else { + temp = 1; + } + if (indexLUT[one_x][temp] > -1) { + one_isInternal = true; + } + if (two_type == LEFT) { + temp = 0; + } else { + temp = 1; + } + if (indexLUT[two_x][temp] > -1) { + two_isInternal = true; + } + + if (one_isInternal && two_isInternal) { + if (one_type == LEFT) { + index = indexLUT[one_x][0]; + } else { + index = indexLUT[one_x][1]; + } + if (two_type == LEFT) { + jndex = indexLUT[two_x][0]; + } else { + jndex = indexLUT[two_x][1]; + } + temp = edgelist[index].y; + edgelist[index].y = edgelist[jndex].y; + edgelist[jndex].y = temp; + + } else if (one_isInternal) { + if (one_type == LEFT) { + index = indexLUT[one_x][0]; indexLUT[one_x][0] = -1; + } else { + index = indexLUT[one_x][1]; indexLUT[one_x][1] = -1; + } + edgelist[index].x = two_x; + edgelist[index].t = two_type; + if (two_type == LEFT) { + indexLUT[two_x][0] = index; + } else { + indexLUT[two_x][1] = index; + } // add new + + } else if (two_isInternal) { + if (two_type == LEFT) { + index = indexLUT[two_x][0]; indexLUT[two_x][0] = -1; + } else { + index = indexLUT[two_x][1]; indexLUT[two_x][1] = -1; + } + edgelist[index].x = one_x; + edgelist[index].t = one_type; + if (one_type == LEFT) { + indexLUT[one_x][0] = index; + } else { + indexLUT[one_x][1] = index; + } // add new + } else { + ; + } // else neither is internal + + return true; + } else { + return false; + } +} + +// ******** Red-Black Tree Methods *************************************** + +splittree::splittree() { + root = new elementsp; + leaf = new elementsp; + + leaf->parent = root; + + root->left = leaf; + root->right = leaf; + support = 0; + total_weight = 0.0; + total_count = 0; +} + +splittree::~splittree() { + if (root != NULL && (root->left != leaf || root->right != leaf)) { + deleteSubTree(root); root = NULL; + } + support = 0; + total_weight = 0.0; + total_count = 0; + if (root) { + delete root; + } + delete leaf; + root = NULL; + leaf = NULL; +} + +void splittree::deleteTree() { + if (root != NULL) { + deleteSubTree(root); + root = NULL; + } + return; +} + +void splittree::deleteSubTree(elementsp *z) { + if (z->left != leaf) { + deleteSubTree(z->left); + z->left = NULL; + } + if (z->right != leaf) { + deleteSubTree(z->right); + z->right = NULL; + } + delete z; + /* No point in setting z to NULL here because z is passed by value */ + /* z = NULL; */ + return; +} + +// ******** Reset Functions ********************************************* + +// O(n lg n) +void splittree::clearTree() { + string *array = returnArrayOfKeys(); + for (int i = 0; i < support; i++) { + deleteItem(array[i]); + } + delete [] array; + return; +} + +// ******** Search Functions ********************************************* +// public search function - if there exists a elementsp in the tree +// with key=searchKey, it returns TRUE and foundNode is set to point +// to the found node; otherwise, it sets foundNode=NULL and returns +// FALSE +elementsp* splittree::findItem(const string searchKey) { + + elementsp *current = root; + if (current->split.empty()) { + return NULL; // empty tree; bail out + } + while (current != leaf) { + if (searchKey.compare(current->split) < 0) { // left-or-right? + // try moving down-left + if (current->left != leaf) { + current = current->left; + } else { + // failure; bail out + return NULL; + } + } else { + if (searchKey.compare(current->split) > 0) { + // left-or-right? + if (current->right != leaf) { + // try moving down-left + current = current->right; + } else { + // failure; bail out + return NULL; + } + } else { + // found (searchKey==current->split) + return current; + } + } + } + return NULL; +} + +double splittree::returnValue(const string searchKey) { + elementsp* test = findItem(searchKey); + if (test == NULL) { + return 0.0; + } else { + return test->weight; + } +} + + +// ******** Return Item Functions *************************************** +// public function which returns the tree, via pre-order traversal, as +// a linked list + +string* splittree::returnArrayOfKeys() { + string* array; + array = new string [support]; + bool flag_go = true; + int index = 0; + elementsp *curr; + + if (support == 1) { + array[0] = root->split; + } else if (support == 2) { + array[0] = root->split; + if (root->left == leaf) { + array[1] = root->right->split; + } else { + array[1] = root->left->split; + } + } else { + for (int i = 0; i < support; i++) { + array[i] = -1; + } + // non-recursive traversal of tree structure + curr = root; + curr->mark = 1; + while (flag_go) { + + // - is it time, and is left child the leaf node? + if (curr->mark == 1 && curr->left == leaf) { + curr->mark = 2; + } + // - is it time, and is right child the leaf node? + if (curr->mark == 2 && curr->right == leaf) { + curr->mark = 3; + } + if (curr->mark == 1) { // - go left + curr->mark = 2; + curr = curr->left; + curr->mark = 1; + } else if (curr->mark == 2) { // - else go right + curr->mark = 3; + curr = curr->right; + curr->mark = 1; + } else { // - else go up a level + curr->mark = 0; + array[index++] = curr->split; + curr = curr->parent; + if (curr == NULL) { + flag_go = false; + } + } + } + } + + return array; +} + +slist* splittree::returnListOfKeys() { + keyValuePairSplit *curr, *prev; + slist *head = NULL, *tail = NULL, *newlist; + + curr = returnTreeAsList(); + while (curr != NULL) { + newlist = new slist; + newlist->x = curr->x; + if (head == NULL) { + head = newlist; tail = head; + } else { + tail->next = newlist; tail = newlist; + } + prev = curr; + curr = curr->next; + delete prev; + prev = NULL; + } + return head; +} + +// pre-order traversal +keyValuePairSplit* splittree::returnTreeAsList() { + keyValuePairSplit *head, *tail; + + head = new keyValuePairSplit; + head->x = root->split; + head->y = root->weight; + head->c = root->count; + tail = head; + + if (root->left != leaf) { + tail = returnSubtreeAsList(root->left, tail); + } + if (root->right != leaf) { + tail = returnSubtreeAsList(root->right, tail); + } + + if (head->x.empty()) { + return NULL; /* empty tree */ + } else { + return head; + } +} + +keyValuePairSplit* splittree::returnSubtreeAsList(elementsp *z, + keyValuePairSplit *head) { + keyValuePairSplit *newnode, *tail; + + newnode = new keyValuePairSplit; + newnode->x = z->split; + newnode->y = z->weight; + newnode->c = z->count; + head->next = newnode; + tail = newnode; + + if (z->left != leaf) { + tail = returnSubtreeAsList(z->left, tail); + } + if (z->right != leaf) { + tail = returnSubtreeAsList(z->right, tail); + } + + return tail; +} + +keyValuePairSplit splittree::returnMaxKey() { + keyValuePairSplit themax; + elementsp *current; + current = root; + // search to bottom-right corner of tree + while (current->right != leaf) { + current = current->right; + } + themax.x = current->split; + themax.y = current->weight; + + return themax; +} + +keyValuePairSplit splittree::returnMinKey() { + keyValuePairSplit themin; + elementsp *current; + current = root; + // search to bottom-left corner of tree + while (current->left != leaf) { + current = current->left; + } + themin.x = current->split; + themin.y = current->weight; + + return themin; +} + +// private functions for deleteItem() (although these could easily be +// made public, I suppose) +elementsp* splittree::returnMinKey(elementsp *z) { + elementsp *current; + + current = z; + // search to bottom-right corner of tree + while (current->left != leaf) { + current = current->left; + } + // return pointer to the minimum + return current; +} + +elementsp* splittree::returnSuccessor(elementsp *z) { + elementsp *current, *w; + + w = z; +// if right-subtree exists, return min of it + if (w->right != leaf) { + return returnMinKey(w->right); + } + // else search up in tree + // move up in tree until find a non-right-child + current = w->parent; + while ((current != NULL) && (w == current->right)) { + w = current; + current = current->parent; + } + return current; +} + +int splittree::returnNodecount() { + return support; +} + +keyValuePairSplit* splittree::returnTheseSplits(const int target) { + keyValuePairSplit *head, *curr, *prev, *newhead, *newtail, *newpair; + int count, len; + + head = returnTreeAsList(); + prev = newhead = newtail = newpair = NULL; + curr = head; + + while (curr != NULL) { + count = 0; + len = curr->x.size(); + for (int i = 0; i < len; i++) { + if (curr->x[i] == 'M') { + count++; + } + } + if (count == target && curr->x[1] != '*') { + newpair = new keyValuePairSplit; + newpair->x = curr->x; + newpair->y = curr->y; + newpair->next = NULL; + if (newhead == NULL) { + newhead = newpair; newtail = newpair; + } else { + newtail->next = newpair; newtail = newpair; + } + } + prev = curr; + curr = curr->next; + delete prev; + prev = NULL; + } + + return newhead; +} + +double splittree::returnTotal() { + return total_weight; +} + +// ******** Insert Functions ********************************************* + +void splittree::finishedThisRound() { + // We need to also keep a running total of how much weight has been + // added to the histogram. + if (total_count == 0) { + total_weight = 1.0; total_count = 1; + } else { + total_weight += 1.0; total_count++; + } + return; +} + +// public insert function +bool splittree::insertItem(string newKey, double newValue) { + + // first we check to see if newKey is already present in the tree; + // if so, we do nothing; if not, we must find where to insert the + // key + elementsp *newNode, *current; + +// find newKey in tree; return pointer to it O(log k) + current = findItem(newKey); + if (current != NULL) { + current->weight += 1.0; + // And finally, we keep track of how many observations went into + // the histogram + current->count++; + return true; + } else { + newNode = new elementsp; // elementsp for the splittree + newNode->split = newKey; // store newKey + newNode->weight = newValue; // store newValue + newNode->color = true; // new nodes are always RED + newNode->parent = NULL; // new node initially has no parent + newNode->left = leaf; // left leaf + newNode->right = leaf; // right leaf + newNode->count = 1; + support++; // increment node count in splittree + + // must now search for where to insert newNode, i.e., find the + // correct parent and set the parent and child to point to each + // other properly + current = root; + if (current->split.empty()) { // insert as root + delete root; // delete old root + root = newNode; // set root to newNode + leaf->parent = newNode; // set leaf's parent + current = leaf; // skip next loop + } + + // search for insertion point + while (current != leaf) { + // left-or-right? + if (newKey.compare(current->split) < 0) { + // try moving down-left + if (current->left != leaf) { + current = current->left; + } else { + // else found new parent + newNode->parent = current; // set parent + current->left = newNode; // set child + current = leaf; // exit search + } + } else { // + if (current->right != leaf) { + // try moving down-right + current = current->right; + } else { + // else found new parent + newNode->parent = current; // set parent + current->right = newNode; // set child + current = leaf; // exit search + } + } + } + + // now do the house-keeping necessary to preserve the red-black + // properties + insertCleanup(newNode); + + } + return true; +} + +// private house-keeping function for insertion +void splittree::insertCleanup(elementsp *z) { + + // fix now if z is root + if (z->parent == NULL) { + z->color = false; return; + } + elementsp *temp; + // while z is not root and z's parent is RED + while (z->parent != NULL && z->parent->color) { + if (z->parent == z->parent->parent->left) { // z's parent is LEFT-CHILD + temp = z->parent->parent->right; // grab z's uncle + if (temp->color) { + z->parent->color = false; // color z's parent BLACK (Case 1) + temp->color = false; // color z's uncle BLACK (Case 1) + z->parent->parent->color = true; // color z's grandpa RED (Case 1) + z = z->parent->parent; // set z = z's grandpa (Case 1) + } else { + if (z == z->parent->right) { // z is RIGHT-CHILD + z = z->parent; // set z = z's parent (Case 2) + rotateLeft(z); // perform left-rotation (Case 2) + } + z->parent->color = false; // color z's parent BLACK (Case 3) + z->parent->parent->color = true; // color z's grandpa RED (Case 3) + rotateRight(z->parent->parent); // perform right-rotation (Case 3) + } + } else { // z's parent is RIGHT-CHILD + temp = z->parent->parent->left; // grab z's uncle + if (temp->color) { + z->parent->color = false; // color z's parent BLACK (Case 1) + temp->color = false; // color z's uncle BLACK (Case 1) + z->parent->parent->color = true; // color z's grandpa RED (Case 1) + z = z->parent->parent; // set z = z's grandpa (Case 1) + } else { + if (z == z->parent->left) { // z is LEFT-CHILD + z = z->parent; // set z = z's parent (Case 2) + rotateRight(z); // perform right-rotation (Case 2) + } + z->parent->color = false; // color z's parent BLACK (Case 3) + z->parent->parent->color = true; // color z's grandpa RED (Case 3) + rotateLeft(z->parent->parent); // perform left-rotation (Case 3) + } + } + } + + root->color = false; // color the root BLACK + return; +} + +// ******** Delete Functions ******************************************** +// public delete function +void splittree::deleteItem(string killKey) { + elementsp *x, *y, *z; + + z = findItem(killKey); + if (z == NULL) { + return; // item not present; bail out + } + + if (support == 1) { // -- attempt to delete the root + root->split = ""; // restore root node to default state + root->weight = 0.0; // + root->color = false; // + root->parent = NULL; // + root->left = leaf; // + root->right = leaf; // + support--; // set support to zero + total_weight = 0.0; // set total weight to zero + total_count--; // + return; // exit - no more work to do + } + + if (z != NULL) { + support--; // decrement node count + if ((z->left == leaf) || (z->right == leaf)) { + // case of less than two children + y = z; // set y to be z + } else { + y = returnSuccessor(z); // set y to be z's key-successor + } + + if (y->left != leaf) { + x = y->left; // pick y's one child (left-child) + } else { + x = y->right; // (right-child) + } + x->parent = y->parent; // make y's child's parent be y's parent + + if (y->parent == NULL) { + root = x; // if y is the root, x is now root + } else { + if (y == y->parent->left) {// decide y's relationship with y's parent + y->parent->left = x; // replace x as y's parent's left child + } else { + y->parent->right = x; + } // replace x as y's parent's left child + } + + if (y != z) { // insert y into z's spot + z->split = y->split; // copy y data into z + z->weight = y->weight; // + z->count = y->count; // + } // + + // do house-keeping to maintain balance + if (y->color == false) { + deleteCleanup(x); + } + delete y; // deallocate y + y = NULL; // point y to NULL for safety + } // + + return; +} + +void splittree::deleteCleanup(elementsp *x) { + elementsp *w, *t; + // until x is the root, or x is RED + while ((x != root) && (x->color == false)) { + if (x == x->parent->left) { // branch on x being a LEFT-CHILD + w = x->parent->right; // grab x's sibling + if (w->color == true) { // if x's sibling is RED + w->color = false; // color w BLACK (case 1) + x->parent->color = true; // color x's parent RED (case 1) + rotateLeft(x->parent); // left rotation on x's parent (case 1) + w = x->parent->right; // make w be x's right sibling (case 1) + } + if ((w->left->color == false) && (w->right->color == false)) { + w->color = true; // color w RED (case 2) + x = x->parent; // examine x's parent (case 2) + } else { // + if (w->right->color == false) { + w->left->color = false; // color w's left child BLACK (case 3) + w->color = true; // color w RED (case 3) + t = x->parent; // store x's parent + rotateRight(w); // right rotation on w (case 3) + x->parent = t; // restore x's parent + w = x->parent->right; // make w be x's right sibling (case 3) + } // + w->color = x->parent->color; // w's color := x's parent's (case 4) + x->parent->color = false; // color x's parent BLACK (case 4) + w->right->color = false; // color w's right child BLACK (case 4) + rotateLeft(x->parent); // left rotation on x's parent (case 4) + x = root; // finished work. bail out (case 4) + } // + } else { // x is RIGHT-CHILD + w = x->parent->left; // grab x's sibling + if (w->color == true) { // if x's sibling is RED + w->color = false; // color w BLACK (case 1) + x->parent->color = true; // color x's parent RED (case 1) + rotateRight(x->parent); // right rotation on x's parent (case 1) + w = x->parent->left; // make w be x's left sibling (case 1) + } + if ((w->right->color == false) && (w->left->color == false)) { + w->color = true; // color w RED (case 2) + x = x->parent; // examine x's parent (case 2) + } else { // + if (w->left->color == false) { // + w->right->color = false; // color w's right child BLACK (case 3) + w->color = true; // color w RED (case 3) + t = x->parent; // store x's parent + rotateLeft(w); // left rotation on w (case 3) + x->parent = t; // restore x's parent + w = x->parent->left; // make w be x's left sibling (case 3) + } // + w->color = x->parent->color; // w's color := x's parent's (case 4) + x->parent->color = false; // color x's parent BLACK (case 4) + w->left->color = false; // color w's left child BLACK (case 4) + rotateRight(x->parent); // right rotation on x's parent (case 4) + x = root; // x is now the root (case 4) + } + } + } + x->color = false; // color x (the root) BLACK (exit) + + return; +} + +// ******** Rotation Functions ******************************************* + +void splittree::rotateLeft(elementsp *x) { + elementsp *y; + // do pointer-swapping operations for left-rotation + y = x->right; // grab right child + x->right = y->left; // make x's RIGHT-CHILD be y's LEFT-CHILD + y->left->parent = x; // make x be y's LEFT-CHILD's parent + y->parent = x->parent; // make y's new parent be x's old parent + + if (x->parent == NULL) { + root = y; // if x was root, make y root + } else { // + if (x == x->parent->left) { // if x is LEFT-CHILD, make y be x's parent's + x->parent->left = y; // left-child + } else { + x->parent->right = y; // right-child + } + } + y->left = x; // make x be y's LEFT-CHILD + x->parent = y; // make y be x's parent + + return; +} + +void splittree::rotateRight(elementsp *y) { + elementsp *x; + // do pointer-swapping operations for right-rotation + x = y->left; // grab left child + y->left = x->right; // replace left child yith x's right subtree + x->right->parent = y; // replace y as x's right subtree's parent + + x->parent = y->parent; // make x's new parent be y's old parent + if (y->parent == NULL) { + root = x; // if y was root, make x root + } else { + if (y == y->parent->right) { // if y is R-CHILD, make x be y's parent's + y->parent->right = x; // right-child + } else { + y->parent->left = x; // left-child + } + } + x->right = y; // make y be x's RIGHT-CHILD + y->parent = x; // make x be y's parent + + return; +} + +// *********************************************************************** +// *** COPYRIGHT NOTICE ************************************************** +// graph_simp.h - graph data structure +// Copyright (C) 2006-2008 Aaron Clauset +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.gnu.org/licenses/gpl.txt for more details. +// +// *********************************************************************** +// Author : Aaron Clauset ( aaronc@santafe.edu | +// http://www.santafe.edu/~aaronc/ ) +// Collaborators: Cristopher Moore and Mark E.J. Newman +// Project : Hierarchical Random Graphs +// Location : University of New Mexico, Dept. of Computer Science +// AND Santa Fe Institute +// Created : 21 June 2006 +// Modified : 23 December 2007 (cleaned up for public consumption) +// +// ************************************************************************ + +// ******** Constructor / Destructor ************************************* + +simpleGraph::simpleGraph(const int size): n(size), m(0), num_groups(0) { + nodes = new simpleVert [n]; + nodeLink = new simpleEdge* [n]; + nodeLinkTail = new simpleEdge* [n]; + A = new double* [n]; + for (int i = 0; i < n; i++) { + nodeLink[i] = NULL; nodeLinkTail[i] = NULL; + A[i] = new double [n]; + for (int j = 0; j < n; j++) { + A[i][j] = 0.0; + } + } + E = NULL; +} + +simpleGraph::~simpleGraph() { + simpleEdge *curr, *prev; + for (int i = 0; i < n; i++) { + curr = nodeLink[i]; + delete [] A[i]; + while (curr != NULL) { + prev = curr; + curr = curr->next; + delete prev; + } + } + curr = NULL; prev = NULL; + if (E != NULL) { + delete [] E; + E = NULL; + } + delete [] A; A = NULL; + delete [] nodeLink; nodeLink = NULL; + delete [] nodeLinkTail; nodeLinkTail = NULL; + delete [] nodes; nodes = NULL; +} + +// *********************************************************************** + +bool simpleGraph::addGroup(const int i, const int group_index) { + if (i >= 0 && i < n) { + nodes[i].group_true = group_index; + return true; + } else { + return false; + } +} + +// *********************************************************************** + +bool simpleGraph::addLink(const int i, const int j) { + // Adds the directed edge (i,j) to the adjacency list for v_i + simpleEdge* newedge; + if (i >= 0 && i < n && j >= 0 && j < n) { + A[i][j] = 1.0; + newedge = new simpleEdge; + newedge->x = j; + if (nodeLink[i] == NULL) { // first neighbor + nodeLink[i] = newedge; + nodeLinkTail[i] = newedge; + nodes[i].degree = 1; + } else { // subsequent neighbor + nodeLinkTail[i]->next = newedge; + nodeLinkTail[i] = newedge; + nodes[i].degree++; + } + m++; // increment edge count + newedge = NULL; + return true; + } else { + return false; + } +} + +// *********************************************************************** + +bool simpleGraph::doesLinkExist(const int i, const int j) { + // This function determines if the edge (i,j) already exists in the + // adjacency list of v_i + if (i >= 0 && i < n && j >= 0 && j < n) { + if (A[i][j] > 0.1) { + return true; + } else { + return false; + } + } else { + return false; + } +} + +// ********************************************************************** + +double simpleGraph::getAdjacency(const int i, const int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + return A[i][j]; + } else { + return -1.0; + } +} + +int simpleGraph::getDegree(const int i) { + if (i >= 0 && i < n) { + return nodes[i].degree; + } else { + return -1; + } +} + +int simpleGraph::getGroupLabel(const int i) { + if (i >= 0 && i < n) { + return nodes[i].group_true; + } else { + return -1; + } +} + +string simpleGraph::getName(const int i) { + if (i >= 0 && i < n) { + return nodes[i].name; + } else { + return ""; + } +} + +// NOTE: The following three functions return addresses; deallocation +// of returned object is dangerous +simpleEdge* simpleGraph::getNeighborList(const int i) { + if (i >= 0 && i < n) { + return nodeLink[i]; + } else { + return NULL; + } +} +// END-NOTE + +// ********************************************************************* + +int simpleGraph::getNumGroups() { + return num_groups; +} +int simpleGraph::getNumLinks() { + return m; +} +int simpleGraph::getNumNodes() { + return n; +} +simpleVert* simpleGraph::getNode(const int i) { + if (i >= 0 && i < n) { + return &nodes[i]; + } else { + return NULL; + } +} + +// ********************************************************************** + +bool simpleGraph::setName(const int i, const string text) { + if (i >= 0 && i < n) { + nodes[i].name = text; + return true; + } else { + return false; + } +} + +// ********************************************************************** + +void simpleGraph::QsortMain (block* array, int left, int right) { + if (right > left) { + int pivot = left; + int part = QsortPartition(array, left, right, pivot); + QsortMain(array, left, part - 1); + QsortMain(array, part + 1, right ); + } + return; +} + +int simpleGraph::QsortPartition (block* array, int left, int right, + int index) { + block p_value, temp; + p_value.x = array[index].x; + p_value.y = array[index].y; + + // swap(array[p_value], array[right]) + temp.x = array[right].x; + temp.y = array[right].y; + array[right].x = array[index].x; + array[right].y = array[index].y; + array[index].x = temp.x; + array[index].y = temp.y; + + int stored = left; + for (int i = left; i < right; i++) { + if (array[i].x <= p_value.x) { + // swap(array[stored], array[i]) + temp.x = array[i].x; + temp.y = array[i].y; + array[i].x = array[stored].x; + array[i].y = array[stored].y; + array[stored].x = temp.x; + array[stored].y = temp.y; + stored++; + } + } + // swap(array[right], array[stored]) + temp.x = array[stored].x; + temp.y = array[stored].y; + array[stored].x = array[right].x; + array[stored].y = array[right].y; + array[right].x = temp.x; + array[right].y = temp.y; + + return stored; +} + +// *********************************************************************** diff --git a/src/rigraph/core/hrg/rbtree.h b/src/rigraph/core/hrg/rbtree.h new file mode 100644 index 0000000..85f4ee7 --- /dev/null +++ b/src/rigraph/core/hrg/rbtree.h @@ -0,0 +1,160 @@ +/* -*- mode: C++ -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +// **************************************************************************************************** +// *** COPYRIGHT NOTICE ******************************************************************************* +// rbtree - red-black tree (self-balancing binary tree data structure) +// Copyright (C) 2004 Aaron Clauset +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.gnu.org/licenses/gpl.txt for more details. +// +// **************************************************************************************************** +// Author : Aaron Clauset ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ ) +// Collaborators: Cristopher Moore and Mark Newman +// Project : Hierarchical Random Graphs +// Location : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute +// Created : Spring 2004 +// Modified : many, many times +// +// **************************************************************************************************** + +#ifndef IGRAPH_HRG_RBTREE +#define IGRAPH_HRG_RBTREE + +namespace fitHRG { + +// ******** Basic Structures ********************************************* + +#ifndef IGRAPH_HRG_LIST +#define IGRAPH_HRG_LIST + +class list { +public: + int x; // stored elementd in linked-list + list* next; // pointer to next elementd + list(): x(-1), next(0) { } + ~list() { } +}; +#endif + +class keyValuePair { +public: + int x; // elementrb key (int) + int y; // stored value (int) + keyValuePair* next; // linked-list pointer + keyValuePair(): x(-1), y(-1), next(0) { } + ~keyValuePair() { } +}; + +// ******** Tree elementrb Class ***************************************** + +class elementrb { +public: + int key; // search key (int) + int value; // stored value (int) + + bool color; // F: BLACK, T: RED + short int mark; // marker + + elementrb *parent; // pointer to parent node + elementrb *left; // pointer for left subtree + elementrb *right; // pointer for right subtree + + elementrb(): key(-1), value(-1), color(false), mark(0), parent(0), + left(0), right(0) { } + ~elementrb() { } +}; + +// ******** Red-Black Tree Class ***************************************** +// This vector implementation is a red-black balanced binary tree data +// structure. It provides find a stored elementrb in time O(log n), +// find the maximum elementrb in time O(1), delete an elementrb in +// time O(log n), and insert an elementrb in time O(log n). +// +// Note that the key=0 is assumed to be a special value, and thus you +// cannot insert such an item. Beware of this limitation. + +class rbtree { +private: + elementrb* root; // binary tree root + elementrb* leaf; // all leaf nodes + int support; // number of nodes in the tree + + void rotateLeft(elementrb *x); // left-rotation operator + void rotateRight(elementrb *y); // right-rotation operator + void insertCleanup(elementrb *z); // house-keeping after insertion + void deleteCleanup(elementrb *x); // house-keeping after deletion + keyValuePair* returnSubtreeAsList(elementrb *z, keyValuePair *head); + void deleteSubTree(elementrb *z); // delete subtree rooted at z + elementrb* returnMinKey(elementrb *z); // returns minimum of subtree + // rooted at z + elementrb* returnSuccessor(elementrb *z); // returns successor of z's key + +public: + rbtree(); ~rbtree(); // default constructor/destructor + + // returns value associated with searchKey + int returnValue(const int searchKey); + // returns T if searchKey found, and points foundNode at the + // corresponding node + elementrb* findItem(const int searchKey); + // insert a new key with stored value + void insertItem(int newKey, int newValue); + // selete a node with given key + void deleteItem(int killKey); + // replace value of a node with given key + void replaceItem(int key, int newValue); + // increment the value of the given key + void incrementValue(int key); + // delete the entire tree + void deleteTree(); + // return array of keys in tree + int* returnArrayOfKeys(); + // return list of keys in tree + list* returnListOfKeys(); + // return the tree as a list of keyValuePairs + keyValuePair* returnTreeAsList(); + // returns the maximum key in the tree + keyValuePair returnMaxKey(); + // returns the minimum key in the tree + keyValuePair returnMinKey(); + // returns number of items in tree + int returnNodecount(); +}; + +} +#endif diff --git a/src/rigraph/core/hrg/splittree_eq.h b/src/rigraph/core/hrg/splittree_eq.h new file mode 100644 index 0000000..8acab77 --- /dev/null +++ b/src/rigraph/core/hrg/splittree_eq.h @@ -0,0 +1,183 @@ +/* -*- mode: C++ -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +// **************************************************************************************************** +// *** COPYRIGHT NOTICE ******************************************************************************* +// splittree_eq.h - a binary search tree data structure for storing dendrogram split frequencies +// Copyright (C) 2006-2008 Aaron Clauset +// +// 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 2 of the License, or +// (at your option) 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.gnu.org/licenses/gpl.txt for more details. +// +// **************************************************************************************************** +// Author : Aaron Clauset ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ ) +// Collaborators: Cristopher Moore and Mark E.J. Newman +// Project : Hierarchical Random Graphs +// Location : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute +// Created : 19 April 2006 +// Modified : 19 May 2007 +// : 20 May 2008 (cleaned up for public consumption) +// +// *********************************************************************** +// +// Data structure for storing the split frequences in the sampled +// dendrograms. Data is stored efficiently as a red-black binary +// search tree (this is a modified version of the rbtree.h file). +// +// *********************************************************************** + +#ifndef IGRAPH_HRG_SPLITTREE +#define IGRAPH_HRG_SPLITTREE + +#include + +namespace fitHRG { + +// ******** Basic Structures ********************************************* + +#ifndef IGRAPH_HRG_SLIST +#define IGRAPH_HRG_SLIST +class slist { +public: + std::string x; // stored elementd in linked-list + slist* next; // pointer to next elementd + slist(): x(""), next(0) { } + ~slist() { } +}; +#endif + +class keyValuePairSplit { +public: + std::string x; // elementsp split (string) + double y; // stored weight (double) + int c; // stored count (int) + keyValuePairSplit* next; // linked-list pointer + keyValuePairSplit(): x(""), y(0.0), c(0), next(0) { } + ~keyValuePairSplit() { } +}; + +// ******** Tree elementsp Class ***************************************** + +class elementsp { +public: + std::string split; // split represented as a string + double weight; // total weight of this split + int count; // number of observations of this split + + bool color; // F: BLACK, T: RED + short int mark; // marker + + elementsp *parent; // pointer to parent node + elementsp *left; // pointer for left subtree + elementsp *right; // pointer for right subtree + + elementsp(): split(""), weight(0.0), count(0), color(false), mark(0), + parent(0), left(0), right(0) { } + ~elementsp() { } +}; + +// ******** Red-Black Tree Class ***************************************** +// This vector implementation is a red-black balanced binary tree data +// structure. It provides find a stored elementsp in time O(log n), +// find the maximum elementsp in time O(1), delete an elementsp in +// time O(log n), and insert an elementsp in time O(log n). +// +// Note that the split="" is assumed to be a special value, and thus +// you cannot insert such an item. Beware of this limitation. +// + +class splittree { +private: + elementsp* root; // binary tree root + elementsp* leaf; // all leaf nodes + int support; // number of nodes in the tree + double total_weight; // total weight stored + int total_count; // total number of observations stored + + // left-rotation operator + void rotateLeft(elementsp*); + // right-rotation operator + void rotateRight(elementsp*); + // house-keeping after insertion + void insertCleanup(elementsp*); + // house-keeping after deletion + void deleteCleanup(elementsp*); + keyValuePairSplit* returnSubtreeAsList(elementsp*, keyValuePairSplit*); + // delete subtree rooted at z + void deleteSubTree(elementsp*); + // returns minimum of subtree rooted at z + elementsp* returnMinKey(elementsp*); + // returns successor of z's key + elementsp* returnSuccessor(elementsp*); + +public: + // default constructor/destructor + splittree(); ~splittree(); + // returns value associated with searchKey + double returnValue(const std::string); + // returns T if searchKey found, and points foundNode at the + // corresponding node + elementsp* findItem(const std::string); + // update total_count and total_weight + void finishedThisRound(); + // insert a new key with stored value + bool insertItem(std::string, double); + void clearTree(); + // delete a node with given key + void deleteItem(std::string); + // delete the entire tree + void deleteTree(); + // return array of keys in tree + std::string* returnArrayOfKeys(); + // return list of keys in tree + slist* returnListOfKeys(); + // return the tree as a list of keyValuePairSplits + keyValuePairSplit* returnTreeAsList(); + // returns the maximum key in the tree + keyValuePairSplit returnMaxKey(); + // returns the minimum key in the tree + keyValuePairSplit returnMinKey(); + // returns number of items in tree + int returnNodecount(); + // returns list of splits with given number of Ms + keyValuePairSplit* returnTheseSplits(const int); + // returns sum of stored values + double returnTotal(); +}; + +} // namespace fitHRG + +#endif diff --git a/src/rigraph/core/internal/glpk_support.c b/src/rigraph/core/internal/glpk_support.c new file mode 100644 index 0000000..341df62 --- /dev/null +++ b/src/rigraph/core/internal/glpk_support.c @@ -0,0 +1,170 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "glpk_support.h" + +#ifdef HAVE_GLPK + +#include "igraph_error.h" + +#include "../core/interruption.h" + +#include + +IGRAPH_THREAD_LOCAL igraph_i_glpk_error_info_t igraph_i_glpk_error_info; + +/* glp_at_error() was added in GLPK 4.57. Due to the R interface, we need to + * support ancient GLPK versions like GLPK 4.38 so we need to guard the + * invocation of glp_at_error(). Note that this is a temporary workaround only + * for sake of supporting R 4.1, so it is enabled only if USING_R is defined */ +#ifdef USING_R +# define HAS_GLP_AT_ERROR (GLP_MAJOR_VERSION > 4 || (GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION >= 57)) +#else +# define HAS_GLP_AT_ERROR 1 +#endif + +int igraph_i_glpk_terminal_hook(void *info, const char *s) { + IGRAPH_UNUSED(info); + + if (igraph_i_interruption_handler && + !igraph_i_glpk_error_info.is_interrupted && + igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) { + /* If an interruption has already occurred, do not set another error, + to avoid an infinite loop between the term_hook (this function) + and the error_hook. */ + igraph_i_glpk_error_info.is_interrupted = 1; + glp_error("GLPK was interrupted."); /* This dummy message is never printed */ +#if HAS_GLP_AT_ERROR + } else if (glp_at_error()) { + /* Copy the error messages into a buffer for later reporting */ + /* We must use glp_at_error() instead of igraph_i_glpk_error_info.is_error + * to determine if a message is an error message, as the reporting function is + * called before the error function. */ + const size_t n = sizeof(igraph_i_glpk_error_info.msg) / sizeof(char) - 1; + while (*s != '\0' && igraph_i_glpk_error_info.msg_ptr < igraph_i_glpk_error_info.msg + n) { + *(igraph_i_glpk_error_info.msg_ptr++) = *(s++); + } + *igraph_i_glpk_error_info.msg_ptr = '\0'; +#endif + } + + return 1; /* Non-zero return value signals to GLPK not to print to the terminal */ +} + +void igraph_i_glpk_error_hook(void *info) { + IGRAPH_UNUSED(info); + igraph_i_glpk_error_info.is_error = 1; + glp_free_env(); + longjmp(igraph_i_glpk_error_info.jmp, 1); +} + +void igraph_i_glpk_interruption_hook(glp_tree *tree, void *info) { + IGRAPH_UNUSED(info); + + /* This is a callback function meant to be used with glp_intopt(), + in order to support interruption. It is essentially a GLPK-compatible + replacement for IGRAPH_ALLOW_INTERRUPTION(). + Calling glp_ios_terminate() from glp_intopt()'s callback function + signals to GLPK that it should terminate the optimization and return + with the code GLP_ESTOP. + */ + if (igraph_i_interruption_handler) { + if (igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) { + glp_ios_terminate(tree); + } + } +} + +/** + * \ingroup internal + * \function igraph_i_glp_delete_prob + * \brief Safe replacement for glp_delete_prob(). + * + * This function is meant to be used with IGRAPH_FINALLY() + * in conjunction with glp_create_prob(). + * + * When using GLPK, normally glp_delete_prob() is used to free + * problems created with glp_create_prob(). However, when GLPK + * encounters an error, the error handler installed by igraph + * will call glp_free_env() which invalidates all problems. + * Calling glp_delete_prob() would then lead to a crash. + * This replacement function avoids this situation by first + * checking if GLPK is at an error state. + */ +void igraph_i_glp_delete_prob(glp_prob *p) { + if (! igraph_i_glpk_error_info.is_error) { + glp_delete_prob(p); + } +} + +int igraph_i_glpk_check(int retval, const char* message) { + char* code = "none"; + char message_and_code[4096]; + + if (retval == IGRAPH_SUCCESS) { + return IGRAPH_SUCCESS; + } + + /* handle errors */ +#define HANDLE_CODE(c) case c: code = #c; retval = IGRAPH_##c; break; +#define HANDLE_CODE2(c) case c: code = #c; retval = IGRAPH_FAILURE; break; +#define HANDLE_CODE3(c) case c: code = #c; retval = IGRAPH_INTERRUPTED; break; + switch (retval) { + HANDLE_CODE(GLP_EBOUND); + HANDLE_CODE(GLP_EROOT); + HANDLE_CODE(GLP_ENOPFS); + HANDLE_CODE(GLP_ENODFS); + HANDLE_CODE(GLP_EFAIL); + HANDLE_CODE(GLP_EMIPGAP); + HANDLE_CODE(GLP_ETMLIM); + + HANDLE_CODE3(GLP_ESTOP); + + HANDLE_CODE2(GLP_EBADB); + HANDLE_CODE2(GLP_ESING); + HANDLE_CODE2(GLP_ECOND); + HANDLE_CODE2(GLP_EOBJLL); + HANDLE_CODE2(GLP_EOBJUL); + HANDLE_CODE2(GLP_EITLIM); + + default: + IGRAPH_ERROR("Unknown GLPK error", IGRAPH_FAILURE); + } +#undef HANDLE_CODE +#undef HANDLE_CODE2 +#undef HANDLE_CODE3 + + sprintf(message_and_code, "%s (%s)", message, code); + IGRAPH_ERROR(message_and_code, retval); +} + +#else + +int igraph_glpk_dummy() { + /* get rid of "ISO C requires a translation unit to contain at least one + * declaration" warning */ + return 'd' + 'u' + 'm' + 'm' + 'y'; +} + +#endif diff --git a/src/rigraph/core/internal/glpk_support.h b/src/rigraph/core/internal/glpk_support.h new file mode 100644 index 0000000..9d0ae73 --- /dev/null +++ b/src/rigraph/core/internal/glpk_support.h @@ -0,0 +1,141 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_GLPK_SUPPORT_H +#define IGRAPH_GLPK_SUPPORT_H + +#include "config.h" + +/* Note: only files calling the GLPK routines directly need to + include this header. +*/ + +#ifdef HAVE_GLPK + +#include +#include + +typedef struct igraph_i_glpk_error_info_s { + jmp_buf jmp; /* used for bailing when there is a GLPK error */ + int is_interrupted; /* Boolean; true if there was an interruption */ + int is_error; /* Boolean; true if the error hook was called */ + char msg[4096]; /* GLPK error messages are collected here */ + char *msg_ptr; /* Points to the end (null terminator) of msg */ +} igraph_i_glpk_error_info_t; + +extern IGRAPH_THREAD_LOCAL igraph_i_glpk_error_info_t igraph_i_glpk_error_info; + +int igraph_i_glpk_check(int retval, const char* message); +void igraph_i_glpk_interruption_hook(glp_tree *tree, void *info); +void igraph_i_glpk_error_hook(void *info); +int igraph_i_glpk_terminal_hook(void *info, const char *s); +void igraph_i_glp_delete_prob(glp_prob *p); + +#define IGRAPH_GLPK_CHECK(func, message) do { \ + int igraph_i_ret = igraph_i_glpk_check(func, message); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != 0)) { \ + return igraph_i_ret; \ + } } while (0) + +/** + * \ingroup internal + * \define IGRAPH_GLPK_SETUP + * + * Use this macro at the start of igraph functions that use GLPK routines + * directly. + * + * - IGRAPH_GLPK_SETUP() must be called in all top-level functions that + * use GLPK, before beginning to use any GLPK functions. + * + * - Do NOT call glp_term_out(OFF) as interruption support relies on + * the terminal hook being called. + * + * - This must be a macro and not a function, as jumping into a function + * that has already returned with longjmp() is not possible. + * + * This setup step is necessary in order to support interruption, as + * well as to handle fatal GLPK errors gracefully. See here for details: + * + * https://lists.gnu.org/archive/html/help-glpk/2019-10/msg00000.html + * + * Interruption support for GLPK is essential because it is practically + * impossible to predict how long it will take to solve a problem. It + * may take less than a second or it may never finish in practice. + * + * It does the following: + * + * - Initialize the data structure where we keep track of GLPK's current + * error and interruption state, \c igraph_i_glpk_error_info. + * - Set an error hook and a terminal hook for GLPK. + * - Provide a return point for the longjmp() called from the error hook. + * + * There are two interruption mechanisms we can use with GLPK. glp_intopt() + * supports a callback function which can signal a request for interruption. + * However, glp_intopt() internally calls glp_simplex(), which may again + * take a very long time. + * + * The recommended way to interrupt glp_simplex() is to check for interruption + * from the terminal hook, which is normally meant for intercepting output. + * This interruption is possible only as often as there is output, which may + * be at intervals of a few seconds in practice. + * + * Interruption is achieved by setting an error with glp_error(), which + * triggers a call to the error hook. From the error hook, we free all + * GLPK resources using glp_free_env() and do a longjmp(). + * + * The use of these mechanisms makes it unsafe to use igraph's GLPK-reliant + * functions from a process which also uses GLPK for other purposes. + * To avoid this problem, GLPK should ideally be linked to igraph statically. + */ +#define IGRAPH_GLPK_SETUP() \ + do { \ + glp_error_hook(igraph_i_glpk_error_hook, NULL); \ + glp_term_hook(igraph_i_glpk_terminal_hook, NULL); \ + igraph_i_glpk_error_info.is_interrupted = 0; \ + igraph_i_glpk_error_info.is_error = 0; \ + igraph_i_glpk_error_info.msg_ptr = igraph_i_glpk_error_info.msg; \ + if (setjmp(igraph_i_glpk_error_info.jmp)) { \ + if (igraph_i_glpk_error_info.is_interrupted) { \ + return IGRAPH_INTERRUPTED; \ + } else { \ + if (igraph_i_glpk_error_info.msg_ptr != igraph_i_glpk_error_info.msg) { \ + while ( *(igraph_i_glpk_error_info.msg_ptr - 1) == '\n' && \ + igraph_i_glpk_error_info.msg_ptr > igraph_i_glpk_error_info.msg ) { \ + igraph_i_glpk_error_info.msg_ptr--; \ + } \ + *igraph_i_glpk_error_info.msg_ptr = '\0'; \ + igraph_error(igraph_i_glpk_error_info.msg, IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EGLP); \ + } else if (igraph_i_glpk_error_info.is_error) { \ + /* This branch can never be reached unless compiled with USING_R and using */ \ + /* the hack to support pre-4.57 GLPK versions. See comments in glpk_support.c. */ \ + igraph_error("Error while running GLPK solver.", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EGLP); \ + } \ + return IGRAPH_EGLP; \ + } \ + } \ + } while (0) + +#endif + +#endif diff --git a/src/rigraph/core/internal/gmp_internal.h b/src/rigraph/core/internal/gmp_internal.h new file mode 100644 index 0000000..e7f6b7e --- /dev/null +++ b/src/rigraph/core/internal/gmp_internal.h @@ -0,0 +1,34 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2020 The igraph development team + + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_GMP_H +#define IGRAPH_GMP_H + +#include "config.h" + +#ifdef INTERNAL_GMP +#include "mini-gmp/mini-gmp.h" +#else +#include +#endif + +#endif diff --git a/src/rigraph/core/internal/hacks.c b/src/rigraph/core/internal/hacks.c new file mode 100644 index 0000000..951189a --- /dev/null +++ b/src/rigraph/core/internal/hacks.c @@ -0,0 +1,53 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +//#include "hacks.h" + +#include +#include + +/* These are implementations of common C functions that may be missing from some + * compilers; for instance, icc does not provide stpcpy so we implement it + * here. */ + +/** + * Drop-in replacement for strdup. + * Used only in compilers that do not have strdup or _strdup + */ +char* igraph_i_strdup(const char *s) { + size_t n = strlen(s) + 1; + char* result = (char*)malloc(sizeof(char) * n); + if (result) { + memcpy(result, s, n); + } + return result; +} + +/** + * Drop-in replacement for stpcpy. + * Used only in compilers that do not have stpcpy + */ +char* igraph_i_stpcpy(char* s1, const char* s2) { + char* result = strcpy(s1, s2); + return result + strlen(s1); +} diff --git a/src/rigraph/core/internal/hacks.h b/src/rigraph/core/internal/hacks.h new file mode 100644 index 0000000..559ca40 --- /dev/null +++ b/src/rigraph/core/internal/hacks.h @@ -0,0 +1,61 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2003-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_HACKS_INTERNAL_H +#define IGRAPH_HACKS_INTERNAL_H + +#include "config.h" + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus + #define __BEGIN_DECLS extern "C" { + #define __END_DECLS } +#else + #define __BEGIN_DECLS /* empty */ + #define __END_DECLS /* empty */ +#endif + +__BEGIN_DECLS + +#ifndef HAVE_STRDUP + #define strdup igraph_i_strdup + char* igraph_i_strdup(const char *s); +#endif + +#ifndef HAVE_STPCPY + #define stpcpy igraph_i_stpcpy + char* igraph_i_stpcpy(char* s1, const char* s2); +#endif + +#ifndef HAVE_STRCASECMP + #ifdef HAVE__STRICMP + #define strcasecmp _stricmp + #else + #error "igraph needs strcasecmp() or _stricmp()" + #endif +#endif + +__END_DECLS + +#endif diff --git a/src/rigraph/core/internal/lsap.c b/src/rigraph/core/internal/lsap.c new file mode 100644 index 0000000..bdbe96c --- /dev/null +++ b/src/rigraph/core/internal/lsap.c @@ -0,0 +1,672 @@ + +#include "igraph_lsap.h" +#include "igraph_error.h" + +/* #include */ +#include +#include /* INT_MAX */ +#include /* DBL_MAX */ +#include + +/* constants used for improving readability of code */ + +#define COVERED 1 +#define UNCOVERED 0 +#define ASSIGNED 1 +#define UNASSIGNED 0 +#define TRUE 1 +#define FALSE 0 + +#define MARKED 1 +#define UNMARKED 0 + +#define REDUCE 1 +#define NOREDUCE 0 + +typedef struct { + int n; /* order of problem */ + double **C; /* cost matrix */ + double **c; /* reduced cost matrix */ + int *s; /* assignment */ + int *f; /* column i is assigned to f[i] */ + int na; /* number of assigned items; */ + int runs; /* number of iterations */ + double cost; /* minimum cost */ + time_t rtime; /* time */ +} AP; + +/* public interface */ + +/* constructors and destructor */ +static AP *ap_create_problem(double *t, int n); +/* static AP *ap_create_problem_from_matrix(double **t, int n); */ +/* static AP *ap_read_problem(char *file); */ +static void ap_free(AP *p); + +static int ap_assignment(AP *p, int *res); +/* static int ap_costmatrix(AP *p, double **m); */ +/* static int ap_datamatrix(AP *p, double **m); */ +/* static int ap_iterations(AP *p); */ +static int ap_hungarian(AP *p); +/* static double ap_mincost(AP *p); */ +/* static void ap_print_solution(AP *p); */ +/* static void ap_show_data(AP *p); */ +/* static int ap_size(AP *p); */ +/* static int ap_time(AP *p); */ + +/* error reporting */ +/* static void ap_error(char *message); */ + +/* private functions */ +static void preprocess(AP *p); +static void preassign(AP *p); +static int cover(AP *p, int *ri, int *ci); +static void reduce(AP *p, int *ri, int *ci); + +int ap_hungarian(AP *p) { + int n; /* size of problem */ + int *ri; /* covered rows */ + int *ci; /* covered columns */ + time_t start, end; /* timer */ + int i, j, ok; + + start = time(0); + + n = p->n; + p->runs = 0; + + /* allocate memory */ + p->s = calloc(1 + n, sizeof(int)); + p->f = calloc(1 + n, sizeof(int)); + + ri = calloc(1 + n, sizeof(int)); + ci = calloc(1 + n, sizeof(int)); + + if (ri == NULL || ci == NULL || p->s == NULL || p->f == NULL) { + IGRAPH_ERROR("ap_hungarian: could not allocate memory", IGRAPH_ENOMEM); + } + + preprocess(p); + preassign(p); + + while (p->na < n) { + if (REDUCE == cover(p, ri, ci)) { + reduce(p, ri, ci); + } + ++p->runs; + } + + end = time(0); + + p->rtime = end - start; + + /* check if assignment is a permutation of (1..n) */ + for (i = 1; i <= n; i++) { + ok = 0; + for (j = 1; j <= n; j++) + if (p->s[j] == i) { + ++ok; + } + if (ok != 1) + IGRAPH_ERROR("ap_hungarian: error in assignment, is not a permutation", + IGRAPH_EINVAL); + } + + /* calculate cost of assignment */ + p->cost = 0; + for (i = 1; i <= n; i++) { + p->cost += p->C[i][p->s[i]]; + } + + /* reset result back to base-0 indexing */ + for (i = 1; i <= n; i++) { + p->s[i - 1] = p->s[i] - 1; + } + + /* free memory */ + + free(ri); + free(ci); + + return 0; +} + +/* abbreviated interface */ +int ap_assignment(AP *p, int *res) { + int i; + + if (p->s == NULL) { + ap_hungarian(p); + } + + for (i = 0; i < p->n; i++) { + res[i] = p->s[i]; + } + + return p->n; +} + + +/*******************************************************************/ +/* constructors */ +/* read data from file */ +/*******************************************************************/ + +#if 0 +AP *ap_read_problem(char *file) { + FILE *f; + int i, j, c; + int m, n; + double x; + double **t; + int nrow, ncol; + AP *p; + + f = fopen(file, "r"); + if (f == NULL) { + return NULL; + } + + t = (double **)malloc(sizeof(double*)); + + m = 0; + n = 0; + + nrow = 0; + ncol = 0; + + while (EOF != (i = fscanf(f, "%lf", &x))) { + if (i == 1) { + if (n == 0) { + t = (double **) realloc(t, (m + 1) * sizeof(double *)); + t[m] = (double *) malloc(sizeof(double)); + } else { + t[m] = (double *) realloc(t[m], (n + 1) * sizeof(double)); + } + + t[m][n++] = x; + + ncol = (ncol < n) ? n : ncol; + c = fgetc(f); + if (c == '\n') { + n = 0; + ++m; + nrow = (nrow < m) ? m : nrow; + } + } + } + fclose(f); + + /* prepare data */ + + if (nrow != ncol) { + /* + fprintf(stderr,"ap_read_problem: problem not quadratic\nrows =%d, cols = %d\n",nrow,ncol); + */ + IGRAPH_WARNINGF("ap_read_problem: problem not quadratic; rows = %d, cols = %d.", nrow, ncol); + return NULL; + } + + p = (AP*) malloc(sizeof(AP)); + p->n = ncol; + + p->C = (double **) malloc((1 + nrow) * sizeof(double *)); + p->c = (double **) malloc((1 + nrow) * sizeof(double *)); + if (p->C == NULL || p->c == NULL) { + return NULL; + } + + for (i = 1; i <= nrow; i++) { + p->C[i] = (double *) calloc(ncol + 1, sizeof(double)); + p->c[i] = (double *) calloc(ncol + 1, sizeof(double)); + if (p->C[i] == NULL || p->c[i] == NULL) { + return NULL; + } + } + + for (i = 1; i <= nrow; i++) + for ( j = 1; j <= ncol; j++) { + p->C[i][j] = t[i - 1][j - 1]; + p->c[i][j] = t[i - 1][j - 1]; + } + + for (i = 0; i < nrow; i++) { + free(t[i]); + } + free(t); + + p->cost = 0; + p->s = NULL; + p->f = NULL; + return p; +} +#endif + +#if 0 +AP *ap_create_problem_from_matrix(double **t, int n) { + int i, j; + AP *p; + + p = (AP*) malloc(sizeof(AP)); + if (p == NULL) { + return NULL; + } + + p->n = n; + + p->C = (double **) malloc((n + 1) * sizeof(double *)); + p->c = (double **) malloc((n + 1) * sizeof(double *)); + if (p->C == NULL || p->c == NULL) { + return NULL; + } + + for (i = 1; i <= n; i++) { + p->C[i] = (double *) calloc(n + 1, sizeof(double)); + p->c[i] = (double *) calloc(n + 1, sizeof(double)); + if (p->C[i] == NULL || p->c[i] == NULL) { + return NULL; + } + } + + + for (i = 1; i <= n; i++) + for ( j = 1; j <= n; j++) { + p->C[i][j] = t[i - 1][j - 1]; + p->c[i][j] = t[i - 1][j - 1]; + } + p->cost = 0; + p->s = NULL; + p->f = NULL; + return p; +} +#endif + +/* read data from vector */ +AP *ap_create_problem(double *t, int n) { + int i, j; + AP *p; + + p = (AP*) malloc(sizeof(AP)); + if (p == NULL) { + return NULL; + } + + p->n = n; + + p->C = (double **) malloc((n + 1) * sizeof(double *)); + p->c = (double **) malloc((n + 1) * sizeof(double *)); + if (p->C == NULL || p->c == NULL) { + return NULL; + } + + for (i = 1; i <= n; i++) { + p->C[i] = (double *) calloc(n + 1, sizeof(double)); + p->c[i] = (double *) calloc(n + 1, sizeof(double)); + if (p->C[i] == NULL || p->c[i] == NULL) { + return NULL; + } + } + + + for (i = 1; i <= n; i++) + for ( j = 1; j <= n; j++) { + p->C[i][j] = t[n * (j - 1) + i - 1]; + p->c[i][j] = t[n * (j - 1) + i - 1]; + } + p->cost = 0; + p->s = NULL; + p->f = NULL; + return p; +} + +/* destructor */ +void ap_free(AP *p) { + int i; + + free(p->s); + free(p->f); + + for (i = 1; i <= p->n; i++) { + free(p->C[i]); + free(p->c[i]); + } + + free(p->C); + free(p->c); + free(p); +} + +/* set + get functions */ + +/* +void ap_show_data(AP *p) +{ + int i, j; + + for(i = 1; i <= p->n; i++){ + for(j = 1; j <= p->n; j++) + printf("%6.2f ", p->c[i][j]); + printf("\n"); + } +} + +double ap_mincost(AP *p) { + if (p->s == NULL) { + ap_hungarian(p); + } + + return p->cost; +} + +int ap_size(AP *p) { + return p->n; +} + +int ap_time(AP *p) { + return (int) p->rtime; +} + +int ap_iterations(AP *p) { + return p->runs; +} + +void ap_print_solution(AP *p) +{ + int i; + + printf("%d itertations, %d secs.\n",p->runs, (int)p->rtime); + printf("Min Cost: %10.4f\n",p->cost); + + for(i = 0; i < p->n; i++) + printf("%4d",p->s[i]); + printf("\n"); +} + +int ap_costmatrix(AP *p, double **m) { + int i, j; + + for (i = 0; i < p->n; i++) + for (j = 0; j < p->n; j++) { + m[i][j] = p->C[i + 1][j + 1]; + } + + return p->n; +} + +int ap_datamatrix(AP *p, double **m) { + int i, j; + + for (i = 0; i < p->n; i++) + for (j = 0; j < p->n; j++) { + m[i][j] = p->c[i + 1][j + 1]; + } + + return p->n; +} +*/ + +/* error reporting */ + +/* +void ap_error(char *message) +{ + fprintf(stderr,"%s\n",message); + exit(1); +} +*/ + +/*************************************************************/ +/* these functions are used internally */ +/* by ap_hungarian */ +/*************************************************************/ + +int cover(AP *p, int *ri, int *ci) { + int *mr, i, r; + int n; + + n = p->n; + mr = calloc(1 + p->n, sizeof(int)); + + /* reset cover indices */ + for (i = 1; i <= n; i++) { + if (p->s[i] == UNASSIGNED) { + ri[i] = UNCOVERED; + mr[i] = MARKED; + } else { + ri[i] = COVERED; + } + ci[i] = UNCOVERED; + } + + while (TRUE) { + /* find marked row */ + r = 0; + for (i = 1; i <= n; i++) + if (mr[i] == MARKED) { + r = i; + break; + } + + if (r == 0) { + break; + } + for (i = 1; i <= n; i++) + if (p->c[r][i] == 0 && ci[i] == UNCOVERED) { + if (p->f[i]) { + ri[p->f[i]] = UNCOVERED; + mr[p->f[i]] = MARKED; + ci[i] = COVERED; + } else { + if (p->s[r] == UNASSIGNED) { + ++p->na; + } + + p->f[p->s[r]] = 0; + p->f[i] = r; + p->s[r] = i; + + free(mr); + return NOREDUCE; + } + } + mr[r] = UNMARKED; + } + free(mr); + return REDUCE; +} + +void reduce(AP *p, int *ri, int *ci) { + int i, j, n; + double min; + + n = p->n; + + /* find minimum in uncovered c-matrix */ + min = DBL_MAX; + for (i = 1; i <= n; i++) + for (j = 1; j <= n; j++) + if (ri[i] == UNCOVERED && ci[j] == UNCOVERED) { + if (p->c[i][j] < min) { + min = p->c[i][j]; + } + } + + /* subtract min from each uncovered element and add it to each element */ + /* which is covered twice */ + for (i = 1; i <= n; i++) + for (j = 1; j <= n; j++) { + if (ri[i] == UNCOVERED && ci[j] == UNCOVERED) { + p->c[i][j] -= min; + } + if (ri[i] == COVERED && ci[j] == COVERED) { + p->c[i][j] += min; + } + } +} + +void preassign(AP *p) { + int i, j, min, r, c, n, count; + int *ri, *ci, *rz, *cz; + + n = p->n; + p->na = 0; + + /* row and column markers */ + ri = calloc(1 + n, sizeof(int)); + ci = calloc(1 + n, sizeof(int)); + + /* row and column counts of zeroes */ + rz = calloc(1 + n, sizeof(int)); + cz = calloc(1 + n, sizeof(int)); + + for (i = 1; i <= n; i++) { + count = 0; + for (j = 1; j <= n; j++) + if (p->c[i][j] == 0) { + ++count; + } + rz[i] = count; + } + + for (i = 1; i <= n; i++) { + count = 0; + for (j = 1; j <= n; j++) + if (p->c[j][i] == 0) { + ++count; + } + cz[i] = count; + } + + while (TRUE) { + /* find unassigned row with least number of zeroes > 0 */ + min = INT_MAX; + r = 0; + for (i = 1; i <= n; i++) + if (rz[i] > 0 && rz[i] < min && ri[i] == UNASSIGNED) { + min = rz[i]; + r = i; + } + /* check if we are done */ + if (r == 0) { + break; + } + + /* find unassigned column in row r with least number of zeroes */ + c = 0; + min = INT_MAX; + for (i = 1; i <= n; i++) + if (p->c[r][i] == 0 && cz[i] < min && ci[i] == UNASSIGNED) { + min = cz[i]; + c = i; + } + + if (c) { + ++p->na; + p->s[r] = c; + p->f[c] = r; + + ri[r] = ASSIGNED; + ci[c] = ASSIGNED; + + /* adjust zero counts */ + cz[c] = 0; + for (i = 1; i <= n; i++) + if (p->c[i][c] == 0) { + --rz[i]; + } + } + } + + /* free memory */ + free(ri); + free(ci); + free(rz); + free(cz); +} + +void preprocess(AP *p) { + int i, j, n; + double min; + + n = p->n; + + /* subtract column minima in each row */ + for (i = 1; i <= n; i++) { + min = p->c[i][1]; + for (j = 2; j <= n; j++) + if (p->c[i][j] < min) { + min = p->c[i][j]; + } + for (j = 1; j <= n; j++) { + p->c[i][j] -= min; + } + } + + /* subtract row minima in each column */ + for (i = 1; i <= n; i++) { + min = p->c[1][i]; + for (j = 2; j <= n; j++) + if (p->c[j][i] < min) { + min = p->c[j][i]; + } + for (j = 1; j <= n; j++) { + p->c[j][i] -= min; + } + } +} + +/** + * \function igraph_solve_lsap + * \brief Solve a balanced linear assignment problem. + * + * This functions solves a linear assinment problem using the Hungarian + * method. A number of tasks, an equal number of agents, and the cost + * of each agent to perform the tasks is given. This function then + * assigns one task to each agent in such a way that the total cost is + * minimized. + * + * + * If the cost should be maximized instead of minimized, the cost matrix + * should be negated. + * + * + * To solve an unbalanced assignment problem, where the number of agents + * is greater than the number of tasks, an extra task with zero cost + * should be added. + * + * \param c The assignment problem, where the number of rows is the + * number of agents, the number of columns is the number of + * tasks, and each element is the cost of an agent to perform + * the task. + * \param n The number of rows and columns of \p c. + * \param p An initialized vector which will store the result. The nth + * entry gives the task the nth agent is assigned to minimize + * the total cost. + * \return Error code. + * + * Time complexity: O(n^3), where n is the number of agents. + */ +int igraph_solve_lsap(igraph_matrix_t *c, igraph_integer_t n, + igraph_vector_int_t *p) { + AP *ap; + + if(n != igraph_matrix_nrow(c)) { + IGRAPH_ERRORF("n (%" IGRAPH_PRId ") " + "not equal to number of agents (%ld).", IGRAPH_EINVAL, + n, igraph_matrix_nrow(c)); + } + if(n != igraph_matrix_ncol(c)) { + IGRAPH_ERRORF("n (%" IGRAPH_PRId ") " + "not equal to number of tasks (%ld).", IGRAPH_EINVAL, + n, igraph_matrix_ncol(c)); + } + IGRAPH_CHECK(igraph_vector_int_resize(p, n)); + igraph_vector_int_null(p); + + ap = ap_create_problem(&MATRIX(*c, 0, 0), n); + ap_hungarian(ap); + ap_assignment(ap, VECTOR(*p)); + ap_free(ap); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/internal/pstdint.h b/src/rigraph/core/internal/pstdint.h new file mode 100644 index 0000000..bd5697f --- /dev/null +++ b/src/rigraph/core/internal/pstdint.h @@ -0,0 +1,817 @@ +/* A portable stdint.h + **************************************************************************** + * BSD License: + **************************************************************************** + * + * Copyright (c) 2005-2007 Paul Hsieh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **************************************************************************** + * + * Version 0.1.11 + * + * The ANSI C standard committee, for the C99 standard, specified the + * inclusion of a new standard include file called stdint.h. This is + * a very useful and long desired include file which contains several + * very precise definitions for integer scalar types that is + * critically important for making portable several classes of + * applications including cryptography, hashing, variable length + * integer libraries and so on. But for most developers its likely + * useful just for programming sanity. + * + * The problem is that most compiler vendors have decided not to + * implement the C99 standard, and the next C++ language standard + * (which has a lot more mindshare these days) will be a long time in + * coming and its unknown whether or not it will include stdint.h or + * how much adoption it will have. Either way, it will be a long time + * before all compilers come with a stdint.h and it also does nothing + * for the extremely large number of compilers available today which + * do not include this file, or anything comparable to it. + * + * So that's what this file is all about. Its an attempt to build a + * single universal include file that works on as many platforms as + * possible to deliver what stdint.h is supposed to. A few things + * that should be noted about this file: + * + * 1) It is not guaranteed to be portable and/or present an identical + * interface on all platforms. The extreme variability of the + * ANSI C standard makes this an impossibility right from the + * very get go. Its really only meant to be useful for the vast + * majority of platforms that possess the capability of + * implementing usefully and precisely defined, standard sized + * integer scalars. Systems which are not intrinsically 2s + * complement may produce invalid constants. + * + * 2) There is an unavoidable use of non-reserved symbols. + * + * 3) Other standard include files are invoked. + * + * 4) This file may come in conflict with future platforms that do + * include stdint.h. The hope is that one or the other can be + * used with no real difference. + * + * 5) In the current verison, if your platform can't represent + * int32_t, int16_t and int8_t, it just dumps out with a compiler + * error. + * + * 6) 64 bit integers may or may not be defined. Test for their + * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX. + * Note that this is different from the C99 specification which + * requires the existence of 64 bit support in the compiler. If + * this is not defined for your platform, yet it is capable of + * dealing with 64 bits then it is because this file has not yet + * been extended to cover all of your system's capabilities. + * + * 7) (u)intptr_t may or may not be defined. Test for its presence + * with the test: #ifdef PTRDIFF_MAX. If this is not defined + * for your platform, then it is because this file has not yet + * been extended to cover all of your system's capabilities, not + * because its optional. + * + * 8) The following might not been defined even if your platform is + * capable of defining it: + * + * WCHAR_MIN + * WCHAR_MAX + * (u)int64_t + * PTRDIFF_MIN + * PTRDIFF_MAX + * (u)intptr_t + * + * 9) The following have not been defined: + * + * WINT_MIN + * WINT_MAX + * + * 10) The criteria for defining (u)int_least(*)_t isn't clear, + * except for systems which don't have a type that precisely + * defined 8, 16, or 32 bit types (which this include file does + * not support anyways). Default definitions have been given. + * + * 11) The criteria for defining (u)int_fast(*)_t isn't something I + * would trust to any particular compiler vendor or the ANSI C + * committee. It is well known that "compatible systems" are + * commonly created that have very different performance + * characteristics from the systems they are compatible with, + * especially those whose vendors make both the compiler and the + * system. Default definitions have been given, but its strongly + * recommended that users never use these definitions for any + * reason (they do *NOT* deliver any serious guarantee of + * improved performance -- not in this file, nor any vendor's + * stdint.h). + * + * 12) The following macros: + * + * PRINTF_INTMAX_MODIFIER + * PRINTF_INT64_MODIFIER + * PRINTF_INT32_MODIFIER + * PRINTF_INT16_MODIFIER + * PRINTF_LEAST64_MODIFIER + * PRINTF_LEAST32_MODIFIER + * PRINTF_LEAST16_MODIFIER + * PRINTF_INTPTR_MODIFIER + * + * are strings which have been defined as the modifiers required + * for the "d", "u" and "x" printf formats to correctly output + * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t, + * (u)least32_t, (u)least16_t and (u)intptr_t types respectively. + * PRINTF_INTPTR_MODIFIER is not defined for some systems which + * provide their own stdint.h. PRINTF_INT64_MODIFIER is not + * defined if INT64_MAX is not defined. These are an extension + * beyond what C99 specifies must be in stdint.h. + * + * In addition, the following macros are defined: + * + * PRINTF_INTMAX_HEX_WIDTH + * PRINTF_INT64_HEX_WIDTH + * PRINTF_INT32_HEX_WIDTH + * PRINTF_INT16_HEX_WIDTH + * PRINTF_INT8_HEX_WIDTH + * PRINTF_INTMAX_DEC_WIDTH + * PRINTF_INT64_DEC_WIDTH + * PRINTF_INT32_DEC_WIDTH + * PRINTF_INT16_DEC_WIDTH + * PRINTF_INT8_DEC_WIDTH + * + * Which specifies the maximum number of characters required to + * print the number of that type in either hexadecimal or decimal. + * These are an extension beyond what C99 specifies must be in + * stdint.h. + * + * Compilers tested (all with 0 warnings at their highest respective + * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32 + * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio + * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3 + * + * This file should be considered a work in progress. Suggestions for + * improvements, especially those which increase coverage are strongly + * encouraged. + * + * Acknowledgements + * + * The following people have made significant contributions to the + * development and testing of this file: + * + * Chris Howie + * John Steele Scott + * Dave Thorup + * + */ + +#include +#include +#include + +/* + * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and + * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. + */ + +#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) )) && !defined (_PSTDINT_H_INCLUDED) + #include + #define _PSTDINT_H_INCLUDED + #ifndef PRINTF_INT64_MODIFIER + #define PRINTF_INT64_MODIFIER "ll" + #endif + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "l" + #endif + #ifndef PRINTF_INT16_MODIFIER + #define PRINTF_INT16_MODIFIER "h" + #endif + #ifndef PRINTF_INTMAX_MODIFIER + #define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER + #endif + #ifndef PRINTF_INT64_HEX_WIDTH + #define PRINTF_INT64_HEX_WIDTH "16" + #endif + #ifndef PRINTF_INT32_HEX_WIDTH + #define PRINTF_INT32_HEX_WIDTH "8" + #endif + #ifndef PRINTF_INT16_HEX_WIDTH + #define PRINTF_INT16_HEX_WIDTH "4" + #endif + #ifndef PRINTF_INT8_HEX_WIDTH + #define PRINTF_INT8_HEX_WIDTH "2" + #endif + #ifndef PRINTF_INT64_DEC_WIDTH + #define PRINTF_INT64_DEC_WIDTH "20" + #endif + #ifndef PRINTF_INT32_DEC_WIDTH + #define PRINTF_INT32_DEC_WIDTH "10" + #endif + #ifndef PRINTF_INT16_DEC_WIDTH + #define PRINTF_INT16_DEC_WIDTH "5" + #endif + #ifndef PRINTF_INT8_DEC_WIDTH + #define PRINTF_INT8_DEC_WIDTH "3" + #endif + #ifndef PRINTF_INTMAX_HEX_WIDTH + #define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH + #endif + #ifndef PRINTF_INTMAX_DEC_WIDTH + #define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH + #endif + + /* + * Something really weird is going on with Open Watcom. Just pull some of + * these duplicated definitions from Open Watcom's stdint.h file for now. + */ + + #if defined (__WATCOMC__) && __WATCOMC__ >= 1250 + #if !defined (INT64_C) + #define INT64_C(x) (x + (INT64_MAX - INT64_MAX)) + #endif + #if !defined (UINT64_C) + #define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) + #endif + #if !defined (INT32_C) + #define INT32_C(x) (x + (INT32_MAX - INT32_MAX)) + #endif + #if !defined (UINT32_C) + #define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX)) + #endif + #if !defined (INT16_C) + #define INT16_C(x) (x) + #endif + #if !defined (UINT16_C) + #define UINT16_C(x) (x) + #endif + #if !defined (INT8_C) + #define INT8_C(x) (x) + #endif + #if !defined (UINT8_C) + #define UINT8_C(x) (x) + #endif + #if !defined (UINT64_MAX) + #define UINT64_MAX 18446744073709551615ULL + #endif + #if !defined (INT64_MAX) + #define INT64_MAX 9223372036854775807LL + #endif + #if !defined (UINT32_MAX) + #define UINT32_MAX 4294967295UL + #endif + #if !defined (INT32_MAX) + #define INT32_MAX 2147483647L + #endif + #if !defined (INTMAX_MAX) + #define INTMAX_MAX INT64_MAX + #endif + #if !defined (INTMAX_MIN) + #define INTMAX_MIN INT64_MIN + #endif + #endif +#endif + +#ifndef _PSTDINT_H_INCLUDED + #define _PSTDINT_H_INCLUDED + + #ifndef SIZE_MAX + #define SIZE_MAX (~(size_t)0) + #endif + + /* + * Deduce the type assignments from limits.h under the assumption that + * integer sizes in bits are powers of 2, and follow the ANSI + * definitions. + */ + + #ifndef UINT8_MAX + #define UINT8_MAX 0xff + #endif + #ifndef uint8_t + #if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S) + typedef unsigned char uint8_t; + #define UINT8_C(v) ((uint8_t) v) + #else + # error "Platform not supported" + #endif + #endif + + #ifndef INT8_MAX + #define INT8_MAX 0x7f + #endif + #ifndef INT8_MIN + #define INT8_MIN INT8_C(0x80) + #endif + #ifndef int8_t + #if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S) + typedef signed char int8_t; + #define INT8_C(v) ((int8_t) v) + #else + # error "Platform not supported" + #endif + #endif + + #ifndef UINT16_MAX + #define UINT16_MAX 0xffff + #endif + #ifndef uint16_t + #if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S) + typedef unsigned int uint16_t; + #ifndef PRINTF_INT16_MODIFIER + #define PRINTF_INT16_MODIFIER "" + #endif + #define UINT16_C(v) ((uint16_t) (v)) + #elif (USHRT_MAX == UINT16_MAX) + typedef unsigned short uint16_t; + #define UINT16_C(v) ((uint16_t) (v)) + #ifndef PRINTF_INT16_MODIFIER + #define PRINTF_INT16_MODIFIER "h" + #endif + #else + #error "Platform not supported" + #endif + #endif + + #ifndef INT16_MAX + #define INT16_MAX 0x7fff + #endif + #ifndef INT16_MIN + #define INT16_MIN INT16_C(0x8000) + #endif + #ifndef int16_t + #if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S) + typedef signed int int16_t; + #define INT16_C(v) ((int16_t) (v)) + #ifndef PRINTF_INT16_MODIFIER + #define PRINTF_INT16_MODIFIER "" + #endif + #elif (SHRT_MAX == INT16_MAX) + typedef signed short int16_t; + #define INT16_C(v) ((int16_t) (v)) + #ifndef PRINTF_INT16_MODIFIER + #define PRINTF_INT16_MODIFIER "h" + #endif + #else + #error "Platform not supported" + #endif + #endif + + #ifndef UINT32_MAX + #define UINT32_MAX (0xffffffffUL) + #endif + #ifndef uint32_t + #if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S) + typedef unsigned long uint32_t; + #define UINT32_C(v) v ## UL + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "l" + #endif + #elif (UINT_MAX == UINT32_MAX) + typedef unsigned int uint32_t; + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "" + #endif + #define UINT32_C(v) v ## U + #elif (USHRT_MAX == UINT32_MAX) + typedef unsigned short uint32_t; + #define UINT32_C(v) ((unsigned short) (v)) + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "" + #endif + #else + #error "Platform not supported" + #endif + #endif + + #ifndef INT32_MAX + #define INT32_MAX (0x7fffffffL) + #endif + #ifndef INT32_MIN + #define INT32_MIN INT32_C(0x80000000) + #endif + #ifndef int32_t + #if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S) + typedef signed long int32_t; + #define INT32_C(v) v ## L + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "l" + #endif + #elif (INT_MAX == INT32_MAX) + typedef signed int int32_t; + #define INT32_C(v) v + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "" + #endif + #elif (SHRT_MAX == INT32_MAX) + typedef signed short int32_t; + #define INT32_C(v) ((short) (v)) + #ifndef PRINTF_INT32_MODIFIER + #define PRINTF_INT32_MODIFIER "" + #endif + #else + #error "Platform not supported" + #endif + #endif + + /* + * The macro stdint_int64_defined is temporarily used to record + * whether or not 64 integer support is available. It must be + * defined for any 64 integer extensions for new platforms that are + * added. + */ + + #undef stdint_int64_defined + #if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S) + #if (__STDC__ && __STDC_VERSION >= 199901L) || defined (S_SPLINT_S) + #define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; + #define UINT64_C(v) v ## ULL + #define INT64_C(v) v ## LL + #ifndef PRINTF_INT64_MODIFIER + #define PRINTF_INT64_MODIFIER "ll" + #endif + #endif + #endif + + #if !defined (stdint_int64_defined) + #if defined(__GNUC__) + #define stdint_int64_defined + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; + #define UINT64_C(v) v ## ULL + #define INT64_C(v) v ## LL + #ifndef PRINTF_INT64_MODIFIER + #define PRINTF_INT64_MODIFIER "ll" + #endif + #elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S) + #define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; + #define UINT64_C(v) v ## ULL + #define INT64_C(v) v ## LL + #ifndef PRINTF_INT64_MODIFIER + #define PRINTF_INT64_MODIFIER "ll" + #endif + #elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC) + #define stdint_int64_defined + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + #define UINT64_C(v) v ## UI64 + #define INT64_C(v) v ## I64 + #ifndef PRINTF_INT64_MODIFIER + #define PRINTF_INT64_MODIFIER "I64" + #endif + #endif + #endif + + #if !defined (LONG_LONG_MAX) && defined (INT64_C) + #define LONG_LONG_MAX INT64_C (9223372036854775807) + #endif + #ifndef ULONG_LONG_MAX + #define ULONG_LONG_MAX UINT64_C (18446744073709551615) + #endif + + #if !defined (INT64_MAX) && defined (INT64_C) + #define INT64_MAX INT64_C (9223372036854775807) + #endif + #if !defined (INT64_MIN) && defined (INT64_C) + #define INT64_MIN INT64_C (-9223372036854775808) + #endif + #if !defined (UINT64_MAX) && defined (INT64_C) + #define UINT64_MAX UINT64_C (18446744073709551615) + #endif + + /* + * Width of hexadecimal for number field. + */ + + #ifndef PRINTF_INT64_HEX_WIDTH + #define PRINTF_INT64_HEX_WIDTH "16" + #endif + #ifndef PRINTF_INT32_HEX_WIDTH + #define PRINTF_INT32_HEX_WIDTH "8" + #endif + #ifndef PRINTF_INT16_HEX_WIDTH + #define PRINTF_INT16_HEX_WIDTH "4" + #endif + #ifndef PRINTF_INT8_HEX_WIDTH + #define PRINTF_INT8_HEX_WIDTH "2" + #endif + + #ifndef PRINTF_INT64_DEC_WIDTH + #define PRINTF_INT64_DEC_WIDTH "20" + #endif + #ifndef PRINTF_INT32_DEC_WIDTH + #define PRINTF_INT32_DEC_WIDTH "10" + #endif + #ifndef PRINTF_INT16_DEC_WIDTH + #define PRINTF_INT16_DEC_WIDTH "5" + #endif + #ifndef PRINTF_INT8_DEC_WIDTH + #define PRINTF_INT8_DEC_WIDTH "3" + #endif + + /* + * Ok, lets not worry about 128 bit integers for now. Moore's law says + * we don't need to worry about that until about 2040 at which point + * we'll have bigger things to worry about. + */ + + #ifdef stdint_int64_defined + typedef int64_t intmax_t; + typedef uint64_t uintmax_t; + #define INTMAX_MAX INT64_MAX + #define INTMAX_MIN INT64_MIN + #define UINTMAX_MAX UINT64_MAX + #define UINTMAX_C(v) UINT64_C(v) + #define INTMAX_C(v) INT64_C(v) + #ifndef PRINTF_INTMAX_MODIFIER + #define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER + #endif + #ifndef PRINTF_INTMAX_HEX_WIDTH + #define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH + #endif + #ifndef PRINTF_INTMAX_DEC_WIDTH + #define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH + #endif + #else + typedef int32_t intmax_t; + typedef uint32_t uintmax_t; + #define INTMAX_MAX INT32_MAX + #define UINTMAX_MAX UINT32_MAX + #define UINTMAX_C(v) UINT32_C(v) + #define INTMAX_C(v) INT32_C(v) + #ifndef PRINTF_INTMAX_MODIFIER + #define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER + #endif + #ifndef PRINTF_INTMAX_HEX_WIDTH + #define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH + #endif + #ifndef PRINTF_INTMAX_DEC_WIDTH + #define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH + #endif + #endif + + /* + * Because this file currently only supports platforms which have + * precise powers of 2 as bit sizes for the default integers, the + * least definitions are all trivial. Its possible that a future + * version of this file could have different definitions. + */ + + #ifndef stdint_least_defined + typedef int8_t int_least8_t; + typedef uint8_t uint_least8_t; + typedef int16_t int_least16_t; + typedef uint16_t uint_least16_t; + typedef int32_t int_least32_t; + typedef uint32_t uint_least32_t; + #define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER + #define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER + #define UINT_LEAST8_MAX UINT8_MAX + #define INT_LEAST8_MAX INT8_MAX + #define UINT_LEAST16_MAX UINT16_MAX + #define INT_LEAST16_MAX INT16_MAX + #define UINT_LEAST32_MAX UINT32_MAX + #define INT_LEAST32_MAX INT32_MAX + #define INT_LEAST8_MIN INT8_MIN + #define INT_LEAST16_MIN INT16_MIN + #define INT_LEAST32_MIN INT32_MIN + #ifdef stdint_int64_defined + typedef int64_t int_least64_t; + typedef uint64_t uint_least64_t; + #define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER + #define UINT_LEAST64_MAX UINT64_MAX + #define INT_LEAST64_MAX INT64_MAX + #define INT_LEAST64_MIN INT64_MIN + #endif + #endif + #undef stdint_least_defined + + /* + * The ANSI C committee pretending to know or specify anything about + * performance is the epitome of misguided arrogance. The mandate of + * this file is to *ONLY* ever support that absolute minimum + * definition of the fast integer types, for compatibility purposes. + * No extensions, and no attempt to suggest what may or may not be a + * faster integer type will ever be made in this file. Developers are + * warned to stay away from these types when using this or any other + * stdint.h. + */ + + typedef int_least8_t int_fast8_t; + typedef uint_least8_t uint_fast8_t; + typedef int_least16_t int_fast16_t; + typedef uint_least16_t uint_fast16_t; + typedef int_least32_t int_fast32_t; + typedef uint_least32_t uint_fast32_t; + #define UINT_FAST8_MAX UINT_LEAST8_MAX + #define INT_FAST8_MAX INT_LEAST8_MAX + #define UINT_FAST16_MAX UINT_LEAST16_MAX + #define INT_FAST16_MAX INT_LEAST16_MAX + #define UINT_FAST32_MAX UINT_LEAST32_MAX + #define INT_FAST32_MAX INT_LEAST32_MAX + #define INT_FAST8_MIN INT_LEAST8_MIN + #define INT_FAST16_MIN INT_LEAST16_MIN + #define INT_FAST32_MIN INT_LEAST32_MIN + #ifdef stdint_int64_defined + typedef int_least64_t int_fast64_t; + typedef uint_least64_t uint_fast64_t; + #define UINT_FAST64_MAX UINT_LEAST64_MAX + #define INT_FAST64_MAX INT_LEAST64_MAX + #define INT_FAST64_MIN INT_LEAST64_MIN + #endif + + #undef stdint_int64_defined + + /* + * Whatever piecemeal, per compiler thing we can do about the wchar_t + * type limits. + */ + + #if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) + #include + #ifndef WCHAR_MIN + #define WCHAR_MIN 0 + #endif + #ifndef WCHAR_MAX + #define WCHAR_MAX ((wchar_t)-1) + #endif + #endif + + /* + * Whatever piecemeal, per compiler/platform thing we can do about the + * (u)intptr_t types and limits. + */ + + #if defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED) + #define STDINT_H_UINTPTR_T_DEFINED + #endif + + #ifndef STDINT_H_UINTPTR_T_DEFINED + #if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) + #define stdint_intptr_bits 64 + #elif defined (__WATCOMC__) || defined (__TURBOC__) + #if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) + #define stdint_intptr_bits 16 + #else + #define stdint_intptr_bits 32 + #endif + #elif defined (__i386__) || defined (_WIN32) || defined (WIN32) + #define stdint_intptr_bits 32 + #elif defined (__INTEL_COMPILER) + /* TODO -- what will Intel do about x86-64? */ + #endif + + #ifdef stdint_intptr_bits + #define stdint_intptr_glue3_i(a,b,c) a##b##c + #define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c) + #ifndef PRINTF_INTPTR_MODIFIER + #define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER) + #endif + #ifndef PTRDIFF_MAX + #define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) + #endif + #ifndef PTRDIFF_MIN + #define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) + #endif + #ifndef UINTPTR_MAX + #define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX) + #endif + #ifndef INTPTR_MAX + #define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) + #endif + #ifndef INTPTR_MIN + #define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) + #endif + #ifndef INTPTR_C + #define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x) + #endif + #ifndef UINTPTR_C + #define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x) + #endif + typedef stdint_intptr_glue3(uint, stdint_intptr_bits, _t) uintptr_t; + typedef stdint_intptr_glue3( int, stdint_intptr_bits, _t) intptr_t; + #else + /* TODO -- This following is likely wrong for some platforms, and does + nothing for the definition of uintptr_t. */ + typedef ptrdiff_t intptr_t; + #endif + #define STDINT_H_UINTPTR_T_DEFINED + #endif + + /* + * Assumes sig_atomic_t is signed and we have a 2s complement machine. + */ + + #ifndef SIG_ATOMIC_MAX + #define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1) + #endif + +#endif + +#if defined (__TEST_PSTDINT_FOR_CORRECTNESS) + +/* + * Please compile with the maximum warning settings to make sure macros are not + * defined more than once. + */ + +#include +#include +#include + +#define glue3_aux(x,y,z) x ## y ## z +#define glue3(x,y,z) glue3_aux(x,y,z) + +#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,=) glue3(UINT,bits,_C) (0); +#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,=) glue3(INT,bits,_C) (0); + +#define DECL(us,bits) glue3(DECL,us,) (bits) + +#define TESTUMAX(bits) glue3(u,bits,=) glue3(~,u,bits); if (glue3(UINT,bits,_MAX) glue3(!=,u,bits)) printf ("Something wrong with UINT%d_MAX\n", bits) + +int main () { + DECL(I, 8) + DECL(U, 8) + DECL(I, 16) + DECL(U, 16) + DECL(I, 32) + DECL(U, 32) +#ifdef INT64_MAX + DECL(I, 64) + DECL(U, 64) +#endif + intmax_t imax = INTMAX_C(0); + uintmax_t umax = UINTMAX_C(0); + char str0[256], str1[256]; + + sprintf (str0, "%d %x\n", 0, ~0); + + sprintf (str1, "%d %x\n", i8, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with i8 : %s\n", str1); + } + sprintf (str1, "%u %x\n", u8, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with u8 : %s\n", str1); + } + sprintf (str1, "%d %x\n", i16, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with i16 : %s\n", str1); + } + sprintf (str1, "%u %x\n", u16, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with u16 : %s\n", str1); + } + sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with i32 : %s\n", str1); + } + sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with u32 : %s\n", str1); + } +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with i64 : %s\n", str1); + } +#endif + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with imax : %s\n", str1); + } + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); + if (0 != strcmp (str0, str1)) { + printf ("Something wrong with umax : %s\n", str1); + } + + TESTUMAX(8); + TESTUMAX(16); + TESTUMAX(32); +#ifdef INT64_MAX + TESTUMAX(64); +#endif + + return EXIT_SUCCESS; +} + +#endif diff --git a/src/rigraph/core/internal/qsort.c b/src/rigraph/core/internal/qsort.c new file mode 100644 index 0000000..f877beb --- /dev/null +++ b/src/rigraph/core/internal/qsort.c @@ -0,0 +1,262 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* This file originates from the following URL: + * + * https://cgit.freebsd.org/src/commit/lib/libc/stdlib/qsort.c?id=7f8f79a5c444a565a32b0c6578b07f8d496f6c49 + * + * Create a diff against the revision given above to see what we have changed + * to facilitate inclusion into igraph + */ + +#include "igraph_qsort.h" + +#ifdef _MSC_VER + /* MSVC does not have inline when compiling C source files */ + #define inline __inline + #define __unused +#endif + +#ifndef __unused + #define __unused __attribute__ ((unused)) +#endif + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#if defined(I_AM_QSORT_R) +typedef int cmp_t(void *, const void *, const void *); +#elif defined(I_AM_QSORT_S) +typedef int cmp_t(const void *, const void *, void *); +#else +typedef int cmp_t(const void *, const void *); +#endif +static inline char *med3(char *, char *, char *, cmp_t *, void *); + +#define MIN(a, b) ((a) < (b) ? a : b) + +/* + * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". + */ + +static inline void +swapfunc(char *a, char *b, size_t es) +{ + char t; + + do { + t = *a; + *a++ = *b; + *b++ = t; + } while (--es > 0); +} + +#define vecswap(a, b, n) \ + if ((n) > 0) swapfunc(a, b, n) + +#if defined(I_AM_QSORT_R) +#define CMP(t, x, y) (cmp((t), (x), (y))) +#elif defined(I_AM_QSORT_S) +#define CMP(t, x, y) (cmp((x), (y), (t))) +#else +#define CMP(t, x, y) (cmp((x), (y))) +#endif + +static inline char * +med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk +#if !defined(I_AM_QSORT_R) && !defined(I_AM_QSORT_S) +__unused +#endif +) +{ + return CMP(thunk, a, b) < 0 ? + (CMP(thunk, b, c) < 0 ? b : (CMP(thunk, a, c) < 0 ? c : a )) + :(CMP(thunk, b, c) > 0 ? b : (CMP(thunk, a, c) < 0 ? a : c )); +} + +/* + * The actual qsort() implementation is static to avoid preemptible calls when + * recursing. Also give them different names for improved debugging. + */ +#if defined(I_AM_QSORT_R) +#define local_qsort local_qsort_r +#elif defined(I_AM_QSORT_S) +#define local_qsort local_qsort_s +#endif +static void +local_qsort(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk) +{ + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + size_t d1, d2; + int cmp_result; + int swap_cnt; + +loop: + swap_cnt = 0; + if (n < 7) { + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; + pl > (char *)a && CMP(thunk, pl - es, pl) > 0; + pl -= es) + swapfunc(pl, pl - es, es); + return; + } + pm = (char *)a + (n / 2) * es; + if (n > 7) { + pl = a; + pn = (char *)a + (n - 1) * es; + if (n > 40) { + size_t d = (n / 8) * es; + + pl = med3(pl, pl + d, pl + 2 * d, cmp, thunk); + pm = med3(pm - d, pm, pm + d, cmp, thunk); + pn = med3(pn - 2 * d, pn - d, pn, cmp, thunk); + } + pm = med3(pl, pm, pn, cmp, thunk); + } + swapfunc(a, pm, es); + pa = pb = (char *)a + es; + + pc = pd = (char *)a + (n - 1) * es; + for (;;) { + while (pb <= pc && (cmp_result = CMP(thunk, pb, a)) <= 0) { + if (cmp_result == 0) { + swap_cnt = 1; + swapfunc(pa, pb, es); + pa += es; + } + pb += es; + } + while (pb <= pc && (cmp_result = CMP(thunk, pc, a)) >= 0) { + if (cmp_result == 0) { + swap_cnt = 1; + swapfunc(pc, pd, es); + pd -= es; + } + pc -= es; + } + if (pb > pc) + break; + swapfunc(pb, pc, es); + swap_cnt = 1; + pb += es; + pc -= es; + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; + pl > (char *)a && CMP(thunk, pl - es, pl) > 0; + pl -= es) + swapfunc(pl, pl - es, es); + return; + } + + pn = (char *)a + n * es; + d1 = MIN(pa - (char *)a, pb - pa); + vecswap(a, pb - d1, d1); + /* + * Cast es to preserve signedness of right-hand side of MIN() + * expression, to avoid sign ambiguity in the implied comparison. es + * is safely within [0, SSIZE_MAX]. + */ + d1 = MIN(pd - pc, pn - pd - (ptrdiff_t)es); + vecswap(pb, pn - d1, d1); + + d1 = pb - pa; + d2 = pd - pc; + if (d1 <= d2) { + /* Recurse on left partition, then iterate on right partition */ + if (d1 > es) { + local_qsort(a, d1 / es, es, cmp, thunk); + } + if (d2 > es) { + /* Iterate rather than recurse to save stack space */ + /* qsort(pn - d2, d2 / es, es, cmp); */ + a = pn - d2; + n = d2 / es; + goto loop; + } + } else { + /* Recurse on right partition, then iterate on left partition */ + if (d2 > es) { + local_qsort(pn - d2, d2 / es, es, cmp, thunk); + } + if (d1 > es) { + /* Iterate rather than recurse to save stack space */ + /* qsort(a, d1 / es, es, cmp); */ + n = d1 / es; + goto loop; + } + } +} + +#if defined(I_AM_QSORT_R) +void +igraph_qsort_r(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp) +{ + local_qsort_r(a, n, es, cmp, thunk); +} +#elif defined(I_AM_QSORT_S) +errno_t +igraph_qsort_s(void *a, rsize_t n, rsize_t es, cmp_t *cmp, void *thunk) +{ + if (n > RSIZE_MAX) { + __throw_constraint_handler_s("qsort_s : n > RSIZE_MAX", EINVAL); + return (EINVAL); + } else if (es > RSIZE_MAX) { + __throw_constraint_handler_s("qsort_s : es > RSIZE_MAX", + EINVAL); + return (EINVAL); + } else if (n != 0) { + if (a == NULL) { + __throw_constraint_handler_s("qsort_s : a == NULL", + EINVAL); + return (EINVAL); + } else if (cmp == NULL) { + __throw_constraint_handler_s("qsort_s : cmp == NULL", + EINVAL); + return (EINVAL); + } + } + + local_qsort_s(a, n, es, cmp, thunk); + return (0); +} +#else +void +igraph_qsort(void *a, size_t n, size_t es, cmp_t *cmp) +{ + local_qsort(a, n, es, cmp, NULL); +} +#endif diff --git a/src/rigraph/core/internal/qsort_r.c b/src/rigraph/core/internal/qsort_r.c new file mode 100644 index 0000000..f7c0e54 --- /dev/null +++ b/src/rigraph/core/internal/qsort_r.c @@ -0,0 +1,8 @@ +/* + * This file is in the public domain. Originally written by Garrett + * A. Wollman. + * + * $FreeBSD: src/lib/libc/stdlib/qsort_r.c,v 1.1 2002/09/10 02:04:49 wollman Exp $ + */ +#define I_AM_QSORT_R +#include "qsort.c" diff --git a/src/rigraph/core/internal/zeroin.c b/src/rigraph/core/internal/zeroin.c new file mode 100644 index 0000000..b6a15ee --- /dev/null +++ b/src/rigraph/core/internal/zeroin.c @@ -0,0 +1,204 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* from GNU R's zeroin.c, minor modifications by Gabor Csardi */ + +/* from NETLIB c/brent.shar with max.iter, add'l info and convergence + details hacked in by Peter Dalgaard */ + +/************************************************************************* + * C math library + * function ZEROIN - obtain a function zero within the given range + * + * Input + * double zeroin(ax,bx,f,info,Tol,Maxit) + * double ax; Root will be seeked for within + * double bx; a range [ax,bx] + * double (*f)(double x, void *info); Name of the function whose zero + * will be seeked for + * void *info; Add'l info passed to f + * double *Tol; Acceptable tolerance for the root + * value. + * May be specified as 0.0 to cause + * the program to find the root as + * accurate as possible + * + * int *Maxit; Max. iterations + * + * + * Output + * Zeroin returns an estimate for the root with accuracy + * 4*EPSILON*abs(x) + tol + * *Tol returns estimated precision + * *Maxit returns actual # of iterations, or -1 if maxit was + * reached without convergence. + * + * Algorithm + * G.Forsythe, M.Malcolm, C.Moler, Computer methods for mathematical + * computations. M., Mir, 1980, p.180 of the Russian edition + * + * The function makes use of the bisection procedure combined with + * the linear or quadric inverse interpolation. + * At every step program operates on three abscissae - a, b, and c. + * b - the last and the best approximation to the root + * a - the last but one approximation + * c - the last but one or even earlier approximation than a that + * 1) |f(b)| <= |f(c)| + * 2) f(b) and f(c) have opposite signs, i.e. b and c confine + * the root + * At every step Zeroin selects one of the two new approximations, the + * former being obtained by the bisection procedure and the latter + * resulting in the interpolation (if a,b, and c are all different + * the quadric interpolation is utilized, otherwise the linear one). + * If the latter (i.e. obtained by the interpolation) point is + * reasonable (i.e. lies within the current interval [b,c] not being + * too close to the boundaries) it is accepted. The bisection result + * is used in the other case. Therefore, the range of uncertainty is + * ensured to be reduced at least by the factor 1.6 + * + ************************************************************************ + */ + +#include "igraph_nongraph.h" +#include "igraph_types.h" + +#include "../core/interruption.h" + +#include +#include + +#define EPSILON DBL_EPSILON + +int igraph_zeroin( /* An estimate of the root */ + igraph_real_t *ax, /* Left border | of the range */ + igraph_real_t *bx, /* Right border| the root is seeked*/ + igraph_real_t (*f)(igraph_real_t x, void *info), /* Function under investigation */ + void *info, /* Add'l info passed on to f */ + igraph_real_t *Tol, /* Acceptable tolerance */ + int *Maxit, /* Max # of iterations */ + igraph_real_t *res) { /* Result is stored here */ + igraph_real_t a, b, c, /* Abscissae, descr. see above */ + fa, fb, fc; /* f(a), f(b), f(c) */ + igraph_real_t tol; + int maxit; + + a = *ax; b = *bx; fa = (*f)(a, info); fb = (*f)(b, info); + c = a; fc = fa; + maxit = *Maxit + 1; tol = * Tol; + + /* First test if we have found a root at an endpoint */ + if (fa == 0.0) { + *Tol = 0.0; + *Maxit = 0; + *res = a; + return 0; + } + if (fb == 0.0) { + *Tol = 0.0; + *Maxit = 0; + *res = b; + return 0; + } + + while (maxit--) { /* Main iteration loop */ + igraph_real_t prev_step = b - a; /* Distance from the last but one + to the last approximation */ + igraph_real_t tol_act; /* Actual tolerance */ + igraph_real_t p; /* Interpolation step is calcu- */ + igraph_real_t q; /* lated in the form p/q; divi- + * sion operations is delayed + * until the last moment */ + igraph_real_t new_step; /* Step at this iteration */ + + IGRAPH_ALLOW_INTERRUPTION(); + + if ( fabs(fc) < fabs(fb) ) { + /* Swap data for b to be the */ + a = b; b = c; c = a; /* best approximation */ + fa = fb; fb = fc; fc = fa; + } + tol_act = 2 * EPSILON * fabs(b) + tol / 2; + new_step = (c - b) / 2; + + if ( fabs(new_step) <= tol_act || fb == (igraph_real_t)0 ) { + *Maxit -= maxit; + *Tol = fabs(c - b); + *res = b; + return 0; /* Acceptable approx. is found */ + } + + /* Decide if the interpolation can be tried */ + if ( fabs(prev_step) >= tol_act /* If prev_step was large enough*/ + && fabs(fa) > fabs(fb) ) { + /* and was in true direction, + * Interpolation may be tried */ + register igraph_real_t t1, cb, t2; + cb = c - b; + if ( a == c ) { /* If we have only two distinct */ + /* points linear interpolation */ + t1 = fb / fa; /* can only be applied */ + p = cb * t1; + q = 1.0 - t1; + } else { /* Quadric inverse interpolation*/ + + q = fa / fc; t1 = fb / fc; t2 = fb / fa; + p = t2 * ( cb * q * (q - t1) - (b - a) * (t1 - 1.0) ); + q = (q - 1.0) * (t1 - 1.0) * (t2 - 1.0); + } + if ( p > (igraph_real_t)0 ) { /* p was calculated with the */ + q = -q; /* opposite sign; make p positive */ + } else { /* and assign possible minus to */ + p = -p; /* q */ + } + + if ( p < (0.75 * cb * q - fabs(tol_act * q) / 2) /* If b+p/q falls in [b,c]*/ + && p < fabs(prev_step * q / 2) ) { /* and isn't too large */ + new_step = p / q; + } /* it is accepted + * If p/q is too large then the + * bisection procedure can + * reduce [b,c] range to more + * extent */ + } + + if ( fabs(new_step) < tol_act) { /* Adjust the step to be not less*/ + if ( new_step > (igraph_real_t)0 ) { /* than tolerance */ + new_step = tol_act; + } else { + new_step = -tol_act; + } + } + a = b; fa = fb; /* Save the previous approx. */ + b += new_step; fb = (*f)(b, info); /* Do step to a new approxim. */ + if ( (fb > 0 && fc > 0) || (fb < 0 && fc < 0) ) { + /* Adjust c for it to have a sign opposite to that of b */ + c = a; fc = fa; + } + + } + /* failed! */ + *Tol = fabs(c - b); + *Maxit = -1; + *res = b; + return IGRAPH_DIVERGED; +} diff --git a/src/rigraph/core/io/dimacs.c b/src/rigraph/core/io/dimacs.c new file mode 100644 index 0000000..c515847 --- /dev/null +++ b/src/rigraph/core/io/dimacs.c @@ -0,0 +1,318 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" + +#include "../core/interruption.h" + +#include + +/** + * \function igraph_read_graph_dimacs + * \brief Read a graph in DIMACS format. + * + * This function reads the DIMACS file format, more specifically the + * version for network flow problems, see the files at + * ftp://dimacs.rutgers.edu/pub/netflow/general-info/ + * + * + * This is a line-oriented text file (ASCII) format. The first + * character of each line defines the type of the line. If the first + * character is c the line is a comment line and it is + * ignored. There is one problem line (p in the file, it + * must appear before any node and arc descriptor lines. The problem + * line has three fields separated by spaces: the problem type + * (min, max or asn), the + * number of vertices and number of edges in the graph. + * Exactly two node identification lines are expected + * (n), one for the source, one for the target vertex. + * These have two fields: the id of the vertex and the type of the + * vertex, either s (=source) or t + * (=target). Arc lines start with a and have three + * fields: the source vertex, the target vertex and the edge capacity. + * + * + * Vertex ids are numbered from 1. + * \param graph Pointer to an uninitialized graph object. + * \param instream The file to read from. + * \param source Pointer to an integer, the id of the source node will + * be stored here. (The igraph vertex id, which is one less than + * the actual number in the file.) It is ignored if + * NULL. + * \param target Pointer to an integer, the (igraph) id of the target + * node will be stored here. It is ignored if NULL. + * \param capacity Pointer to an initialized vector, the capacity of + * the edges will be stored here if not NULL. + * \param directed Boolean, whether to create a directed graph. + * \return Error code. + * + * Time complexity: O(|V|+|E|+c), the number of vertices plus the + * number of edges, plus the size of the file in characters. + * + * \sa \ref igraph_write_graph_dimacs() + */ +int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, + igraph_strvector_t *problem, + igraph_vector_t *label, + igraph_integer_t *source, + igraph_integer_t *target, + igraph_vector_t *capacity, + igraph_bool_t directed) { + + igraph_vector_t edges; + long int no_of_nodes = -1; + long int no_of_edges = -1; + long int tsource = -1; + long int ttarget = -1; + char prob[21]; + char c; + int problem_type = 0; + +#define PROBLEM_EDGE 1 +#define PROBLEM_MAX 2 + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + if (capacity) { + igraph_vector_clear(capacity); + } + + while (!feof(instream)) { + int read; + char str[3]; + + IGRAPH_ALLOW_INTERRUPTION(); + + read = fscanf(instream, "%2c", str); + if (feof(instream)) { + break; + } + if (read != 1) { + IGRAPH_ERROR("parsing dimacs file failed", IGRAPH_PARSEERROR); + } + switch (str[0]) { + long int tmp, tmp2; + long int from, to; + igraph_real_t cap; + + case 'c': + /* comment */ + break; + + case 'p': + if (no_of_nodes != -1) { + IGRAPH_ERROR("reading dimacs file failed, double 'p' line", + IGRAPH_PARSEERROR); + } + read = fscanf(instream, "%20s %li %li", prob, + &no_of_nodes, &no_of_edges); + if (read != 3) { + IGRAPH_ERROR("reading dimacs file failed", IGRAPH_PARSEERROR); + } + if (!strcmp(prob, "edge")) { + /* edge list */ + problem_type = PROBLEM_EDGE; + if (label) { + long int i; + IGRAPH_CHECK(igraph_vector_resize(label, no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*label)[i] = i + 1; + } + } + } else if (!strcmp(prob, "max")) { + /* maximum flow problem */ + problem_type = PROBLEM_MAX; + if (capacity) { + IGRAPH_CHECK(igraph_vector_reserve(capacity, no_of_edges)); + } + } else { + IGRAPH_ERROR("Unknown problem type, should be 'edge' or 'max'", + IGRAPH_PARSEERROR); + } + if (problem) { + igraph_strvector_clear(problem); + IGRAPH_CHECK(igraph_strvector_add(problem, prob)); + } + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + break; + + case 'n': + /* for MAX this is either the source or target vertex, + for EDGE this is a vertex label */ + if (problem_type == PROBLEM_MAX) { + str[0] = 'x'; + read = fscanf(instream, "%li %1s", &tmp, str); + if (str[0] == 's') { + if (tsource != -1) { + IGRAPH_ERROR("reading dimacsfile: multiple source vertex line", + IGRAPH_PARSEERROR); + } else { + tsource = tmp; + } + } else if (str[0] == 't') { + if (ttarget != -1) { + IGRAPH_ERROR("reading dimacsfile: multiple target vertex line", + IGRAPH_PARSEERROR); + } else { + ttarget = tmp; + } + } else { + IGRAPH_ERROR("invalid node descriptor line in dimacs file", + IGRAPH_PARSEERROR); + } + } else { + read = fscanf(instream, "%li %li", &tmp, &tmp2); + if (label) { + VECTOR(*label)[tmp] = tmp2; + } + } + + break; + + case 'a': + /* This is valid only for MAX, a weighted edge */ + if (problem_type != PROBLEM_MAX) { + IGRAPH_ERROR("'a' lines are allowed only in MAX problem files", + IGRAPH_PARSEERROR); + } + read = fscanf(instream, "%li %li %lf", &from, &to, &cap); + if (read != 3) { + IGRAPH_ERROR("reading dimacs file", IGRAPH_PARSEERROR); + } + IGRAPH_CHECK(igraph_vector_push_back(&edges, from - 1)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to - 1)); + if (capacity) { + IGRAPH_CHECK(igraph_vector_push_back(capacity, cap)); + } + break; + + case 'e': + /* Edge line, only in EDGE */ + if (problem_type != PROBLEM_EDGE) { + IGRAPH_ERROR("'e' lines are allowed only in EDGE problem files", + IGRAPH_PARSEERROR); + } + read = fscanf(instream, "%li %li", &from, &to); + if (read != 2) { + IGRAPH_ERROR("reading dimacs file", IGRAPH_PARSEERROR); + } + IGRAPH_CHECK(igraph_vector_push_back(&edges, from - 1)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to - 1)); + break; + + default: + IGRAPH_ERROR("unknown line type in dimacs file", IGRAPH_PARSEERROR); + } + + /* Go to next line */ + while (!feof(instream) && (c = (char) getc(instream)) != '\n') ; + } + + if (source) { + *source = (igraph_integer_t) tsource - 1; + } + if (target) { + *target = (igraph_integer_t) ttarget - 1; + } + + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + directed)); + igraph_vector_destroy(&edges); + + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_write_graph_dimacs + * \brief Write a graph in DIMACS format. + * + * This function writes a graph to an output stream in DIMACS format, + * describing a maximum flow problem. + * See ftp://dimacs.rutgers.edu/pub/netflow/general-info/ + * + * + * This file format is discussed in the documentation of \ref + * igraph_read_graph_dimacs(), see that for more information. + * + * \param graph The graph to write to the stream. + * \param outstream The stream. + * \param source Integer, the id of the source vertex for the maximum + * flow. + * \param target Integer, the id of the target vertex. + * \param capacity Pointer to an initialized vector containing the + * edge capacity values. + * \return Error code. + * + * Time complexity: O(|E|), the number of edges in the graph. + * + * \sa igraph_read_graph_dimacs() + */ +int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, + long int source, long int target, + const igraph_vector_t *capacity) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_eit_t it; + long int i = 0; + int ret, ret1, ret2, ret3; + + if (igraph_vector_size(capacity) != no_of_edges) { + IGRAPH_ERROR("invalid capacity vector length", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), + &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + + ret = fprintf(outstream, + "c created by igraph\np max %li %li\nn %li s\nn %li t\n", + no_of_nodes, no_of_edges, source + 1, target + 1); + if (ret < 0) { + IGRAPH_ERROR("Write error", IGRAPH_EFILE); + } + + + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t from, to; + igraph_real_t cap; + igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); + cap = VECTOR(*capacity)[i++]; + ret1 = fprintf(outstream, "a %li %li ", + (long int) from + 1, (long int) to + 1); + ret2 = igraph_real_fprintf_precise(outstream, cap); + ret3 = fputc('\n', outstream); + if (ret1 < 0 || ret2 < 0 || ret3 == EOF) { + IGRAPH_ERROR("Write error", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/io/dl-header.h b/src/rigraph/core/io/dl-header.h new file mode 100644 index 0000000..1b5b141 --- /dev/null +++ b/src/rigraph/core/io/dl-header.h @@ -0,0 +1,44 @@ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_error.h" +#include "igraph_types.h" + +#include "../core/trie.h" + +typedef enum { IGRAPH_DL_MATRIX, + IGRAPH_DL_EDGELIST1, IGRAPH_DL_NODELIST1 + } igraph_i_dl_type_t; + +typedef struct { + void *scanner; + int eof; + int mode; + long int n; + long int from, to; + igraph_vector_t edges; + igraph_vector_t weights; + igraph_strvector_t labels; + igraph_trie_t trie; + igraph_i_dl_type_t type; + char errmsg[300]; +} igraph_i_dl_parsedata_t; diff --git a/src/rigraph/core/io/dl-lexer.c b/src/rigraph/core/io/dl-lexer.c new file mode 100644 index 0000000..1f6a90b --- /dev/null +++ b/src/rigraph/core/io/dl-lexer.c @@ -0,0 +1,2478 @@ +#line 2 "src/core/io/dl-lexer.c" + +#line 4 "src/core/io/dl-lexer.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_dl_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_dl_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_dl_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_dl_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_dl_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_dl_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_dl_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_dl_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_dl_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_dl_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_dl_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_dl_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_dl_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_dl_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_dl_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_dl_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_dl_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_dl_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_dl_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_dl_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_dl_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_dl_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_dl_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_dl_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_dl_yylex_ALREADY_DEFINED +#else +#define yylex igraph_dl_yylex +#endif + +#ifdef yyrestart +#define igraph_dl_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_dl_yyrestart +#endif + +#ifdef yylex_init +#define igraph_dl_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_dl_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_dl_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_dl_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_dl_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_dl_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_dl_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_dl_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_dl_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_dl_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_dl_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_dl_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_dl_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_dl_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_dl_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_dl_yyget_in +#endif + +#ifdef yyset_in +#define igraph_dl_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_dl_yyset_in +#endif + +#ifdef yyget_out +#define igraph_dl_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_dl_yyget_out +#endif + +#ifdef yyset_out +#define igraph_dl_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_dl_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_dl_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_dl_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_dl_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_dl_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_dl_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_dl_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_dl_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_dl_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_dl_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_dl_yyget_column +#endif + +#ifdef yyset_column +#define igraph_dl_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_dl_yyset_column +#endif + +#ifdef yywrap +#define igraph_dl_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_dl_yywrap +#endif + +#ifdef yyget_lval +#define igraph_dl_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_dl_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_dl_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_dl_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_dl_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_dl_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_dl_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_dl_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_dl_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_dl_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_dl_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_dl_yyrealloc +#endif + +#ifdef yyfree +#define igraph_dl_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_dl_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +#define igraph_dl_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void __attribute__((unused)) yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 24 +#define YY_END_OF_BUFFER 25 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[129] = + { 0, + 0, 0, 0, 0, 0, 0, 18, 18, 21, 21, + 25, 23, 22, 1, 1, 4, 23, 23, 23, 23, + 12, 11, 12, 12, 14, 15, 13, 17, 18, 17, + 16, 20, 21, 19, 22, 1, 4, 0, 0, 0, + 0, 0, 3, 12, 12, 12, 12, 14, 13, 17, + 18, 16, 17, 17, 20, 21, 19, 0, 2, 0, + 0, 3, 12, 12, 16, 17, 16, 0, 0, 0, + 12, 12, 5, 0, 0, 5, 12, 0, 0, 12, + 0, 0, 0, 6, 12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 9, 0, + 10, 7, 7, 9, 8, 10, 8, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 6, 7, 8, 9, 1, 10, 11, 10, + 10, 10, 10, 10, 10, 10, 10, 12, 1, 1, + 13, 1, 1, 1, 14, 15, 1, 16, 17, 18, + 19, 1, 20, 1, 1, 21, 22, 23, 24, 1, + 1, 25, 26, 27, 28, 1, 1, 29, 1, 1, + 1, 1, 1, 1, 1, 1, 14, 15, 1, 16, + + 17, 18, 19, 1, 20, 1, 1, 21, 22, 23, + 24, 1, 1, 25, 26, 27, 28, 1, 1, 29, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[30] = + { 0, + 1, 2, 3, 3, 2, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1 + } ; + +static const flex_int16_t yy_base[138] = + { 0, + 0, 22, 44, 64, 84, 94, 104, 114, 124, 134, + 287, 288, 4, 282, 282, 2, 1, 260, 269, 15, + 29, 288, 39, 50, 0, 288, 34, 0, 52, 19, + 64, 0, 54, 51, 74, 288, 67, 255, 88, 256, + 265, 138, 98, 108, 118, 128, 144, 0, 145, 0, + 151, 151, 72, 159, 0, 152, 153, 265, 169, 256, + 260, 170, 171, 175, 171, 168, 173, 264, 261, 253, + 184, 185, 288, 246, 246, 189, 193, 195, 197, 199, + 205, 218, 209, 288, 210, 0, 255, 242, 245, 246, + 248, 245, 249, 231, 228, 217, 211, 200, 184, 181, + + 172, 150, 138, 138, 128, 126, 106, 75, 66, 67, + 45, 45, 36, 42, 39, 22, 26, 219, 211, 6, + 220, 227, 228, 232, 237, 238, 242, 288, 247, 250, + 253, 256, 259, 262, 7, 6, 0 + } ; + +static const flex_int16_t yy_def[138] = + { 0, + 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 134, 128, 134, 134, 135, 128, 135, 136, 128, 136, + 136, 137, 128, 137, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 134, 128, 134, 134, 135, 128, 136, + 128, 136, 136, 136, 137, 128, 137, 128, 128, 128, + 128, 128, 134, 134, 136, 136, 136, 128, 128, 128, + 134, 134, 128, 128, 128, 134, 134, 128, 128, 134, + 128, 128, 128, 128, 128, 82, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 0, 128, 128, + 128, 128, 128, 128, 128, 128, 128 + } ; + +static const flex_int16_t yy_nxt[318] = + { 0, + 55, 13, 14, 15, 13, 35, 50, 48, 35, 16, + 16, 37, 37, 128, 38, 17, 42, 18, 128, 42, + 19, 39, 20, 13, 14, 15, 13, 43, 52, 52, + 45, 16, 16, 45, 125, 49, 121, 17, 49, 18, + 45, 120, 19, 45, 20, 12, 14, 15, 22, 119, + 22, 45, 46, 51, 45, 56, 51, 118, 56, 23, + 57, 57, 117, 47, 24, 12, 14, 15, 22, 116, + 22, 115, 53, 52, 52, 35, 37, 37, 35, 23, + 54, 65, 65, 114, 24, 26, 14, 15, 26, 59, + 12, 113, 59, 27, 27, 26, 14, 15, 26, 62, + + 12, 112, 62, 27, 27, 29, 14, 15, 29, 45, + 12, 30, 45, 31, 31, 29, 14, 15, 29, 45, + 12, 30, 45, 31, 31, 33, 14, 15, 33, 45, + 12, 111, 45, 34, 34, 33, 14, 15, 33, 42, + 12, 110, 42, 34, 34, 45, 49, 109, 45, 49, + 43, 108, 51, 56, 63, 51, 56, 107, 64, 53, + 52, 52, 57, 57, 66, 106, 66, 54, 67, 67, + 59, 62, 45, 59, 62, 45, 45, 67, 67, 45, + 65, 65, 67, 67, 71, 45, 45, 54, 45, 45, + 45, 72, 105, 45, 45, 76, 81, 45, 83, 81, + + 85, 83, 104, 85, 103, 77, 81, 82, 84, 81, + 83, 85, 124, 83, 85, 124, 102, 82, 80, 86, + 122, 126, 86, 122, 126, 90, 90, 101, 122, 122, + 123, 122, 122, 124, 87, 88, 124, 100, 127, 126, + 89, 127, 126, 127, 99, 98, 127, 12, 12, 12, + 21, 21, 21, 25, 25, 25, 28, 28, 28, 32, + 32, 32, 44, 44, 97, 96, 95, 94, 93, 92, + 91, 79, 78, 75, 74, 73, 70, 69, 68, 61, + 60, 58, 41, 40, 36, 36, 128, 11, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128 + } ; + +static const flex_int16_t yy_chk[318] = + { 0, + 137, 1, 1, 1, 1, 13, 136, 135, 13, 1, + 1, 16, 16, 0, 17, 1, 20, 1, 0, 20, + 1, 17, 1, 2, 2, 2, 2, 20, 30, 30, + 21, 2, 2, 21, 120, 27, 117, 2, 27, 2, + 23, 116, 2, 23, 2, 3, 3, 3, 3, 115, + 3, 24, 23, 29, 24, 33, 29, 114, 33, 3, + 34, 34, 113, 24, 3, 4, 4, 4, 4, 112, + 4, 111, 31, 31, 31, 35, 37, 37, 35, 4, + 31, 53, 53, 110, 4, 5, 5, 5, 5, 39, + 5, 109, 39, 5, 5, 6, 6, 6, 6, 43, + + 6, 108, 43, 6, 6, 7, 7, 7, 7, 44, + 7, 7, 44, 7, 7, 8, 8, 8, 8, 45, + 8, 8, 45, 8, 8, 9, 9, 9, 9, 46, + 9, 107, 46, 9, 9, 10, 10, 10, 10, 42, + 10, 106, 42, 10, 10, 47, 49, 105, 47, 49, + 42, 104, 51, 56, 46, 51, 56, 103, 47, 52, + 52, 52, 57, 57, 54, 102, 54, 52, 54, 54, + 59, 62, 63, 59, 62, 63, 64, 66, 66, 64, + 65, 65, 67, 67, 63, 71, 72, 65, 71, 72, + 76, 64, 101, 76, 77, 71, 78, 77, 79, 78, + + 80, 79, 100, 80, 99, 72, 81, 78, 79, 81, + 83, 85, 119, 83, 85, 119, 98, 81, 77, 82, + 118, 121, 82, 118, 121, 83, 85, 97, 122, 123, + 118, 122, 123, 124, 82, 82, 124, 96, 125, 126, + 82, 125, 126, 127, 95, 94, 127, 129, 129, 129, + 130, 130, 130, 131, 131, 131, 132, 132, 132, 133, + 133, 133, 134, 134, 93, 92, 91, 90, 89, 88, + 87, 75, 74, 70, 69, 68, 61, 60, 58, 41, + 40, 38, 19, 18, 15, 14, 11, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "src/core/io/dl-lexer.l" +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ +#line 24 "src/core/io/dl-lexer.l" + +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "config.h" +#include +#include + +#include "dl-header.h" +#include "dl-parser.h" + +#define YY_EXTRA_TYPE igraph_i_dl_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in DL parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +#line 827 "src/core/io/dl-lexer.c" +#define YY_NO_INPUT 1 + +#line 830 "src/core/io/dl-lexer.c" + +#define INITIAL 0 +#define LABELM 1 +#define FULLMATRIX 2 +#define EDGELIST 3 +#define NODELIST 4 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + yy_size_t yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yylval = yylval_param; + + yylloc = yylloc_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 80 "src/core/io/dl-lexer.l" + + +#line 1119 "src/core/io/dl-lexer.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 129 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 288 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +#line 82 "src/core/io/dl-lexer.l" +{ return NEWLINE; } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 84 "src/core/io/dl-lexer.l" +{ return DL; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 85 "src/core/io/dl-lexer.l" +{ + return NEQ; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 87 "src/core/io/dl-lexer.l" +{ return NUM; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 89 "src/core/io/dl-lexer.l" +{ + switch (yyextra->mode) { + case 0: BEGIN(FULLMATRIX); + break; + case 1: BEGIN(EDGELIST); + break; + case 2: BEGIN(NODELIST); + break; + } + return DATA; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 100 "src/core/io/dl-lexer.l" +{ BEGIN(LABELM); return LABELS; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 101 "src/core/io/dl-lexer.l" +{ + return LABELSEMBEDDED; } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 103 "src/core/io/dl-lexer.l" +{ + yyextra->mode=0; return FORMATFULLMATRIX; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 105 "src/core/io/dl-lexer.l" +{ + yyextra->mode=1; return FORMATEDGELIST1; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 107 "src/core/io/dl-lexer.l" +{ + yyextra->mode=2; return FORMATNODELIST1; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 110 "src/core/io/dl-lexer.l" +{ /* eaten up */ } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 111 "src/core/io/dl-lexer.l" +{ return LABEL; } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 113 "src/core/io/dl-lexer.l" +{ return DIGIT; } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 114 "src/core/io/dl-lexer.l" +{ return LABEL; } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 115 "src/core/io/dl-lexer.l" +{ } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 117 "src/core/io/dl-lexer.l" +{ return NUM; } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 118 "src/core/io/dl-lexer.l" +{ return LABEL; } + YY_BREAK +case 18: +YY_RULE_SETUP +#line 119 "src/core/io/dl-lexer.l" +{ } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 121 "src/core/io/dl-lexer.l" +{ return NUM; } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 122 "src/core/io/dl-lexer.l" +{ return LABEL; } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 123 "src/core/io/dl-lexer.l" +{ } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 125 "src/core/io/dl-lexer.l" +{ /* eaten up */ } + YY_BREAK +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(LABELM): +case YY_STATE_EOF(FULLMATRIX): +case YY_STATE_EOF(EDGELIST): +case YY_STATE_EOF(NODELIST): +#line 127 "src/core/io/dl-lexer.l" +{ + if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + BEGIN(INITIAL); + return EOFF; + } + } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 137 "src/core/io/dl-lexer.l" +{ return 0; } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 138 "src/core/io/dl-lexer.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1327 "src/core/io/dl-lexer.c" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap( yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin , yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 129 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 129 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 128); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( yyscanner ) ) + return 0; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void __attribute__((unused)) yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +yy_size_t yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void yyset_column (int _column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = _column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int _bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 138 "src/core/io/dl-lexer.l" diff --git a/src/rigraph/core/io/dl-lexer.h b/src/rigraph/core/io/dl-lexer.h new file mode 100644 index 0000000..e41453b --- /dev/null +++ b/src/rigraph/core/io/dl-lexer.h @@ -0,0 +1,737 @@ +#ifndef igraph_dl_yyHEADER_H +#define igraph_dl_yyHEADER_H 1 +#define igraph_dl_yyIN_HEADER 1 + +#line 6 "src/core/io/dl-lexer.h" + +#line 8 "src/core/io/dl-lexer.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_dl_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_dl_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_dl_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_dl_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_dl_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_dl_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_dl_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_dl_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_dl_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_dl_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_dl_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_dl_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_dl_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_dl_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_dl_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_dl_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_dl_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_dl_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_dl_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_dl_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_dl_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_dl_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_dl_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_dl_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_dl_yylex_ALREADY_DEFINED +#else +#define yylex igraph_dl_yylex +#endif + +#ifdef yyrestart +#define igraph_dl_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_dl_yyrestart +#endif + +#ifdef yylex_init +#define igraph_dl_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_dl_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_dl_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_dl_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_dl_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_dl_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_dl_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_dl_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_dl_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_dl_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_dl_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_dl_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_dl_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_dl_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_dl_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_dl_yyget_in +#endif + +#ifdef yyset_in +#define igraph_dl_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_dl_yyset_in +#endif + +#ifdef yyget_out +#define igraph_dl_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_dl_yyget_out +#endif + +#ifdef yyset_out +#define igraph_dl_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_dl_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_dl_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_dl_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_dl_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_dl_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_dl_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_dl_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_dl_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_dl_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_dl_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_dl_yyget_column +#endif + +#ifdef yyset_column +#define igraph_dl_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_dl_yyset_column +#endif + +#ifdef yywrap +#define igraph_dl_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_dl_yywrap +#endif + +#ifdef yyget_lval +#define igraph_dl_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_dl_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_dl_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_dl_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_dl_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_dl_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_dl_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_dl_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_dl_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_dl_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_dl_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_dl_yyrealloc +#endif + +#ifdef yyfree +#define igraph_dl_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_dl_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define igraph_dl_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 +#define LABELM 1 +#define FULLMATRIX 2 +#define EDGELIST 3 +#define NODELIST 4 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef igraph_dl_yy_create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef igraph_dl_yy_delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef igraph_dl_yy_scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef igraph_dl_yy_scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef igraph_dl_yy_scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef igraph_dl_yy_init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef igraph_dl_yy_flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef igraph_dl_yy_load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef igraph_dl_yy_switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef igraph_dl_yypush_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef igraph_dl_yypop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef igraph_dl_yyensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef igraph_dl_yylex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef igraph_dl_yyrestart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef igraph_dl_yylex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef igraph_dl_yylex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef igraph_dl_yylex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef igraph_dl_yyget_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef igraph_dl_yyset_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef igraph_dl_yyget_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef igraph_dl_yyset_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef igraph_dl_yyget_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef igraph_dl_yyset_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef igraph_dl_yyget_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef igraph_dl_yyset_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef igraph_dl_yyget_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef igraph_dl_yyget_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef igraph_dl_yyget_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef igraph_dl_yyset_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef igraph_dl_yyget_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef igraph_dl_yyset_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef igraph_dl_yywrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef igraph_dl_yyget_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef igraph_dl_yyset_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef igraph_dl_yyget_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef igraph_dl_yyset_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef igraph_dl_yyalloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef igraph_dl_yyrealloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef igraph_dl_yyfree_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef igraph_dl_yytext_ALREADY_DEFINED +#undef yytext +#endif +#ifndef igraph_dl_yyleng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef igraph_dl_yyin_ALREADY_DEFINED +#undef yyin +#endif +#ifndef igraph_dl_yyout_ALREADY_DEFINED +#undef yyout +#endif +#ifndef igraph_dl_yy_flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef igraph_dl_yylineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef igraph_dl_yytables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef igraph_dl_yytables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef igraph_dl_yyTABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 138 "src/core/io/dl-lexer.l" + +#line 736 "src/core/io/dl-lexer.h" +#undef igraph_dl_yyIN_HEADER +#endif /* igraph_dl_yyHEADER_H */ diff --git a/src/rigraph/core/io/dl-lexer.l b/src/rigraph/core/io/dl-lexer.l new file mode 100644 index 0000000..7fa6ec8 --- /dev/null +++ b/src/rigraph/core/io/dl-lexer.l @@ -0,0 +1,137 @@ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "config.h" +#include +#include + +#include "dl-header.h" +#include "parsers/dl-parser.h" + +#define YY_EXTRA_TYPE igraph_i_dl_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in DL parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +%} + +%option noyywrap +%option prefix="igraph_dl_yy" +%option nounput +%option noinput +%option nodefault +%option reentrant +%option bison-bridge +%option bison-locations + +digit [0-9] +whitespace [ \t\v\f] + +%x LABELM FULLMATRIX EDGELIST NODELIST + +%% + +<*>\n\r|\r\n|\r|\n { return NEWLINE; } + +[dD][lL]{whitespace}+ { return DL; } +[nN]{whitespace}*[=]{whitespace}* { + return NEQ; } +{digit}+ { return NUM; } + +[dD][aA][tT][aA][:] { + switch (yyextra->mode) { + case 0: BEGIN(FULLMATRIX); + break; + case 1: BEGIN(EDGELIST); + break; + case 2: BEGIN(NODELIST); + break; + } + return DATA; } + +[lL][aA][bB][eE][lL][sS]: { BEGIN(LABELM); return LABELS; } +[lL][aA][bB][eE][lL][sS]{whitespace}+[eE][mM][bB][eE][dD][dD][eE][dD]:?{whitespace}* { + return LABELSEMBEDDED; } +[fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[fF][uU][lL][lL][mM][aA][tT][rR][iI][xX]{whitespace}* { + yyextra->mode=0; return FORMATFULLMATRIX; } +[fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[eE][dD][gG][eE][lL][iI][sS][tT][1]{whitespace}* { + yyextra->mode=1; return FORMATEDGELIST1; } +[fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[nN][oO][dD][eE][lL][iI][sS][tT][1]{whitespace}* { + yyextra->mode=2; return FORMATNODELIST1; } + +[, ] { /* eaten up */ } +[^, \t\n\r\f\v]+{whitespace}* { return LABEL; } + +{digit}{whitespace}* { return DIGIT; } +[^ \t\n\r\v\f,]+ { return LABEL; } +{whitespace} { } + +\-?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { return NUM; } +[^ \t\n\r\v\f,]+ { return LABEL; } +{whitespace}* { } + +{digit}+ { return NUM; } +[^ \t\r\n\v\f,]+ { return LABEL; } +{whitespace}* { } + +{whitespace}+ { /* eaten up */ } + +<> { + if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + BEGIN(INITIAL); + return EOFF; + } + } + +<*>. { return 0; } diff --git a/src/rigraph/core/io/dl-parser.c b/src/rigraph/core/io/dl-parser.c new file mode 100644 index 0000000..312388d --- /dev/null +++ b/src/rigraph/core/io/dl-parser.c @@ -0,0 +1,2237 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + 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 + (at your option) 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse igraph_dl_yyparse +#define yylex igraph_dl_yylex +#define yyerror igraph_dl_yyerror +#define yydebug igraph_dl_yydebug +#define yynerrs igraph_dl_yynerrs + +/* First part of user prologue. */ +#line 23 "src/core/io/dl-parser.y" + + +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "config.h" + +#include "../core/math.h" +//#include "../internal/hacks.h" +#include "dl-header.h" +#include "dl-parser.h" +#include "dl-lexer.h" + +#include + +int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, + const char *s); +int igraph_i_dl_add_str(char *newstr, int length, + igraph_i_dl_parsedata_t *context); +int igraph_i_dl_add_edge(long int from, long int to, + igraph_i_dl_parsedata_t *context); +int igraph_i_dl_add_edge_w(long int from, long int to, + igraph_real_t weight, + igraph_i_dl_parsedata_t *context); + +extern igraph_real_t igraph_pajek_get_number(const char *str, long int len); + +#define scanner context->scanner + + +#line 125 "yy.tab.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_IGRAPH_DL_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_DL_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_dl_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + NUM = 258, + NEWLINE = 259, + DL = 260, + NEQ = 261, + DATA = 262, + LABELS = 263, + LABELSEMBEDDED = 264, + FORMATFULLMATRIX = 265, + FORMATEDGELIST1 = 266, + FORMATNODELIST1 = 267, + DIGIT = 268, + LABEL = 269, + EOFF = 270, + ERROR = 271 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 83 "src/core/io/dl-parser.y" + + long int integer; + igraph_real_t real; + +#line 199 "yy.tab.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_dl_yyparse (igraph_i_dl_parsedata_t* context); + +#endif /* !YY_IGRAPH_DL_YY_YY_TAB_H_INCLUDED */ + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_uint8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 4 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 118 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 17 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 37 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 66 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 138 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 271 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 108, 108, 110, 110, 112, 112, 114, 115, 116, + 119, 119, 121, 121, 123, 124, 125, 128, 129, 135, + 135, 140, 140, 142, 152, 154, 156, 156, 158, 162, + 166, 171, 175, 177, 178, 179, 180, 181, 184, 185, + 188, 190, 194, 197, 198, 201, 203, 207, 210, 226, + 228, 229, 230, 231, 232, 235, 236, 239, 241, 244, + 244, 250, 251, 254, 256, 260, 260 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "NUM", "NEWLINE", "DL", "NEQ", "DATA", + "LABELS", "LABELSEMBEDDED", "FORMATFULLMATRIX", "FORMATEDGELIST1", + "FORMATNODELIST1", "DIGIT", "LABEL", "EOFF", "ERROR", "$accept", "input", + "trail", "eof", "rest", "formfullmatrix", "newline", "fullmatrix", + "labels", "fullmatrixdata", "zerooneseq", "zeroone", + "labeledfullmatrixdata", "reallabeledfullmatrixdata", "labelseq", + "label", "labeledmatrixlines", "labeledmatrixline", "edgelist1", + "edgelist1rest", "edgelist1data", "edgelist1dataline", "integer", + "labelededgelist1data", "labelededgelist1dataline", "weight", "elabel", + "nodelist1", "nodelist1rest", "nodelist1data", "nodelist1dataline", + "from", "tolist", "labelednodelist1data", "labelednodelist1dataline", + "fromelabel", "labeltolist", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271 +}; +# endif + +#define YYPACT_NINF (-114) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-22) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + 8, 38, 11, 43, -114, -114, 44, 57, 46, 46, + 46, 46, 46, 46, -114, -114, -114, -114, -114, -114, + -114, -114, 69, 53, 63, 66, 6, 65, 46, 46, + -114, 46, 46, 46, -114, -114, 46, 46, -114, -114, + -114, -114, 5, 19, -114, -114, -114, 76, 84, -114, + 82, -114, -114, -114, 46, -114, -114, -114, 93, 43, + 46, 46, 46, -114, -114, -114, 46, 46, 46, -114, + 85, 86, -114, 43, 23, -114, -114, 88, 33, -114, + -114, 65, -114, 85, -114, -114, -114, 90, 46, 46, + 87, 46, -114, -114, 46, 46, 87, 46, 25, -114, + -114, -114, 94, -114, 95, -114, -114, 87, 29, -114, + 96, -114, -114, -114, 49, -114, -114, 43, 46, 92, + 46, 84, 46, 2, 46, -114, -114, 100, -114, -114, + -114, -114, -114, 87, -114, 87, 87, 87 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 0, 0, 0, 0, 1, 42, 0, 0, 12, 12, + 12, 12, 12, 12, 3, 7, 11, 8, 9, 13, + 19, 17, 0, 0, 0, 0, 5, 14, 12, 12, + 10, 12, 12, 12, 32, 55, 12, 12, 49, 6, + 2, 4, 0, 0, 26, 38, 17, 0, 50, 17, + 0, 20, 23, 22, 12, 18, 16, 24, 12, 33, + 12, 12, 12, 58, 56, 59, 12, 12, 12, 19, + 0, 0, 39, 0, 0, 43, 17, 0, 0, 61, + 17, 15, 21, 25, 29, 28, 27, 0, 12, 12, + 35, 12, 57, 60, 12, 12, 52, 12, 0, 30, + 47, 41, 0, 38, 0, 48, 44, 0, 0, 55, + 0, 64, 62, 65, 0, 31, 40, 34, 12, 0, + 12, 51, 12, 0, 12, 43, 46, 0, 43, 61, + 63, 66, 61, 36, 45, 37, 53, 54 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -114, -114, -114, -114, -114, -114, -9, 83, -41, 36, + 26, -114, -114, -114, -114, -114, -114, 24, -114, -114, + 7, -114, 4, -113, -114, -7, -82, -114, -114, 9, + -114, -114, -114, -98, -114, -114, -114 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 2, 26, 40, 14, 15, 20, 16, 28, 27, + 42, 53, 56, 57, 58, 86, 83, 84, 17, 34, + 59, 72, 73, 90, 106, 102, 107, 18, 38, 48, + 64, 65, 77, 96, 112, 113, 123 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 21, 22, 23, 24, 25, 60, 130, 6, 66, 51, + 19, 4, 133, 1, 111, 135, 105, 41, 52, 43, + 44, 39, 45, 46, 47, 119, 54, 49, 50, 115, + 88, 136, 89, 55, 137, 91, 120, 55, 52, 97, + 94, 131, 95, 55, 3, 69, 5, 55, 7, 71, + 19, 74, 75, 76, 111, 111, 124, 78, 79, 80, + 8, 9, 10, 55, 8, 9, 10, 11, 12, 13, + 31, 32, 33, 35, 36, 37, 29, 87, -21, 103, + 104, 93, 108, 61, 62, 109, 110, 63, 114, 67, + 68, 5, 92, 100, 101, 100, 126, 70, 116, 82, + 85, 105, 118, 122, 134, 81, 30, 99, 98, 125, + 117, 128, 127, 129, 0, 132, 0, 0, 121 +}; + +static const yytype_int16 yycheck[] = +{ + 9, 10, 11, 12, 13, 46, 4, 3, 49, 4, + 4, 0, 125, 5, 96, 128, 14, 26, 13, 28, + 29, 15, 31, 32, 33, 107, 7, 36, 37, 4, + 7, 129, 9, 14, 132, 76, 7, 14, 13, 80, + 7, 123, 9, 14, 6, 54, 3, 14, 4, 58, + 4, 60, 61, 62, 136, 137, 7, 66, 67, 68, + 7, 8, 9, 14, 7, 8, 9, 10, 11, 12, + 7, 8, 9, 7, 8, 9, 7, 73, 13, 88, + 89, 77, 91, 7, 8, 94, 95, 3, 97, 7, + 8, 3, 4, 3, 4, 3, 4, 4, 4, 14, + 14, 14, 7, 7, 4, 69, 23, 83, 82, 118, + 103, 120, 119, 122, -1, 124, -1, -1, 109 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 5, 18, 6, 0, 3, 39, 4, 7, 8, + 9, 10, 11, 12, 21, 22, 24, 35, 44, 4, + 23, 23, 23, 23, 23, 23, 19, 26, 25, 7, + 24, 7, 8, 9, 36, 7, 8, 9, 45, 15, + 20, 23, 27, 23, 23, 23, 23, 23, 46, 23, + 23, 4, 13, 28, 7, 14, 29, 30, 31, 37, + 25, 7, 8, 3, 47, 48, 25, 7, 8, 23, + 4, 23, 38, 39, 23, 23, 23, 49, 23, 23, + 23, 26, 14, 33, 34, 14, 32, 39, 7, 9, + 40, 25, 4, 39, 7, 9, 50, 25, 27, 34, + 3, 4, 42, 23, 23, 14, 41, 43, 23, 23, + 23, 43, 51, 52, 23, 4, 4, 37, 7, 43, + 7, 46, 7, 53, 7, 23, 4, 42, 23, 23, + 4, 43, 23, 40, 4, 40, 50, 50 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 17, 18, 19, 19, 20, 20, 21, 21, 21, + 22, 22, 23, 23, 24, 24, 24, 25, 25, 26, + 26, 27, 27, 28, 29, 30, 31, 31, 32, 33, + 33, 34, 35, 36, 36, 36, 36, 36, 37, 37, + 38, 38, 39, 40, 40, 41, 41, 42, 43, 44, + 45, 45, 45, 45, 45, 46, 46, 47, 48, 49, + 49, 50, 50, 51, 52, 53, 53 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 7, 0, 2, 0, 1, 1, 1, 1, + 3, 1, 0, 1, 3, 7, 5, 0, 3, 0, + 3, 0, 2, 1, 1, 3, 0, 3, 1, 1, + 2, 3, 3, 3, 7, 5, 9, 9, 0, 2, + 4, 3, 1, 0, 2, 4, 3, 1, 1, 3, + 2, 7, 5, 9, 9, 0, 2, 3, 1, 0, + 2, 0, 2, 3, 1, 0, 2 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_dl_parsedata_t* context) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (yylocationp); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_dl_parsedata_t* context) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_dl_parsedata_t* context) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[+yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, context); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[+*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_dl_parsedata_t* context) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (igraph_i_dl_parsedata_t* context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + 'yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, &yylloc, scanner); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 108 "src/core/io/dl-parser.y" + { context->n=(yyvsp[-4].integer); } +#line 1578 "yy.tab.c" + break; + + case 7: +#line 114 "src/core/io/dl-parser.y" + { context->type=IGRAPH_DL_MATRIX; } +#line 1584 "yy.tab.c" + break; + + case 8: +#line 115 "src/core/io/dl-parser.y" + { context->type=IGRAPH_DL_EDGELIST1; } +#line 1590 "yy.tab.c" + break; + + case 9: +#line 116 "src/core/io/dl-parser.y" + { context->type=IGRAPH_DL_NODELIST1; } +#line 1596 "yy.tab.c" + break; + + case 10: +#line 119 "src/core/io/dl-parser.y" + {} +#line 1602 "yy.tab.c" + break; + + case 11: +#line 119 "src/core/io/dl-parser.y" + {} +#line 1608 "yy.tab.c" + break; + + case 14: +#line 123 "src/core/io/dl-parser.y" + { } +#line 1614 "yy.tab.c" + break; + + case 15: +#line 124 "src/core/io/dl-parser.y" + { } +#line 1620 "yy.tab.c" + break; + + case 16: +#line 125 "src/core/io/dl-parser.y" + { } +#line 1626 "yy.tab.c" + break; + + case 17: +#line 128 "src/core/io/dl-parser.y" + {} +#line 1632 "yy.tab.c" + break; + + case 18: +#line 129 "src/core/io/dl-parser.y" + { + igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context); } +#line 1641 "yy.tab.c" + break; + + case 19: +#line 135 "src/core/io/dl-parser.y" + {} +#line 1647 "yy.tab.c" + break; + + case 20: +#line 135 "src/core/io/dl-parser.y" + { + context->from += 1; + context->to = 0; + } +#line 1656 "yy.tab.c" + break; + + case 22: +#line 140 "src/core/io/dl-parser.y" + { } +#line 1662 "yy.tab.c" + break; + + case 23: +#line 142 "src/core/io/dl-parser.y" + { + if (igraph_dl_yyget_text(scanner)[0]=='1') { + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + context->from)); + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + context->to)); + } + context->to += 1; +} +#line 1676 "yy.tab.c" + break; + + case 24: +#line 152 "src/core/io/dl-parser.y" + {} +#line 1682 "yy.tab.c" + break; + + case 25: +#line 154 "src/core/io/dl-parser.y" + {} +#line 1688 "yy.tab.c" + break; + + case 28: +#line 158 "src/core/io/dl-parser.y" + { igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context); } +#line 1696 "yy.tab.c" + break; + + case 29: +#line 162 "src/core/io/dl-parser.y" + { + context->from += 1; + context->to = 0; + } +#line 1705 "yy.tab.c" + break; + + case 30: +#line 166 "src/core/io/dl-parser.y" + { + context->from += 1; + context->to = 0; + } +#line 1714 "yy.tab.c" + break; + + case 31: +#line 171 "src/core/io/dl-parser.y" + { } +#line 1720 "yy.tab.c" + break; + + case 32: +#line 175 "src/core/io/dl-parser.y" + {} +#line 1726 "yy.tab.c" + break; + + case 33: +#line 177 "src/core/io/dl-parser.y" + {} +#line 1732 "yy.tab.c" + break; + + case 34: +#line 178 "src/core/io/dl-parser.y" + {} +#line 1738 "yy.tab.c" + break; + + case 35: +#line 179 "src/core/io/dl-parser.y" + {} +#line 1744 "yy.tab.c" + break; + + case 36: +#line 180 "src/core/io/dl-parser.y" + {} +#line 1750 "yy.tab.c" + break; + + case 37: +#line 181 "src/core/io/dl-parser.y" + {} +#line 1756 "yy.tab.c" + break; + + case 38: +#line 184 "src/core/io/dl-parser.y" + {} +#line 1762 "yy.tab.c" + break; + + case 39: +#line 185 "src/core/io/dl-parser.y" + {} +#line 1768 "yy.tab.c" + break; + + case 40: +#line 188 "src/core/io/dl-parser.y" + { + igraph_i_dl_add_edge_w((yyvsp[-3].integer)-1, (yyvsp[-2].integer)-1, (yyvsp[-1].real), context); } +#line 1775 "yy.tab.c" + break; + + case 41: +#line 190 "src/core/io/dl-parser.y" + { + igraph_i_dl_add_edge((yyvsp[-2].integer)-1, (yyvsp[-1].integer)-1, context); +} +#line 1783 "yy.tab.c" + break; + + case 42: +#line 194 "src/core/io/dl-parser.y" + { (yyval.integer)=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); } +#line 1790 "yy.tab.c" + break; + + case 43: +#line 197 "src/core/io/dl-parser.y" + {} +#line 1796 "yy.tab.c" + break; + + case 44: +#line 198 "src/core/io/dl-parser.y" + {} +#line 1802 "yy.tab.c" + break; + + case 45: +#line 201 "src/core/io/dl-parser.y" + { + igraph_i_dl_add_edge_w((yyvsp[-3].integer), (yyvsp[-2].integer), (yyvsp[-1].real), context); } +#line 1809 "yy.tab.c" + break; + + case 46: +#line 203 "src/core/io/dl-parser.y" + { + igraph_i_dl_add_edge((yyvsp[-2].integer), (yyvsp[-1].integer), context); + } +#line 1817 "yy.tab.c" + break; + + case 47: +#line 207 "src/core/io/dl-parser.y" + { (yyval.real)=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); } +#line 1824 "yy.tab.c" + break; + + case 48: +#line 210 "src/core/io/dl-parser.y" + { + /* Copy label list to trie, if needed */ + if (igraph_strvector_size(&context->labels) != 0) { + long int i, id, n=igraph_strvector_size(&context->labels); + for (i=0; itrie, + STR(context->labels, i), &id); + } + igraph_strvector_clear(&context->labels); + } + igraph_trie_get2(&context->trie, igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), &(yyval.integer)); + } +#line 1842 "yy.tab.c" + break; + + case 49: +#line 226 "src/core/io/dl-parser.y" + {} +#line 1848 "yy.tab.c" + break; + + case 50: +#line 228 "src/core/io/dl-parser.y" + {} +#line 1854 "yy.tab.c" + break; + + case 51: +#line 229 "src/core/io/dl-parser.y" + {} +#line 1860 "yy.tab.c" + break; + + case 52: +#line 230 "src/core/io/dl-parser.y" + {} +#line 1866 "yy.tab.c" + break; + + case 53: +#line 231 "src/core/io/dl-parser.y" + {} +#line 1872 "yy.tab.c" + break; + + case 54: +#line 232 "src/core/io/dl-parser.y" + {} +#line 1878 "yy.tab.c" + break; + + case 55: +#line 235 "src/core/io/dl-parser.y" + {} +#line 1884 "yy.tab.c" + break; + + case 56: +#line 236 "src/core/io/dl-parser.y" + {} +#line 1890 "yy.tab.c" + break; + + case 57: +#line 239 "src/core/io/dl-parser.y" + {} +#line 1896 "yy.tab.c" + break; + + case 58: +#line 241 "src/core/io/dl-parser.y" + { context->from=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); } +#line 1903 "yy.tab.c" + break; + + case 59: +#line 244 "src/core/io/dl-parser.y" + {} +#line 1909 "yy.tab.c" + break; + + case 60: +#line 244 "src/core/io/dl-parser.y" + { + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + context->from-1)); + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, (yyvsp[0].integer)-1)); + } +#line 1919 "yy.tab.c" + break; + + case 61: +#line 250 "src/core/io/dl-parser.y" + {} +#line 1925 "yy.tab.c" + break; + + case 62: +#line 251 "src/core/io/dl-parser.y" + {} +#line 1931 "yy.tab.c" + break; + + case 63: +#line 254 "src/core/io/dl-parser.y" + { } +#line 1937 "yy.tab.c" + break; + + case 64: +#line 256 "src/core/io/dl-parser.y" + { + context->from=(yyvsp[0].integer); + } +#line 1945 "yy.tab.c" + break; + + case 66: +#line 260 "src/core/io/dl-parser.y" + { + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + context->from)); + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, (yyvsp[0].integer))); + } +#line 1955 "yy.tab.c" + break; + + +#line 1959 "yy.tab.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[+*yyssp], yyvsp, yylsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} +#line 266 "src/core/io/dl-parser.y" + + +int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, + const char *s) { + snprintf(context->errmsg, + sizeof(context->errmsg)/sizeof(char)-1, + "%s in line %i", s, locp->first_line); + return 0; +} + +int igraph_i_dl_add_str(char *newstr, int length, + igraph_i_dl_parsedata_t *context) { + int tmp=newstr[length]; + newstr[length]='\0'; + IGRAPH_CHECK(igraph_strvector_add(&context->labels, newstr)); + newstr[length]=tmp; + return 0; +} + +int igraph_i_dl_add_edge(long int from, long int to, + igraph_i_dl_parsedata_t *context) { + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, from)); + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, to)); + return 0; +} + +int igraph_i_dl_add_edge_w(long int from, long int to, + igraph_real_t weight, + igraph_i_dl_parsedata_t *context) { + long int n=igraph_vector_size(&context->weights); + long int n2=igraph_vector_size(&context->edges)/2; + if (n != n2) { + igraph_vector_resize(&context->weights, n2); + for (; nweights)[n]=IGRAPH_NAN; + } + } + IGRAPH_CHECK(igraph_i_dl_add_edge(from, to, context)); + IGRAPH_CHECK(igraph_vector_push_back(&context->weights, weight)); + return 0; +} diff --git a/src/rigraph/core/io/dl-parser.h b/src/rigraph/core/io/dl-parser.h new file mode 100644 index 0000000..675958f --- /dev/null +++ b/src/rigraph/core/io/dl-parser.h @@ -0,0 +1,104 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + 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 + (at your option) 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_IGRAPH_DL_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_DL_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_dl_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + NUM = 258, + NEWLINE = 259, + DL = 260, + NEQ = 261, + DATA = 262, + LABELS = 263, + LABELSEMBEDDED = 264, + FORMATFULLMATRIX = 265, + FORMATEDGELIST1 = 266, + FORMATNODELIST1 = 267, + DIGIT = 268, + LABEL = 269, + EOFF = 270, + ERROR = 271 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 83 "src/core/io/dl-parser.y" + + long int integer; + igraph_real_t real; + +#line 79 "yy.tab.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_dl_yyparse (igraph_i_dl_parsedata_t* context); + +#endif /* !YY_IGRAPH_DL_YY_YY_TAB_H_INCLUDED */ diff --git a/src/rigraph/core/io/dl-parser.y b/src/rigraph/core/io/dl-parser.y new file mode 100644 index 0000000..db51de0 --- /dev/null +++ b/src/rigraph/core/io/dl-parser.y @@ -0,0 +1,306 @@ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "config.h" + +#include "../core/math.h" +#include "../internal/hacks.h" +#include "../io/dl-header.h" +#include "../io/parsers/dl-parser.h" +#include "../io/parsers/dl-lexer.h" + +#include + +int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, + const char *s); +int igraph_i_dl_add_str(char *newstr, int length, + igraph_i_dl_parsedata_t *context); +int igraph_i_dl_add_edge(long int from, long int to, + igraph_i_dl_parsedata_t *context); +int igraph_i_dl_add_edge_w(long int from, long int to, + igraph_real_t weight, + igraph_i_dl_parsedata_t *context); + +extern igraph_real_t igraph_pajek_get_number(const char *str, long int len); + +#define scanner context->scanner + +%} + +%pure-parser +/* bison: do not remove the equals sign; macOS XCode ships with bison 2.3, which + * needs the equals sign */ +%name-prefix="igraph_dl_yy" +%defines +%locations +%error-verbose +%parse-param { igraph_i_dl_parsedata_t* context } +%lex-param { void* scanner } + +%union { + long int integer; + igraph_real_t real; +}; + +%type integer elabel; +%type weight; + +%token NUM +%token NEWLINE +%token DL +%token NEQ +%token DATA +%token LABELS +%token LABELSEMBEDDED +%token FORMATFULLMATRIX +%token FORMATEDGELIST1 +%token FORMATNODELIST1 +%token DIGIT +%token LABEL +%token EOFF +%token ERROR + +%% + +input: DL NEQ integer NEWLINE rest trail eof { context->n=$3; }; + +trail: | trail newline; + +eof: | EOFF; + +rest: formfullmatrix { context->type=IGRAPH_DL_MATRIX; } + | edgelist1 { context->type=IGRAPH_DL_EDGELIST1; } + | nodelist1 { context->type=IGRAPH_DL_NODELIST1; } +; + +formfullmatrix: FORMATFULLMATRIX newline fullmatrix {} | fullmatrix {} ; + +newline: | NEWLINE ; + +fullmatrix: DATA newline fullmatrixdata { } + | LABELS newline labels newline DATA newline fullmatrixdata { } + | LABELSEMBEDDED newline DATA newline labeledfullmatrixdata { } +; + +labels: {} /* nothing, empty matrix */ + | labels newline LABEL { + igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context); } +; + +fullmatrixdata: {} | fullmatrixdata zerooneseq NEWLINE { + context->from += 1; + context->to = 0; + } ; + +zerooneseq: | zerooneseq zeroone { } ; + +zeroone: DIGIT { + if (igraph_dl_yyget_text(scanner)[0]=='1') { + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + context->from)); + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + context->to)); + } + context->to += 1; +} ; + +labeledfullmatrixdata: reallabeledfullmatrixdata {} ; + +reallabeledfullmatrixdata: labelseq NEWLINE labeledmatrixlines {} ; + +labelseq: | labelseq newline label ; + +label: LABEL { igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context); }; + +labeledmatrixlines: labeledmatrixline { + context->from += 1; + context->to = 0; + } + | labeledmatrixlines labeledmatrixline { + context->from += 1; + context->to = 0; + }; + +labeledmatrixline: LABEL zerooneseq NEWLINE { } ; + +/*-----------------------------------------------------------*/ + +edgelist1: FORMATEDGELIST1 newline edgelist1rest {} ; + +edgelist1rest: DATA newline edgelist1data {} + | LABELS newline labels newline DATA newline edgelist1data {} + | LABELSEMBEDDED newline DATA newline labelededgelist1data {} + | LABELS newline labels newline LABELSEMBEDDED newline DATA newline labelededgelist1data {} + | LABELSEMBEDDED newline LABELS newline labels newline DATA newline labelededgelist1data {} +; + +edgelist1data: {} /* nothing, empty graph */ + | edgelist1data edgelist1dataline {} +; + +edgelist1dataline: integer integer weight NEWLINE { + igraph_i_dl_add_edge_w($1-1, $2-1, $3, context); } + | integer integer NEWLINE { + igraph_i_dl_add_edge($1-1, $2-1, context); +} ; + +integer: NUM { $$=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); }; + +labelededgelist1data: {} /* nothing, empty graph */ + | labelededgelist1data labelededgelist1dataline {} +; + +labelededgelist1dataline: elabel elabel weight NEWLINE { + igraph_i_dl_add_edge_w($1, $2, $3, context); } + | elabel elabel NEWLINE { + igraph_i_dl_add_edge($1, $2, context); + }; + +weight: NUM { $$=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); }; + +elabel: LABEL { + /* Copy label list to trie, if needed */ + if (igraph_strvector_size(&context->labels) != 0) { + long int i, id, n=igraph_strvector_size(&context->labels); + for (i=0; itrie, + STR(context->labels, i), &id); + } + igraph_strvector_clear(&context->labels); + } + igraph_trie_get2(&context->trie, igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), &$$); + }; + +/*-----------------------------------------------------------*/ + +nodelist1: FORMATNODELIST1 newline nodelist1rest {} ; + +nodelist1rest: DATA nodelist1data {} + | LABELS newline labels newline DATA newline nodelist1data {} + | LABELSEMBEDDED newline DATA newline labelednodelist1data {} + | LABELS newline labels newline LABELSEMBEDDED newline DATA newline labelednodelist1data {} + | LABELSEMBEDDED newline LABELS newline labels newline DATA newline labelednodelist1data {} +; + +nodelist1data: {} /* nothing, empty graph */ + | nodelist1data nodelist1dataline {} +; + +nodelist1dataline: from tolist NEWLINE {} ; + +from: NUM { context->from=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); } ; + +tolist: {} | tolist integer { + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + context->from-1)); + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, $2-1)); + } ; + +labelednodelist1data: {} /* nothing, empty graph */ + | labelednodelist1data labelednodelist1dataline {} +; + +labelednodelist1dataline: fromelabel labeltolist NEWLINE { } ; + +fromelabel: elabel { + context->from=$1; + }; + +labeltolist: | labeltolist elabel { + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + context->from)); + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, $2)); + } ; + +%% + +int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, + const char *s) { + snprintf(context->errmsg, + sizeof(context->errmsg)/sizeof(char)-1, + "%s in line %i", s, locp->first_line); + return 0; +} + +int igraph_i_dl_add_str(char *newstr, int length, + igraph_i_dl_parsedata_t *context) { + int tmp=newstr[length]; + newstr[length]='\0'; + IGRAPH_CHECK(igraph_strvector_add(&context->labels, newstr)); + newstr[length]=tmp; + return 0; +} + +int igraph_i_dl_add_edge(long int from, long int to, + igraph_i_dl_parsedata_t *context) { + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, from)); + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, to)); + return 0; +} + +int igraph_i_dl_add_edge_w(long int from, long int to, + igraph_real_t weight, + igraph_i_dl_parsedata_t *context) { + long int n=igraph_vector_size(&context->weights); + long int n2=igraph_vector_size(&context->edges)/2; + if (n != n2) { + igraph_vector_resize(&context->weights, n2); + for (; nweights)[n]=IGRAPH_NAN; + } + } + IGRAPH_CHECK(igraph_i_dl_add_edge(from, to, context)); + IGRAPH_CHECK(igraph_vector_push_back(&context->weights, weight)); + return 0; +} diff --git a/src/rigraph/core/io/dl.c b/src/rigraph/core/io/dl.c new file mode 100644 index 0000000..8782ffc --- /dev/null +++ b/src/rigraph/core/io/dl.c @@ -0,0 +1,175 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_interface.h" + +#include "dl-header.h" + +int igraph_dl_yylex_init_extra (igraph_i_dl_parsedata_t* user_defined, + void* scanner); +void igraph_dl_yylex_destroy (void *scanner ); +int igraph_dl_yyparse (igraph_i_dl_parsedata_t* context); +void igraph_dl_yyset_in (FILE * in_str, void* yyscanner ); + +/** + * \function igraph_read_graph_dl + * \brief Read a file in the DL format of UCINET + * + * This is a simple textual file format used by UCINET. See + * http://www.analytictech.com/networks/dataentry.htm for + * examples. All the forms described here are supported by + * igraph. Vertex names and edge weights are also supported and they + * are added as attributes. (If an attribute handler is attached.) + * + * Note the specification does not mention whether the + * format is case sensitive or not. For igraph DL files are case + * sensitive, i.e. \c Larry and \c larry are not the same. + * \param graph Pointer to an uninitialized graph object. + * \param instream The stream to read the DL file from. + * \param directed Logical scalar, whether to create a directed file. + * \return Error code. + * + * Time complexity: linear in terms of the number of edges and + * vertices, except for the matrix format, which is quadratic in the + * number of vertices. + * + * \example examples/simple/igraph_read_graph_dl.c + */ + +int igraph_read_graph_dl(igraph_t *graph, FILE *instream, + igraph_bool_t directed) { + + int i; + long int n, n2; + const igraph_strvector_t *namevec = 0; + igraph_vector_ptr_t name, weight; + igraph_vector_ptr_t *pname = 0, *pweight = 0; + igraph_attribute_record_t namerec, weightrec; + const char *namestr = "name", *weightstr = "weight"; + igraph_i_dl_parsedata_t context; + + context.eof = 0; + context.mode = 0; + context.n = -1; + context.from = 0; + context.to = 0; + + IGRAPH_VECTOR_INIT_FINALLY(&context.edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&context.weights, 0); + IGRAPH_CHECK(igraph_strvector_init(&context.labels, 0)); + IGRAPH_FINALLY(igraph_strvector_destroy, &context.labels); + IGRAPH_TRIE_INIT_FINALLY(&context.trie, /*names=*/ 1); + + igraph_dl_yylex_init_extra(&context, &context.scanner); + IGRAPH_FINALLY(igraph_dl_yylex_destroy, context.scanner); + + igraph_dl_yyset_in(instream, context.scanner); + + i = igraph_dl_yyparse(&context); + if (i != 0) { + if (context.errmsg[0] != 0) { + IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else { + IGRAPH_ERROR("Cannot read DL file", IGRAPH_PARSEERROR); + } + } + + /* Extend the weight vector, if needed */ + n = igraph_vector_size(&context.weights); + n2 = igraph_vector_size(&context.edges) / 2; + if (n != 0) { + igraph_vector_resize(&context.weights, n2); + for (; n < n2; n++) { + VECTOR(context.weights)[n] = IGRAPH_NAN; + } + } + + /* Check number of vertices */ + if (n2 > 0) { + n = (long int) igraph_vector_max(&context.edges); + } else { + n = 0; + } + if (n >= context.n) { + IGRAPH_WARNING("More vertices than specified in `DL' file"); + context.n = n; + } + + /* OK, everything is ready, create the graph */ + IGRAPH_CHECK(igraph_empty(graph, 0, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + + /* Labels */ + if (igraph_strvector_size(&context.labels) != 0) { + namevec = (const igraph_strvector_t*) &context.labels; + } else if (igraph_trie_size(&context.trie) != 0) { + igraph_trie_getkeys(&context.trie, &namevec); + } + if (namevec) { + IGRAPH_CHECK(igraph_vector_ptr_init(&name, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &name); + pname = &name; + namerec.name = namestr; + namerec.type = IGRAPH_ATTRIBUTE_STRING; + namerec.value = namevec; + VECTOR(name)[0] = &namerec; + } + + /* Weights */ + if (igraph_vector_size(&context.weights) != 0) { + IGRAPH_CHECK(igraph_vector_ptr_init(&weight, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &weight); + pweight = &weight; + weightrec.name = weightstr; + weightrec.type = IGRAPH_ATTRIBUTE_NUMERIC; + weightrec.value = &context.weights; + VECTOR(weight)[0] = &weightrec; + } + + IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) context.n, pname)); + IGRAPH_CHECK(igraph_add_edges(graph, &context.edges, pweight)); + + if (pweight) { + igraph_vector_ptr_destroy(pweight); + IGRAPH_FINALLY_CLEAN(1); + } + + if (pname) { + igraph_vector_ptr_destroy(pname); + IGRAPH_FINALLY_CLEAN(1); + } + + /* don't destroy the graph itself but pop it from the finally stack */ + IGRAPH_FINALLY_CLEAN(1); + + igraph_trie_destroy(&context.trie); + igraph_strvector_destroy(&context.labels); + igraph_vector_destroy(&context.edges); + igraph_vector_destroy(&context.weights); + igraph_dl_yylex_destroy(context.scanner); + IGRAPH_FINALLY_CLEAN(5); + + return 0; +} diff --git a/src/rigraph/core/io/dot.c b/src/rigraph/core/io/dot.c new file mode 100644 index 0000000..f76cae8 --- /dev/null +++ b/src/rigraph/core/io/dot.c @@ -0,0 +1,319 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_version.h" + +#include "../graph/attributes.h" +//#include "internal/hacks.h" /* strcasecmp */ + +#include +#include + +#define CHECK(cmd) do { ret=cmd; if (ret<0) IGRAPH_ERROR("Write DOT format failed.", IGRAPH_EFILE); } while (0) + +static int igraph_i_dot_escape(const char *orig, char **result) { + /* do we have to escape the string at all? */ + long int i, j, len = (long int) strlen(orig), newlen = 0; + igraph_bool_t need_quote = 0, is_number = 1; + + /* first, check whether the string is equal to some reserved word */ + if (!strcasecmp(orig, "graph") || !strcasecmp(orig, "digraph") || + !strcasecmp(orig, "node") || !strcasecmp(orig, "edge") || + !strcasecmp(orig, "strict") || !strcasecmp(orig, "subgraph")) { + need_quote = 1; + is_number = 0; + } + + /* next, check whether we need to escape the string for any other reason. + * Also update is_number and newlen */ + for (i = 0; i < len; i++) { + if (isdigit(orig[i])) { + newlen++; + } else if (orig[i] == '-' && i == 0) { + newlen++; + } else if (orig[i] == '.') { + if (is_number) { + newlen++; + } else { + need_quote = 1; + newlen++; + } + } else if (orig[i] == '_') { + is_number = 0; newlen++; + } else if (orig[i] == '\\' || orig[i] == '"' || orig[i] == '\n') { + need_quote = 1; is_number = 0; newlen += 2; /* will be escaped */ + } else if (isalpha(orig[i])) { + is_number = 0; newlen++; + } else { + is_number = 0; need_quote = 1; newlen++; + } + } + if (is_number && orig[len - 1] == '.') { + is_number = 0; + } + if (!is_number && isdigit(orig[0])) { + need_quote = 1; + } + + if (is_number || !need_quote) { + *result = strdup(orig); + if (!*result) { + IGRAPH_ERROR("Writing DOT format failed.", IGRAPH_ENOMEM); + } + } else { + *result = IGRAPH_CALLOC(newlen + 3, char); + if (!*result) { + IGRAPH_ERROR("Writing DOT format failed.", IGRAPH_ENOMEM); + } + (*result)[0] = '"'; + (*result)[newlen + 1] = '"'; + (*result)[newlen + 2] = '\0'; + for (i = 0, j = 1; i < len; i++) { + if (orig[i] == '\n') { + (*result)[j++] = '\\'; + (*result)[j++] = 'n'; + continue; + } + if (orig[i] == '\\' || orig[i] == '"') { + (*result)[j++] = '\\'; + } + (*result)[j++] = orig[i]; + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_write_graph_dot + * \brief Write the graph to a stream in DOT format + * + * DOT is the format used by the widely known GraphViz software, see + * http://www.graphviz.org for details. The grammar of the DOT format + * can be found here: http://www.graphviz.org/doc/info/lang.html + * + * This is only a preliminary implementation, only the vertices + * and the edges are written but not the attributes or any visualization + * information. + * + * \param graph The graph to write to the stream. + * \param outstream The stream to write the file to. + * + * Time complexity: should be proportional to the number of characters written + * to the file. + * + * \sa \ref igraph_write_graph_graphml() for a more modern format. + * + * \example examples/simple/dot.c + */ +int igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { + int ret; + long int i, j; + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + char edgeop[3]; + igraph_strvector_t gnames, vnames, enames; + igraph_vector_t gtypes, vtypes, etypes; + igraph_vector_t numv; + igraph_strvector_t strv; + igraph_vector_bool_t boolv; + + IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0); + IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0); + IGRAPH_STRVECTOR_INIT_FINALLY(&enames, 0); + IGRAPH_VECTOR_INIT_FINALLY(>ypes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&etypes, 0); + IGRAPH_CHECK(igraph_i_attribute_get_info(graph, + &gnames, >ypes, + &vnames, &vtypes, + &enames, &etypes)); + + IGRAPH_VECTOR_INIT_FINALLY(&numv, 1); + IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&boolv, 1); + + CHECK(fprintf(outstream, "/* Created by igraph %s */\n", + IGRAPH_VERSION)); + + if (igraph_is_directed(graph)) { + CHECK(fprintf(outstream, "digraph {\n")); + strcpy(edgeop, "->"); + } else { + CHECK(fprintf(outstream, "graph {\n")); + strcpy(edgeop, "--"); + } + + /* Write the graph attributes */ + if (igraph_vector_size(>ypes) > 0) { + CHECK(fprintf(outstream, " graph [\n")); + for (i = 0; i < igraph_vector_size(>ypes); i++) { + char *name, *newname; + igraph_strvector_get(&gnames, i, &name); + IGRAPH_CHECK(igraph_i_dot_escape(name, &newname)); + IGRAPH_FINALLY(igraph_free, newname); + if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_graph_attr(graph, name, &numv)); + if (VECTOR(numv)[0] == (long)VECTOR(numv)[0]) { + CHECK(fprintf(outstream, " %s=%ld\n", newname, (long)VECTOR(numv)[0])); + } else { + CHECK(fprintf(outstream, " %s=", newname)); + CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0])); + CHECK(fputc('\n', outstream)); + } + } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { + char *s, *news; + IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); + igraph_strvector_get(&strv, 0, &s); + IGRAPH_CHECK(igraph_i_dot_escape(s, &news)); + CHECK(fprintf(outstream, " %s=%s\n", newname, news)); + IGRAPH_FREE(news); + } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_CHECK(igraph_i_attribute_get_bool_graph_attr(graph, name, &boolv)); + CHECK(fprintf(outstream, " %s=%d\n", newname, VECTOR(boolv)[0] ? 1 : 0)); + IGRAPH_WARNING("A boolean graph attribute was converted to numeric"); + } else { + IGRAPH_WARNING("A non-numeric, non-string, non-boolean graph attribute ignored"); + } + IGRAPH_FREE(newname); + IGRAPH_FINALLY_CLEAN(1); + } + CHECK(fprintf(outstream, " ];\n")); + } + + /* Write the vertices */ + if (igraph_vector_size(&vtypes) > 0) { + for (i = 0; i < no_of_nodes; i++) { + CHECK(fprintf(outstream, " %ld [\n", i)); + for (j = 0; j < igraph_vector_size(&vtypes); j++) { + char *name, *newname; + igraph_strvector_get(&vnames, j, &name); + IGRAPH_CHECK(igraph_i_dot_escape(name, &newname)); + IGRAPH_FINALLY(igraph_free, newname); + if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, name, igraph_vss_1((igraph_integer_t) i), &numv)); + if (VECTOR(numv)[0] == (long)VECTOR(numv)[0]) { + CHECK(fprintf(outstream, " %s=%ld\n", newname, (long)VECTOR(numv)[0])); + } else { + CHECK(fprintf(outstream, " %s=", newname)); + CHECK(igraph_real_fprintf_precise(outstream, + VECTOR(numv)[0])); + CHECK(fputc('\n', outstream)); + } + } else if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_STRING) { + char *s, *news; + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, igraph_vss_1((igraph_integer_t) i), &strv)); + igraph_strvector_get(&strv, 0, &s); + IGRAPH_CHECK(igraph_i_dot_escape(s, &news)); + CHECK(fprintf(outstream, " %s=%s\n", newname, news)); + IGRAPH_FREE(news); + } else if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph, name, igraph_vss_1((igraph_integer_t) i), &boolv)); + CHECK(fprintf(outstream, " %s=%d\n", newname, VECTOR(boolv)[0] ? 1 : 0)); + IGRAPH_WARNING("A boolean vertex attribute was converted to numeric"); + } else { + IGRAPH_WARNING("A non-numeric, non-string, non-boolean vertex attribute was ignored"); + } + IGRAPH_FREE(newname); + IGRAPH_FINALLY_CLEAN(1); + } + CHECK(fprintf(outstream, " ];\n")); + } + } else { + for (i = 0; i < no_of_nodes; i++) { + CHECK(fprintf(outstream, " %ld;\n", i)); + } + } + CHECK(fprintf(outstream, "\n")); + + /* Write the edges */ + if (igraph_vector_size(&etypes) > 0) { + for (i = 0; i < no_of_edges; i++) { + long int from = IGRAPH_FROM(graph, i); + long int to = IGRAPH_TO(graph, i); + CHECK(fprintf(outstream, " %ld %s %ld [\n", from, edgeop, to)); + for (j = 0; j < igraph_vector_size(&etypes); j++) { + char *name, *newname; + igraph_strvector_get(&enames, j, &name); + IGRAPH_CHECK(igraph_i_dot_escape(name, &newname)); + IGRAPH_FINALLY(igraph_free, newname); + if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, + name, igraph_ess_1((igraph_integer_t) i), &numv)); + if (VECTOR(numv)[0] == (long)VECTOR(numv)[0]) { + CHECK(fprintf(outstream, " %s=%ld\n", newname, (long)VECTOR(numv)[0])); + } else { + CHECK(fprintf(outstream, " %s=", newname)); + CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0])); + CHECK(fputc('\n', outstream)); + } + } else if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_STRING) { + char *s, *news; + IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, + name, igraph_ess_1((igraph_integer_t) i), &strv)); + igraph_strvector_get(&strv, 0, &s); + IGRAPH_CHECK(igraph_i_dot_escape(s, &news)); + CHECK(fprintf(outstream, " %s=%s\n", newname, news)); + IGRAPH_FREE(news); + } else if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr(graph, + name, igraph_ess_1((igraph_integer_t) i), &boolv)); + CHECK(fprintf(outstream, " %s=%d\n", newname, VECTOR(boolv)[0] ? 1 : 0)); + IGRAPH_WARNING("A boolean edge attribute was converted to numeric"); + } else { + IGRAPH_WARNING("A non-numeric, non-string graph attribute ignored"); + } + IGRAPH_FREE(newname); + IGRAPH_FINALLY_CLEAN(1); + } + CHECK(fprintf(outstream, " ];\n")); + } + } else { + for (i = 0; i < no_of_edges; i++) { + long int from = IGRAPH_FROM(graph, i); + long int to = IGRAPH_TO(graph, i); + CHECK(fprintf(outstream, " %ld %s %ld;\n", from, edgeop, to)); + } + } + CHECK(fprintf(outstream, "}\n")); + + igraph_vector_bool_destroy(&boolv); + igraph_strvector_destroy(&strv); + igraph_vector_destroy(&numv); + igraph_vector_destroy(&etypes); + igraph_vector_destroy(&vtypes); + igraph_vector_destroy(>ypes); + igraph_strvector_destroy(&enames); + igraph_strvector_destroy(&vnames); + igraph_strvector_destroy(&gnames); + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} + +#undef CHECK diff --git a/src/rigraph/core/io/edgelist.c b/src/rigraph/core/io/edgelist.c new file mode 100644 index 0000000..9ce2f37 --- /dev/null +++ b/src/rigraph/core/io/edgelist.c @@ -0,0 +1,159 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" + +#include "../core/interruption.h" + +#include + +/** + * \section about_loadsave + * + * These functions can write a graph to a file, or read a graph + * from a file. + * + * Note that as \a igraph uses the traditional C streams, it is + * possible to read/write files from/to memory, at least on GNU + * operating systems supporting \quote non-standard\endquote streams. + */ + +/** + * \ingroup loadsave + * \function igraph_read_graph_edgelist + * \brief Reads an edge list from a file and creates a graph. + * + * + * This format is simply a series of an even number of non-negative integers separated by + * whitespace. The integers represent vertex IDs. Placing each edge (i.e. pair of integers) + * on a separate line is not required, but it is recommended for readability. + * Edges of directed graphs are assumed to be in "from, to" order. + * \param graph Pointer to an uninitialized graph object. + * \param instream Pointer to a stream, it should be readable. + * \param n The number of vertices in the graph. If smaller than the + * largest integer in the file it will be ignored. It is thus + * safe to supply zero here. + * \param directed Logical, if true the graph is directed, if false it + * will be undirected. + * \return Error code: + * \c IGRAPH_PARSEERROR: if there is a + * problem reading the file, or the file is syntactically + * incorrect. + * + * Time complexity: O(|V|+|E|), the + * number of vertices plus the number of edges. It is assumed that + * reading an integer requires O(1) + * time. + */ +int igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, + igraph_integer_t n, igraph_bool_t directed) { + + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + long int from, to; + int c; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, 100)); + + /* skip all whitespace */ + do { + c = getc (instream); + } while (isspace (c)); + ungetc (c, instream); + + while (!feof(instream)) { + int read; + + IGRAPH_ALLOW_INTERRUPTION(); + + read = fscanf(instream, "%li", &from); + if (read != 1) { + IGRAPH_ERROR("parsing edgelist file failed", IGRAPH_PARSEERROR); + } + read = fscanf(instream, "%li", &to); + if (read != 1) { + IGRAPH_ERROR("parsing edgelist file failed", IGRAPH_PARSEERROR); + } + IGRAPH_CHECK(igraph_vector_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + + /* skip all whitespace */ + do { + c = getc (instream); + } while (isspace (c)); + ungetc (c, instream); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \ingroup loadsave + * \function igraph_write_graph_edgelist + * \brief Writes the edge list of a graph to a file. + * + * + * One edge is written per line, separated by a single space. + * For directed graphs edges are written in from, to order. + * \param graph The graph object to write. + * \param outstream Pointer to a stream, it should be writable. + * \return Error code: + * \c IGRAPH_EFILE if there is an error writing the + * file. + * + * Time complexity: O(|E|), the + * number of edges in the graph. It is assumed that writing an + * integer to the file requires O(1) + * time. + */ +int igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream) { + + igraph_eit_t it; + + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM), + &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t from, to; + int ret; + igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); + ret = fprintf(outstream, "%li %li\n", + (long int) from, + (long int) to); + if (ret < 0) { + IGRAPH_ERROR("Write error", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/io/gml-header.h b/src/rigraph/core/io/gml-header.h new file mode 100644 index 0000000..1a55365 --- /dev/null +++ b/src/rigraph/core/io/gml-header.h @@ -0,0 +1,44 @@ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge MA, 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_error.h" + +#include "gml-tree.h" + +typedef struct { + void *scanner; + int eof; + int depth; + char errmsg[300]; + igraph_gml_tree_t *tree; +} igraph_i_gml_parsedata_t; + +/** + * Initializes a GML parser context. + */ +int igraph_i_gml_parsedata_init(igraph_i_gml_parsedata_t* context); + +/** + * Destroys a GML parser context, freeing all memory currently used by the + * context. + */ +void igraph_i_gml_parsedata_destroy(igraph_i_gml_parsedata_t* context); diff --git a/src/rigraph/core/io/gml-lexer.c b/src/rigraph/core/io/gml-lexer.c new file mode 100644 index 0000000..717503d --- /dev/null +++ b/src/rigraph/core/io/gml-lexer.c @@ -0,0 +1,2319 @@ +#line 2 "src/core/io/gml-lexer.c" + +#line 4 "src/core/io/gml-lexer.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_gml_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_gml_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_gml_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_gml_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_gml_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_gml_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_gml_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_gml_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_gml_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_gml_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_gml_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_gml_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_gml_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_gml_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_gml_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_gml_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_gml_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_gml_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_gml_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_gml_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_gml_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_gml_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_gml_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_gml_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_gml_yylex_ALREADY_DEFINED +#else +#define yylex igraph_gml_yylex +#endif + +#ifdef yyrestart +#define igraph_gml_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_gml_yyrestart +#endif + +#ifdef yylex_init +#define igraph_gml_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_gml_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_gml_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_gml_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_gml_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_gml_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_gml_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_gml_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_gml_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_gml_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_gml_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_gml_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_gml_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_gml_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_gml_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_gml_yyget_in +#endif + +#ifdef yyset_in +#define igraph_gml_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_gml_yyset_in +#endif + +#ifdef yyget_out +#define igraph_gml_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_gml_yyget_out +#endif + +#ifdef yyset_out +#define igraph_gml_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_gml_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_gml_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_gml_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_gml_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_gml_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_gml_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_gml_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_gml_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_gml_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_gml_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_gml_yyget_column +#endif + +#ifdef yyset_column +#define igraph_gml_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_gml_yyset_column +#endif + +#ifdef yywrap +#define igraph_gml_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_gml_yywrap +#endif + +#ifdef yyget_lval +#define igraph_gml_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_gml_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_gml_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_gml_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_gml_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_gml_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_gml_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_gml_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_gml_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_gml_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_gml_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_gml_yyrealloc +#endif + +#ifdef yyfree +#define igraph_gml_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_gml_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define igraph_gml_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void __attribute__((unused)) yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 10 +#define YY_END_OF_BUFFER 11 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[29] = + { 0, + 0, 0, 11, 9, 8, 7, 7, 9, 9, 3, + 4, 5, 6, 1, 9, 7, 0, 2, 3, 0, + 0, 4, 0, 1, 3, 0, 3, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 5, 6, 1, 1, 1, 1, 1, + 1, 1, 7, 1, 8, 9, 1, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 1, 1, 1, + 1, 1, 1, 1, 11, 11, 11, 11, 12, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 13, 1, 14, 1, 11, 1, 11, 11, 11, 11, + + 12, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[16] = + { 0, + 1, 1, 1, 2, 1, 1, 1, 1, 1, 3, + 3, 3, 1, 1, 4 + } ; + +static const flex_int16_t yy_base[32] = + { 0, + 0, 12, 44, 45, 45, 39, 39, 36, 30, 10, + 0, 45, 45, 36, 35, 45, 32, 45, 0, 26, + 16, 0, 32, 45, 15, 22, 11, 45, 27, 14, + 30 + } ; + +static const flex_int16_t yy_def[32] = + { 0, + 28, 1, 28, 28, 28, 28, 28, 29, 28, 28, + 30, 28, 28, 28, 31, 28, 29, 28, 10, 28, + 28, 30, 31, 28, 28, 28, 28, 0, 28, 28, + 28 + } ; + +static const flex_int16_t yy_nxt[61] = + { 0, + 4, 5, 6, 7, 8, 4, 4, 9, 4, 10, + 11, 11, 12, 13, 4, 14, 22, 15, 20, 19, + 27, 21, 26, 26, 25, 27, 21, 17, 17, 17, + 23, 27, 23, 23, 24, 25, 18, 24, 16, 19, + 18, 16, 16, 28, 3, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28 + } ; + +static const flex_int16_t yy_chk[61] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 30, 2, 10, 10, + 27, 10, 21, 21, 25, 21, 25, 29, 29, 29, + 31, 26, 31, 31, 23, 20, 17, 15, 14, 9, + 8, 7, 6, 3, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "src/core/io/gml-lexer.l" +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ +#line 24 "src/core/io/gml-lexer.l" + +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "config.h" +#include + +#include "gml-header.h" +#include "gml-parser.h" + +#define YY_EXTRA_TYPE igraph_i_gml_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in GML parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +#line 736 "src/core/io/gml-lexer.c" +#define YY_NO_INPUT 1 +#line 738 "src/core/io/gml-lexer.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + yy_size_t yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + if ( yyleng > 0 ) \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ + (yytext[yyleng - 1] == '\n'); \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yylval = yylval_param; + + yylloc = yylloc_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 77 "src/core/io/gml-lexer.l" + + +#line 1026 "src/core/io/gml-lexer.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; + yy_current_state += YY_AT_BOL(); +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 29 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 45 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +#line 79 "src/core/io/gml-lexer.l" +{ /* comments ignored */ } + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 81 "src/core/io/gml-lexer.l" +{ return STRING; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 82 "src/core/io/gml-lexer.l" +{ return NUM; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 83 "src/core/io/gml-lexer.l" +{ return KEYWORD; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 84 "src/core/io/gml-lexer.l" +{ + yyextra->depth++; + if (yyextra->depth >= 32) { + return ERROR; + } else { + return LISTOPEN; + } + } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 92 "src/core/io/gml-lexer.l" +{ + yyextra->depth--; + if (yyextra->depth < 0) { + return ERROR; + } else { + return LISTCLOSE; + } + } + YY_BREAK +case 7: +/* rule 7 can match eol */ +YY_RULE_SETUP +#line 100 "src/core/io/gml-lexer.l" +{ } + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +#line 101 "src/core/io/gml-lexer.l" +{ /* other whitespace ignored */ } + YY_BREAK +case YY_STATE_EOF(INITIAL): +#line 103 "src/core/io/gml-lexer.l" +{ + if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return EOFF; + } + } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 111 "src/core/io/gml-lexer.l" +{ return ERROR; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 112 "src/core/io/gml-lexer.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1163 "src/core/io/gml-lexer.c" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap( yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin , yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + yy_current_state += YY_AT_BOL(); + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 15); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 29 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 15; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 29 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 28); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( yyscanner ) ) + return 0; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void __attribute__((unused)) yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +yy_size_t yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void yyset_column (int _column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = _column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int _bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 112 "src/core/io/gml-lexer.l" + + diff --git a/src/rigraph/core/io/gml-lexer.h b/src/rigraph/core/io/gml-lexer.h new file mode 100644 index 0000000..0d2c1e4 --- /dev/null +++ b/src/rigraph/core/io/gml-lexer.h @@ -0,0 +1,736 @@ +#ifndef igraph_gml_yyHEADER_H +#define igraph_gml_yyHEADER_H 1 +#define igraph_gml_yyIN_HEADER 1 + +#line 6 "src/core/io/gml-lexer.h" + +#line 8 "src/core/io/gml-lexer.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_gml_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_gml_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_gml_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_gml_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_gml_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_gml_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_gml_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_gml_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_gml_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_gml_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_gml_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_gml_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_gml_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_gml_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_gml_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_gml_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_gml_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_gml_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_gml_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_gml_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_gml_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_gml_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_gml_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_gml_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_gml_yylex_ALREADY_DEFINED +#else +#define yylex igraph_gml_yylex +#endif + +#ifdef yyrestart +#define igraph_gml_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_gml_yyrestart +#endif + +#ifdef yylex_init +#define igraph_gml_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_gml_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_gml_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_gml_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_gml_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_gml_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_gml_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_gml_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_gml_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_gml_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_gml_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_gml_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_gml_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_gml_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_gml_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_gml_yyget_in +#endif + +#ifdef yyset_in +#define igraph_gml_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_gml_yyset_in +#endif + +#ifdef yyget_out +#define igraph_gml_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_gml_yyget_out +#endif + +#ifdef yyset_out +#define igraph_gml_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_gml_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_gml_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_gml_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_gml_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_gml_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_gml_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_gml_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_gml_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_gml_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_gml_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_gml_yyget_column +#endif + +#ifdef yyset_column +#define igraph_gml_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_gml_yyset_column +#endif + +#ifdef yywrap +#define igraph_gml_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_gml_yywrap +#endif + +#ifdef yyget_lval +#define igraph_gml_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_gml_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_gml_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_gml_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_gml_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_gml_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_gml_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_gml_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_gml_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_gml_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_gml_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_gml_yyrealloc +#endif + +#ifdef yyfree +#define igraph_gml_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_gml_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define igraph_gml_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef igraph_gml_yy_create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef igraph_gml_yy_delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef igraph_gml_yy_scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef igraph_gml_yy_scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef igraph_gml_yy_scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef igraph_gml_yy_init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef igraph_gml_yy_flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef igraph_gml_yy_load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef igraph_gml_yy_switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef igraph_gml_yypush_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef igraph_gml_yypop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef igraph_gml_yyensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef igraph_gml_yylex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef igraph_gml_yyrestart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef igraph_gml_yylex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef igraph_gml_yylex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef igraph_gml_yylex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef igraph_gml_yyget_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef igraph_gml_yyset_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef igraph_gml_yyget_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef igraph_gml_yyset_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef igraph_gml_yyget_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef igraph_gml_yyset_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef igraph_gml_yyget_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef igraph_gml_yyset_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef igraph_gml_yyget_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef igraph_gml_yyget_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef igraph_gml_yyget_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef igraph_gml_yyset_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef igraph_gml_yyget_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef igraph_gml_yyset_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef igraph_gml_yywrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef igraph_gml_yyget_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef igraph_gml_yyset_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef igraph_gml_yyget_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef igraph_gml_yyset_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef igraph_gml_yyalloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef igraph_gml_yyrealloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef igraph_gml_yyfree_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef igraph_gml_yytext_ALREADY_DEFINED +#undef yytext +#endif +#ifndef igraph_gml_yyleng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef igraph_gml_yyin_ALREADY_DEFINED +#undef yyin +#endif +#ifndef igraph_gml_yyout_ALREADY_DEFINED +#undef yyout +#endif +#ifndef igraph_gml_yy_flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef igraph_gml_yylineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef igraph_gml_yytables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef igraph_gml_yytables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef igraph_gml_yyTABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 112 "src/core/io/gml-lexer.l" + + +#line 735 "src/core/io/gml-lexer.h" +#undef igraph_gml_yyIN_HEADER +#endif /* igraph_gml_yyHEADER_H */ diff --git a/src/rigraph/core/io/gml-lexer.l b/src/rigraph/core/io/gml-lexer.l new file mode 100644 index 0000000..3078dd7 --- /dev/null +++ b/src/rigraph/core/io/gml-lexer.l @@ -0,0 +1,112 @@ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "config.h" +#include + +#include "gml-header.h" +#include "gml-parser.h" + +#define YY_EXTRA_TYPE igraph_i_gml_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in GML parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +%} + +%option noyywrap +%option prefix="igraph_gml_yy" +%option nounput +%option noinput +%option nodefault +%option reentrant +%option bison-bridge +%option bison-locations + +digit [0-9] +whitespace [ \r\n\t] + +%% + +^#[^\n\r]*[\n]|[\r] { /* comments ignored */ } + +\"[^\x00\"]*\" { return STRING; } +\-?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { return NUM; } +[a-zA-Z_][a-zA-Z_0-9]* { return KEYWORD; } +\[ { + yyextra->depth++; + if (yyextra->depth >= 32) { + return ERROR; + } else { + return LISTOPEN; + } + } +\] { + yyextra->depth--; + if (yyextra->depth < 0) { + return ERROR; + } else { + return LISTCLOSE; + } + } +\n\r|\r\n|\r|\n { } +{whitespace} { /* other whitespace ignored */ } + +<> { + if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return EOFF; + } + } +. { return ERROR; } +%% diff --git a/src/rigraph/core/io/gml-parser.c b/src/rigraph/core/io/gml-parser.c new file mode 100644 index 0000000..e60758a --- /dev/null +++ b/src/rigraph/core/io/gml-parser.c @@ -0,0 +1,1987 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + 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 + (at your option) 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse igraph_gml_yyparse +#define yylex igraph_gml_yylex +#define yyerror igraph_gml_yyerror +#define yydebug igraph_gml_yydebug +#define yynerrs igraph_gml_yynerrs + +/* First part of user prologue. */ +#line 23 "src/core/io/gml-parser.y" + + +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include +#include +#include + +#include "igraph_error.h" +#include "igraph_memory.h" +#include "config.h" + +#include "../core/math.h" +#include "gml-header.h" +#include "gml-tree.h" +#include "gml-parser.h" +#include "gml-lexer.h" +//#include "internal/hacks.h" /* strcasecmp */ + +int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, + const char *s); +void igraph_i_gml_get_keyword(char *s, int len, void *res); +void igraph_i_gml_get_string(char *s, int len, void *res); +double igraph_i_gml_get_real(char *s, int len); +igraph_gml_tree_t *igraph_i_gml_make_numeric(char* s, int len, double value); +igraph_gml_tree_t *igraph_i_gml_make_numeric2(char* s, int len, + char *v, int vlen); +igraph_gml_tree_t *igraph_i_gml_make_string(char* s, int len, + char *value, int valuelen); +igraph_gml_tree_t *igraph_i_gml_make_list(char* s, int len, + igraph_gml_tree_t *list); +igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2); + +#define scanner context->scanner +#define USE(x) /*(x)*/ + + +#line 133 "yy.tab.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_IGRAPH_GML_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_GML_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_gml_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + STRING = 258, + NUM = 259, + KEYWORD = 260, + LISTOPEN = 261, + LISTCLOSE = 262, + EOFF = 263, + ERROR = 264 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 91 "src/core/io/gml-parser.y" + + struct { + char *s; + int len; + } str; + void *tree; + double real; + +#line 204 "yy.tab.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_gml_yyparse (igraph_i_gml_parsedata_t* context); + +#endif /* !YY_IGRAPH_GML_YY_YY_TAB_H_INCLUDED */ + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 6 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 14 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 10 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 7 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 12 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 17 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 264 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 119, 119, 120, 123, 124, 126, 128, 130, 132, + 136, 139, 142 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "STRING", "NUM", "KEYWORD", "LISTOPEN", + "LISTCLOSE", "EOFF", "ERROR", "$accept", "input", "list", "keyvalue", + "key", "num", "string", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264 +}; +# endif + +#define YYPACT_NINF (-4) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + 1, -4, 10, 0, -4, -2, -4, -4, -4, -4, + -4, 1, -4, -4, -4, 2, -4 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 0, 10, 0, 2, 4, 0, 1, 3, 5, 12, + 11, 0, 9, 6, 7, 0, 8 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -4, -4, 3, -3, 6, -4, -4 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 2, 3, 4, 5, 13, 14 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 8, 9, 10, 1, 11, 1, 1, 1, 7, 16, + 6, 12, 8, 0, 15 +}; + +static const yytype_int8 yycheck[] = +{ + 3, 3, 4, 5, 6, 5, 5, 5, 8, 7, + 0, 5, 15, -1, 11 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 5, 11, 12, 13, 14, 0, 8, 13, 3, + 4, 6, 14, 15, 16, 12, 7 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 10, 11, 11, 12, 12, 13, 13, 13, 13, + 14, 15, 16 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 2, 1, 2, 2, 2, 4, 2, + 1, 1, 1 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_gml_parsedata_t* context) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (yylocationp); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_gml_parsedata_t* context) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_gml_parsedata_t* context) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[+yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, context); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[+*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_gml_parsedata_t* context) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + switch (yytype) + { + case 5: /* KEYWORD */ +#line 114 "src/core/io/gml-parser.y" + { IGRAPH_FREE(((*yyvaluep).str).s); } +#line 1203 "yy.tab.c" + break; + + case 12: /* list */ +#line 115 "src/core/io/gml-parser.y" + { igraph_gml_tree_destroy(((*yyvaluep).tree)); } +#line 1209 "yy.tab.c" + break; + + case 13: /* keyvalue */ +#line 115 "src/core/io/gml-parser.y" + { igraph_gml_tree_destroy(((*yyvaluep).tree)); } +#line 1215 "yy.tab.c" + break; + + case 14: /* key */ +#line 114 "src/core/io/gml-parser.y" + { IGRAPH_FREE(((*yyvaluep).str).s); } +#line 1221 "yy.tab.c" + break; + + case 16: /* string */ +#line 114 "src/core/io/gml-parser.y" + { IGRAPH_FREE(((*yyvaluep).str).s); } +#line 1227 "yy.tab.c" + break; + + default: + break; + } + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (igraph_i_gml_parsedata_t* context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + 'yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, &yylloc, scanner); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 119 "src/core/io/gml-parser.y" + { context->tree=(yyvsp[0].tree); } +#line 1529 "yy.tab.c" + break; + + case 3: +#line 120 "src/core/io/gml-parser.y" + { context->tree=(yyvsp[-1].tree); } +#line 1535 "yy.tab.c" + break; + + case 4: +#line 123 "src/core/io/gml-parser.y" + { (yyval.tree)=(yyvsp[0].tree); } +#line 1541 "yy.tab.c" + break; + + case 5: +#line 124 "src/core/io/gml-parser.y" + { (yyval.tree)=igraph_i_gml_merge((yyvsp[-1].tree), (yyvsp[0].tree)); } +#line 1547 "yy.tab.c" + break; + + case 6: +#line 127 "src/core/io/gml-parser.y" + { (yyval.tree)=igraph_i_gml_make_numeric((yyvsp[-1].str).s, (yyvsp[-1].str).len, (yyvsp[0].real)); } +#line 1553 "yy.tab.c" + break; + + case 7: +#line 129 "src/core/io/gml-parser.y" + { (yyval.tree)=igraph_i_gml_make_string((yyvsp[-1].str).s, (yyvsp[-1].str).len, (yyvsp[0].str).s, (yyvsp[0].str).len); } +#line 1559 "yy.tab.c" + break; + + case 8: +#line 131 "src/core/io/gml-parser.y" + { (yyval.tree)=igraph_i_gml_make_list((yyvsp[-3].str).s, (yyvsp[-3].str).len, (yyvsp[-1].tree)); } +#line 1565 "yy.tab.c" + break; + + case 9: +#line 133 "src/core/io/gml-parser.y" + { (yyval.tree)=igraph_i_gml_make_numeric2((yyvsp[-1].str).s, (yyvsp[-1].str).len, (yyvsp[0].str).s, (yyvsp[0].str).len); } +#line 1571 "yy.tab.c" + break; + + case 10: +#line 136 "src/core/io/gml-parser.y" + { igraph_i_gml_get_keyword(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner), + &(yyval.str)); USE((yyvsp[0].str)); } +#line 1579 "yy.tab.c" + break; + + case 11: +#line 139 "src/core/io/gml-parser.y" + { (yyval.real)=igraph_i_gml_get_real(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner)); } +#line 1586 "yy.tab.c" + break; + + case 12: +#line 142 "src/core/io/gml-parser.y" + { igraph_i_gml_get_string(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner), + &(yyval.str)); } +#line 1594 "yy.tab.c" + break; + + +#line 1598 "yy.tab.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[+*yyssp], yyvsp, yylsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} +#line 146 "src/core/io/gml-parser.y" + + +int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, + const char *s) { + snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, + "Parse error in GML file, line %i (%s)", + locp->first_line, s); + return 0; +} + +void igraph_i_gml_get_keyword(char *s, int len, void *res) { + struct { char *s; int len; } *p=res; + p->s=IGRAPH_CALLOC(len+1, char); + if (!p->s) { + igraph_error("Cannot read GML file", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_PARSEERROR); + } + memcpy(p->s, s, sizeof(char)*len); + p->s[len]='\0'; + p->len=len; +} + +void igraph_i_gml_get_string(char *s, int len, void *res) { + struct { char *s; int len; } *p=res; + p->s=IGRAPH_CALLOC(len-1, char); + if (!p->s) { + igraph_error("Cannot read GML file", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_PARSEERROR); + } + memcpy(p->s, s+1, sizeof(char)*(len-2)); + p->s[len-2]='\0'; + p->len=len-2; +} + +double igraph_i_gml_get_real(char *s, int len) { + igraph_real_t num; + char tmp=s[len]; + s[len]='\0'; + sscanf(s, "%lf", &num); + s[len]=tmp; + return num; +} + +igraph_gml_tree_t *igraph_i_gml_make_numeric(char* s, int len, double value) { + igraph_gml_tree_t *t = IGRAPH_CALLOC(1, igraph_gml_tree_t); + + if (!t) { + igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + if (floor(value)==value) { + if (igraph_gml_tree_init_integer(t, s, len, value)) { + free(t); + return 0; + } + } else { + if (igraph_gml_tree_init_real(t, s, len, value)) { + free(t); + return 0; + } + } + + return t; +} + +igraph_gml_tree_t *igraph_i_gml_make_numeric2(char* s, int len, + char* v, int vlen) { + igraph_gml_tree_t *t = IGRAPH_CALLOC(1, igraph_gml_tree_t); + char tmp = v[vlen]; + + if (!t) { + igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + v[vlen]='\0'; + + /* if v == "inf" or v == "nan", the newly created tree node will take ownership + * of s. If the creation fails, we need to free s and v as well in order not + * to leak memory */ + if (strcasecmp(v, "inf")) { + if (igraph_gml_tree_init_real(t, s, len, IGRAPH_INFINITY)) { + free(t); + t = 0; + } + } else if (strcasecmp(v, "nan")) { + if (igraph_gml_tree_init_real(t, s, len, IGRAPH_NAN)) { + free(t); + t = 0; + } + } else { + igraph_error("Parse error", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_PARSEERROR); + free(t); + t = 0; + } + + v[vlen]=tmp; + free(v); + + if (t == 0) { + /* no new tree node was created so s has no owner any more */ + free(s); + } + + return t; +} + +igraph_gml_tree_t *igraph_i_gml_make_string(char* s, int len, + char *value, int valuelen) { + igraph_gml_tree_t *t = IGRAPH_CALLOC(1, igraph_gml_tree_t); + + if (!t) { + igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + /* if igraph_gml_tree_init_string succeeds, the newly created tree node takes + * ownership of 'value'. If it fails, we need to free 'value' ourselves in order + * not to leak memory */ + if (igraph_gml_tree_init_string(t, s, len, value, valuelen)) { + free(t); + free(value); + t = 0; + } + + return t; +} + +igraph_gml_tree_t *igraph_i_gml_make_list(char* s, int len, + igraph_gml_tree_t *list) { + + igraph_gml_tree_t *t=IGRAPH_CALLOC(1, igraph_gml_tree_t); + + if (!t) { + igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + if (igraph_gml_tree_init_tree(t, s, len, list)) { + free(t); + return 0; + } + + return t; +} + +igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2) { + + igraph_gml_tree_mergedest(t1, t2); + IGRAPH_FREE(t2); + + return t1; +} diff --git a/src/rigraph/core/io/gml-parser.h b/src/rigraph/core/io/gml-parser.h new file mode 100644 index 0000000..8434a6b --- /dev/null +++ b/src/rigraph/core/io/gml-parser.h @@ -0,0 +1,101 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + 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 + (at your option) 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_IGRAPH_GML_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_GML_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_gml_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + STRING = 258, + NUM = 259, + KEYWORD = 260, + LISTOPEN = 261, + LISTCLOSE = 262, + EOFF = 263, + ERROR = 264 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 91 "src/core/io/gml-parser.y" + + struct { + char *s; + int len; + } str; + void *tree; + double real; + +#line 76 "yy.tab.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_gml_yyparse (igraph_i_gml_parsedata_t* context); + +#endif /* !YY_IGRAPH_GML_YY_YY_TAB_H_INCLUDED */ diff --git a/src/rigraph/core/io/gml-parser.y b/src/rigraph/core/io/gml-parser.y new file mode 100644 index 0000000..241b39f --- /dev/null +++ b/src/rigraph/core/io/gml-parser.y @@ -0,0 +1,297 @@ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include +#include +#include + +#include "igraph_error.h" +#include "igraph_memory.h" +#include "config.h" + +#include "core/math.h" +#include "io/gml-header.h" +#include "io/gml-tree.h" +#include "io/parsers/gml-parser.h" +#include "io/parsers/gml-lexer.h" +#include "internal/hacks.h" /* strcasecmp */ + +int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, + const char *s); +void igraph_i_gml_get_keyword(char *s, int len, void *res); +void igraph_i_gml_get_string(char *s, int len, void *res); +double igraph_i_gml_get_real(char *s, int len); +igraph_gml_tree_t *igraph_i_gml_make_numeric(char* s, int len, double value); +igraph_gml_tree_t *igraph_i_gml_make_numeric2(char* s, int len, + char *v, int vlen); +igraph_gml_tree_t *igraph_i_gml_make_string(char* s, int len, + char *value, int valuelen); +igraph_gml_tree_t *igraph_i_gml_make_list(char* s, int len, + igraph_gml_tree_t *list); +igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2); + +#define scanner context->scanner +#define USE(x) /*(x)*/ + +%} + +%pure-parser +/* bison: do not remove the equals sign; macOS XCode ships with bison 2.3, which + * needs the equals sign */ +%name-prefix="igraph_gml_yy" +%defines +%locations +%error-verbose +%parse-param { igraph_i_gml_parsedata_t* context } +%lex-param { void *scanner } + +%union { + struct { + char *s; + int len; + } str; + void *tree; + double real; +} + +%type list; +%type keyvalue; +%type key; +%type num; +%type string; + +%token STRING +%token NUM +%token KEYWORD +%token LISTOPEN +%token LISTCLOSE +%token EOFF +%token ERROR + +%destructor { IGRAPH_FREE($$.s); } string key KEYWORD; +%destructor { igraph_gml_tree_destroy($$); } list keyvalue; + +%% + +input: list { context->tree=$1; } + | list EOFF { context->tree=$1; } +; + +list: keyvalue { $$=$1; } + | list keyvalue { $$=igraph_i_gml_merge($1, $2); }; + +keyvalue: key num + { $$=igraph_i_gml_make_numeric($1.s, $1.len, $2); } + | key string + { $$=igraph_i_gml_make_string($1.s, $1.len, $2.s, $2.len); } + | key LISTOPEN list LISTCLOSE + { $$=igraph_i_gml_make_list($1.s, $1.len, $3); } + | key key + { $$=igraph_i_gml_make_numeric2($1.s, $1.len, $2.s, $2.len); } +; + +key: KEYWORD { igraph_i_gml_get_keyword(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner), + &$$); USE($1); }; +num : NUM { $$=igraph_i_gml_get_real(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner)); }; + +string: STRING { igraph_i_gml_get_string(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner), + &$$); }; + +%% + +int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, + const char *s) { + snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, + "Parse error in GML file, line %i (%s)", + locp->first_line, s); + return 0; +} + +void igraph_i_gml_get_keyword(char *s, int len, void *res) { + struct { char *s; int len; } *p=res; + p->s=IGRAPH_CALLOC(len+1, char); + if (!p->s) { + igraph_error("Cannot read GML file", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_PARSEERROR); + } + memcpy(p->s, s, sizeof(char)*len); + p->s[len]='\0'; + p->len=len; +} + +void igraph_i_gml_get_string(char *s, int len, void *res) { + struct { char *s; int len; } *p=res; + p->s=IGRAPH_CALLOC(len-1, char); + if (!p->s) { + igraph_error("Cannot read GML file", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_PARSEERROR); + } + memcpy(p->s, s+1, sizeof(char)*(len-2)); + p->s[len-2]='\0'; + p->len=len-2; +} + +double igraph_i_gml_get_real(char *s, int len) { + igraph_real_t num; + char tmp=s[len]; + s[len]='\0'; + sscanf(s, "%lf", &num); + s[len]=tmp; + return num; +} + +igraph_gml_tree_t *igraph_i_gml_make_numeric(char* s, int len, double value) { + igraph_gml_tree_t *t = IGRAPH_CALLOC(1, igraph_gml_tree_t); + + if (!t) { + igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + if (floor(value)==value) { + if (igraph_gml_tree_init_integer(t, s, len, value)) { + free(t); + return 0; + } + } else { + if (igraph_gml_tree_init_real(t, s, len, value)) { + free(t); + return 0; + } + } + + return t; +} + +igraph_gml_tree_t *igraph_i_gml_make_numeric2(char* s, int len, + char* v, int vlen) { + igraph_gml_tree_t *t = IGRAPH_CALLOC(1, igraph_gml_tree_t); + char tmp = v[vlen]; + + if (!t) { + igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + v[vlen]='\0'; + + /* if v == "inf" or v == "nan", the newly created tree node will take ownership + * of s. If the creation fails, we need to free s and v as well in order not + * to leak memory */ + if (strcasecmp(v, "inf")) { + if (igraph_gml_tree_init_real(t, s, len, IGRAPH_INFINITY)) { + free(t); + t = 0; + } + } else if (strcasecmp(v, "nan")) { + if (igraph_gml_tree_init_real(t, s, len, IGRAPH_NAN)) { + free(t); + t = 0; + } + } else { + igraph_error("Parse error", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_PARSEERROR); + free(t); + t = 0; + } + + v[vlen]=tmp; + free(v); + + if (t == 0) { + /* no new tree node was created so s has no owner any more */ + free(s); + } + + return t; +} + +igraph_gml_tree_t *igraph_i_gml_make_string(char* s, int len, + char *value, int valuelen) { + igraph_gml_tree_t *t = IGRAPH_CALLOC(1, igraph_gml_tree_t); + + if (!t) { + igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + /* if igraph_gml_tree_init_string succeeds, the newly created tree node takes + * ownership of 'value'. If it fails, we need to free 'value' ourselves in order + * not to leak memory */ + if (igraph_gml_tree_init_string(t, s, len, value, valuelen)) { + free(t); + free(value); + t = 0; + } + + return t; +} + +igraph_gml_tree_t *igraph_i_gml_make_list(char* s, int len, + igraph_gml_tree_t *list) { + + igraph_gml_tree_t *t=IGRAPH_CALLOC(1, igraph_gml_tree_t); + + if (!t) { + igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; + } + + if (igraph_gml_tree_init_tree(t, s, len, list)) { + free(t); + return 0; + } + + return t; +} + +igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2) { + + igraph_gml_tree_mergedest(t1, t2); + IGRAPH_FREE(t2); + + return t1; +} diff --git a/src/rigraph/core/io/gml-tree.c b/src/rigraph/core/io/gml-tree.c new file mode 100644 index 0000000..8e798d1 --- /dev/null +++ b/src/rigraph/core/io/gml-tree.c @@ -0,0 +1,260 @@ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_memory.h" +#include "igraph_error.h" + +#include "gml-tree.h" + +#include + +int igraph_gml_tree_init_integer(igraph_gml_tree_t *t, + const char *name, int namelen, + igraph_integer_t value) { + + igraph_integer_t *p; + + IGRAPH_UNUSED(namelen); + + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1); + IGRAPH_CHECK(igraph_vector_char_init(&t->types, 1)); + IGRAPH_FINALLY(igraph_vector_char_destroy, &t->types); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); + + /* names */ + VECTOR(t->names)[0] = (void*)name; + + /* types */ + VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_INTEGER; + + /* children */ + p = IGRAPH_CALLOC(1, igraph_integer_t); + if (!p) { + IGRAPH_ERROR("Cannot create integer GML tree node", IGRAPH_ENOMEM); + } + *p = value; + VECTOR(t->children)[0] = p; + + IGRAPH_FINALLY_CLEAN(3); + return 0; +} + +int igraph_gml_tree_init_real(igraph_gml_tree_t *t, + const char *name, int namelen, + igraph_real_t value) { + + igraph_real_t *p; + + IGRAPH_UNUSED(namelen); + + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1); + IGRAPH_CHECK(igraph_vector_char_init(&t->types, 1)); + IGRAPH_FINALLY(igraph_vector_char_destroy, &t->types); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); + + /* names */ + VECTOR(t->names)[0] = (void*) name; + + /* types */ + VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_REAL; + + /* children */ + p = IGRAPH_CALLOC(1, igraph_real_t); + if (!p) { + IGRAPH_ERROR("Cannot create real GML tree node", IGRAPH_ENOMEM); + } + *p = value; + VECTOR(t->children)[0] = p; + + IGRAPH_FINALLY_CLEAN(3); + return 0; +} + +int igraph_gml_tree_init_string(igraph_gml_tree_t *t, + const char *name, int namelen, + const char *value, int valuelen) { + + IGRAPH_UNUSED(namelen); + IGRAPH_UNUSED(valuelen); + + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1); + IGRAPH_CHECK(igraph_vector_char_init(&t->types, 1)); + IGRAPH_FINALLY(igraph_vector_char_destroy, &t->types); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); + + /* names */ + VECTOR(t->names)[0] = (void*) name; + + /* types */ + VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_STRING; + + /* children */ + VECTOR(t->children)[0] = (void*)value; + + IGRAPH_FINALLY_CLEAN(3); + return 0; +} + +int igraph_gml_tree_init_tree(igraph_gml_tree_t *t, + const char *name, int namelen, + igraph_gml_tree_t *value) { + + IGRAPH_UNUSED(namelen); + + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1); + IGRAPH_CHECK(igraph_vector_char_init(&t->types, 1)); + IGRAPH_FINALLY(igraph_vector_char_destroy, &t->types); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); + + /* names */ + VECTOR(t->names)[0] = (void*)name; + + /* types */ + VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_TREE; + + /* children */ + VECTOR(t->children)[0] = value; + + IGRAPH_FINALLY_CLEAN(3); + return 0; + +} + +/* merge is destructive, the _second_ tree is destroyed */ +int igraph_gml_tree_mergedest(igraph_gml_tree_t *t1, igraph_gml_tree_t *t2) { + long int i, n = igraph_vector_ptr_size(&t2->children); + for (i = 0; i < n; i++) { + IGRAPH_CHECK(igraph_vector_ptr_push_back(&t1->names, VECTOR(t2->names)[i])); + IGRAPH_CHECK(igraph_vector_char_push_back(&t1->types, VECTOR(t2->types)[i])); + IGRAPH_CHECK(igraph_vector_ptr_push_back(&t1->children, + VECTOR(t2->children)[i])); + } + + igraph_vector_ptr_destroy(&t2->names); + igraph_vector_char_destroy(&t2->types); + igraph_vector_ptr_destroy(&t2->children); + return 0; +} + +void igraph_gml_tree_destroy(igraph_gml_tree_t *t) { + + long int i, n = igraph_vector_ptr_size(&t->children); + for (i = 0; i < n; i++) { + int type = VECTOR(t->types)[i]; + switch (type) { + case IGRAPH_I_GML_TREE_TREE: + igraph_gml_tree_destroy(VECTOR(t->children)[i]); + IGRAPH_FREE(VECTOR(t->names)[i]); + break; + case IGRAPH_I_GML_TREE_INTEGER: + IGRAPH_FREE(VECTOR(t->children)[i]); + IGRAPH_FREE(VECTOR(t->names)[i]); + break; + case IGRAPH_I_GML_TREE_REAL: + IGRAPH_FREE(VECTOR(t->children)[i]); + IGRAPH_FREE(VECTOR(t->names)[i]); + break; + case IGRAPH_I_GML_TREE_STRING: + IGRAPH_FREE(VECTOR(t->children)[i]); + IGRAPH_FREE(VECTOR(t->names)[i]); + break; + case IGRAPH_I_GML_TREE_DELETED: + break; + } + } + igraph_vector_ptr_destroy(&t->names); + igraph_vector_char_destroy(&t->types); + igraph_vector_ptr_destroy(&t->children); + IGRAPH_FREE(t); +} + +long int igraph_gml_tree_length(const igraph_gml_tree_t *t) { + return igraph_vector_ptr_size(&t->names); +} + +long int igraph_gml_tree_find(const igraph_gml_tree_t *t, + const char *name, long int from) { + + long int size = igraph_vector_ptr_size(&t->names); + while ( from < size && (! VECTOR(t->names)[from] || + strcmp(VECTOR(t->names)[from], name)) ) { + from++; + } + + if (from == size) { + from = -1; + } + return from; +} + +long int igraph_gml_tree_findback(const igraph_gml_tree_t *t, + const char *name, long int from) { + while ( from >= 0 && (! VECTOR(t->names)[from] || + strcmp(VECTOR(t->names)[from], name)) ) { + from--; + } + + return from; +} + +int igraph_gml_tree_type(const igraph_gml_tree_t *t, long int pos) { + return VECTOR(t->types)[pos]; +} + +const char *igraph_gml_tree_name(const igraph_gml_tree_t *t, long int pos) { + return VECTOR(t->names)[pos]; +} + +igraph_integer_t igraph_gml_tree_get_integer(const igraph_gml_tree_t *t, + long int pos) { + igraph_integer_t *i = VECTOR(t->children)[pos]; + return *i; +} + +igraph_real_t igraph_gml_tree_get_real(const igraph_gml_tree_t *t, + long int pos) { + igraph_real_t *d = VECTOR(t->children)[pos]; + return *d; +} + +const char *igraph_gml_tree_get_string(const igraph_gml_tree_t *t, + long int pos) { + const char *s = VECTOR(t->children)[pos]; + return s; +} + +igraph_gml_tree_t *igraph_gml_tree_get_tree(const igraph_gml_tree_t *t, + long int pos) { + igraph_gml_tree_t *tree = VECTOR(t->children)[pos]; + return tree; +} + +void igraph_gml_tree_delete(igraph_gml_tree_t *t, long int pos) { + if (VECTOR(t->types)[pos] == IGRAPH_I_GML_TREE_TREE) { + igraph_gml_tree_destroy(VECTOR(t->children)[pos]); + } + IGRAPH_FREE(VECTOR(t->names)[pos]); + IGRAPH_FREE(VECTOR(t->children)[pos]); + VECTOR(t->children)[pos] = 0; + VECTOR(t->names)[pos] = 0; + VECTOR(t->types)[pos] = IGRAPH_I_GML_TREE_DELETED; +} diff --git a/src/rigraph/core/io/gml-tree.h b/src/rigraph/core/io/gml-tree.h new file mode 100644 index 0000000..074721c --- /dev/null +++ b/src/rigraph/core/io/gml-tree.h @@ -0,0 +1,82 @@ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef REST_GML_TREE_H +#define REST_GML_TREE_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" + +__BEGIN_DECLS + +typedef enum { IGRAPH_I_GML_TREE_TREE = 0, + IGRAPH_I_GML_TREE_INTEGER, + IGRAPH_I_GML_TREE_REAL, + IGRAPH_I_GML_TREE_STRING, + IGRAPH_I_GML_TREE_DELETED + } igraph_i_gml_tree_type_t; + +typedef struct igraph_gml_tree_t { + igraph_vector_ptr_t names; + igraph_vector_char_t types; + igraph_vector_ptr_t children; +} igraph_gml_tree_t; + +int igraph_gml_tree_init_integer(igraph_gml_tree_t *t, + const char *name, int namelen, + igraph_integer_t value); +int igraph_gml_tree_init_real(igraph_gml_tree_t *t, + const char *name, int namelen, + igraph_real_t value); +int igraph_gml_tree_init_string(igraph_gml_tree_t *t, + const char *name, int namelen, + const char *value, int valuelen); +int igraph_gml_tree_init_tree(igraph_gml_tree_t *t, + const char *name, int namelen, + igraph_gml_tree_t *value); +void igraph_gml_tree_destroy(igraph_gml_tree_t *t); + +void igraph_gml_tree_delete(igraph_gml_tree_t *t, long int pos); +int igraph_gml_tree_mergedest(igraph_gml_tree_t *t1, igraph_gml_tree_t *t2); + +long int igraph_gml_tree_length(const igraph_gml_tree_t *t); +long int igraph_gml_tree_find(const igraph_gml_tree_t *t, + const char *name, long int from); +long int igraph_gml_tree_findback(const igraph_gml_tree_t *t, + const char *name, long int from); +int igraph_gml_tree_type(const igraph_gml_tree_t *t, long int pos); +const char *igraph_gml_tree_name(const igraph_gml_tree_t *t, long int pos); +igraph_integer_t igraph_gml_tree_get_integer(const igraph_gml_tree_t *t, + long int pos); +igraph_real_t igraph_gml_tree_get_real(const igraph_gml_tree_t *t, + long int pos); +const char *igraph_gml_tree_get_string(const igraph_gml_tree_t *t, + long int pos); + +igraph_gml_tree_t *igraph_gml_tree_get_tree(const igraph_gml_tree_t *t, + long int pos); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/io/gml.c b/src/rigraph/core/io/gml.c new file mode 100644 index 0000000..83d2f9b --- /dev/null +++ b/src/rigraph/core/io/gml.c @@ -0,0 +1,794 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_version.h" + +#include "../core/trie.h" +#include "../graph/attributes.h" +#include "gml-header.h" + +#include +#include +#include + +int igraph_gml_yylex_init_extra (igraph_i_gml_parsedata_t* user_defined, + void* scanner); +void igraph_gml_yylex_destroy (void *scanner ); +int igraph_gml_yyparse (igraph_i_gml_parsedata_t* context); +void igraph_gml_yyset_in (FILE * in_str, void* yyscanner ); + +static void igraph_i_gml_destroy_attrs(igraph_vector_ptr_t **ptr) { + long int i; + igraph_vector_ptr_t *vec; + for (i = 0; i < 3; i++) { + long int j; + vec = ptr[i]; + for (j = 0; j < igraph_vector_ptr_size(vec); j++) { + igraph_attribute_record_t *atrec = VECTOR(*vec)[j]; + if (atrec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *value = (igraph_vector_t*)atrec->value; + if (value != 0) { + igraph_vector_destroy(value); + IGRAPH_FREE(value); + } + } else { + igraph_strvector_t *value = (igraph_strvector_t*)atrec->value; + if (value != 0) { + igraph_strvector_destroy(value); + IGRAPH_FREE(value); + } + } + IGRAPH_FREE(atrec->name); + IGRAPH_FREE(atrec); + } + igraph_vector_ptr_destroy(vec); + } +} + +static int igraph_i_gml_toreal(igraph_gml_tree_t *node, long int pos, igraph_real_t *result) { + + igraph_real_t value = 0.0; + int type = igraph_gml_tree_type(node, pos); + + switch (type) { + case IGRAPH_I_GML_TREE_INTEGER: + value = igraph_gml_tree_get_integer(node, pos); + break; + case IGRAPH_I_GML_TREE_REAL: + value = igraph_gml_tree_get_real(node, pos); + break; + default: + IGRAPH_ERROR("Internal error while parsing GML file.", IGRAPH_FAILURE); + break; + } + + *result = value; + return IGRAPH_SUCCESS; +} + +static const char *igraph_i_gml_tostring(igraph_gml_tree_t *node, long int pos) { + + int type = igraph_gml_tree_type(node, pos); + static char tmp[256]; + const char *p = tmp; + long int i; + igraph_real_t d; + + switch (type) { + case IGRAPH_I_GML_TREE_INTEGER: + i = igraph_gml_tree_get_integer(node, pos); + snprintf(tmp, sizeof(tmp) / sizeof(char), "%li", i); + break; + case IGRAPH_I_GML_TREE_REAL: + d = igraph_gml_tree_get_real(node, pos); + igraph_real_snprintf_precise(tmp, sizeof(tmp) / sizeof(char), d); + break; + case IGRAPH_I_GML_TREE_STRING: + p = igraph_gml_tree_get_string(node, pos); + break; + default: + break; + } + + return p; +} + +int igraph_i_gml_parsedata_init(igraph_i_gml_parsedata_t* context) { + context->eof = 0; + context->depth = 0; + context->scanner = 0; + context->tree = 0; + context->errmsg[0] = 0; + + return IGRAPH_SUCCESS; +} + +void igraph_i_gml_parsedata_destroy(igraph_i_gml_parsedata_t* context) { + if (context->tree != 0) { + igraph_gml_tree_destroy(context->tree); + context->tree = 0; + } + + if (context->scanner != 0) { + igraph_gml_yylex_destroy(context->scanner); + context->scanner = 0; + } +} + +/** + * \function igraph_read_graph_gml + * \brief Read a graph in GML format. + * + * GML is a simple textual format, see + * http://www.fim.uni-passau.de/en/fim/faculty/chairs/theoretische-informatik/projects.html for details. + * + * + * Although all syntactically correct GML can be parsed, + * we implement only a subset of this format, some attributes might be + * ignored. Here is a list of all the differences: + * \olist + * \oli Only node and edge attributes are + * used, and only if they have a simple type: integer, real or + * string. So if an attribute is an array or a record, then it is + * ignored. This is also true if only some values of the + * attribute are complex. + * \oli Top level attributes except for Version and the + * first graph attribute are completely ignored. + * \oli Graph attributes except for node and + * edge are completely ignored. + * \oli There is no maximum line length. + * \oli There is no maximum keyword length. + * \oli Character entities in strings are not interpreted. + * \oli We allow inf (infinity) and nan + * (not a number) as a real number. This is case insensitive, so + * nan, NaN and NAN are equal. + * \endolist + * + * Please contact us if you cannot live with these + * limitations of the GML parser. + * \param graph Pointer to an uninitialized graph object. + * \param instream The stream to read the GML file from. + * \return Error code. + * + * Time complexity: should be proportional to the length of the file. + * + * \sa \ref igraph_read_graph_graphml() for a more modern format, + * \ref igraph_write_graph_gml() for writing GML files. + * + * \example examples/simple/gml.c + */ +int igraph_read_graph_gml(igraph_t *graph, FILE *instream) { + + long int i, p; + long int no_of_nodes = 0, no_of_edges = 0; + igraph_trie_t trie; + igraph_vector_t edges; + igraph_bool_t directed = IGRAPH_UNDIRECTED; + igraph_gml_tree_t *gtree; + long int gidx; + igraph_trie_t vattrnames; + igraph_trie_t eattrnames; + igraph_trie_t gattrnames; + igraph_vector_ptr_t gattrs = IGRAPH_VECTOR_PTR_NULL, + vattrs = IGRAPH_VECTOR_PTR_NULL, eattrs = IGRAPH_VECTOR_PTR_NULL; + igraph_vector_ptr_t *attrs[3]; + long int edgeptr = 0; + igraph_i_gml_parsedata_t context; + + attrs[0] = &gattrs; attrs[1] = &vattrs; attrs[2] = &eattrs; + + IGRAPH_CHECK(igraph_i_gml_parsedata_init(&context)); + IGRAPH_FINALLY(igraph_i_gml_parsedata_destroy, &context); + + igraph_gml_yylex_init_extra(&context, &context.scanner); + + igraph_gml_yyset_in(instream, context.scanner); + + i = igraph_gml_yyparse(&context); + if (i != 0) { + if (context.errmsg[0] != 0) { + IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else { + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_PARSEERROR); + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + /* Check version, if present, integer and not '1' then ignored */ + i = igraph_gml_tree_find(context.tree, "Version", 0); + if (i >= 0 && + igraph_gml_tree_type(context.tree, i) == IGRAPH_I_GML_TREE_INTEGER && + igraph_gml_tree_get_integer(context.tree, i) != 1) { + IGRAPH_ERROR("Unknown GML version.", IGRAPH_UNIMPLEMENTED); + /* RETURN HERE!!!! */ + } + + /* get the graph */ + gidx = igraph_gml_tree_find(context.tree, "graph", 0); + if (gidx == -1) { + IGRAPH_ERROR("No 'graph' object in GML file.", IGRAPH_PARSEERROR); + } + if (igraph_gml_tree_type(context.tree, gidx) != + IGRAPH_I_GML_TREE_TREE) { + IGRAPH_ERROR("Invalid type for 'graph' object in GML file.", IGRAPH_PARSEERROR); + } + gtree = igraph_gml_tree_get_tree(context.tree, gidx); + + IGRAPH_FINALLY(igraph_i_gml_destroy_attrs, attrs); + igraph_vector_ptr_init(&gattrs, 0); + igraph_vector_ptr_init(&vattrs, 0); + igraph_vector_ptr_init(&eattrs, 0); + + IGRAPH_TRIE_INIT_FINALLY(&trie, 0); + IGRAPH_TRIE_INIT_FINALLY(&vattrnames, 0); + IGRAPH_TRIE_INIT_FINALLY(&eattrnames, 0); + IGRAPH_TRIE_INIT_FINALLY(&gattrnames, 0); + + /* Is is directed? */ + i = igraph_gml_tree_find(gtree, "directed", 0); + if (i >= 0 && igraph_gml_tree_type(gtree, i) == IGRAPH_I_GML_TREE_INTEGER) { + if (igraph_gml_tree_get_integer(gtree, i) == 1) { + directed = IGRAPH_DIRECTED; + } + } + + /* Now we go over all objects in the graph and collect the attribute names and + types. Plus we collect node ids. We also do some checks. */ + for (i = 0; i < igraph_gml_tree_length(gtree); i++) { + long int j; + char cname[100]; + const char *name = igraph_gml_tree_name(gtree, i); + if (!strcmp(name, "node")) { + igraph_gml_tree_t *node; + igraph_bool_t hasid; + no_of_nodes++; + if (igraph_gml_tree_type(gtree, i) != IGRAPH_I_GML_TREE_TREE) { + IGRAPH_ERROR("'node' is not a list in GML file.", IGRAPH_PARSEERROR); + } + node = igraph_gml_tree_get_tree(gtree, i); + hasid = 0; + for (j = 0; j < igraph_gml_tree_length(node); j++) { + const char *name = igraph_gml_tree_name(node, j); + long int trieid, triesize = igraph_trie_size(&vattrnames); + IGRAPH_CHECK(igraph_trie_get(&vattrnames, name, &trieid)); + if (trieid == triesize) { + /* new attribute */ + igraph_attribute_record_t *atrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + int type = igraph_gml_tree_type(node, j); + if (!atrec) { + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(&vattrs, atrec)); + atrec->name = strdup(name); + if (type == IGRAPH_I_GML_TREE_INTEGER || type == IGRAPH_I_GML_TREE_REAL) { + atrec->type = IGRAPH_ATTRIBUTE_NUMERIC; + } else { + atrec->type = IGRAPH_ATTRIBUTE_STRING; + } + } else { + /* already seen, should we update type? */ + igraph_attribute_record_t *atrec = VECTOR(vattrs)[trieid]; + int type1 = atrec->type; + int type2 = igraph_gml_tree_type(node, j); + if (type1 == IGRAPH_ATTRIBUTE_NUMERIC && type2 == IGRAPH_I_GML_TREE_STRING) { + atrec->type = IGRAPH_ATTRIBUTE_STRING; + } + } + /* check id */ + if (!hasid && !strcmp(name, "id")) { + long int id; + if (igraph_gml_tree_type(node, j) != IGRAPH_I_GML_TREE_INTEGER) { + IGRAPH_ERROR("Non-integer node id in GML file.", IGRAPH_PARSEERROR); + } + id = igraph_gml_tree_get_integer(node, j); + snprintf(cname, sizeof(cname) / sizeof(char) -1, "%li", id); + IGRAPH_CHECK(igraph_trie_get(&trie, cname, &id)); + hasid = 1; + } + } + if (!hasid) { + IGRAPH_ERROR("Node without 'id' while parsing GML file.", IGRAPH_PARSEERROR); + } + } else if (!strcmp(name, "edge")) { + igraph_gml_tree_t *edge; + igraph_bool_t has_source = 0, has_target = 0; + no_of_edges++; + if (igraph_gml_tree_type(gtree, i) != IGRAPH_I_GML_TREE_TREE) { + IGRAPH_ERROR("'edge' is not a list in GML file.", IGRAPH_PARSEERROR); + } + edge = igraph_gml_tree_get_tree(gtree, i); + has_source = has_target = 0; + for (j = 0; j < igraph_gml_tree_length(edge); j++) { + const char *name = igraph_gml_tree_name(edge, j); + if (!strcmp(name, "source")) { + has_source = 1; + if (igraph_gml_tree_type(edge, j) != IGRAPH_I_GML_TREE_INTEGER) { + IGRAPH_ERROR("Non-integer 'source' for an edge in GML file.", + IGRAPH_PARSEERROR); + } + } else if (!strcmp(name, "target")) { + has_target = 1; + if (igraph_gml_tree_type(edge, j) != IGRAPH_I_GML_TREE_INTEGER) { + IGRAPH_ERROR("Non-integer 'source' for an edge in GML file.", + IGRAPH_PARSEERROR); + } + } else { + long int trieid, triesize = igraph_trie_size(&eattrnames); + IGRAPH_CHECK(igraph_trie_get(&eattrnames, name, &trieid)); + if (trieid == triesize) { + /* new attribute */ + igraph_attribute_record_t *atrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + int type = igraph_gml_tree_type(edge, j); + if (!atrec) { + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(&eattrs, atrec)); + atrec->name = strdup(name); + if (type == IGRAPH_I_GML_TREE_INTEGER || type == IGRAPH_I_GML_TREE_REAL) { + atrec->type = IGRAPH_ATTRIBUTE_NUMERIC; + } else { + atrec->type = IGRAPH_ATTRIBUTE_STRING; + } + } else { + /* already seen, should we update type? */ + igraph_attribute_record_t *atrec = VECTOR(eattrs)[trieid]; + int type1 = atrec->type; + int type2 = igraph_gml_tree_type(edge, j); + if (type1 == IGRAPH_ATTRIBUTE_NUMERIC && type2 == IGRAPH_I_GML_TREE_STRING) { + atrec->type = IGRAPH_ATTRIBUTE_STRING; + } + } + } + } /* for */ + if (!has_source) { + IGRAPH_ERROR("No 'source' for edge in GML file.", IGRAPH_PARSEERROR); + } + if (!has_target) { + IGRAPH_ERROR("No 'target' for edge in GML file.", IGRAPH_PARSEERROR); + } + } else { + /* anything to do? Maybe add as graph attribute.... */ + } + } + + /* check vertex id uniqueness */ + if (igraph_trie_size(&trie) != no_of_nodes) { + IGRAPH_ERROR("Node 'id' not unique in GML file.", IGRAPH_PARSEERROR); + } + + /* now we allocate the vectors and strvectors for the attributes */ + for (i = 0; i < igraph_vector_ptr_size(&vattrs); i++) { + igraph_attribute_record_t *atrec = VECTOR(vattrs)[i]; + int type = atrec->type; + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *p = IGRAPH_CALLOC(1, igraph_vector_t); + atrec->value = p; + IGRAPH_CHECK(igraph_vector_init(p, no_of_nodes)); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *p = IGRAPH_CALLOC(1, igraph_strvector_t); + atrec->value = p; + IGRAPH_CHECK(igraph_strvector_init(p, no_of_nodes)); + } else { + IGRAPH_WARNING("A composite attribute was ignored in the GML file."); + } + } + + for (i = 0; i < igraph_vector_ptr_size(&eattrs); i++) { + igraph_attribute_record_t *atrec = VECTOR(eattrs)[i]; + int type = atrec->type; + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *p = IGRAPH_CALLOC(1, igraph_vector_t); + atrec->value = p; + IGRAPH_CHECK(igraph_vector_init(p, no_of_edges)); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *p = IGRAPH_CALLOC(1, igraph_strvector_t); + atrec->value = p; + IGRAPH_CHECK(igraph_strvector_init(p, no_of_edges)); + } else { + IGRAPH_WARNING("A composite attribute was ignored in the GML file."); + } + } + + /* Ok, now the edges, attributes too */ + IGRAPH_CHECK(igraph_vector_resize(&edges, no_of_edges * 2)); + p = -1; + while ( (p = igraph_gml_tree_find(gtree, "edge", p + 1)) != -1) { + igraph_gml_tree_t *edge; + long int from, to, fromidx = 0, toidx = 0; + char name[100]; + long int j; + edge = igraph_gml_tree_get_tree(gtree, p); + for (j = 0; j < igraph_gml_tree_length(edge); j++) { + const char *n = igraph_gml_tree_name(edge, j); + if (!strcmp(n, "source")) { + fromidx = igraph_gml_tree_find(edge, "source", 0); + } else if (!strcmp(n, "target")) { + toidx = igraph_gml_tree_find(edge, "target", 0); + } else { + long int edgeid = edgeptr / 2; + long int trieidx; + igraph_attribute_record_t *atrec; + int type; + igraph_trie_get(&eattrnames, n, &trieidx); + atrec = VECTOR(eattrs)[trieidx]; + type = atrec->type; + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *v = (igraph_vector_t *)atrec->value; + IGRAPH_CHECK(igraph_i_gml_toreal(edge, j, VECTOR(*v) + edgeid)); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *v = (igraph_strvector_t *)atrec->value; + const char *value = igraph_i_gml_tostring(edge, j); + IGRAPH_CHECK(igraph_strvector_set(v, edgeid, value)); + } + } + } + from = igraph_gml_tree_get_integer(edge, fromidx); + to = igraph_gml_tree_get_integer(edge, toidx); + snprintf(name, sizeof(name) / sizeof(char) -1, "%li", from); + IGRAPH_CHECK(igraph_trie_get(&trie, name, &from)); + snprintf(name, sizeof(name) / sizeof(char) -1, "%li", to); + IGRAPH_CHECK(igraph_trie_get(&trie, name, &to)); + if (igraph_trie_size(&trie) != no_of_nodes) { + IGRAPH_ERROR("Unknown node id found in an edge in GML file.", IGRAPH_PARSEERROR); + } + VECTOR(edges)[edgeptr++] = from; + VECTOR(edges)[edgeptr++] = to; + } + + /* and add vertex attributes */ + for (i = 0; i < igraph_gml_tree_length(gtree); i++) { + const char *n; + char name[100]; + long int j, k; + n = igraph_gml_tree_name(gtree, i); + if (!strcmp(n, "node")) { + igraph_gml_tree_t *node = igraph_gml_tree_get_tree(gtree, i); + long int iidx = igraph_gml_tree_find(node, "id", 0); + long int id = igraph_gml_tree_get_integer(node, iidx); + snprintf(name, sizeof(name) / sizeof(char) -1, "%li", id); + igraph_trie_get(&trie, name, &id); + for (j = 0; j < igraph_gml_tree_length(node); j++) { + const char *aname = igraph_gml_tree_name(node, j); + igraph_attribute_record_t *atrec; + int type; + igraph_trie_get(&vattrnames, aname, &k); + atrec = VECTOR(vattrs)[k]; + type = atrec->type; + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *v = (igraph_vector_t *)atrec->value; + IGRAPH_CHECK(igraph_i_gml_toreal(node, j, VECTOR(*v) + id)); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *v = (igraph_strvector_t *)atrec->value; + const char *value = igraph_i_gml_tostring(node, j); + IGRAPH_CHECK(igraph_strvector_set(v, id, value)); + } + } + } + } + + igraph_trie_destroy(&trie); + igraph_trie_destroy(&gattrnames); + igraph_trie_destroy(&vattrnames); + igraph_trie_destroy(&eattrnames); + IGRAPH_FINALLY_CLEAN(4); + + IGRAPH_CHECK(igraph_empty_attrs(graph, 0, directed, 0)); /* TODO */ + IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) no_of_nodes, + &vattrs)); + IGRAPH_CHECK(igraph_add_edges(graph, &edges, &eattrs)); + + igraph_i_gml_destroy_attrs(attrs); + igraph_vector_destroy(&edges); + igraph_i_gml_parsedata_destroy(&context); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_gml_convert_to_key(const char *orig, char **key) { + int no = 1; + char strno[50]; + size_t i, len = strlen(orig), newlen = 0, plen = 0; + + /* do we need a prefix? */ + if (len == 0 || !isalpha(orig[0])) { + no++; + snprintf(strno, sizeof(strno) - 1, "igraph"); + plen = newlen = strlen(strno); + } + for (i = 0; i < len; i++) { + if (isalnum(orig[i])) { + newlen++; + } + } + *key = IGRAPH_CALLOC(newlen + 1, char); + if (! *key) { + IGRAPH_ERROR("Writing GML format failed.", IGRAPH_ENOMEM); + } + memcpy(*key, strno, plen * sizeof(char)); + for (i = 0; i < len; i++) { + if (isalnum(orig[i])) { + (*key)[plen++] = orig[i]; + } + } + (*key)[newlen] = '\0'; + + return IGRAPH_SUCCESS; +} + +#define CHECK(cmd) do { ret=cmd; if (ret<0) IGRAPH_ERROR("Writing GML format failed.", IGRAPH_EFILE); } while (0) + +/** + * \function igraph_write_graph_gml + * \brief Write the graph to a stream in GML format + * + * GML is a quite general textual format, see + * http://www.fim.uni-passau.de/en/fim/faculty/chairs/theoretische-informatik/projects.html for details. + * + * The graph, vertex and edges attributes are written to the + * file as well, if they are numeric or string. + * + * As igraph is more forgiving about attribute names, it might + * be necessary to simplify the them before writing to the GML file. + * This way we'll have a syntactically correct GML file. The following + * simple procedure is performed on each attribute name: first the alphanumeric + * characters are extracted, the others are ignored. Then if the first character + * is not a letter then the attribute name is prefixed with igraph. + * Note that this might result identical names for two attributes, igraph + * does not check this. + * + * The id vertex attribute is treated specially. + * If the id argument is not 0 then it should be a numeric + * vector with the vertex ids and the id vertex attribute is + * ignored (if there is one). If id is 0 and there is a + * numeric id vertex attribute that is used instead. If ids + * are not specified in either way then the regular igraph vertex ids are used. + * + * Note that whichever way vertex ids are specified, their + * uniqueness is not checked. + * + * If the graph has edge attributes named source + * or target they're silently ignored. GML uses these attributes + * to specify the edges, so we cannot write them to the file. Rename them + * before calling this function if you want to preserve them. + * \param graph The graph to write to the stream. + * \param outstream The stream to write the file to. + * \param id Either NULL or a numeric vector with the vertex ids. + * See details above. + * \param creator An optional string to write to the stream in the creator line. + * If this is 0 then the current date and time is added. + * \return Error code. + * + * Time complexity: should be proportional to the number of characters written + * to the file. + * + * \sa \ref igraph_read_graph_gml() for reading GML files, + * \ref igraph_read_graph_graphml() for a more modern format. + * + * \example examples/simple/gml.c + */ + +int igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, + const igraph_vector_t *id, const char *creator) { + int ret; + igraph_strvector_t gnames, vnames, enames; + igraph_vector_t gtypes, vtypes, etypes; + igraph_vector_t numv; + igraph_strvector_t strv; + igraph_vector_bool_t boolv; + long int i; + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + + igraph_vector_t v_myid; + const igraph_vector_t *myid = id; + + time_t curtime = time(0); + char *timestr = ctime(&curtime); + timestr[strlen(timestr) - 1] = '\0'; /* nicely remove \n */ + + CHECK(fprintf(outstream, + "Creator \"igraph version %s %s\"\nVersion 1\ngraph\n[\n", + IGRAPH_VERSION, creator ? creator : timestr)); + + IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0); + IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0); + IGRAPH_STRVECTOR_INIT_FINALLY(&enames, 0); + IGRAPH_VECTOR_INIT_FINALLY(>ypes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&etypes, 0); + IGRAPH_CHECK(igraph_i_attribute_get_info(graph, + &gnames, >ypes, + &vnames, &vtypes, + &enames, &etypes)); + + IGRAPH_VECTOR_INIT_FINALLY(&numv, 1); + IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&boolv, 1); + + /* Check whether there is an 'id' node attribute if the supplied is 0 */ + if (!id) { + igraph_bool_t found = 0; + for (i = 0; i < igraph_vector_size(&vtypes); i++) { + char *n; + igraph_strvector_get(&vnames, i, &n); + if (!strcmp(n, "id") && VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + found = 1; break; + } + } + if (found) { + IGRAPH_VECTOR_INIT_FINALLY(&v_myid, no_of_nodes); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, "id", + igraph_vss_all(), + &v_myid)); + myid = &v_myid; + } + } + + /* directedness */ + CHECK(fprintf(outstream, " directed %i\n", igraph_is_directed(graph) ? 1 : 0)); + + /* Graph attributes first */ + for (i = 0; i < igraph_vector_size(>ypes); i++) { + char *name, *newname; + igraph_strvector_get(&gnames, i, &name); + IGRAPH_CHECK(igraph_i_gml_convert_to_key(name, &newname)); + IGRAPH_FINALLY(igraph_free, newname); + if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_graph_attr(graph, name, &numv)); + CHECK(fprintf(outstream, " %s ", newname)); + CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0])); + CHECK(fputc('\n', outstream)); + } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { + char *s; + IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); + igraph_strvector_get(&strv, 0, &s); + CHECK(fprintf(outstream, " %s \"%s\"\n", newname, s)); + } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_CHECK(igraph_i_attribute_get_bool_graph_attr(graph, name, &boolv)); + CHECK(fprintf(outstream, " %s %d\n", newname, VECTOR(boolv)[0] ? 1 : 0)); + IGRAPH_WARNING("A boolean graph attribute was converted to numeric"); + } else { + IGRAPH_WARNING("A non-numeric, non-string, non-boolean graph attribute ignored"); + } + IGRAPH_FREE(newname); + IGRAPH_FINALLY_CLEAN(1); + } + + /* Now come the vertices */ + for (i = 0; i < no_of_nodes; i++) { + long int j; + CHECK(fprintf(outstream, " node\n [\n")); + /* id */ + CHECK(fprintf(outstream, " id %li\n", myid ? (long int)VECTOR(*myid)[i] : i)); + /* other attributes */ + for (j = 0; j < igraph_vector_size(&vtypes); j++) { + int type = (int) VECTOR(vtypes)[j]; + char *name, *newname; + igraph_strvector_get(&vnames, j, &name); + if (!strcmp(name, "id")) { + continue; + } + IGRAPH_CHECK(igraph_i_gml_convert_to_key(name, &newname)); + IGRAPH_FINALLY(igraph_free, newname); + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, name, + igraph_vss_1((igraph_integer_t) i), &numv)); + CHECK(fprintf(outstream, " %s ", newname)); + CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0])); + CHECK(fputc('\n', outstream)); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + char *s; + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, + igraph_vss_1((igraph_integer_t) i), &strv)); + igraph_strvector_get(&strv, 0, &s); + CHECK(fprintf(outstream, " %s \"%s\"\n", newname, s)); + } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph, name, + igraph_vss_1((igraph_integer_t) i), &boolv)); + CHECK(fprintf(outstream, " %s %d\n", newname, VECTOR(boolv)[0] ? 1 : 0)); + IGRAPH_WARNING("A boolean vertex attribute was converted to numeric"); + } else { + IGRAPH_WARNING("A non-numeric, non-string, non-boolean edge attribute was ignored"); + } + IGRAPH_FREE(newname); + IGRAPH_FINALLY_CLEAN(1); + } + CHECK(fprintf(outstream, " ]\n")); + } + + /* The edges too */ + for (i = 0; i < no_of_edges; i++) { + long int from = IGRAPH_FROM(graph, i); + long int to = IGRAPH_TO(graph, i); + long int j; + CHECK(fprintf(outstream, " edge\n [\n")); + /* source and target */ + CHECK(fprintf(outstream, " source %li\n", + myid ? (long int)VECTOR(*myid)[from] : from)); + CHECK(fprintf(outstream, " target %li\n", + myid ? (long int)VECTOR(*myid)[to] : to)); + + /* other attributes */ + for (j = 0; j < igraph_vector_size(&etypes); j++) { + int type = (int) VECTOR(etypes)[j]; + char *name, *newname; + igraph_strvector_get(&enames, j, &name); + if (!strcmp(name, "source") || !strcmp(name, "target")) { + continue; + } + IGRAPH_CHECK(igraph_i_gml_convert_to_key(name, &newname)); + IGRAPH_FINALLY(igraph_free, newname); + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, name, + igraph_ess_1((igraph_integer_t) i), &numv)); + CHECK(fprintf(outstream, " %s ", newname)); + CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0])); + CHECK(fputc('\n', outstream)); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + char *s; + IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, name, + igraph_ess_1((igraph_integer_t) i), &strv)); + igraph_strvector_get(&strv, 0, &s); + CHECK(fprintf(outstream, " %s \"%s\"\n", newname, s)); + } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr(graph, name, + igraph_ess_1((igraph_integer_t) i), &boolv)); + CHECK(fprintf(outstream, " %s %d\n", newname, VECTOR(boolv)[0] ? 1 : 0)); + IGRAPH_WARNING("A boolean edge attribute was converted to numeric"); + } else { + IGRAPH_WARNING("A non-numeric, non-string, non-boolean edge attribute was ignored"); + } + IGRAPH_FREE(newname); + IGRAPH_FINALLY_CLEAN(1); + } + CHECK(fprintf(outstream, " ]\n")); + } + + CHECK(fprintf(outstream, "]\n")); + + if (&v_myid == myid) { + igraph_vector_destroy(&v_myid); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_bool_destroy(&boolv); + igraph_strvector_destroy(&strv); + igraph_vector_destroy(&numv); + igraph_vector_destroy(&etypes); + igraph_vector_destroy(&vtypes); + igraph_vector_destroy(>ypes); + igraph_strvector_destroy(&enames); + igraph_strvector_destroy(&vnames); + igraph_strvector_destroy(&gnames); + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} + +#undef CHECK diff --git a/src/rigraph/core/io/graphdb.c b/src/rigraph/core/io/graphdb.c new file mode 100644 index 0000000..a0bdef1 --- /dev/null +++ b/src/rigraph/core/io/graphdb.c @@ -0,0 +1,118 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_constructors.h" + +#include "../core/trie.h" + +static int igraph_i_read_graph_graphdb_getword(FILE *instream) { + int b1, b2; + unsigned char c1, c2; + b1 = fgetc(instream); + b2 = fgetc(instream); + if (b1 != EOF) { + c1 = (unsigned char) b1; c2 = (unsigned char) b2; + return c1 | (c2 << 8); + } else { + return -1; + } +} + +/** + * \function igraph_read_graph_graphdb + * \brief Read a graph in the binary graph database format. + * + * This is a binary format, used in the graph database + * for isomorphism testing. From the (now defunct) graph database + * homepage: + * + * + * \blockquote + * The graphs are stored in a compact binary format, one graph per + * file. The file is composed of 16 bit words, which are represented + * using the so-called little-endian convention, i.e. the least + * significant byte of the word is stored first. + * + * + * Then, for each node, the file contains the list of edges coming + * out of the node itself. The list is represented by a word encoding + * its length, followed by a word for each edge, representing the + * destination node of the edge. Node numeration is 0-based, so the + * first node of the graph has index 0. \endblockquote + * + * + * Only unlabelled graphs are implemented. + * \param graph Pointer to an uninitialized graph object. + * \param instream The stream to read from. + * \param directed Logical scalar, whether to create a directed graph. + * \return Error code. + * + * Time complexity: O(|V|+|E|), the number of vertices plus the + * number of edges. + * + * \example examples/simple/igraph_read_graph_graphdb.c + */ + +int igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, + igraph_bool_t directed) { + + igraph_vector_t edges; + long int nodes; + long int i, j; + igraph_bool_t end = 0; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + nodes = igraph_i_read_graph_graphdb_getword(instream); + if (nodes < 0) { + IGRAPH_ERROR("Can't read from file", IGRAPH_EFILE); + } + for (i = 0; !end && i < nodes; i++) { + long int len = igraph_i_read_graph_graphdb_getword(instream); + if (len < 0) { + end = 1; + break; + } + for (j = 0; ! end && j < len; j++) { + long int to = igraph_i_read_graph_graphdb_getword(instream); + if (to < 0) { + end = 1; + break; + } + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + } + } + + if (end) { + IGRAPH_ERROR("Truncated graphdb file", IGRAPH_EFILE); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) nodes, + directed)); + igraph_vector_destroy(&edges); + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/io/graphml.c b/src/rigraph/core/io/graphml.c new file mode 100644 index 0000000..ff838bd --- /dev/null +++ b/src/rigraph/core/io/graphml.c @@ -0,0 +1,1861 @@ +/* -*- mode: C -*- */ +/* + IGraph R package. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" +#include "igraph_attributes.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "../core/math.h" +#include "../core/trie.h" +#include "../graph/attributes.h" +//#include "internal/hacks.h" /* strcasecmp */ + +#include "config.h" + +#include +#include /* isnan */ +#include +#include /* va_start & co */ + +#define GRAPHML_NAMESPACE_URI "http://graphml.graphdrawing.org/xmlns" + +#if HAVE_LIBXML == 1 +#include +#include + +xmlEntity blankEntityStruct = { +#ifndef XML_WITHOUT_CORBA + 0, +#endif + XML_ENTITY_DECL, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + XML_EXTERNAL_GENERAL_PARSED_ENTITY, + 0, + 0, + 0, + 0, + 0, + 1 +}; + +xmlEntityPtr blankEntity = &blankEntityStruct; + +#define GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code) do { \ + if (state->successful) { \ + igraph_i_graphml_sax_handler_error(state, msg); \ + } \ + } while (0) +#define GRAPHML_PARSE_ERROR(state, msg) \ + GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, IGRAPH_PARSEERROR) +#define RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code) do { \ + GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code); \ + return; \ + } while (1) +#define RETURN_GRAPHML_PARSE_ERROR(state, msg) do { \ + GRAPHML_PARSE_ERROR(state, msg); \ + return; \ + } while (1) + +/* TODO: proper error handling */ + +typedef struct igraph_i_graphml_attribute_record_t { + const char *id; /* GraphML id */ + enum { I_GRAPHML_BOOLEAN, I_GRAPHML_INTEGER, I_GRAPHML_LONG, + I_GRAPHML_FLOAT, I_GRAPHML_DOUBLE, I_GRAPHML_STRING, + I_GRAPHML_UNKNOWN_TYPE + } type; /* GraphML type */ + union { + igraph_real_t as_numeric; + igraph_bool_t as_boolean; + char* as_string; + } default_value; /* Default value of the attribute, if any */ + igraph_attribute_record_t record; +} igraph_i_graphml_attribute_record_t; + +struct igraph_i_graphml_parser_state { + enum { START, INSIDE_GRAPHML, INSIDE_GRAPH, INSIDE_NODE, INSIDE_EDGE, + INSIDE_KEY, INSIDE_DEFAULT, INSIDE_DATA, FINISH, UNKNOWN, ERROR + } st; + igraph_t *g; + igraph_trie_t node_trie; + igraph_strvector_t edgeids; + igraph_vector_t edgelist; + igraph_vector_int_t prev_state_stack; + unsigned int unknown_depth; + int index; + igraph_bool_t successful; + igraph_bool_t edges_directed; + igraph_trie_t v_names; + igraph_vector_ptr_t v_attrs; + igraph_trie_t e_names; + igraph_vector_ptr_t e_attrs; + igraph_trie_t g_names; + igraph_vector_ptr_t g_attrs; + igraph_i_graphml_attribute_record_t* current_attr_record; + xmlChar *data_key; + igraph_attribute_elemtype_t data_type; + char *error_message; + char *data_char; + long int act_node; + igraph_bool_t ignore_namespaces; +}; + +static void igraph_i_report_unhandled_attribute_target(const char* target, + const char* file, int line) { + igraph_warningf("Attribute target '%s' is not handled; ignoring corresponding " + "attribute specifications", file, line, 0, target); +} + +static igraph_real_t igraph_i_graphml_parse_numeric(const char* char_data, + igraph_real_t default_value) { + double result; + + if (char_data == 0) { + return default_value; + } + + if (sscanf(char_data, "%lf", &result) == 0) { + return default_value; + } + + return result; +} + +static igraph_bool_t igraph_i_graphml_parse_boolean(const char* char_data, + igraph_bool_t default_value) { + int value; + if (char_data == 0) { + return default_value; + } + if (!strcasecmp("true", char_data)) { + return 1; + } + if (!strcasecmp("yes", char_data)) { + return 1; + } + if (!strcasecmp("false", char_data)) { + return 0; + } + if (!strcasecmp("no", char_data)) { + return 0; + } + if (sscanf(char_data, "%d", &value) == 0) { + return default_value; + } + return value != 0; +} + +static void igraph_i_graphml_attribute_record_destroy(igraph_i_graphml_attribute_record_t* rec) { + if (rec->record.type == IGRAPH_ATTRIBUTE_NUMERIC) { + if (rec->record.value != 0) { + igraph_vector_destroy((igraph_vector_t*)rec->record.value); + IGRAPH_FREE(rec->record.value); + } + } else if (rec->record.type == IGRAPH_ATTRIBUTE_STRING) { + if (rec->record.value != 0) { + igraph_strvector_destroy((igraph_strvector_t*)rec->record.value); + IGRAPH_FREE(rec->record.value); + } + if (rec->default_value.as_string != 0) { + IGRAPH_FREE(rec->default_value.as_string); + } + } else if (rec->record.type == IGRAPH_ATTRIBUTE_BOOLEAN) { + if (rec->record.value != 0) { + igraph_vector_bool_destroy((igraph_vector_bool_t*)rec->record.value); + IGRAPH_FREE(rec->record.value); + } + } + if (rec->id != 0) { + IGRAPH_FREE(rec->id); + } + if (rec->record.name != 0) { + IGRAPH_FREE(rec->record.name); + } +} + +static int igraph_i_graphml_parser_state_init(struct igraph_i_graphml_parser_state* state, igraph_t* graph, int index) { + memset(state, 0, sizeof(struct igraph_i_graphml_parser_state)); + + state->g = graph; + state->index = index < 0 ? 0 : index; + state->successful = 1; + state->error_message = NULL; + + IGRAPH_CHECK(igraph_vector_int_init(&state->prev_state_stack, 0)); + IGRAPH_CHECK(igraph_vector_int_reserve(&state->prev_state_stack, 32)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &state->prev_state_stack); + + IGRAPH_CHECK(igraph_vector_ptr_init(&state->v_attrs, 0)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&state->v_attrs, + igraph_i_graphml_attribute_record_destroy); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &state->v_attrs); + + IGRAPH_CHECK(igraph_vector_ptr_init(&state->e_attrs, 0)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&state->e_attrs, + igraph_i_graphml_attribute_record_destroy); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &state->e_attrs); + + IGRAPH_CHECK(igraph_vector_ptr_init(&state->g_attrs, 0)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&state->g_attrs, + igraph_i_graphml_attribute_record_destroy); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &state->g_attrs); + + IGRAPH_CHECK(igraph_vector_init(&state->edgelist, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &state->edgelist); + + IGRAPH_CHECK(igraph_trie_init(&state->node_trie, 1)); + IGRAPH_FINALLY(igraph_trie_destroy, &state->node_trie); + + IGRAPH_CHECK(igraph_strvector_init(&state->edgeids, 0)); + IGRAPH_FINALLY(igraph_strvector_destroy, &state->edgeids); + + IGRAPH_CHECK(igraph_trie_init(&state->v_names, 0)); + IGRAPH_FINALLY(igraph_trie_destroy, &state->v_names); + + IGRAPH_CHECK(igraph_trie_init(&state->e_names, 0)); + IGRAPH_FINALLY(igraph_trie_destroy, &state->e_names); + + IGRAPH_CHECK(igraph_trie_init(&state->g_names, 0)); + + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} + +static void igraph_i_graphml_parser_state_destroy(struct igraph_i_graphml_parser_state* state) { + igraph_trie_destroy(&state->node_trie); + igraph_strvector_destroy(&state->edgeids); + igraph_trie_destroy(&state->v_names); + igraph_trie_destroy(&state->e_names); + igraph_trie_destroy(&state->g_names); + igraph_vector_destroy(&state->edgelist); + igraph_vector_int_destroy(&state->prev_state_stack); + + igraph_vector_ptr_destroy_all(&state->v_attrs); + igraph_vector_ptr_destroy_all(&state->e_attrs); + igraph_vector_ptr_destroy_all(&state->g_attrs); + + if (state->data_key) { + free(state->data_key); + state->data_key = NULL; + } + if (state->data_char) { + free(state->data_char); + state->data_char = NULL; + } + + if (state->error_message) { + free(state->error_message); + state->error_message = NULL; + } +} + +static void igraph_i_graphml_sax_handler_error(void *state0, const char* msg, ...) { + struct igraph_i_graphml_parser_state *state = + (struct igraph_i_graphml_parser_state*)state0; + const size_t max_error_message_length = 4096; + + va_list ap; + + va_start(ap, msg); + + if (state->error_message == 0) { + /* ownership of state->error_message passed on immediately to + * state so the state destructor is responsible for freeing it */ + state->error_message = IGRAPH_CALLOC(max_error_message_length, char); + } + + state->successful = 0; + state->st = ERROR; + vsnprintf(state->error_message, max_error_message_length, msg, ap); + + va_end(ap); +} + +static xmlEntityPtr igraph_i_graphml_sax_handler_get_entity(void *state0, + const xmlChar* name) { + xmlEntityPtr predef = xmlGetPredefinedEntity(name); + IGRAPH_UNUSED(state0); + if (predef != NULL) { + return predef; + } + IGRAPH_WARNING("unknown XML entity found\n"); + return blankEntity; +} + +static void igraph_i_graphml_handle_unknown_start_tag(struct igraph_i_graphml_parser_state *state) { + if (state->st != UNKNOWN) { + igraph_vector_int_push_back(&state->prev_state_stack, state->st); + state->st = UNKNOWN; + state->unknown_depth = 1; + } else { + state->unknown_depth++; + } +} + +static void igraph_i_graphml_sax_handler_start_document(void *state0) { + struct igraph_i_graphml_parser_state *state = + (struct igraph_i_graphml_parser_state*)state0; + + state->st = START; + state->successful = 1; + state->edges_directed = 0; + state->data_key = NULL; + state->data_char = NULL; + state->unknown_depth = 0; + state->ignore_namespaces = 0; +} + +static int igraph_i_graphml_parser_state_finish_parsing(struct igraph_i_graphml_parser_state *state) { + long i, l; + igraph_attribute_record_t idrec, eidrec; + const char *idstr = "id"; + igraph_bool_t already_has_vertex_id = 0, already_has_edge_id = 0; + igraph_vector_ptr_t vattr, eattr, gattr; + long int esize; + const void **tmp; + + IGRAPH_ASSERT(state->successful); + + /* check that we have found and parsed the graph the user is interested in */ + IGRAPH_ASSERT(state->index < 0); + + IGRAPH_CHECK(igraph_vector_ptr_init(&vattr, igraph_vector_ptr_size(&state->v_attrs) + 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vattr); + + esize = igraph_vector_ptr_size(&state->e_attrs); + if (igraph_strvector_size(&state->edgeids) != 0) { + esize++; + } + IGRAPH_CHECK(igraph_vector_ptr_init(&eattr, esize)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &eattr); + + IGRAPH_CHECK(igraph_vector_ptr_init(&gattr, igraph_vector_ptr_size(&state->g_attrs))); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &gattr); + + for (i = 0; i < igraph_vector_ptr_size(&state->v_attrs); i++) { + igraph_i_graphml_attribute_record_t *graphmlrec = + VECTOR(state->v_attrs)[i]; + igraph_attribute_record_t *rec = &graphmlrec->record; + + /* Check that the name of the vertex attribute is not 'id'. + If it is then we cannot the complimentary 'id' attribute. */ + if (! strcmp(rec->name, idstr)) { + already_has_vertex_id = 1; + } + + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *vec = (igraph_vector_t*)rec->value; + long int origsize = igraph_vector_size(vec); + long int nodes = igraph_trie_size(&state->node_trie); + IGRAPH_CHECK(igraph_vector_resize(vec, nodes)); + for (l = origsize; l < nodes; l++) { + VECTOR(*vec)[l] = graphmlrec->default_value.as_numeric; + } + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *strvec = (igraph_strvector_t*)rec->value; + long int origsize = igraph_strvector_size(strvec); + long int nodes = igraph_trie_size(&state->node_trie); + IGRAPH_CHECK(igraph_strvector_resize(strvec, nodes)); + for (l = origsize; l < nodes; l++) { + IGRAPH_CHECK(igraph_strvector_set(strvec, l, graphmlrec->default_value.as_string)); + } + } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_vector_bool_t *boolvec = (igraph_vector_bool_t*)rec->value; + long int origsize = igraph_vector_bool_size(boolvec); + long int nodes = igraph_trie_size(&state->node_trie); + IGRAPH_CHECK(igraph_vector_bool_resize(boolvec, nodes)); + for (l = origsize; l < nodes; l++) { + VECTOR(*boolvec)[l] = graphmlrec->default_value.as_boolean; + } + } + VECTOR(vattr)[i] = rec; + } + if (!already_has_vertex_id) { + idrec.name = idstr; + idrec.type = IGRAPH_ATTRIBUTE_STRING; + tmp = &idrec.value; + IGRAPH_CHECK(igraph_trie_getkeys(&state->node_trie, (const igraph_strvector_t **)tmp)); + VECTOR(vattr)[i] = &idrec; + } else { + igraph_vector_ptr_pop_back(&vattr); + } + + for (i = 0; i < igraph_vector_ptr_size(&state->e_attrs); i++) { + igraph_i_graphml_attribute_record_t *graphmlrec = + VECTOR(state->e_attrs)[i]; + igraph_attribute_record_t *rec = &graphmlrec->record; + + if (! strcmp(rec->name, idstr)) { + already_has_edge_id = 1; + } + + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *vec = (igraph_vector_t*)rec->value; + long int origsize = igraph_vector_size(vec); + long int edges = igraph_vector_size(&state->edgelist) / 2; + IGRAPH_CHECK(igraph_vector_resize(vec, edges)); + for (l = origsize; l < edges; l++) { + VECTOR(*vec)[l] = graphmlrec->default_value.as_numeric; + } + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *strvec = (igraph_strvector_t*)rec->value; + long int origsize = igraph_strvector_size(strvec); + long int edges = igraph_vector_size(&state->edgelist) / 2; + IGRAPH_CHECK(igraph_strvector_resize(strvec, edges)); + for (l = origsize; l < edges; l++) { + IGRAPH_CHECK(igraph_strvector_set(strvec, l, graphmlrec->default_value.as_string)); + } + } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_vector_bool_t *boolvec = (igraph_vector_bool_t*)rec->value; + long int origsize = igraph_vector_bool_size(boolvec); + long int edges = igraph_vector_size(&state->edgelist) / 2; + IGRAPH_CHECK(igraph_vector_bool_resize(boolvec, edges)); + for (l = origsize; l < edges; l++) { + VECTOR(*boolvec)[l] = graphmlrec->default_value.as_boolean; + } + } + VECTOR(eattr)[i] = rec; + } + if (igraph_strvector_size(&state->edgeids) != 0) { + if (!already_has_edge_id) { + long int origsize = igraph_strvector_size(&state->edgeids); + eidrec.name = idstr; + eidrec.type = IGRAPH_ATTRIBUTE_STRING; + IGRAPH_CHECK(igraph_strvector_resize(&state->edgeids, igraph_vector_size(&state->edgelist) / 2)); + for (; origsize < igraph_strvector_size(&state->edgeids); origsize++) { + IGRAPH_CHECK(igraph_strvector_set(&state->edgeids, origsize, "")); + } + eidrec.value = &state->edgeids; + VECTOR(eattr)[(long int)igraph_vector_ptr_size(&eattr) - 1] = &eidrec; + } else { + igraph_vector_ptr_pop_back(&eattr); + IGRAPH_WARNING("Could not add edge ids, " + "there is already an 'id' edge attribute"); + } + } + + for (i = 0; i < igraph_vector_ptr_size(&state->g_attrs); i++) { + igraph_i_graphml_attribute_record_t *graphmlrec = + VECTOR(state->g_attrs)[i]; + igraph_attribute_record_t *rec = &graphmlrec->record; + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *vec = (igraph_vector_t*)rec->value; + long int origsize = igraph_vector_size(vec); + IGRAPH_CHECK(igraph_vector_resize(vec, 1)); + for (l = origsize; l < 1; l++) { + VECTOR(*vec)[l] = graphmlrec->default_value.as_numeric; + } + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *strvec = (igraph_strvector_t*)rec->value; + long int origsize = igraph_strvector_size(strvec); + IGRAPH_CHECK(igraph_strvector_resize(strvec, 1)); + for (l = origsize; l < 1; l++) { + IGRAPH_CHECK(igraph_strvector_set(strvec, l, graphmlrec->default_value.as_string)); + } + } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_vector_bool_t *boolvec = (igraph_vector_bool_t*)rec->value; + long int origsize = igraph_vector_bool_size(boolvec); + IGRAPH_CHECK(igraph_vector_bool_resize(boolvec, 1)); + for (l = origsize; l < 1; l++) { + VECTOR(*boolvec)[l] = graphmlrec->default_value.as_boolean; + } + } + VECTOR(gattr)[i] = rec; + } + + IGRAPH_CHECK(igraph_empty_attrs(state->g, 0, state->edges_directed, &gattr)); + IGRAPH_CHECK(igraph_add_vertices(state->g, (igraph_integer_t) igraph_trie_size(&state->node_trie), &vattr)); + IGRAPH_CHECK(igraph_add_edges(state->g, &state->edgelist, &eattr)); + + igraph_vector_ptr_destroy(&vattr); + igraph_vector_ptr_destroy(&eattr); + igraph_vector_ptr_destroy(&gattr); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +#define toXmlChar(a) (BAD_CAST(a)) +#define fromXmlChar(a) ((char *)(a)) /* not the most elegant way... */ + +#define XML_ATTR_LOCALNAME(it) (*(it)) +#define XML_ATTR_PREFIX(it) (*(it+1)) +#define XML_ATTR_URI(it) (*(it+2)) +#define XML_ATTR_VALUE_START(it) (*(it+3)) +#define XML_ATTR_VALUE_END(it) (*(it+4)) +#define XML_ATTR_VALUE(it) *(it+3), (*(it+4))-(*(it+3)) + +static igraph_i_graphml_attribute_record_t* igraph_i_graphml_add_attribute_key( + const xmlChar** attrs, int nb_attrs, + struct igraph_i_graphml_parser_state *state) { + xmlChar **it; + xmlChar *localname; + igraph_trie_t *trie = NULL; + igraph_vector_ptr_t *ptrvector = NULL; + long int id; + unsigned short int skip = 0; + int i, ret; + igraph_i_graphml_attribute_record_t *rec; + + if (!state->successful) { + return 0; + } + + rec = IGRAPH_CALLOC(1, igraph_i_graphml_attribute_record_t); + if (rec == 0) { + GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); + return 0; + } + IGRAPH_FINALLY(igraph_free, rec); + IGRAPH_FINALLY(igraph_i_graphml_attribute_record_destroy, rec); + + rec->type = I_GRAPHML_UNKNOWN_TYPE; + + for (i = 0, it = (xmlChar**)attrs; i < nb_attrs; i++, it += 5) { + if (XML_ATTR_URI(it) != 0 && + !xmlStrEqual(toXmlChar(GRAPHML_NAMESPACE_URI), XML_ATTR_URI(it))) { + continue; + } + + localname = XML_ATTR_LOCALNAME(it); + + if (xmlStrEqual(localname, toXmlChar("id"))) { + rec->id = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it))); + } else if (xmlStrEqual(localname, toXmlChar("attr.name"))) { + rec->record.name = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it))); + } else if (xmlStrEqual(localname, toXmlChar("attr.type"))) { + if (!xmlStrncmp(toXmlChar("boolean"), XML_ATTR_VALUE(it))) { + rec->type = I_GRAPHML_BOOLEAN; + rec->record.type = IGRAPH_ATTRIBUTE_BOOLEAN; + rec->default_value.as_boolean = 0; + } else if (!xmlStrncmp(toXmlChar("string"), XML_ATTR_VALUE(it))) { + rec->type = I_GRAPHML_STRING; + rec->record.type = IGRAPH_ATTRIBUTE_STRING; + rec->default_value.as_string = strdup(""); + } else if (!xmlStrncmp(toXmlChar("float"), XML_ATTR_VALUE(it))) { + rec->type = I_GRAPHML_FLOAT; + rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC; + rec->default_value.as_numeric = IGRAPH_NAN; + } else if (!xmlStrncmp(toXmlChar("double"), XML_ATTR_VALUE(it))) { + rec->type = I_GRAPHML_DOUBLE; + rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC; + rec->default_value.as_numeric = IGRAPH_NAN; + } else if (!xmlStrncmp(toXmlChar("int"), XML_ATTR_VALUE(it))) { + rec->type = I_GRAPHML_INTEGER; + rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC; + rec->default_value.as_numeric = IGRAPH_NAN; + } else if (!xmlStrncmp(toXmlChar("long"), XML_ATTR_VALUE(it))) { + rec->type = I_GRAPHML_LONG; + rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC; + rec->default_value.as_numeric = IGRAPH_NAN; + } else { + GRAPHML_PARSE_ERROR(state, + "Cannot parse GraphML file, unknown attribute type"); + return 0; + } + } else if (xmlStrEqual(*it, toXmlChar("for"))) { + /* graph, vertex or edge attribute? */ + if (!xmlStrncmp(toXmlChar("graph"), XML_ATTR_VALUE(it))) { + trie = &state->g_names; + ptrvector = &state->g_attrs; + } else if (!xmlStrncmp(toXmlChar("node"), XML_ATTR_VALUE(it))) { + trie = &state->v_names; + ptrvector = &state->v_attrs; + } else if (!xmlStrncmp(toXmlChar("edge"), XML_ATTR_VALUE(it))) { + trie = &state->e_names; + ptrvector = &state->e_attrs; + } else if (!xmlStrncmp(toXmlChar("graphml"), XML_ATTR_VALUE(it))) { + igraph_i_report_unhandled_attribute_target("graphml", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else if (!xmlStrncmp(toXmlChar("hyperedge"), XML_ATTR_VALUE(it))) { + igraph_i_report_unhandled_attribute_target("hyperedge", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else if (!xmlStrncmp(toXmlChar("port"), XML_ATTR_VALUE(it))) { + igraph_i_report_unhandled_attribute_target("port", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else if (!xmlStrncmp(toXmlChar("endpoint"), XML_ATTR_VALUE(it))) { + igraph_i_report_unhandled_attribute_target("endpoint", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else if (!xmlStrncmp(toXmlChar("all"), XML_ATTR_VALUE(it))) { + /* TODO: we should handle this */ + igraph_i_report_unhandled_attribute_target("all", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else { + GRAPHML_PARSE_ERROR(state, + "Cannot parse GraphML file, unknown value in the 'for' attribute of a tag"); + return 0; + } + } + } + + /* throw an error if there is no ID; this is a clear violation of the GraphML + * DTD */ + if (rec->id == 0) { + GRAPHML_PARSE_ERROR(state, "Found tag with no 'id' attribute"); + return 0; + } + + /* in case of a missing attr.name attribute, use the id as the attribute name */ + if (rec->record.name == 0) { + rec->record.name = strdup(rec->id); + } + + /* if the attribute type is missing, throw an error */ + if (!skip && rec->type == I_GRAPHML_UNKNOWN_TYPE) { + igraph_warningf("Ignoring because of a missing or unknown 'attr.type' attribute", IGRAPH_FILE_BASENAME, __LINE__, 0, rec->id); + skip = 1; + } + + /* if the value of the 'for' attribute was unknown, throw an error */ + if (!skip && trie == 0) { + GRAPHML_PARSE_ERROR(state, + "Cannot parse GraphML file, missing 'for' attribute in a tag"); + return 0; + } + + /* if the code above requested skipping the attribute, free everything and + * return */ + if (skip) { + igraph_i_graphml_attribute_record_destroy(rec); + igraph_free(rec); + IGRAPH_FINALLY_CLEAN(2); + return 0; + } + + /* add to trie, attribues */ + igraph_trie_get(trie, rec->id, &id); + if (id != igraph_trie_size(trie) - 1) { + GRAPHML_PARSE_ERROR(state, "Cannot parse GraphML file, duplicate attribute"); + return 0; + } + + ret = igraph_vector_ptr_push_back(ptrvector, rec); + if (ret) { + GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot read GraphML file", ret); + return 0; + } + + /* Ownership of 'rec' is now taken by ptrvector so we can clean the + * finally stack */ + IGRAPH_FINALLY_CLEAN(2); /* rec, destructor + igraph_free */ + + /* create the attribute values */ + switch (rec->record.type) { + igraph_vector_t *vec; + igraph_vector_bool_t *boolvec; + igraph_strvector_t *strvec; + case IGRAPH_ATTRIBUTE_BOOLEAN: + boolvec = IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (boolvec == 0) { + GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); + return 0; + } + rec->record.value = boolvec; + igraph_vector_bool_init(boolvec, 0); + break; + case IGRAPH_ATTRIBUTE_NUMERIC: + vec = IGRAPH_CALLOC(1, igraph_vector_t); + if (vec == 0) { + GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); + return 0; + } + rec->record.value = vec; + igraph_vector_init(vec, 0); + break; + case IGRAPH_ATTRIBUTE_STRING: + strvec = IGRAPH_CALLOC(1, igraph_strvector_t); + if (strvec == 0) { + GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); + return 0; + } + rec->record.value = strvec; + igraph_strvector_init(strvec, 0); + break; + default: break; + } + + return rec; +} + +static void igraph_i_graphml_attribute_data_setup(struct igraph_i_graphml_parser_state *state, + const xmlChar **attrs, + int nb_attrs, + igraph_attribute_elemtype_t type) { + xmlChar **it; + int i; + + if (!state->successful) { + return; + } + + for (i = 0, it = (xmlChar**)attrs; i < nb_attrs; i++, it += 5) { + if (XML_ATTR_URI(it) != 0 && + !xmlStrEqual(toXmlChar(GRAPHML_NAMESPACE_URI), XML_ATTR_URI(it))) { + continue; + } + + if (xmlStrEqual(*it, toXmlChar("key"))) { + if (state->data_key) { + free(state->data_key); + } + state->data_key = xmlStrndup(XML_ATTR_VALUE(it)); + if (state->data_char) { + free(state->data_char); + } + state->data_char = NULL; + state->data_type = type; + } else { + /* ignore */ + } + } +} + +static void igraph_i_graphml_append_to_data_char(struct igraph_i_graphml_parser_state *state, + const xmlChar *data, int len) { + long int data_char_new_start = 0; + + if (!state->successful) { + return; + } + + if (state->data_char) { + data_char_new_start = (long int) strlen(state->data_char); + state->data_char = IGRAPH_REALLOC(state->data_char, + (size_t)(data_char_new_start + len + 1), char); + } else { + state->data_char = IGRAPH_CALLOC((size_t) len + 1, char); + } + if (state->data_char == 0) { + RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); + } + memcpy(state->data_char + data_char_new_start, data, + (size_t) len * sizeof(xmlChar)); + state->data_char[data_char_new_start + len] = '\0'; +} + +static void igraph_i_graphml_attribute_data_finish(struct igraph_i_graphml_parser_state *state) { + const char *key = fromXmlChar(state->data_key); + igraph_attribute_elemtype_t type = state->data_type; + igraph_trie_t *trie = NULL; + igraph_vector_ptr_t *ptrvector = NULL; + igraph_i_graphml_attribute_record_t *graphmlrec; + igraph_attribute_record_t *rec; + long int recid, id = 0; + int ret; + + switch (type) { + case IGRAPH_ATTRIBUTE_GRAPH: + trie = &state->g_names; + ptrvector = &state->g_attrs; + id = 0; + break; + case IGRAPH_ATTRIBUTE_VERTEX: + trie = &state->v_names; + ptrvector = &state->v_attrs; + id = state->act_node; + break; + case IGRAPH_ATTRIBUTE_EDGE: + trie = &state->e_names; + ptrvector = &state->e_attrs; + id = igraph_vector_size(&state->edgelist) / 2 - 1; /* hack */ + break; + default: + /* impossible */ + break; + } + + if (key == 0) { + /* no key specified, issue a warning */ + IGRAPH_WARNING("missing attribute key in a tag, ignoring attribute"); + IGRAPH_FREE(state->data_char); + return; + } + + igraph_trie_check(trie, key, &recid); + if (recid < 0) { + /* no such attribute key, issue a warning */ + igraph_warningf( + "unknown attribute key '%s' in a tag, ignoring attribute", + IGRAPH_FILE_BASENAME, __LINE__, 0, + key + ); + IGRAPH_FREE(state->data_char); + return; + } + + graphmlrec = VECTOR(*ptrvector)[recid]; + rec = &graphmlrec->record; + + switch (rec->type) { + igraph_vector_bool_t *boolvec; + igraph_vector_t *vec; + igraph_strvector_t *strvec; + long int s, i; + const char* strvalue; + case IGRAPH_ATTRIBUTE_BOOLEAN: + boolvec = (igraph_vector_bool_t *)rec->value; + s = igraph_vector_bool_size(boolvec); + if (id >= s) { + ret = igraph_vector_bool_resize(boolvec, id + 1); + if (ret) { + RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); + } + for (i = s; i < id; i++) { + VECTOR(*boolvec)[i] = graphmlrec->default_value.as_boolean; + } + } + VECTOR(*boolvec)[id] = igraph_i_graphml_parse_boolean(state->data_char, + graphmlrec->default_value.as_boolean); + break; + case IGRAPH_ATTRIBUTE_NUMERIC: + vec = (igraph_vector_t *)rec->value; + s = igraph_vector_size(vec); + if (id >= s) { + ret = igraph_vector_resize(vec, id + 1); + if (ret) { + RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); + } + for (i = s; i < id; i++) { + VECTOR(*vec)[i] = graphmlrec->default_value.as_numeric; + } + } + VECTOR(*vec)[id] = igraph_i_graphml_parse_numeric(state->data_char, + graphmlrec->default_value.as_numeric); + break; + case IGRAPH_ATTRIBUTE_STRING: + strvec = (igraph_strvector_t *)rec->value; + s = igraph_strvector_size(strvec); + if (id >= s) { + ret = igraph_strvector_resize(strvec, id + 1); + if (ret) { + RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); + } + strvalue = graphmlrec->default_value.as_string; + for (i = s; i < id; i++) { + igraph_strvector_set(strvec, i, strvalue); + } + } + if (state->data_char) { + strvalue = state->data_char; + } else { + strvalue = graphmlrec->default_value.as_string; + } + ret = igraph_strvector_set(strvec, id, strvalue); + if (ret) { + RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); + } + break; + default: + break; + } + + if (state->data_char) { + IGRAPH_FREE(state->data_char); + } +} + +static void igraph_i_graphml_attribute_default_value_finish( + struct igraph_i_graphml_parser_state *state) { + igraph_i_graphml_attribute_record_t *graphmlrec = state->current_attr_record; + + if (graphmlrec == 0) { + IGRAPH_FATAL( + "state->current_attr_record was null where it should have been " + "non-null; please report as a bug." + ); + return; + } + + if (state->data_char == 0) { + return; + } + + switch (graphmlrec->record.type) { + case IGRAPH_ATTRIBUTE_BOOLEAN: + graphmlrec->default_value.as_boolean = igraph_i_graphml_parse_boolean( + state->data_char, 0); + break; + case IGRAPH_ATTRIBUTE_NUMERIC: + graphmlrec->default_value.as_numeric = igraph_i_graphml_parse_numeric( + state->data_char, IGRAPH_NAN); + break; + case IGRAPH_ATTRIBUTE_STRING: + if (state->data_char) { + if (graphmlrec->default_value.as_string != 0) { + free(graphmlrec->default_value.as_string); + } + graphmlrec->default_value.as_string = strdup(state->data_char); + } + break; + default: + break; + } + + if (state->data_char) { + IGRAPH_FREE(state->data_char); + } +} + +static void igraph_i_graphml_sax_handler_start_element_ns( + void *state0, const xmlChar* localname, const xmlChar* prefix, + const xmlChar* uri, int nb_namespaces, const xmlChar** namespaces, + int nb_attributes, int nb_defaulted, const xmlChar** attributes) { + struct igraph_i_graphml_parser_state *state = + (struct igraph_i_graphml_parser_state*)state0; + xmlChar** it; + char* attr_value; + long int id1, id2; + int i; + igraph_bool_t tag_is_unknown = 0; + + IGRAPH_UNUSED(prefix); + IGRAPH_UNUSED(nb_namespaces); + IGRAPH_UNUSED(namespaces); + IGRAPH_UNUSED(nb_defaulted); + + if (!state->successful) { + return; + } + + if (uri) { + if (!xmlStrEqual(toXmlChar(GRAPHML_NAMESPACE_URI), uri)) { + /* Tag is in a different namespace, so treat it as an unknown start + * tag irrespectively of our state */ + tag_is_unknown = 1; + } + } else { + /* No namespace URI. If we are in lenient mode, accept it and proceed + * as if we are in the GraphML namespace to handle lots of naive + * non-namespace-aware GraphML files floating out there. If we are not + * in lenient mode _but_ we are in the START state, accept it as well + * and see whether the root tag is (in which case we will + * enter lenient mode). Otherwise, reject the tag */ + if (!state->ignore_namespaces && state->st != START) { + tag_is_unknown = 1; + } + } + + if (tag_is_unknown) { + igraph_i_graphml_handle_unknown_start_tag(state); + return; + } + + switch (state->st) { + case START: + /* If we are in the START state and received a graphml tag, + * change to INSIDE_GRAPHML state. Otherwise, change to UNKNOWN. */ + if (xmlStrEqual(localname, toXmlChar("graphml"))) { + if (uri == 0) { + state->ignore_namespaces = 1; + } + state->st = INSIDE_GRAPHML; + } else { + igraph_i_graphml_handle_unknown_start_tag(state); + } + break; + + case INSIDE_GRAPHML: + /* If we are in the INSIDE_GRAPHML state and received a graph tag, + * change to INSIDE_GRAPH state if the state->index counter reached + * zero (this is to handle multiple graphs in the same file). + * Otherwise, change to UNKNOWN. */ + if (xmlStrEqual(localname, toXmlChar("graph"))) { + if (state->index == 0) { + state->st = INSIDE_GRAPH; + for (i = 0, it = (xmlChar**)attributes; i < nb_attributes; i++, it += 5) { + if (XML_ATTR_URI(it) != 0 && + !xmlStrEqual(toXmlChar(GRAPHML_NAMESPACE_URI), XML_ATTR_URI(it))) { + /* Attribute is from a different namespace, so skip it */ + continue; + } + if (xmlStrEqual(*it, toXmlChar("edgedefault"))) { + if (!xmlStrncmp(toXmlChar("directed"), XML_ATTR_VALUE(it))) { + state->edges_directed = 1; + } else if (!xmlStrncmp(toXmlChar("undirected"), XML_ATTR_VALUE(it))) { + state->edges_directed = 0; + } + } + } + } + state->index--; + } else if (xmlStrEqual(localname, toXmlChar("key"))) { + state->current_attr_record = + igraph_i_graphml_add_attribute_key(attributes, nb_attributes, state); + state->st = INSIDE_KEY; + } else { + igraph_i_graphml_handle_unknown_start_tag(state); + } + break; + + case INSIDE_KEY: + /* If we are in the INSIDE_KEY state and we are not skipping the current + * attribute, check for default tag */ + if (state->current_attr_record != NULL && xmlStrEqual(localname, toXmlChar("default"))) { + state->st = INSIDE_DEFAULT; + } else { + igraph_i_graphml_handle_unknown_start_tag(state); + } + break; + + case INSIDE_DEFAULT: + /* If we are in the INSIDE_DEFAULT state, every further tag will be unknown */ + igraph_i_graphml_handle_unknown_start_tag(state); + break; + + case INSIDE_GRAPH: + /* If we are in the INSIDE_GRAPH state, check for node and edge tags */ + if (xmlStrEqual(localname, toXmlChar("edge"))) { + id1 = -1; id2 = -1; + for (i = 0, it = (xmlChar**)attributes; i < nb_attributes; i++, it += 5) { + if (XML_ATTR_URI(it) != 0 && + !xmlStrEqual(toXmlChar(GRAPHML_NAMESPACE_URI), XML_ATTR_URI(it))) { + /* Attribute is from a different namespace, so skip it */ + continue; + } + if (xmlStrEqual(*it, toXmlChar("source"))) { + attr_value = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it))); + igraph_trie_get(&state->node_trie, attr_value, &id1); + free(attr_value); + } else if (xmlStrEqual(*it, toXmlChar("target"))) { + attr_value = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it))); + igraph_trie_get(&state->node_trie, attr_value, &id2); + free(attr_value); + } else if (xmlStrEqual(*it, toXmlChar("id"))) { + long int edges = igraph_vector_size(&state->edgelist) / 2 + 1; + long int origsize = igraph_strvector_size(&state->edgeids); + attr_value = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it))); + igraph_strvector_resize(&state->edgeids, edges); + for (; origsize < edges - 1; origsize++) { + igraph_strvector_set(&state->edgeids, origsize, ""); + } + igraph_strvector_set(&state->edgeids, edges - 1, attr_value); + free(attr_value); + } + } + if (id1 >= 0 && id2 >= 0) { + igraph_vector_push_back(&state->edgelist, id1); + igraph_vector_push_back(&state->edgelist, id2); + } else { + igraph_i_graphml_sax_handler_error(state, "Edge with missing source or target encountered"); + return; + } + state->st = INSIDE_EDGE; + } else if (xmlStrEqual(localname, toXmlChar("node"))) { + id1 = -1; + for (i = 0, it = (xmlChar**)attributes; i < nb_attributes; i++, it += 5) { + if (XML_ATTR_URI(it) != 0 && + !xmlStrEqual(toXmlChar(GRAPHML_NAMESPACE_URI), XML_ATTR_URI(it))) { + /* Attribute is from a different namespace, so skip it */ + continue; + } + if (xmlStrEqual(XML_ATTR_LOCALNAME(it), toXmlChar("id"))) { + attr_value = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it))); + igraph_trie_get(&state->node_trie, attr_value, &id1); + free(attr_value); + break; + } + } + if (id1 >= 0) { + state->act_node = id1; + } else { + state->act_node = -1; + igraph_i_graphml_sax_handler_error(state, "Node with missing id encountered"); + return; + } + state->st = INSIDE_NODE; + } else if (xmlStrEqual(localname, toXmlChar("data"))) { + igraph_i_graphml_attribute_data_setup(state, attributes, nb_attributes, + IGRAPH_ATTRIBUTE_GRAPH); + igraph_vector_int_push_back(&state->prev_state_stack, state->st); + state->st = INSIDE_DATA; + } else { + igraph_i_graphml_handle_unknown_start_tag(state); + } + break; + + case INSIDE_NODE: + if (xmlStrEqual(localname, toXmlChar("data"))) { + igraph_i_graphml_attribute_data_setup(state, attributes, nb_attributes, + IGRAPH_ATTRIBUTE_VERTEX); + igraph_vector_int_push_back(&state->prev_state_stack, state->st); + state->st = INSIDE_DATA; + } + break; + + case INSIDE_EDGE: + if (xmlStrEqual(localname, toXmlChar("data"))) { + igraph_i_graphml_attribute_data_setup(state, attributes, nb_attributes, + IGRAPH_ATTRIBUTE_EDGE); + igraph_vector_int_push_back(&state->prev_state_stack, state->st); + state->st = INSIDE_DATA; + } + break; + + case INSIDE_DATA: + /* We do not expect any new tags within a tag */ + igraph_i_graphml_handle_unknown_start_tag(state); + break; + + case UNKNOWN: + igraph_i_graphml_handle_unknown_start_tag(state); + break; + + default: + break; + } +} + +static void igraph_i_graphml_sax_handler_end_element_ns( + void *state0, + const xmlChar* localname, const xmlChar* prefix, + const xmlChar* uri) { + struct igraph_i_graphml_parser_state *state = + (struct igraph_i_graphml_parser_state*)state0; + + if (!state->successful) { + return; + } + + IGRAPH_UNUSED(localname); + IGRAPH_UNUSED(prefix); + IGRAPH_UNUSED(uri); + + switch (state->st) { + case INSIDE_GRAPHML: + state->st = FINISH; + break; + + case INSIDE_GRAPH: + state->st = INSIDE_GRAPHML; + break; + + case INSIDE_KEY: + state->current_attr_record = NULL; + state->st = INSIDE_GRAPHML; + break; + + case INSIDE_DEFAULT: + igraph_i_graphml_attribute_default_value_finish(state); + state->st = INSIDE_KEY; + break; + + case INSIDE_NODE: + state->st = INSIDE_GRAPH; + break; + + case INSIDE_EDGE: + state->st = INSIDE_GRAPH; + break; + + case INSIDE_DATA: + igraph_i_graphml_attribute_data_finish(state); + state->st = igraph_vector_int_pop_back(&state->prev_state_stack); + break; + + case UNKNOWN: + state->unknown_depth--; + if (!state->unknown_depth) { + state->st = igraph_vector_int_pop_back(&state->prev_state_stack); + } + break; + + default: + break; + } +} + +static void igraph_i_graphml_sax_handler_chars(void* state0, const xmlChar* ch, int len) { + struct igraph_i_graphml_parser_state *state = + (struct igraph_i_graphml_parser_state*)state0; + + if (!state->successful) { + return; + } + + switch (state->st) { + case INSIDE_KEY: + break; + + case INSIDE_DATA: + case INSIDE_DEFAULT: + igraph_i_graphml_append_to_data_char(state, ch, len); + break; + + default: + /* just ignore it */ + break; + } +} + +static xmlSAXHandler igraph_i_graphml_sax_handler = { + /* internalSubset = */ 0, + /* isStandalone = */ 0, + /* hasInternalSubset = */ 0, + /* hasExternalSubset = */ 0, + /* resolveEntity = */ 0, + /* getEntity = */ igraph_i_graphml_sax_handler_get_entity, + /* entityDecl = */ 0, + /* notationDecl = */ 0, + /* attributeDecl = */ 0, + /* elementDecl = */ 0, + /* unparsedEntityDecl = */ 0, + /* setDocumentLocator = */ 0, + /* startDocument = */ igraph_i_graphml_sax_handler_start_document, + /* endDocument = */ 0, + /* startElement = */ 0, + /* endElement = */ 0, + /* reference = */ 0, + /* characters = */ igraph_i_graphml_sax_handler_chars, + /* ignorableWhitespaceFunc = */ 0, + /* processingInstruction = */ 0, + /* comment = */ 0, + /* warning = */ igraph_i_graphml_sax_handler_error, + /* error = */ igraph_i_graphml_sax_handler_error, + /* fatalError = */ igraph_i_graphml_sax_handler_error, + /* getParameterEntity = */ 0, + /* cdataBlock = */ 0, + /* externalSubset = */ 0, + /* initialized = */ XML_SAX2_MAGIC, + /* _private = */ 0, + /* startElementNs = */ igraph_i_graphml_sax_handler_start_element_ns, + /* endElementNs = */ igraph_i_graphml_sax_handler_end_element_ns, + /* serror = */ 0 +}; + +#endif + +#define IS_FORBIDDEN_CONTROL_CHAR(x) ((x) < ' ' && (x) != '\t' && (x) != '\r' && (x) != '\n') + +static int igraph_i_xml_escape(char* src, char** dest) { + long int destlen = 0; + char *s, *d; + unsigned char ch; + + for (s = src; *s; s++, destlen++) { + ch = (unsigned char)(*s); + if (ch == '&') { + destlen += 4; + } else if (ch == '<') { + destlen += 3; + } else if (ch == '>') { + destlen += 3; + } else if (ch == '"') { + destlen += 5; + } else if (ch == '\'') { + destlen += 5; + } else if (IS_FORBIDDEN_CONTROL_CHAR(ch)) { + char msg[4096]; + snprintf(msg, 4096, "Forbidden control character 0x%02X found in igraph_i_xml_escape", + ch); + IGRAPH_ERROR(msg, IGRAPH_EINVAL); + } + } + *dest = IGRAPH_CALLOC(destlen + 1, char); + if (!*dest) { + IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); + } + for (s = src, d = *dest; *s; s++, d++) { + ch = (unsigned char)(*s); + switch (ch) { + case '&': + strcpy(d, "&"); d += 4; break; + case '<': + strcpy(d, "<"); d += 3; break; + case '>': + strcpy(d, ">"); d += 3; break; + case '"': + strcpy(d, """); d += 5; break; + case '\'': + strcpy(d, "'"); d += 5; break; + default: + *d = ch; + } + } + *d = 0; + return 0; +} + +/** + * \ingroup loadsave + * \function igraph_read_graph_graphml + * \brief Reads a graph from a GraphML file. + * + * + * GraphML is an XML-based file format for representing various types of + * graphs. Currently only the most basic import functionality is implemented + * in igraph: it can read GraphML files without nested graphs and hyperedges. + * Attributes of the graph are loaded only if an attribute interface + * is attached, i.e. if you use igraph from R or Python. + * + * + * Graph attribute names are taken from the attr.name attributes of the + * \c key tags in the GraphML file. Since attr.name is not mandatory, + * igraph will fall back to the \c id attribute of the \c key tag if + * attr.name is missing. + * + * \param graph Pointer to an uninitialized graph object. + * \param instream A stream, it should be readable. + * \param index If the GraphML file contains more than one graph, the one + * specified by this index will be loaded. Indices start from + * zero, so supply zero here if your GraphML file contains only + * a single graph. + * + * \return Error code: + * \c IGRAPH_PARSEERROR: if there is a + * problem reading the file, or the file is syntactically + * incorrect. + * \c IGRAPH_UNIMPLEMENTED: the GraphML functionality was disabled + * at compile-time + * + * \example examples/simple/graphml.c + */ +int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, int index) { + +#if HAVE_LIBXML == 1 + xmlParserCtxtPtr ctxt; + xmlDocPtr doc; + + struct igraph_i_graphml_parser_state state; + int res; + char buffer[4096]; + igraph_bool_t parsing_successful; + char* error_message; + + if (index < 0) { + IGRAPH_ERROR("Graph index must be non-negative", IGRAPH_EINVAL); + } + + xmlInitParser(); + IGRAPH_CHECK(igraph_i_graphml_parser_state_init(&state, graph, index)); + IGRAPH_FINALLY(igraph_i_graphml_parser_state_destroy, &state); + + /* Create a progressive parser context */ + res = (int) fread(buffer, 1, sizeof(buffer), instream); + if (res < sizeof(buffer) && !feof(instream)) { + IGRAPH_ERROR("IO error while reading GraphML data", IGRAPH_PARSEERROR); + } + ctxt = xmlCreatePushParserCtxt(&igraph_i_graphml_sax_handler, + &state, + buffer, + res, + NULL); + /* ctxt=xmlCreateIOParserCtxt(&igraph_i_graphml_sax_handler, &state, */ + /* igraph_i_libxml2_read_callback, */ + /* igraph_i_libxml2_close_callback, */ + /* instream, XML_CHAR_ENCODING_NONE); */ + if (ctxt == NULL) { + IGRAPH_ERROR("Can't create progressive parser context", IGRAPH_PARSEERROR); + } + + /* Set parsing options */ + if (xmlCtxtUseOptions(ctxt, + XML_PARSE_NOENT | XML_PARSE_NOBLANKS | + XML_PARSE_NONET | XML_PARSE_NSCLEAN | + XML_PARSE_NOCDATA | XML_PARSE_HUGE + )) { + xmlFreeParserCtxt(ctxt); + IGRAPH_ERROR("Cannot set options for the parser context", IGRAPH_EINVAL); + } + + /* Okay, parsing will start now. The parser might do things that eventually + * trigger the igraph error handler, but we want the parser state to + * survive whatever happens here. So, we need to pop off + * igraph_i_graphml_parser_state_destroy() from the stack and temporarily + * assume responsibility for calling it ourselves until we are back from the + * parser */ + IGRAPH_FINALLY_CLEAN(1); + + /* Do the parsing */ + while ((res = (int) fread(buffer, 1, sizeof(buffer), instream)) > 0) { + xmlParseChunk(ctxt, buffer, res, 0); + if (!state.successful) { + break; + } + } + xmlParseChunk(ctxt, buffer, res, 1); + + /* Free the context */ + doc = ctxt->myDoc; + xmlFreeParserCtxt(ctxt); + if (doc) { + /* In theory this should not be necessary, but it looks like certain malformed + * GraphML files leave a partially-parsed doc in memory */ + xmlFreeDoc(doc); + } + + /* Extract the error message from the parser state (if any), and make a + * copy so we can safely destroy the parser state before triggering the + * error */ + parsing_successful = state.successful; + error_message = parsing_successful || state.error_message == NULL ? NULL : strdup(state.error_message); + + /* Now that we have lifted error_message out of the parser state, we can + * put the destructor of the parser state back on the FINALLY stack */ + IGRAPH_FINALLY(igraph_i_graphml_parser_state_destroy, &state); + + /* ...and we can also put the error message pointer on the FINALLY stack */ + if (error_message != NULL) { + IGRAPH_FINALLY(free, error_message); + } + + /* Trigger the stored error if needed */ + if (!parsing_successful) { + if (error_message != NULL) { + IGRAPH_ERROR(error_message, IGRAPH_PARSEERROR); + } else { + IGRAPH_ERROR("Malformed GraphML file", IGRAPH_PARSEERROR); + } + } + + /* Did we actually manage to reach the graph to be parsed, given its index? + * If not, that's an error as well. */ + if (state.index >= 0) { + IGRAPH_ERROR("Graph index was too large", IGRAPH_EINVAL); + } + + /* Okay, everything seems good. We can now take the parser state and + * construct our graph from the data gathered during the parsing */ + IGRAPH_CHECK(igraph_i_graphml_parser_state_finish_parsing(&state)); + + igraph_i_graphml_parser_state_destroy(&state); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +#else + IGRAPH_UNUSED(graph); + IGRAPH_UNUSED(instream); + IGRAPH_UNUSED(index); + + IGRAPH_ERROR("GraphML support is disabled", IGRAPH_UNIMPLEMENTED); +#endif +} + +/** + * \ingroup loadsave + * \function igraph_write_graph_graphml + * \brief Writes the graph to a file in GraphML format + * + * + * GraphML is an XML-based file format for representing various types of + * graphs. See the GraphML Primer (http://graphml.graphdrawing.org/primer/graphml-primer.html) + * for detailed format description. + * + * \param graph The graph to write. + * \param outstream The stream object to write to, it should be + * writable. + * \param prefixattr Logical value, whether to put a prefix in front of the + * attribute names to ensure uniqueness if the graph has vertex and + * edge (or graph) attributes with the same name. + * \return Error code: + * \c IGRAPH_EFILE if there is an error + * writing the file. + * + * Time complexity: O(|V|+|E|) otherwise. All + * file operations are expected to have time complexity + * O(1). + * + * \example examples/simple/graphml.c + */ +int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, + igraph_bool_t prefixattr) { + int ret; + igraph_integer_t l, vc; + igraph_eit_t it; + igraph_strvector_t gnames, vnames, enames; + igraph_vector_t gtypes, vtypes, etypes; + long int i; + igraph_vector_t numv; + igraph_strvector_t strv; + igraph_vector_bool_t boolv; + const char *gprefix = prefixattr ? "g_" : ""; + const char *vprefix = prefixattr ? "v_" : ""; + const char *eprefix = prefixattr ? "e_" : ""; + + /* set standard C locale lest we sometimes get commas instead of dots */ + char *saved_locale = strdup(setlocale(LC_NUMERIC, NULL)); + if (saved_locale == NULL) { + IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, saved_locale); + setlocale(LC_NUMERIC, "C"); + + ret = fprintf(outstream, "\n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = fprintf(outstream, "\n", GRAPHML_NAMESPACE_URI); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = fprintf(outstream, "\n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + + /* dump the elements if any */ + + IGRAPH_VECTOR_INIT_FINALLY(&numv, 1); + IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&boolv, 1); + + IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0); + IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0); + IGRAPH_STRVECTOR_INIT_FINALLY(&enames, 0); + IGRAPH_VECTOR_INIT_FINALLY(>ypes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&etypes, 0); + igraph_i_attribute_get_info(graph, + &gnames, >ypes, + &vnames, &vtypes, + &enames, &etypes); + + /* graph attributes */ + for (i = 0; i < igraph_vector_size(>ypes); i++) { + char *name, *name_escaped; + igraph_strvector_get(&gnames, i, &name); + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { + ret = fprintf(outstream, " \n", gprefix, name_escaped, name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + ret = fprintf(outstream, " \n", gprefix, name_escaped, name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { + ret = fprintf(outstream, " \n", gprefix, name_escaped, name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } + IGRAPH_FREE(name_escaped); + } + + /* vertex attributes */ + for (i = 0; i < igraph_vector_size(&vtypes); i++) { + char *name, *name_escaped; + igraph_strvector_get(&vnames, i, &name); + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { + ret = fprintf(outstream, " \n", vprefix, name_escaped, name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + ret = fprintf(outstream, " \n", vprefix, name_escaped, name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { + ret = fprintf(outstream, " \n", vprefix, name_escaped, name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } + IGRAPH_FREE(name_escaped); + } + + /* edge attributes */ + for (i = 0; i < igraph_vector_size(&etypes); i++) { + char *name, *name_escaped; + igraph_strvector_get(&enames, i, &name); + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_STRING) { + ret = fprintf(outstream, " \n", eprefix, name_escaped, name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + ret = fprintf(outstream, " \n", eprefix, name_escaped, name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { + ret = fprintf(outstream, " \n", eprefix, name_escaped, name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } + IGRAPH_FREE(name_escaped); + } + + ret = fprintf(outstream, " \n", (igraph_is_directed(graph) ? "directed" : "undirected")); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + + /* Write the graph atributes before anything else */ + + for (i = 0; i < igraph_vector_size(>ypes); i++) { + char *name, *name_escaped; + if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_strvector_get(&gnames, i, &name); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_graph_attr(graph, name, &numv)); + if (!isnan(VECTOR(numv)[0])) { + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + ret = fprintf(outstream, " ", gprefix, name_escaped); + IGRAPH_FREE(name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = fprintf(outstream, "\n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } + } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { + char *s, *s_escaped; + igraph_strvector_get(&gnames, i, &name); + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + ret = fprintf(outstream, " ", gprefix, + name_escaped); + IGRAPH_FREE(name_escaped); + IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); + igraph_strvector_get(&strv, 0, &s); + IGRAPH_CHECK(igraph_i_xml_escape(s, &s_escaped)); + ret = fprintf(outstream, "%s", s_escaped); + IGRAPH_FREE(s_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = fprintf(outstream, "\n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_strvector_get(&gnames, i, &name); + IGRAPH_CHECK(igraph_i_attribute_get_bool_graph_attr(graph, name, &boolv)); + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + ret = fprintf(outstream, " %s\n", + gprefix, name_escaped, VECTOR(boolv)[0] ? "true" : "false"); + IGRAPH_FREE(name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } + } + + /* Let's dump the nodes first */ + vc = igraph_vcount(graph); + for (l = 0; l < vc; l++) { + char *name, *name_escaped; + ret = fprintf(outstream, " \n", (long)l); + + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + + for (i = 0; i < igraph_vector_size(&vtypes); i++) { + if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_strvector_get(&vnames, i, &name); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, name, + igraph_vss_1(l), &numv)); + if (!isnan(VECTOR(numv)[0])) { + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + ret = fprintf(outstream, " ", vprefix, name_escaped); + IGRAPH_FREE(name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = fprintf(outstream, "\n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } + } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { + char *s, *s_escaped; + igraph_strvector_get(&vnames, i, &name); + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + ret = fprintf(outstream, " ", vprefix, + name_escaped); + IGRAPH_FREE(name_escaped); + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, + igraph_vss_1(l), &strv)); + igraph_strvector_get(&strv, 0, &s); + IGRAPH_CHECK(igraph_i_xml_escape(s, &s_escaped)); + ret = fprintf(outstream, "%s", s_escaped); + IGRAPH_FREE(s_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = fprintf(outstream, "\n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_strvector_get(&vnames, i, &name); + IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph, name, + igraph_vss_1(l), &boolv)); + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + ret = fprintf(outstream, " %s\n", + vprefix, name_escaped, VECTOR(boolv)[0] ? "true" : "false"); + IGRAPH_FREE(name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } + } + + ret = fprintf(outstream, " \n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } + + /* Now the edges */ + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t from, to; + char *name, *name_escaped; + long int edge = IGRAPH_EIT_GET(it); + igraph_edge(graph, (igraph_integer_t) edge, &from, &to); + ret = fprintf(outstream, " \n", + (long int)from, (long int)to); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + + for (i = 0; i < igraph_vector_size(&etypes); i++) { + if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_strvector_get(&enames, i, &name); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, name, + igraph_ess_1((igraph_integer_t) edge), &numv)); + if (!isnan(VECTOR(numv)[0])) { + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + ret = fprintf(outstream, " ", eprefix, name_escaped); + IGRAPH_FREE(name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = fprintf(outstream, "\n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } + } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_STRING) { + char *s, *s_escaped; + igraph_strvector_get(&enames, i, &name); + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + ret = fprintf(outstream, " ", eprefix, + name_escaped); + IGRAPH_FREE(name_escaped); + IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, name, + igraph_ess_1((igraph_integer_t) edge), &strv)); + igraph_strvector_get(&strv, 0, &s); + IGRAPH_CHECK(igraph_i_xml_escape(s, &s_escaped)); + ret = fprintf(outstream, "%s", s_escaped); + IGRAPH_FREE(s_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = fprintf(outstream, "\n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_strvector_get(&enames, i, &name); + IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr(graph, name, + igraph_ess_1((igraph_integer_t) edge), &boolv)); + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + ret = fprintf(outstream, " %s\n", + eprefix, name_escaped, VECTOR(boolv)[0] ? "true" : "false"); + IGRAPH_FREE(name_escaped); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } + } + + ret = fprintf(outstream, " \n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + + ret = fprintf(outstream, " \n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + fprintf(outstream, "\n"); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + + /* reset locale to whatever was before this function */ + setlocale(LC_NUMERIC, saved_locale); + + igraph_free(saved_locale); + igraph_strvector_destroy(&gnames); + igraph_strvector_destroy(&vnames); + igraph_strvector_destroy(&enames); + igraph_vector_destroy(>ypes); + igraph_vector_destroy(&vtypes); + igraph_vector_destroy(&etypes); + igraph_vector_destroy(&numv); + igraph_strvector_destroy(&strv); + igraph_vector_bool_destroy(&boolv); + IGRAPH_FINALLY_CLEAN(10); + + return 0; +} diff --git a/src/rigraph/core/io/leda.c b/src/rigraph/core/io/leda.c new file mode 100644 index 0000000..ac6295b --- /dev/null +++ b/src/rigraph/core/io/leda.c @@ -0,0 +1,258 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" + +#include "../graph/attributes.h" + +#include + +#define CHECK(cmd) do { ret=cmd; if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } while (0) + +/** + * \function igraph_write_graph_leda + * \brief Write a graph in LEDA native graph format. + * + * This function writes a graph to an output stream in LEDA format. + * See http://www.algorithmic-solutions.info/leda_guide/graphs/leda_native_graph_fileformat.html + * + * + * The support for the LEDA format is very basic at the moment; igraph + * writes only the LEDA graph section which supports one selected vertex + * and edge attribute and no layout information or visual attributes. + * + * \param graph The graph to write to the stream. + * \param outstream The stream. + * \param vertex_attr_name The name of the vertex attribute whose values + * are to be stored in the output or \c NULL if no + * vertex attribute has to be stored. + * \param edge_attr_name The name of the edge attribute whose values + * are to be stored in the output or \c NULL if no + * edge attribute has to be stored. + * \return Error code. + * + * Time complexity: O(|V|+|E|), the number of vertices and edges in the + * graph. + * + * \example examples/simple/igraph_write_graph_leda.c + */ + +int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, + const char* vertex_attr_name, + const char* edge_attr_name) { + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_eit_t it; + long int i = 0; + int ret; + igraph_attribute_type_t vertex_attr_type = IGRAPH_ATTRIBUTE_DEFAULT; + igraph_attribute_type_t edge_attr_type = IGRAPH_ATTRIBUTE_DEFAULT; + igraph_integer_t from, to, rev; + + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM), + &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + + /* Check if we have the vertex attribute */ + if (vertex_attr_name && + !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, vertex_attr_name)) { + vertex_attr_name = 0; + IGRAPH_WARNING("specified vertex attribute does not exist"); + } + if (vertex_attr_name) { + IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &vertex_attr_type, + IGRAPH_ATTRIBUTE_VERTEX, vertex_attr_name)); + if (vertex_attr_type != IGRAPH_ATTRIBUTE_NUMERIC && + vertex_attr_type != IGRAPH_ATTRIBUTE_STRING) { + vertex_attr_name = 0; vertex_attr_type = IGRAPH_ATTRIBUTE_DEFAULT; + IGRAPH_WARNING("specified vertex attribute must be numeric or string"); + } + } + + /* Check if we have the edge attribute */ + if (edge_attr_name && + !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, edge_attr_name)) { + edge_attr_name = 0; + IGRAPH_WARNING("specified edge attribute does not exist"); + } + if (edge_attr_name) { + IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &edge_attr_type, + IGRAPH_ATTRIBUTE_EDGE, edge_attr_name)); + if (edge_attr_type != IGRAPH_ATTRIBUTE_NUMERIC && + edge_attr_type != IGRAPH_ATTRIBUTE_STRING) { + edge_attr_name = 0; edge_attr_type = IGRAPH_ATTRIBUTE_DEFAULT; + IGRAPH_WARNING("specified edge attribute must be numeric or string"); + } + } + + /* Start writing header */ + CHECK(fprintf(outstream, "LEDA.GRAPH\n")); + + switch (vertex_attr_type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + CHECK(fprintf(outstream, "float\n")); + break; + case IGRAPH_ATTRIBUTE_STRING: + CHECK(fprintf(outstream, "string\n")); + break; + default: + CHECK(fprintf(outstream, "void\n")); + } + + switch (edge_attr_type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + CHECK(fprintf(outstream, "float\n")); + break; + case IGRAPH_ATTRIBUTE_STRING: + CHECK(fprintf(outstream, "string\n")); + break; + default: + CHECK(fprintf(outstream, "void\n")); + } + + CHECK(fprintf(outstream, "%d\n", (igraph_is_directed(graph) ? -1 : -2))); + + /* Start writing vertices */ + CHECK(fprintf(outstream, "# Vertices\n")); + CHECK(fprintf(outstream, "%ld\n", no_of_nodes)); + + if (vertex_attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { + /* Vertices with numeric attributes */ + igraph_vector_t values; + + IGRAPH_VECTOR_INIT_FINALLY(&values, no_of_nodes); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr( + graph, vertex_attr_name, igraph_vss_all(), &values)); + + for (i = 0; i < no_of_nodes; i++) { + CHECK(fprintf(outstream, "|{")); + CHECK(igraph_real_fprintf_precise(outstream, VECTOR(values)[i])); + CHECK(fprintf(outstream, "}|\n")); + } + + igraph_vector_destroy(&values); + IGRAPH_FINALLY_CLEAN(1); + } else if (vertex_attr_type == IGRAPH_ATTRIBUTE_STRING) { + /* Vertices with string attributes */ + igraph_strvector_t values; + + IGRAPH_CHECK(igraph_strvector_init(&values, no_of_nodes)); + IGRAPH_FINALLY(igraph_strvector_destroy, &values); + + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr( + graph, vertex_attr_name, igraph_vss_all(), &values)); + + for (i = 0; i < no_of_nodes; i++) { + const char* str = STR(values, i); + if (strchr(str, '\n') != 0) { + IGRAPH_ERROR("edge attribute values cannot contain newline characters", + IGRAPH_EINVAL); + } + CHECK(fprintf(outstream, "|{%s}|\n", str)); + } + + igraph_strvector_destroy(&values); + IGRAPH_FINALLY_CLEAN(1); + } else { + /* Vertices with no attributes */ + for (i = 0; i < no_of_nodes; i++) { + CHECK(fprintf(outstream, "|{}|\n")); + } + } + + CHECK(fprintf(outstream, "# Edges\n")); + CHECK(fprintf(outstream, "%ld\n", no_of_edges)); + + if (edge_attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { + /* Edges with numeric attributes */ + igraph_vector_t values; + IGRAPH_VECTOR_INIT_FINALLY(&values, no_of_nodes); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr( + graph, edge_attr_name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &values)); + while (!IGRAPH_EIT_END(it)) { + long int eid = IGRAPH_EIT_GET(it); + igraph_edge(graph, (igraph_integer_t) eid, &from, &to); + igraph_get_eid(graph, &rev, to, from, 1, 0); + if (rev == IGRAPH_EIT_GET(it)) { + rev = -1; + } + CHECK(fprintf(outstream, "%ld %ld %ld |{", + (long int) from + 1, (long int) to + 1, + (long int) rev + 1)); + CHECK(igraph_real_fprintf_precise(outstream, VECTOR(values)[eid])); + CHECK(fprintf(outstream, "}|\n")); + IGRAPH_EIT_NEXT(it); + } + igraph_vector_destroy(&values); + IGRAPH_FINALLY_CLEAN(1); + } else if (edge_attr_type == IGRAPH_ATTRIBUTE_STRING) { + /* Edges with string attributes */ + igraph_strvector_t values; + IGRAPH_CHECK(igraph_strvector_init(&values, no_of_nodes)); + IGRAPH_FINALLY(igraph_strvector_destroy, &values); + IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr( + graph, edge_attr_name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &values)); + while (!IGRAPH_EIT_END(it)) { + long int eid = IGRAPH_EIT_GET(it); + const char* str = STR(values, eid); + igraph_edge(graph, (igraph_integer_t) eid, &from, &to); + igraph_get_eid(graph, &rev, to, from, 1, 0); + if (rev == IGRAPH_EIT_GET(it)) { + rev = -1; + } + if (strchr(str, '\n') != 0) { + IGRAPH_ERROR("edge attribute values cannot contain newline characters", + IGRAPH_EINVAL); + } + CHECK(fprintf(outstream, "%ld %ld %ld |{%s}|\n", + (long int) from + 1, (long int) to + 1, + (long int) rev + 1, str)); + IGRAPH_EIT_NEXT(it); + } + igraph_strvector_destroy(&values); + IGRAPH_FINALLY_CLEAN(1); + } else { + /* Edges with no attributes */ + while (!IGRAPH_EIT_END(it)) { + igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); + igraph_get_eid(graph, &rev, to, from, 1, 0); + if (rev == IGRAPH_EIT_GET(it)) { + rev = -1; + } + CHECK(fprintf(outstream, "%ld %ld %ld |{}|\n", + (long int) from + 1, (long int) to + 1, + (long int) rev + 1)); + IGRAPH_EIT_NEXT(it); + } + } + + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +#undef CHECK diff --git a/src/rigraph/core/io/lgl-header.h b/src/rigraph/core/io/lgl-header.h new file mode 100644 index 0000000..b624171 --- /dev/null +++ b/src/rigraph/core/io/lgl-header.h @@ -0,0 +1,37 @@ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge MA, 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_error.h" +#include "igraph_vector.h" + +#include "../core/trie.h" + +typedef struct { + void *scanner; + int eof; + char errmsg[300]; + int has_weights; + igraph_vector_t *vector; + igraph_vector_t *weights; + igraph_trie_t *trie; + int actvertex; +} igraph_i_lgl_parsedata_t; diff --git a/src/rigraph/core/io/lgl-lexer.c b/src/rigraph/core/io/lgl-lexer.c new file mode 100644 index 0000000..0f75ccf --- /dev/null +++ b/src/rigraph/core/io/lgl-lexer.c @@ -0,0 +1,2265 @@ +#line 2 "src/core/io/lgl-lexer.c" + +#line 4 "src/core/io/lgl-lexer.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_lgl_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_lgl_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_lgl_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_lgl_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_lgl_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_lgl_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_lgl_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_lgl_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_lgl_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_lgl_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_lgl_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_lgl_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_lgl_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_lgl_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_lgl_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_lgl_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_lgl_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_lgl_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_lgl_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_lgl_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_lgl_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_lgl_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_lgl_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_lgl_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_lgl_yylex_ALREADY_DEFINED +#else +#define yylex igraph_lgl_yylex +#endif + +#ifdef yyrestart +#define igraph_lgl_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_lgl_yyrestart +#endif + +#ifdef yylex_init +#define igraph_lgl_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_lgl_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_lgl_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_lgl_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_lgl_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_lgl_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_lgl_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_lgl_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_lgl_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_lgl_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_lgl_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_lgl_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_lgl_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_lgl_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_lgl_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_lgl_yyget_in +#endif + +#ifdef yyset_in +#define igraph_lgl_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_lgl_yyset_in +#endif + +#ifdef yyget_out +#define igraph_lgl_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_lgl_yyget_out +#endif + +#ifdef yyset_out +#define igraph_lgl_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_lgl_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_lgl_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_lgl_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_lgl_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_lgl_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_lgl_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_lgl_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_lgl_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_lgl_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_lgl_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_lgl_yyget_column +#endif + +#ifdef yyset_column +#define igraph_lgl_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_lgl_yyset_column +#endif + +#ifdef yywrap +#define igraph_lgl_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_lgl_yywrap +#endif + +#ifdef yyget_lval +#define igraph_lgl_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_lgl_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_lgl_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_lgl_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_lgl_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_lgl_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_lgl_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_lgl_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_lgl_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_lgl_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_lgl_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_lgl_yyrealloc +#endif + +#ifdef yyfree +#define igraph_lgl_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_lgl_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define igraph_lgl_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void __attribute__((unused)) yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 6 +#define YY_END_OF_BUFFER 7 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[14] = + { 0, + 0, 0, 7, 4, 2, 3, 3, 1, 5, 4, + 2, 3, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 5, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[7] = + { 0, + 1, 2, 3, 4, 5, 5 + } ; + +static const flex_int16_t yy_base[18] = + { 0, + 0, 0, 11, 0, 0, 0, 0, 12, 12, 0, + 0, 12, 12, 9, 7, 4, 4 + } ; + +static const flex_int16_t yy_def[18] = + { 0, + 13, 1, 13, 14, 15, 16, 17, 13, 13, 14, + 15, 13, 0, 13, 13, 13, 13 + } ; + +static const flex_int16_t yy_nxt[19] = + { 0, + 4, 5, 6, 7, 8, 9, 12, 12, 11, 10, + 13, 3, 13, 13, 13, 13, 13, 13 + } ; + +static const flex_int16_t yy_chk[19] = + { 0, + 1, 1, 1, 1, 1, 1, 17, 16, 15, 14, + 3, 13, 13, 13, 13, 13, 13, 13 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "src/core/io/lgl-lexer.l" +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ +#line 24 "src/core/io/lgl-lexer.l" + +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "config.h" +#include + +#include "lgl-header.h" +#include "lgl-parser.h" + +#define YY_EXTRA_TYPE igraph_i_lgl_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in LGL parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +#line 722 "src/core/io/lgl-lexer.c" +#define YY_NO_INPUT 1 +#line 724 "src/core/io/lgl-lexer.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + yy_size_t yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yylval = yylval_param; + + yylloc = yylloc_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 76 "src/core/io/lgl-lexer.l" + + +#line 79 "src/core/io/lgl-lexer.l" + /* --------------------------------------------------hashmark------*/ +#line 1011 "src/core/io/lgl-lexer.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 14 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 12 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 80 "src/core/io/lgl-lexer.l" +{ return HASH; } + YY_BREAK +/* ------------------------------------------------whitespace------*/ +case 2: +YY_RULE_SETUP +#line 83 "src/core/io/lgl-lexer.l" +{ } + YY_BREAK +/* ---------------------------------------------------newline------*/ +case 3: +/* rule 3 can match eol */ +YY_RULE_SETUP +#line 86 "src/core/io/lgl-lexer.l" +{ return NEWLINE; } + YY_BREAK +/* ----------------------------------------------alphanumeric------*/ +case 4: +YY_RULE_SETUP +#line 89 "src/core/io/lgl-lexer.l" +{ return ALNUM; } + YY_BREAK +case YY_STATE_EOF(INITIAL): +#line 91 "src/core/io/lgl-lexer.l" +{ if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return NEWLINE; + } + } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 99 "src/core/io/lgl-lexer.l" +{ return ERROR; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 101 "src/core/io/lgl-lexer.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1112 "src/core/io/lgl-lexer.c" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap( yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin , yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 6); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 14 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 6; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 14 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 13); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( yyscanner ) ) + return 0; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void __attribute__((unused)) yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +yy_size_t yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void yyset_column (int _column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = _column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int _bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 101 "src/core/io/lgl-lexer.l" + + diff --git a/src/rigraph/core/io/lgl-lexer.h b/src/rigraph/core/io/lgl-lexer.h new file mode 100644 index 0000000..117b4cb --- /dev/null +++ b/src/rigraph/core/io/lgl-lexer.h @@ -0,0 +1,736 @@ +#ifndef igraph_lgl_yyHEADER_H +#define igraph_lgl_yyHEADER_H 1 +#define igraph_lgl_yyIN_HEADER 1 + +#line 6 "src/core/io/lgl-lexer.h" + +#line 8 "src/core/io/lgl-lexer.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_lgl_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_lgl_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_lgl_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_lgl_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_lgl_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_lgl_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_lgl_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_lgl_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_lgl_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_lgl_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_lgl_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_lgl_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_lgl_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_lgl_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_lgl_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_lgl_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_lgl_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_lgl_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_lgl_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_lgl_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_lgl_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_lgl_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_lgl_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_lgl_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_lgl_yylex_ALREADY_DEFINED +#else +#define yylex igraph_lgl_yylex +#endif + +#ifdef yyrestart +#define igraph_lgl_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_lgl_yyrestart +#endif + +#ifdef yylex_init +#define igraph_lgl_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_lgl_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_lgl_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_lgl_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_lgl_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_lgl_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_lgl_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_lgl_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_lgl_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_lgl_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_lgl_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_lgl_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_lgl_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_lgl_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_lgl_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_lgl_yyget_in +#endif + +#ifdef yyset_in +#define igraph_lgl_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_lgl_yyset_in +#endif + +#ifdef yyget_out +#define igraph_lgl_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_lgl_yyget_out +#endif + +#ifdef yyset_out +#define igraph_lgl_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_lgl_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_lgl_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_lgl_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_lgl_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_lgl_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_lgl_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_lgl_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_lgl_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_lgl_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_lgl_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_lgl_yyget_column +#endif + +#ifdef yyset_column +#define igraph_lgl_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_lgl_yyset_column +#endif + +#ifdef yywrap +#define igraph_lgl_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_lgl_yywrap +#endif + +#ifdef yyget_lval +#define igraph_lgl_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_lgl_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_lgl_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_lgl_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_lgl_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_lgl_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_lgl_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_lgl_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_lgl_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_lgl_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_lgl_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_lgl_yyrealloc +#endif + +#ifdef yyfree +#define igraph_lgl_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_lgl_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define igraph_lgl_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef igraph_lgl_yy_create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef igraph_lgl_yy_delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef igraph_lgl_yy_scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef igraph_lgl_yy_scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef igraph_lgl_yy_scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef igraph_lgl_yy_init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef igraph_lgl_yy_flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef igraph_lgl_yy_load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef igraph_lgl_yy_switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef igraph_lgl_yypush_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef igraph_lgl_yypop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef igraph_lgl_yyensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef igraph_lgl_yylex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef igraph_lgl_yyrestart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef igraph_lgl_yylex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef igraph_lgl_yylex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef igraph_lgl_yylex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef igraph_lgl_yyget_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef igraph_lgl_yyset_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef igraph_lgl_yyget_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef igraph_lgl_yyset_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef igraph_lgl_yyget_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef igraph_lgl_yyset_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef igraph_lgl_yyget_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef igraph_lgl_yyset_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef igraph_lgl_yyget_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef igraph_lgl_yyget_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef igraph_lgl_yyget_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef igraph_lgl_yyset_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef igraph_lgl_yyget_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef igraph_lgl_yyset_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef igraph_lgl_yywrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef igraph_lgl_yyget_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef igraph_lgl_yyset_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef igraph_lgl_yyget_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef igraph_lgl_yyset_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef igraph_lgl_yyalloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef igraph_lgl_yyrealloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef igraph_lgl_yyfree_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef igraph_lgl_yytext_ALREADY_DEFINED +#undef yytext +#endif +#ifndef igraph_lgl_yyleng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef igraph_lgl_yyin_ALREADY_DEFINED +#undef yyin +#endif +#ifndef igraph_lgl_yyout_ALREADY_DEFINED +#undef yyout +#endif +#ifndef igraph_lgl_yy_flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef igraph_lgl_yylineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef igraph_lgl_yytables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef igraph_lgl_yytables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef igraph_lgl_yyTABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 101 "src/core/io/lgl-lexer.l" + + +#line 735 "src/core/io/lgl-lexer.h" +#undef igraph_lgl_yyIN_HEADER +#endif /* igraph_lgl_yyHEADER_H */ diff --git a/src/rigraph/core/io/lgl-lexer.l b/src/rigraph/core/io/lgl-lexer.l new file mode 100644 index 0000000..7746b2a --- /dev/null +++ b/src/rigraph/core/io/lgl-lexer.l @@ -0,0 +1,100 @@ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "config.h" +#include + +#include "lgl-header.h" +#include "lgl-parser.h" + +#define YY_EXTRA_TYPE igraph_i_lgl_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in LGL parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +%} + +%option noyywrap +%option prefix="igraph_lgl_yy" +%option nounput +%option noinput +%option nodefault +%option reentrant +%option bison-bridge +%option bison-locations + +alnum [^ \t\r\n\0#] + +%% + + /* --------------------------------------------------hashmark------*/ +# { return HASH; } + + /* ------------------------------------------------whitespace------*/ +[ \t]+ { } + + /* ---------------------------------------------------newline------*/ +\n\r|\r\n|\n|\r { return NEWLINE; } + + /* ----------------------------------------------alphanumeric------*/ +{alnum}+ { return ALNUM; } + +<> { if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return NEWLINE; + } + } + +. { return ERROR; } + +%% diff --git a/src/rigraph/core/io/lgl-parser.c b/src/rigraph/core/io/lgl-parser.c new file mode 100644 index 0000000..c370788 --- /dev/null +++ b/src/rigraph/core/io/lgl-parser.c @@ -0,0 +1,1773 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + 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 + (at your option) 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse igraph_lgl_yyparse +#define yylex igraph_lgl_yylex +#define yyerror igraph_lgl_yyerror +#define yydebug igraph_lgl_yydebug +#define yynerrs igraph_lgl_yynerrs + +/* First part of user prologue. */ +#line 23 "src/core/io/lgl-parser.y" + + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include +#include + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "config.h" + +#include "../core/math.h" +#include "lgl-header.h" +#include "lgl-parser.h" +#include "lgl-lexer.h" +//#include "internal/hacks.h" + +int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, + const char *s); +igraph_real_t igraph_lgl_get_number(const char *str, long int len); + +#define scanner context->scanner + +#line 120 "yy.tab.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_IGRAPH_LGL_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_LGL_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_lgl_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + ALNUM = 258, + NEWLINE = 259, + HASH = 260, + ERROR = 261 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 78 "src/core/io/lgl-parser.y" + + long int edgenum; + double weightnum; + +#line 184 "yy.tab.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_lgl_yyparse (igraph_i_lgl_parsedata_t* context); + +#endif /* !YY_IGRAPH_LGL_YY_YY_TAB_H_INCLUDED */ + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 10 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 7 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 8 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 12 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 17 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 261 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int8 yyrline[] = +{ + 0, 93, 93, 94, 95, 98, 100, 102, 102, 104, + 109, 118, 123 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "ALNUM", "NEWLINE", "HASH", "ERROR", + "$accept", "input", "vertex", "vertexdef", "edges", "edge", "edgeid", + "weight", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261 +}; +# endif + +#define YYPACT_NINF (-3) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + -3, 0, -3, -3, 3, -3, -3, -3, -1, 3, + -3, -3, -2, -3, -3, 4, -3 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 2, 0, 1, 3, 0, 4, 7, 11, 0, 5, + 6, 8, 0, 12, 9, 0, 10 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -3, -3, -3, -3, -3, -3, 1, -3 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 1, 5, 6, 9, 11, 8, 15 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 2, 13, 14, 10, 3, 4, 7, 0, 16, 0, + 12 +}; + +static const yytype_int8 yycheck[] = +{ + 0, 3, 4, 4, 4, 5, 3, -1, 4, -1, + 9 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 8, 0, 4, 5, 9, 10, 3, 13, 11, + 4, 12, 13, 3, 4, 14, 4 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 7, 8, 8, 8, 9, 10, 11, 11, 12, + 12, 13, 14 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 0, 2, 2, 2, 3, 0, 2, 2, + 3, 1, 1 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_lgl_parsedata_t* context) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (yylocationp); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_lgl_parsedata_t* context) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_lgl_parsedata_t* context) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[+yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, context); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[+*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_lgl_parsedata_t* context) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (igraph_i_lgl_parsedata_t* context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + 'yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, &yylloc, scanner); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 6: +#line 100 "src/core/io/lgl-parser.y" + { context->actvertex=(yyvsp[-1].edgenum); } +#line 1475 "yy.tab.c" + break; + + case 9: +#line 104 "src/core/io/lgl-parser.y" + { + igraph_vector_push_back(context->vector, context->actvertex); + igraph_vector_push_back(context->vector, (yyvsp[-1].edgenum)); + igraph_vector_push_back(context->weights, 0); + } +#line 1485 "yy.tab.c" + break; + + case 10: +#line 109 "src/core/io/lgl-parser.y" + { + igraph_vector_push_back(context->vector, context->actvertex); + igraph_vector_push_back(context->vector, (yyvsp[-2].edgenum)); + igraph_vector_push_back(context->weights, (yyvsp[-1].weightnum)); + context->has_weights = 1; + } +#line 1496 "yy.tab.c" + break; + + case 11: +#line 118 "src/core/io/lgl-parser.y" + { igraph_trie_get2(context->trie, + igraph_lgl_yyget_text(scanner), + igraph_lgl_yyget_leng(scanner), + &(yyval.edgenum)); } +#line 1505 "yy.tab.c" + break; + + case 12: +#line 123 "src/core/io/lgl-parser.y" + { (yyval.weightnum)=igraph_lgl_get_number(igraph_lgl_yyget_text(scanner), + igraph_lgl_yyget_leng(scanner)); } +#line 1512 "yy.tab.c" + break; + + +#line 1516 "yy.tab.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[+*yyssp], yyvsp, yylsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} +#line 126 "src/core/io/lgl-parser.y" + + +int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, + const char *s) { + snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char), + "Parse error in LGL file, line %i (%s)", + locp->first_line, s); + return 0; +} + +igraph_real_t igraph_lgl_get_number(const char *str, long int length) { + igraph_real_t num; + char *tmp=IGRAPH_CALLOC(length+1, char); + + strncpy(tmp, str, length); + tmp[length]='\0'; + sscanf(tmp, "%lf", &num); + IGRAPH_FREE(tmp); + return num; +} diff --git a/src/rigraph/core/io/lgl-parser.h b/src/rigraph/core/io/lgl-parser.h new file mode 100644 index 0000000..72a7b95 --- /dev/null +++ b/src/rigraph/core/io/lgl-parser.h @@ -0,0 +1,94 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + 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 + (at your option) 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_IGRAPH_LGL_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_LGL_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_lgl_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + ALNUM = 258, + NEWLINE = 259, + HASH = 260, + ERROR = 261 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 78 "src/core/io/lgl-parser.y" + + long int edgenum; + double weightnum; + +#line 69 "yy.tab.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_lgl_yyparse (igraph_i_lgl_parsedata_t* context); + +#endif /* !YY_IGRAPH_LGL_YY_YY_TAB_H_INCLUDED */ diff --git a/src/rigraph/core/io/lgl-parser.y b/src/rigraph/core/io/lgl-parser.y new file mode 100644 index 0000000..7043594 --- /dev/null +++ b/src/rigraph/core/io/lgl-parser.y @@ -0,0 +1,145 @@ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include +#include + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "config.h" + +#include "../core/math.h" +#include "lgl-header.h" +#include "lgl-parser.h" +#include "lgl-lexer.h" +//#include "internal/hacks.h" + +int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, + const char *s); +igraph_real_t igraph_lgl_get_number(const char *str, long int len); + +#define scanner context->scanner +%} + +%pure-parser +/* bison: do not remove the equals sign; macOS XCode ships with bison 2.3, which + * needs the equals sign */ +%name-prefix="igraph_lgl_yy" +%defines +%locations +%error-verbose +%parse-param { igraph_i_lgl_parsedata_t* context } +%lex-param { void *scanner } + +%union { + long int edgenum; + double weightnum; +} + +%type edgeid +%type weight + +%token ALNUM +%token NEWLINE +%token HASH +%token ERROR + +%% + +input : /* empty */ + | input NEWLINE + | input vertex +; + +vertex : vertexdef edges ; + +vertexdef : HASH edgeid NEWLINE { context->actvertex=$2; } ; + +edges : /* empty */ | edges edge ; + +edge : edgeid NEWLINE { + igraph_vector_push_back(context->vector, context->actvertex); + igraph_vector_push_back(context->vector, $1); + igraph_vector_push_back(context->weights, 0); + } + | edgeid weight NEWLINE { + igraph_vector_push_back(context->vector, context->actvertex); + igraph_vector_push_back(context->vector, $1); + igraph_vector_push_back(context->weights, $2); + context->has_weights = 1; + } +; + + +edgeid : ALNUM { igraph_trie_get2(context->trie, + igraph_lgl_yyget_text(scanner), + igraph_lgl_yyget_leng(scanner), + &$$); }; + +weight : ALNUM { $$=igraph_lgl_get_number(igraph_lgl_yyget_text(scanner), + igraph_lgl_yyget_leng(scanner)); } ; + +%% + +int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, + const char *s) { + snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char), + "Parse error in LGL file, line %i (%s)", + locp->first_line, s); + return 0; +} + +igraph_real_t igraph_lgl_get_number(const char *str, long int length) { + igraph_real_t num; + char *tmp=IGRAPH_CALLOC(length+1, char); + + strncpy(tmp, str, length); + tmp[length]='\0'; + sscanf(tmp, "%lf", &num); + IGRAPH_FREE(tmp); + return num; +} diff --git a/src/rigraph/core/io/lgl.c b/src/rigraph/core/io/lgl.c new file mode 100644 index 0000000..ab251ba --- /dev/null +++ b/src/rigraph/core/io/lgl.c @@ -0,0 +1,413 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_interface.h" + +#include "../graph/attributes.h" + +#include "lgl-header.h" + +int igraph_lgl_yylex_init_extra (igraph_i_lgl_parsedata_t* user_defined, + void* scanner); +void igraph_lgl_yylex_destroy (void *scanner ); +int igraph_lgl_yyparse (igraph_i_lgl_parsedata_t* context); +void igraph_lgl_yyset_in (FILE * in_str, void* yyscanner ); + +/** + * \ingroup loadsave + * \function igraph_read_graph_lgl + * \brief Reads a graph from an .lgl file + * + * + * The .lgl format is used by the Large Graph + * Layout visualization software + * (http://lgl.sourceforge.net), it can + * describe undirected optionally weighted graphs. From the LGL + * manual: + * + * \blockquote The second format is the LGL file format + * (.lgl file + * suffix). This is yet another graph file format that tries to be as + * stingy as possible with space, yet keeping the edge file in a human + * readable (not binary) format. The format itself is like the + * following: + * \verbatim # vertex1name +vertex2name [optionalWeight] +vertex3name [optionalWeight] \endverbatim + * Here, the first vertex of an edge is preceded with a pound sign + * '#'. Then each vertex that shares an edge with that vertex is + * listed one per line on subsequent lines. \endblockquote + * + * + * LGL cannot handle loop and multiple edges or directed graphs, but + * in \a igraph it is not an error to have multiple and loop edges. + * \param graph Pointer to an uninitialized graph object. + * \param instream A stream, it should be readable. + * \param names Logical value, if TRUE the symbolic names of the + * vertices will be added to the graph as a vertex attribute + * called \quote name\endquote. + * \param weights Whether to add the weights of the edges to the + * graph as an edge attribute called \quote weight\endquote. + * \c IGRAPH_ADD_WEIGHTS_YES adds the weights (even if they + * are not present in the file, in this case they are assumed + * to be zero). \c IGRAPH_ADD_WEIGHTS_NO does not add any + * edge attribute. \c IGRAPH_ADD_WEIGHTS_IF_PRESENT adds the + * attribute if and only if there is at least one explicit + * edge weight in the input file. + * \param directed Whether to create a directed graph. As this format + * was originally used only for undirected graphs there is no + * information in the file about the directedness of the graph. + * Set this parameter to \c IGRAPH_DIRECTED or \c + * IGRAPH_UNDIRECTED to create a directed or undirected graph. + * \return Error code: + * \c IGRAPH_PARSEERROR: if there is a + * problem reading the file, or the file is syntactically + * incorrect. + * + * Time complexity: + * O(|V|+|E|log(|V|)) if we neglect + * the time required by the parsing. As usual + * |V| is the number of vertices, + * while |E| is the number of edges. + * + * \sa \ref igraph_read_graph_ncol(), \ref igraph_write_graph_lgl() + * + * \example examples/simple/igraph_read_graph_lgl.c + */ +int igraph_read_graph_lgl(igraph_t *graph, FILE *instream, + igraph_bool_t names, + igraph_add_weights_t weights, + igraph_bool_t directed) { + + igraph_vector_t edges = IGRAPH_VECTOR_NULL, ws = IGRAPH_VECTOR_NULL; + igraph_trie_t trie = IGRAPH_TRIE_NULL; + igraph_vector_ptr_t name, weight; + igraph_vector_ptr_t *pname = 0, *pweight = 0; + igraph_attribute_record_t namerec, weightrec; + const char *namestr = "name", *weightstr = "weight"; + igraph_i_lgl_parsedata_t context; + + IGRAPH_VECTOR_INIT_FINALLY(&ws, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_TRIE_INIT_FINALLY(&trie, names); + + context.has_weights = 0; + context.vector = &edges; + context.weights = &ws; + context.trie = ≜ + context.eof = 0; + + igraph_lgl_yylex_init_extra(&context, &context.scanner); + IGRAPH_FINALLY(igraph_lgl_yylex_destroy, context.scanner); + + igraph_lgl_yyset_in(instream, context.scanner); + + if (igraph_lgl_yyparse(&context)) { + if (context.errmsg[0] != 0) { + IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else { + IGRAPH_ERROR("Cannot read LGL file", IGRAPH_PARSEERROR); + } + } + + IGRAPH_CHECK(igraph_empty(graph, 0, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + + if (names) { + const igraph_strvector_t *namevec; + IGRAPH_CHECK(igraph_vector_ptr_init(&name, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &name); + pname = &name; + igraph_trie_getkeys(&trie, &namevec); /* dirty */ + namerec.name = namestr; + namerec.type = IGRAPH_ATTRIBUTE_STRING; + namerec.value = namevec; + VECTOR(name)[0] = &namerec; + } + + if (weights == IGRAPH_ADD_WEIGHTS_YES || + (weights == IGRAPH_ADD_WEIGHTS_IF_PRESENT && context.has_weights)) { + IGRAPH_CHECK(igraph_vector_ptr_init(&weight, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &weight); + pweight = &weight; + weightrec.name = weightstr; + weightrec.type = IGRAPH_ATTRIBUTE_NUMERIC; + weightrec.value = &ws; + VECTOR(weight)[0] = &weightrec; + } + + IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) + igraph_trie_size(&trie), pname)); + IGRAPH_CHECK(igraph_add_edges(graph, &edges, pweight)); + + if (pweight) { + igraph_vector_ptr_destroy(pweight); + IGRAPH_FINALLY_CLEAN(1); + } + if (pname) { + igraph_vector_ptr_destroy(pname); + IGRAPH_FINALLY_CLEAN(1); + } + igraph_trie_destroy(&trie); + igraph_vector_destroy(&edges); + igraph_vector_destroy(&ws); + igraph_lgl_yylex_destroy(context.scanner); + IGRAPH_FINALLY_CLEAN(5); + + return 0; +} + +/** + * \ingroup loadsave + * \function igraph_write_graph_lgl + * \brief Writes the graph to a file in .lgl format + * + * + * .lgl is a format used by LGL, see \ref + * igraph_read_graph_lgl() for details. + * + * + * Note that having multiple or loop edges in an + * .lgl file breaks the LGL software but \a igraph + * does not check for this condition. + * + * \param graph The graph to write. + * \param outstream The stream object to write to, it should be + * writable. + * \param names The name of a string vertex attribute, if symbolic names + * are to be written to the file. Supply \c NULL to write vertex + * ids instead. + * \param weights The name of a numerical edge attribute, which will be + * written as weights to the file. Supply \c NULL to skip writing + * edge weights. + * \param isolates Logical, if TRUE isolated vertices are also written + * to the file. If FALSE they will be omitted. + * \return Error code: + * \c IGRAPH_EFILE if there is an error + * writing the file. + * + * Time complexity: O(|E|), the + * number of edges if \p isolates is + * FALSE, O(|V|+|E|) otherwise. All + * file operations are expected to have time complexity + * O(1). + * + * \sa \ref igraph_read_graph_lgl(), \ref igraph_write_graph_ncol() + * + * \example examples/simple/igraph_write_graph_lgl.c + */ +int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, + const char *names, const char *weights, + igraph_bool_t isolates) { + igraph_eit_t it; + long int actvertex = -1; + igraph_attribute_type_t nametype, weighttype; + + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM), + &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + + /* Check if we have the names attribute */ + if (names && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, + names)) { + names = 0; + IGRAPH_WARNING("names attribute does not exists"); + } + if (names) { + IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &nametype, + IGRAPH_ATTRIBUTE_VERTEX, names)); + if (nametype != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_WARNING("ignoring names attribute, unknown attribute type"); + names = 0; + } + } + + /* Check the weights as well */ + if (weights && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, + weights)) { + weights = 0; + IGRAPH_WARNING("weights attribute does not exists"); + } + if (weights) { + IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &weighttype, + IGRAPH_ATTRIBUTE_EDGE, weights)); + if (weighttype != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_WARNING("ignoring weights attribute, unknown attribute type"); + weights = 0; + } + } + + if (names == 0 && weights == 0) { + /* No names, no weights */ + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t from, to; + int ret; + igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); + if (from == actvertex) { + ret = fprintf(outstream, "%li\n", (long int)to); + } else { + actvertex = from; + ret = fprintf(outstream, "# %li\n%li\n", (long int)from, (long int)to); + } + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + } else if (weights == 0) { + /* No weights but use names */ + igraph_strvector_t nvec; + IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph))); + IGRAPH_FINALLY(igraph_strvector_destroy, &nvec); + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names, + igraph_vss_all(), + &nvec)); + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t edge = IGRAPH_EIT_GET(it); + igraph_integer_t from, to; + int ret = 0; + char *str1, *str2; + igraph_edge(graph, edge, &from, &to); + igraph_strvector_get(&nvec, to, &str2); + + if (from == actvertex) { + ret = fprintf(outstream, "%s\n", str2); + } else { + actvertex = from; + igraph_strvector_get(&nvec, from, &str1); + ret = fprintf(outstream, "# %s\n%s\n", str1, str2); + } + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + IGRAPH_FINALLY_CLEAN(1); + } else if (names == 0) { + /* No names but weights */ + igraph_vector_t wvec; + IGRAPH_VECTOR_INIT_FINALLY(&wvec, igraph_ecount(graph)); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, weights, + igraph_ess_all(IGRAPH_EDGEORDER_ID), + &wvec)); + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t edge = IGRAPH_EIT_GET(it); + igraph_integer_t from, to; + int ret1, ret2, ret3; + igraph_edge(graph, edge, &from, &to); + if (from == actvertex) { + ret1 = fprintf(outstream, "%li ", (long)to); + } else { + actvertex = from; + ret1 = fprintf(outstream, "# %li\n%li ", (long)from, (long)to); + } + ret2 = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[(long int)edge]); + ret3 = fputc('\n', outstream); + if (ret1 < 0 || ret2 < 0 || ret3 == EOF) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + igraph_vector_destroy(&wvec); + IGRAPH_FINALLY_CLEAN(1); + } else { + /* Both names and weights */ + igraph_strvector_t nvec; + igraph_vector_t wvec; + IGRAPH_VECTOR_INIT_FINALLY(&wvec, igraph_ecount(graph)); + IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph))); + IGRAPH_FINALLY(igraph_strvector_destroy, &nvec); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, weights, + igraph_ess_all(IGRAPH_EDGEORDER_ID), + &wvec)); + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names, + igraph_vss_all(), + &nvec)); + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t edge = IGRAPH_EIT_GET(it); + igraph_integer_t from, to; + int ret = 0, ret2; + char *str1, *str2; + igraph_edge(graph, edge, &from, &to); + igraph_strvector_get(&nvec, to, &str2); + if (from == actvertex) { + ret = fprintf(outstream, "%s ", str2); + } else { + actvertex = from; + igraph_strvector_get(&nvec, from, &str1); + ret = fprintf(outstream, "# %s\n%s ", str1, str2); + } + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[(long int)edge]); + ret2 = fputc('\n', outstream); + if (ret < 0 || ret2 == EOF) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + igraph_strvector_destroy(&nvec); + igraph_vector_destroy(&wvec); + IGRAPH_FINALLY_CLEAN(2); + } + + if (isolates) { + long int nov = igraph_vcount(graph); + long int i; + int ret = 0; + igraph_vector_t deg; + igraph_strvector_t nvec; + char *str; + + IGRAPH_VECTOR_INIT_FINALLY(°, 1); + IGRAPH_CHECK(igraph_strvector_init(&nvec, 1)); + IGRAPH_FINALLY(igraph_strvector_destroy, &nvec); + for (i = 0; i < nov; i++) { + igraph_degree(graph, °, igraph_vss_1((igraph_integer_t) i), + IGRAPH_ALL, IGRAPH_LOOPS); + if (VECTOR(deg)[0] == 0) { + if (names == 0) { + ret = fprintf(outstream, "# %li\n", i); + } else { + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names, + igraph_vss_1((igraph_integer_t) i), &nvec)); + igraph_strvector_get(&nvec, 0, &str); + ret = fprintf(outstream, "# %s\n", str); + } + } + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + } + igraph_strvector_destroy(&nvec); + igraph_vector_destroy(°); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/io/ncol-header.h b/src/rigraph/core/io/ncol-header.h new file mode 100644 index 0000000..c6e9129 --- /dev/null +++ b/src/rigraph/core/io/ncol-header.h @@ -0,0 +1,36 @@ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge MA, 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_error.h" +#include "igraph_vector.h" + +#include "../core/trie.h" + +typedef struct { + void *scanner; + int eof; + char errmsg[300]; + int has_weights; + igraph_vector_t *vector; + igraph_vector_t *weights; + igraph_trie_t *trie; +} igraph_i_ncol_parsedata_t; diff --git a/src/rigraph/core/io/ncol-lexer.c b/src/rigraph/core/io/ncol-lexer.c new file mode 100644 index 0000000..d8190ea --- /dev/null +++ b/src/rigraph/core/io/ncol-lexer.c @@ -0,0 +1,2260 @@ +#line 2 "src/core/io/ncol-lexer.c" + +#line 4 "src/core/io/ncol-lexer.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_ncol_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_ncol_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_ncol_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_ncol_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_ncol_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_ncol_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_ncol_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_ncol_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_ncol_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_ncol_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_ncol_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_ncol_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_ncol_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_ncol_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_ncol_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_ncol_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_ncol_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_ncol_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_ncol_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_ncol_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_ncol_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_ncol_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_ncol_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_ncol_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_ncol_yylex_ALREADY_DEFINED +#else +#define yylex igraph_ncol_yylex +#endif + +#ifdef yyrestart +#define igraph_ncol_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_ncol_yyrestart +#endif + +#ifdef yylex_init +#define igraph_ncol_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_ncol_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_ncol_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_ncol_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_ncol_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_ncol_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_ncol_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_ncol_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_ncol_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_ncol_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_ncol_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_ncol_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_ncol_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_ncol_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_ncol_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_ncol_yyget_in +#endif + +#ifdef yyset_in +#define igraph_ncol_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_ncol_yyset_in +#endif + +#ifdef yyget_out +#define igraph_ncol_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_ncol_yyget_out +#endif + +#ifdef yyset_out +#define igraph_ncol_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_ncol_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_ncol_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_ncol_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_ncol_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_ncol_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_ncol_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_ncol_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_ncol_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_ncol_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_ncol_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_ncol_yyget_column +#endif + +#ifdef yyset_column +#define igraph_ncol_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_ncol_yyset_column +#endif + +#ifdef yywrap +#define igraph_ncol_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_ncol_yywrap +#endif + +#ifdef yyget_lval +#define igraph_ncol_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_ncol_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_ncol_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_ncol_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_ncol_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_ncol_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_ncol_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_ncol_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_ncol_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_ncol_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_ncol_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_ncol_yyrealloc +#endif + +#ifdef yyfree +#define igraph_ncol_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_ncol_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define igraph_ncol_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void __attribute__((unused)) yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 5 +#define YY_END_OF_BUFFER 6 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[13] = + { 0, + 0, 0, 6, 3, 1, 2, 2, 4, 3, 1, + 2, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[6] = + { 0, + 1, 2, 3, 4, 5 + } ; + +static const flex_int16_t yy_base[17] = + { 0, + 0, 0, 10, 0, 0, 0, 0, 11, 0, 0, + 11, 11, 8, 6, 3, 3 + } ; + +static const flex_int16_t yy_def[17] = + { 0, + 12, 1, 12, 13, 14, 15, 16, 12, 13, 14, + 12, 0, 12, 12, 12, 12 + } ; + +static const flex_int16_t yy_nxt[17] = + { 0, + 4, 5, 6, 7, 8, 11, 11, 10, 9, 12, + 3, 12, 12, 12, 12, 12 + } ; + +static const flex_int16_t yy_chk[17] = + { 0, + 1, 1, 1, 1, 1, 16, 15, 14, 13, 3, + 12, 12, 12, 12, 12, 12 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "src/core/io/ncol-lexer.l" +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ +#line 24 "src/core/io/ncol-lexer.l" + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "config.h" +#include + +#include "ncol-header.h" +#include "ncol-parser.h" + +#define YY_EXTRA_TYPE igraph_i_ncol_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in NCOL parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +#line 722 "src/core/io/ncol-lexer.c" +#define YY_NO_INPUT 1 +#line 724 "src/core/io/ncol-lexer.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + yy_size_t yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yylval = yylval_param; + + yylloc = yylloc_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 76 "src/core/io/ncol-lexer.l" + + +#line 79 "src/core/io/ncol-lexer.l" + /* ------------------------------------------------whitespace------*/ +#line 1011 "src/core/io/ncol-lexer.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 13 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 11 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 80 "src/core/io/ncol-lexer.l" +{ } + YY_BREAK +/* ---------------------------------------------------newline------*/ +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 83 "src/core/io/ncol-lexer.l" +{ return NEWLINE; } + YY_BREAK +/* ----------------------------------------------alphanumeric------*/ +case 3: +YY_RULE_SETUP +#line 86 "src/core/io/ncol-lexer.l" +{ return ALNUM; } + YY_BREAK +case YY_STATE_EOF(INITIAL): +#line 88 "src/core/io/ncol-lexer.l" +{ if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return NEWLINE; + } + } + YY_BREAK +/* ---------------------------------------------anything else------*/ +case 4: +YY_RULE_SETUP +#line 97 "src/core/io/ncol-lexer.l" +{ return ERROR; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 99 "src/core/io/ncol-lexer.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1107 "src/core/io/ncol-lexer.c" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap( yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin , yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 5); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 13 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 5; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 13 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 12); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( yyscanner ) ) + return 0; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void __attribute__((unused)) yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +yy_size_t yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void yyset_column (int _column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = _column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int _bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 99 "src/core/io/ncol-lexer.l" + + diff --git a/src/rigraph/core/io/ncol-lexer.h b/src/rigraph/core/io/ncol-lexer.h new file mode 100644 index 0000000..2dbaa76 --- /dev/null +++ b/src/rigraph/core/io/ncol-lexer.h @@ -0,0 +1,736 @@ +#ifndef igraph_ncol_yyHEADER_H +#define igraph_ncol_yyHEADER_H 1 +#define igraph_ncol_yyIN_HEADER 1 + +#line 6 "src/core/io/ncol-lexer.h" + +#line 8 "src/core/io/ncol-lexer.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_ncol_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_ncol_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_ncol_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_ncol_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_ncol_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_ncol_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_ncol_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_ncol_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_ncol_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_ncol_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_ncol_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_ncol_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_ncol_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_ncol_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_ncol_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_ncol_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_ncol_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_ncol_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_ncol_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_ncol_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_ncol_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_ncol_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_ncol_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_ncol_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_ncol_yylex_ALREADY_DEFINED +#else +#define yylex igraph_ncol_yylex +#endif + +#ifdef yyrestart +#define igraph_ncol_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_ncol_yyrestart +#endif + +#ifdef yylex_init +#define igraph_ncol_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_ncol_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_ncol_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_ncol_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_ncol_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_ncol_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_ncol_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_ncol_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_ncol_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_ncol_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_ncol_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_ncol_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_ncol_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_ncol_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_ncol_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_ncol_yyget_in +#endif + +#ifdef yyset_in +#define igraph_ncol_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_ncol_yyset_in +#endif + +#ifdef yyget_out +#define igraph_ncol_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_ncol_yyget_out +#endif + +#ifdef yyset_out +#define igraph_ncol_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_ncol_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_ncol_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_ncol_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_ncol_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_ncol_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_ncol_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_ncol_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_ncol_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_ncol_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_ncol_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_ncol_yyget_column +#endif + +#ifdef yyset_column +#define igraph_ncol_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_ncol_yyset_column +#endif + +#ifdef yywrap +#define igraph_ncol_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_ncol_yywrap +#endif + +#ifdef yyget_lval +#define igraph_ncol_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_ncol_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_ncol_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_ncol_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_ncol_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_ncol_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_ncol_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_ncol_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_ncol_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_ncol_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_ncol_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_ncol_yyrealloc +#endif + +#ifdef yyfree +#define igraph_ncol_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_ncol_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define igraph_ncol_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef igraph_ncol_yy_create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef igraph_ncol_yy_delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef igraph_ncol_yy_scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef igraph_ncol_yy_scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef igraph_ncol_yy_scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef igraph_ncol_yy_init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef igraph_ncol_yy_flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef igraph_ncol_yy_load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef igraph_ncol_yy_switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef igraph_ncol_yypush_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef igraph_ncol_yypop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef igraph_ncol_yyensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef igraph_ncol_yylex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef igraph_ncol_yyrestart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef igraph_ncol_yylex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef igraph_ncol_yylex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef igraph_ncol_yylex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef igraph_ncol_yyget_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef igraph_ncol_yyset_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef igraph_ncol_yyget_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef igraph_ncol_yyset_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef igraph_ncol_yyget_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef igraph_ncol_yyset_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef igraph_ncol_yyget_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef igraph_ncol_yyset_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef igraph_ncol_yyget_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef igraph_ncol_yyget_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef igraph_ncol_yyget_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef igraph_ncol_yyset_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef igraph_ncol_yyget_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef igraph_ncol_yyset_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef igraph_ncol_yywrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef igraph_ncol_yyget_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef igraph_ncol_yyset_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef igraph_ncol_yyget_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef igraph_ncol_yyset_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef igraph_ncol_yyalloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef igraph_ncol_yyrealloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef igraph_ncol_yyfree_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef igraph_ncol_yytext_ALREADY_DEFINED +#undef yytext +#endif +#ifndef igraph_ncol_yyleng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef igraph_ncol_yyin_ALREADY_DEFINED +#undef yyin +#endif +#ifndef igraph_ncol_yyout_ALREADY_DEFINED +#undef yyout +#endif +#ifndef igraph_ncol_yy_flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef igraph_ncol_yylineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef igraph_ncol_yytables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef igraph_ncol_yytables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef igraph_ncol_yyTABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 99 "src/core/io/ncol-lexer.l" + + +#line 735 "src/core/io/ncol-lexer.h" +#undef igraph_ncol_yyIN_HEADER +#endif /* igraph_ncol_yyHEADER_H */ diff --git a/src/rigraph/core/io/ncol-lexer.l b/src/rigraph/core/io/ncol-lexer.l new file mode 100644 index 0000000..e2be4bb --- /dev/null +++ b/src/rigraph/core/io/ncol-lexer.l @@ -0,0 +1,98 @@ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "config.h" +#include + +#include "ncol-header.h" +#include "ncol-parser.h" + +#define YY_EXTRA_TYPE igraph_i_ncol_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in NCOL parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +%} + +%option noyywrap +%option prefix="igraph_ncol_yy" +%option nounput +%option noinput +%option nodefault +%option reentrant +%option bison-bridge +%option bison-locations + +alnum [^ \t\n\r\0] + +%% + + /* ------------------------------------------------whitespace------*/ +[ \t]+ { } + + /* ---------------------------------------------------newline------*/ +\n\r|\r\n|\n|\r { return NEWLINE; } + + /* ----------------------------------------------alphanumeric------*/ +{alnum}+ { return ALNUM; } + +<> { if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return NEWLINE; + } + } + + /* ---------------------------------------------anything else------*/ +. { return ERROR; } + +%% diff --git a/src/rigraph/core/io/ncol-parser.c b/src/rigraph/core/io/ncol-parser.c new file mode 100644 index 0000000..b1eb7d2 --- /dev/null +++ b/src/rigraph/core/io/ncol-parser.c @@ -0,0 +1,1764 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + 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 + (at your option) 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse igraph_ncol_yyparse +#define yylex igraph_ncol_yylex +#define yyerror igraph_ncol_yyerror +#define yydebug igraph_ncol_yydebug +#define yynerrs igraph_ncol_yynerrs + +/* First part of user prologue. */ +#line 23 "src/core/io/ncol-parser.y" + + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include +#include + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "config.h" + +#include "../core/math.h" +#include "ncol-header.h" +#include "ncol-parser.h" +#include "ncol-lexer.h" +//#include "internal/hacks.h" + +int igraph_ncol_yyerror(YYLTYPE* locp, + igraph_i_ncol_parsedata_t *context, + const char *s); +igraph_real_t igraph_ncol_get_number(const char *str, long int len); + +#define scanner context->scanner + +#line 121 "yy.tab.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_IGRAPH_NCOL_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_NCOL_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_ncol_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + ALNUM = 258, + NEWLINE = 259, + ERROR = 260 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 79 "src/core/io/ncol-parser.y" + + long int edgenum; + double weightnum; + +#line 184 "yy.tab.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_ncol_yyparse (igraph_i_ncol_parsedata_t* context); + +#endif /* !YY_IGRAPH_NCOL_YY_YY_TAB_H_INCLUDED */ + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 10 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 6 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 5 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 8 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 12 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 260 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int8 yyrline[] = +{ + 0, 93, 93, 94, 95, 98, 103, 111, 116 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "ALNUM", "NEWLINE", "ERROR", "$accept", + "input", "edge", "edgeid", "weight", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260 +}; +# endif + +#define YYPACT_NINF (-3) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + -3, 0, -3, -3, -3, -3, 2, -2, -3, -3, + 3, -3 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 2, 0, 1, 7, 3, 4, 0, 0, 8, 5, + 0, 6 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -3, -3, -3, 4, -3 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 1, 5, 6, 10 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 2, 8, 9, 3, 4, 3, 0, 11, 0, 0, + 7 +}; + +static const yytype_int8 yycheck[] = +{ + 0, 3, 4, 3, 4, 3, -1, 4, -1, -1, + 6 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 7, 0, 3, 4, 8, 9, 9, 3, 4, + 10, 4 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 6, 7, 7, 7, 8, 8, 9, 10 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 0, 2, 2, 3, 4, 1, 1 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_ncol_parsedata_t* context) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (yylocationp); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_ncol_parsedata_t* context) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_ncol_parsedata_t* context) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[+yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, context); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[+*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_ncol_parsedata_t* context) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (igraph_i_ncol_parsedata_t* context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + 'yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, &yylloc, scanner); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 5: +#line 98 "src/core/io/ncol-parser.y" + { + igraph_vector_push_back(context->vector, (yyvsp[-2].edgenum)); + igraph_vector_push_back(context->vector, (yyvsp[-1].edgenum)); + igraph_vector_push_back(context->weights, 0); + } +#line 1475 "yy.tab.c" + break; + + case 6: +#line 103 "src/core/io/ncol-parser.y" + { + igraph_vector_push_back(context->vector, (yyvsp[-3].edgenum)); + igraph_vector_push_back(context->vector, (yyvsp[-2].edgenum)); + igraph_vector_push_back(context->weights, (yyvsp[-1].weightnum)); + context->has_weights = 1; + } +#line 1486 "yy.tab.c" + break; + + case 7: +#line 111 "src/core/io/ncol-parser.y" + { igraph_trie_get2(context->trie, + igraph_ncol_yyget_text(scanner), + igraph_ncol_yyget_leng(scanner), + &(yyval.edgenum)); } +#line 1495 "yy.tab.c" + break; + + case 8: +#line 116 "src/core/io/ncol-parser.y" + { (yyval.weightnum)=igraph_ncol_get_number(igraph_ncol_yyget_text(scanner), + igraph_ncol_yyget_leng(scanner)); } +#line 1502 "yy.tab.c" + break; + + +#line 1506 "yy.tab.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[+*yyssp], yyvsp, yylsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} +#line 119 "src/core/io/ncol-parser.y" + + +int igraph_ncol_yyerror(YYLTYPE* locp, + igraph_i_ncol_parsedata_t *context, + const char *s) { + snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, + "Parse error in NCOL file, line %i (%s)", + locp->first_line, s); + return 0; +} + +igraph_real_t igraph_ncol_get_number(const char *str, long int length) { + igraph_real_t num; + char *tmp=IGRAPH_CALLOC(length+1, char); + + strncpy(tmp, str, length); + tmp[length]='\0'; + sscanf(tmp, "%lf", &num); + IGRAPH_FREE(tmp); + return num; +} diff --git a/src/rigraph/core/io/ncol-parser.h b/src/rigraph/core/io/ncol-parser.h new file mode 100644 index 0000000..91c660a --- /dev/null +++ b/src/rigraph/core/io/ncol-parser.h @@ -0,0 +1,93 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + 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 + (at your option) 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_IGRAPH_NCOL_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_NCOL_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_ncol_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + ALNUM = 258, + NEWLINE = 259, + ERROR = 260 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 79 "src/core/io/ncol-parser.y" + + long int edgenum; + double weightnum; + +#line 68 "yy.tab.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_ncol_yyparse (igraph_i_ncol_parsedata_t* context); + +#endif /* !YY_IGRAPH_NCOL_YY_YY_TAB_H_INCLUDED */ diff --git a/src/rigraph/core/io/ncol-parser.y b/src/rigraph/core/io/ncol-parser.y new file mode 100644 index 0000000..e9a20bf --- /dev/null +++ b/src/rigraph/core/io/ncol-parser.y @@ -0,0 +1,139 @@ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include +#include + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "config.h" + +#include "../core/math.h" +#include "ncol-header.h" +#include "ncol-parser.h" +#include "ncol-lexer.h" +//#include "internal/hacks.h" + +int igraph_ncol_yyerror(YYLTYPE* locp, + igraph_i_ncol_parsedata_t *context, + const char *s); +igraph_real_t igraph_ncol_get_number(const char *str, long int len); + +#define scanner context->scanner +%} + +%pure-parser +/* bison: do not remove the equals sign; macOS XCode ships with bison 2.3, which + * needs the equals sign */ +%name-prefix="igraph_ncol_yy" +%defines +%locations +%error-verbose +%parse-param { igraph_i_ncol_parsedata_t* context } +%lex-param { void *scanner } + +%union { + long int edgenum; + double weightnum; +} + +%type edgeid +%type weight + +%token ALNUM +%token NEWLINE +%token ERROR + +%% + +input : /* empty */ + | input NEWLINE + | input edge +; + +edge : edgeid edgeid NEWLINE { + igraph_vector_push_back(context->vector, $1); + igraph_vector_push_back(context->vector, $2); + igraph_vector_push_back(context->weights, 0); + } + | edgeid edgeid weight NEWLINE { + igraph_vector_push_back(context->vector, $1); + igraph_vector_push_back(context->vector, $2); + igraph_vector_push_back(context->weights, $3); + context->has_weights = 1; + } +; + +edgeid : ALNUM { igraph_trie_get2(context->trie, + igraph_ncol_yyget_text(scanner), + igraph_ncol_yyget_leng(scanner), + &$$); }; + +weight : ALNUM { $$=igraph_ncol_get_number(igraph_ncol_yyget_text(scanner), + igraph_ncol_yyget_leng(scanner)); } ; + +%% + +int igraph_ncol_yyerror(YYLTYPE* locp, + igraph_i_ncol_parsedata_t *context, + const char *s) { + snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, + "Parse error in NCOL file, line %i (%s)", + locp->first_line, s); + return 0; +} + +igraph_real_t igraph_ncol_get_number(const char *str, long int length) { + igraph_real_t num; + char *tmp=IGRAPH_CALLOC(length+1, char); + + strncpy(tmp, str, length); + tmp[length]='\0'; + sscanf(tmp, "%lf", &num); + IGRAPH_FREE(tmp); + return num; +} diff --git a/src/rigraph/core/io/ncol.c b/src/rigraph/core/io/ncol.c new file mode 100644 index 0000000..ee657de --- /dev/null +++ b/src/rigraph/core/io/ncol.c @@ -0,0 +1,383 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_interface.h" + +#include "../graph/attributes.h" + +#include "ncol-header.h" + +int igraph_ncol_yylex_init_extra (igraph_i_ncol_parsedata_t* user_defined, + void* scanner); +void igraph_ncol_yylex_destroy (void *scanner ); +int igraph_ncol_yyparse (igraph_i_ncol_parsedata_t* context); +void igraph_ncol_yyset_in (FILE * in_str, void* yyscanner ); + +/** + * \ingroup loadsave + * \function igraph_read_graph_ncol + * \brief Reads an .ncol file used by LGL. + * + * Also useful for creating graphs from \quote named\endquote (and + * optionally weighted) edge lists. + * + * + * This format is used by the Large Graph Layout program + * (http://lgl.sourceforge.net), and it is simply a + * symbolic weighted edge list. It is a simple text file with one edge + * per line. An edge is defined by two symbolic vertex names separated + * by whitespace. The vertex names themselves cannot contain + * whitespace. They may be followed by an optional number, + * the weight of the edge; the number can be negative and can be in + * scientific notation. If there is no weight specified to an edge it + * is assumed to be zero. + * + * + * The resulting graph is always undirected. + * LGL cannot deal with files which contain multiple or loop edges, + * this is however not checked here, as \a igraph is happy with + * these. + * + * \param graph Pointer to an uninitialized graph object. + * \param instream Pointer to a stream, it should be readable. + * \param predefnames Pointer to the symbolic names of the vertices in + * the file. If \c NULL is given here then vertex ids will be + * assigned to vertex names in the order of their appearance in + * the .ncol file. If it is not \c NULL and some unknown + * vertex names are found in the .ncol file then new vertex + * ids will be assigned to them. + * \param names Logical value, if TRUE the symbolic names of the + * vertices will be added to the graph as a vertex attribute + * called \quote name\endquote. + * \param weights Whether to add the weights of the edges to the + * graph as an edge attribute called \quote weight\endquote. + * \c IGRAPH_ADD_WEIGHTS_YES adds the weights (even if they + * are not present in the file, in this case they are assumed + * to be zero). \c IGRAPH_ADD_WEIGHTS_NO does not add any + * edge attribute. \c IGRAPH_ADD_WEIGHTS_IF_PRESENT adds the + * attribute if and only if there is at least one explicit + * edge weight in the input file. + * \param directed Whether to create a directed graph. As this format + * was originally used only for undirected graphs there is no + * information in the file about the directedness of the graph. + * Set this parameter to \c IGRAPH_DIRECTED or \c + * IGRAPH_UNDIRECTED to create a directed or undirected graph. + * \return Error code: + * \c IGRAPH_PARSEERROR: if there is a + * problem reading + * the file, or the file is syntactically incorrect. + * + * Time complexity: + * O(|V|+|E|log(|V|)) if we neglect + * the time required by the parsing. As usual + * |V| is the number of vertices, + * while |E| is the number of edges. + * + * \sa \ref igraph_read_graph_lgl(), \ref igraph_write_graph_ncol() + */ +int igraph_read_graph_ncol(igraph_t *graph, FILE *instream, + const igraph_strvector_t *predefnames, + igraph_bool_t names, + igraph_add_weights_t weights, + igraph_bool_t directed) { + + igraph_vector_t edges, ws; + igraph_trie_t trie = IGRAPH_TRIE_NULL; + igraph_integer_t no_of_nodes; + long int no_predefined = 0; + igraph_vector_ptr_t name, weight; + igraph_vector_ptr_t *pname = 0, *pweight = 0; + igraph_attribute_record_t namerec, weightrec; + const char *namestr = "name", *weightstr = "weight"; + igraph_i_ncol_parsedata_t context; + + IGRAPH_CHECK(igraph_empty(graph, 0, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + IGRAPH_TRIE_INIT_FINALLY(&trie, names); + IGRAPH_VECTOR_INIT_FINALLY(&ws, 0); + + /* Add the predefined names, if any */ + if (predefnames != 0) { + long int i, id, n; + char *key; + n = no_predefined = igraph_strvector_size(predefnames); + for (i = 0; i < n; i++) { + igraph_strvector_get(predefnames, i, &key); + igraph_trie_get(&trie, key, &id); + if (id != i) { + IGRAPH_WARNING("reading NCOL file, duplicate entry in predefnames"); + no_predefined--; + } + } + } + + context.has_weights = 0; + context.vector = &edges; + context.weights = &ws; + context.trie = ≜ + context.eof = 0; + + igraph_ncol_yylex_init_extra(&context, &context.scanner); + IGRAPH_FINALLY(igraph_ncol_yylex_destroy, context.scanner); + + igraph_ncol_yyset_in(instream, context.scanner); + + if (igraph_ncol_yyparse(&context)) { + if (context.errmsg[0] != 0) { + IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else { + IGRAPH_ERROR("Cannot read NCOL file", IGRAPH_PARSEERROR); + } + } + + if (predefnames != 0 && + igraph_trie_size(&trie) != no_predefined) { + IGRAPH_WARNING("unknown vertex/vertices found, predefnames extended"); + } + + if (names) { + const igraph_strvector_t *namevec; + IGRAPH_CHECK(igraph_vector_ptr_init(&name, 1)); + pname = &name; + igraph_trie_getkeys(&trie, &namevec); /* dirty */ + namerec.name = namestr; + namerec.type = IGRAPH_ATTRIBUTE_STRING; + namerec.value = namevec; + VECTOR(name)[0] = &namerec; + } + + if (weights == IGRAPH_ADD_WEIGHTS_YES || + (weights == IGRAPH_ADD_WEIGHTS_IF_PRESENT && context.has_weights)) { + IGRAPH_CHECK(igraph_vector_ptr_init(&weight, 1)); + pweight = &weight; + weightrec.name = weightstr; + weightrec.type = IGRAPH_ATTRIBUTE_NUMERIC; + weightrec.value = &ws; + VECTOR(weight)[0] = &weightrec; + } + + if (igraph_vector_empty(&edges)) { + no_of_nodes = 0; + } else { + no_of_nodes = igraph_vector_max(&edges) + 1; + } + + IGRAPH_CHECK(igraph_add_vertices(graph, no_of_nodes, pname)); + IGRAPH_CHECK(igraph_add_edges(graph, &edges, pweight)); + + if (pname) { + igraph_vector_ptr_destroy(pname); + } + if (pweight) { + igraph_vector_ptr_destroy(pweight); + } + igraph_vector_destroy(&ws); + igraph_trie_destroy(&trie); + igraph_vector_destroy(&edges); + igraph_ncol_yylex_destroy(context.scanner); + IGRAPH_FINALLY_CLEAN(5); + + return 0; +} + +/** + * \ingroup loadsave + * \function igraph_write_graph_ncol + * \brief Writes the graph to a file in .ncol format. + * + * + * .ncol is a format used by LGL, see \ref + * igraph_read_graph_ncol() for details. + * + * + * Note that having multiple or loop edges in an + * .ncol file breaks the LGL software but + * \a igraph does not check for this condition. + * + * + * This format cannot represent zero-degree vertices. + * + * \param graph The graph to write. + * \param outstream The stream object to write to, it should be + * writable. + * \param names The name of a string vertex attribute, if symbolic names + * are to be written to the file. Supply \c NULL to write vertex + * ids instead. + * \param weights The name of a numerical edge attribute, which will be + * written as weights to the file. Supply \c NULL to skip writing + * edge weights. + * \return Error code: + * \c IGRAPH_EFILE if there is an error writing the + * file. + * + * Time complexity: O(|E|), the + * number of edges. All file operations are expected to have time + * complexity O(1). + * + * \sa \ref igraph_read_graph_ncol(), \ref igraph_write_graph_lgl() + */ +int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, + const char *names, const char *weights) { + igraph_eit_t it; + igraph_attribute_type_t nametype, weighttype; + + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM), + &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + + /* Check if we have the names attribute */ + if (names && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, + names)) { + names = 0; + IGRAPH_WARNING("names attribute does not exists"); + } + if (names) { + IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &nametype, + IGRAPH_ATTRIBUTE_VERTEX, names)); + if (nametype != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_WARNING("ignoring names attribute, unknown attribute type"); + names = 0; + } + } + + /* Check the weights as well */ + if (weights && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, + weights)) { + weights = 0; + IGRAPH_WARNING("weights attribute does not exists"); + } + if (weights) { + IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &weighttype, + IGRAPH_ATTRIBUTE_EDGE, weights)); + if (weighttype != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_WARNING("ignoring weights attribute, unknown attribute type"); + weights = 0; + } + } + + if (names == 0 && weights == 0) { + /* No names, no weights */ + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t from, to; + int ret; + igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); + ret = fprintf(outstream, "%li %li\n", + (long int) from, + (long int) to); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + } else if (weights == 0) { + /* No weights, but use names */ + igraph_strvector_t nvec; + IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph))); + IGRAPH_FINALLY(igraph_strvector_destroy, &nvec); + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names, + igraph_vss_all(), + &nvec)); + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t edge = IGRAPH_EIT_GET(it); + igraph_integer_t from, to; + int ret = 0; + char *str1, *str2; + igraph_edge(graph, edge, &from, &to); + igraph_strvector_get(&nvec, from, &str1); + igraph_strvector_get(&nvec, to, &str2); + ret = fprintf(outstream, "%s %s\n", str1, str2); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + igraph_strvector_destroy(&nvec); + IGRAPH_FINALLY_CLEAN(1); + } else if (names == 0) { + /* No names but weights */ + igraph_vector_t wvec; + IGRAPH_VECTOR_INIT_FINALLY(&wvec, igraph_ecount(graph)); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, weights, + igraph_ess_all(IGRAPH_EDGEORDER_ID), + &wvec)); + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t edge = IGRAPH_EIT_GET(it); + igraph_integer_t from, to; + int ret1, ret2, ret3; + igraph_edge(graph, edge, &from, &to); + ret1 = fprintf(outstream, "%li %li ", + (long int)from, (long int)to); + ret2 = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[(long int)edge]); + ret3 = fputc('\n', outstream); + if (ret1 < 0 || ret2 < 0 || ret3 == EOF) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + igraph_vector_destroy(&wvec); + IGRAPH_FINALLY_CLEAN(1); + } else { + /* Both names and weights */ + igraph_strvector_t nvec; + igraph_vector_t wvec; + IGRAPH_VECTOR_INIT_FINALLY(&wvec, igraph_ecount(graph)); + IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph))); + IGRAPH_FINALLY(igraph_strvector_destroy, &nvec); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, weights, + igraph_ess_all(IGRAPH_EDGEORDER_ID), + &wvec)); + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names, + igraph_vss_all(), + &nvec)); + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t edge = IGRAPH_EIT_GET(it); + igraph_integer_t from, to; + int ret = 0, ret2 = 0; + char *str1, *str2; + igraph_edge(graph, edge, &from, &to); + igraph_strvector_get(&nvec, from, &str1); + igraph_strvector_get(&nvec, to, &str2); + ret = fprintf(outstream, "%s %s ", str1, str2); + if (ret < 0) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + ret = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[(long int)edge]); + ret2 = fputc('\n', outstream); + if (ret < 0 || ret2 == EOF) { + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + igraph_strvector_destroy(&nvec); + igraph_vector_destroy(&wvec); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/io/pajek-header.h b/src/rigraph/core/io/pajek-header.h new file mode 100644 index 0000000..aebd338 --- /dev/null +++ b/src/rigraph/core/io/pajek-header.h @@ -0,0 +1,46 @@ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge MA, 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_error.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" + +#include "../core/trie.h" + +typedef struct { + void *scanner; + int eof; + char errmsg[300]; + igraph_vector_t *vector; + igraph_bool_t directed; + int vcount, vcount2; + int actfrom; + int actto; + int mode; /* 0: general, 1: vertex, 2: edge */ + igraph_trie_t *vertex_attribute_names; + igraph_vector_ptr_t *vertex_attributes; + igraph_trie_t *edge_attribute_names; + igraph_vector_ptr_t *edge_attributes; + int vertexid; + int actvertex; + int actedge; +} igraph_i_pajek_parsedata_t; diff --git a/src/rigraph/core/io/pajek-lexer.c b/src/rigraph/core/io/pajek-lexer.c new file mode 100644 index 0000000..8592591 --- /dev/null +++ b/src/rigraph/core/io/pajek-lexer.c @@ -0,0 +1,2752 @@ +#line 2 "src/core/io/pajek-lexer.c" + +#line 4 "src/core/io/pajek-lexer.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_pajek_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_pajek_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_pajek_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_pajek_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_pajek_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_pajek_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_pajek_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_pajek_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_pajek_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_pajek_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_pajek_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_pajek_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_pajek_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_pajek_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_pajek_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_pajek_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_pajek_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_pajek_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_pajek_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_pajek_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_pajek_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_pajek_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_pajek_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_pajek_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_pajek_yylex_ALREADY_DEFINED +#else +#define yylex igraph_pajek_yylex +#endif + +#ifdef yyrestart +#define igraph_pajek_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_pajek_yyrestart +#endif + +#ifdef yylex_init +#define igraph_pajek_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_pajek_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_pajek_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_pajek_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_pajek_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_pajek_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_pajek_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_pajek_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_pajek_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_pajek_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_pajek_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_pajek_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_pajek_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_pajek_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_pajek_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_pajek_yyget_in +#endif + +#ifdef yyset_in +#define igraph_pajek_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_pajek_yyset_in +#endif + +#ifdef yyget_out +#define igraph_pajek_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_pajek_yyget_out +#endif + +#ifdef yyset_out +#define igraph_pajek_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_pajek_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_pajek_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_pajek_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_pajek_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_pajek_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_pajek_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_pajek_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_pajek_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_pajek_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_pajek_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_pajek_yyget_column +#endif + +#ifdef yyset_column +#define igraph_pajek_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_pajek_yyset_column +#endif + +#ifdef yywrap +#define igraph_pajek_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_pajek_yywrap +#endif + +#ifdef yyget_lval +#define igraph_pajek_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_pajek_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_pajek_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_pajek_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_pajek_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_pajek_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_pajek_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_pajek_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_pajek_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_pajek_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_pajek_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_pajek_yyrealloc +#endif + +#ifdef yyfree +#define igraph_pajek_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_pajek_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define igraph_pajek_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void __attribute__((unused)) yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 48 +#define YY_END_OF_BUFFER 49 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[161] = + { 0, + 0, 0, 49, 46, 1, 12, 12, 46, 46, 46, + 46, 46, 15, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, + 46, 1, 12, 46, 0, 13, 46, 0, 2, 3, + 46, 0, 14, 46, 46, 46, 46, 46, 15, 46, + 46, 29, 46, 46, 46, 46, 46, 26, 46, 46, + 46, 46, 46, 46, 38, 46, 46, 46, 46, 27, + 46, 23, 22, 28, 46, 46, 30, 46, 46, 13, + 2, 2, 14, 46, 46, 46, 46, 46, 15, 46, + 15, 33, 34, 37, 19, 20, 46, 46, 31, 32, + + 18, 35, 36, 43, 41, 39, 46, 42, 46, 46, + 46, 46, 46, 3, 46, 46, 46, 4, 46, 46, + 45, 46, 21, 46, 25, 46, 46, 7, 46, 46, + 46, 46, 24, 40, 44, 46, 46, 46, 8, 46, + 46, 46, 46, 46, 46, 46, 11, 46, 46, 16, + 17, 46, 46, 5, 46, 9, 46, 6, 10, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 5, 1, 1, 6, 1, 1, 7, + 8, 9, 10, 1, 11, 12, 1, 13, 14, 15, + 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, + 1, 1, 1, 1, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 1, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 1, 1, 1, 1, 41, 1, 16, 17, 18, 19, + + 20, 21, 22, 23, 24, 1, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[43] = + { 0, + 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2 + } ; + +static const flex_int16_t yy_base[168] = + { 0, + 0, 0, 295, 0, 292, 289, 289, 41, 45, 48, + 37, 45, 54, 74, 43, 68, 262, 40, 272, 48, + 103, 89, 82, 92, 96, 257, 99, 247, 246, 296, + 0, 284, 296, 106, 280, 0, 111, 78, 280, 113, + 120, 275, 0, 250, 262, 264, 259, 258, 117, 112, + 128, 296, 142, 147, 152, 155, 161, 296, 119, 164, + 167, 170, 173, 176, 296, 179, 182, 185, 188, 296, + 253, 296, 296, 296, 236, 249, 296, 253, 252, 296, + 268, 157, 296, 253, 248, 235, 234, 235, 180, 183, + 188, 296, 296, 296, 296, 296, 232, 202, 296, 296, + + 296, 296, 296, 296, 296, 296, 241, 296, 207, 244, + 210, 247, 246, 258, 227, 239, 226, 220, 222, 213, + 296, 216, 296, 219, 296, 237, 236, 227, 219, 227, + 221, 225, 296, 296, 296, 214, 213, 222, 219, 206, + 211, 224, 222, 225, 174, 175, 0, 115, 116, 296, + 296, 100, 85, 0, 64, 0, 45, 0, 0, 296, + 74, 229, 231, 233, 235, 237, 239 + } ; + +static const flex_int16_t yy_def[168] = + { 0, + 160, 1, 160, 161, 160, 160, 160, 162, 163, 164, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 160, + 161, 160, 160, 162, 165, 161, 163, 166, 160, 166, + 164, 167, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 160, 161, 161, 161, 161, 161, 160, 161, 161, + 161, 161, 161, 161, 160, 161, 161, 161, 161, 160, + 161, 160, 160, 160, 161, 161, 160, 161, 161, 160, + 160, 160, 160, 161, 161, 161, 161, 161, 161, 161, + 161, 160, 160, 160, 160, 160, 161, 161, 160, 160, + + 160, 160, 160, 160, 160, 160, 161, 160, 161, 161, + 161, 161, 161, 160, 161, 161, 161, 161, 161, 161, + 160, 161, 160, 161, 160, 161, 161, 161, 161, 161, + 161, 161, 160, 160, 160, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 160, + 160, 161, 161, 161, 161, 161, 161, 161, 161, 0, + 160, 160, 160, 160, 160, 160, 160 + } ; + +static const flex_int16_t yy_nxt[339] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 4, 11, 4, + 12, 4, 13, 13, 13, 14, 15, 16, 4, 4, + 17, 4, 18, 19, 20, 21, 4, 4, 4, 22, + 23, 24, 25, 4, 26, 4, 27, 28, 29, 4, + 4, 30, 35, 35, 35, 36, 38, 39, 40, 42, + 42, 42, 44, 60, 61, 43, 45, 49, 49, 49, + 56, 63, 64, 46, 47, 50, 49, 49, 49, 58, + 58, 58, 48, 51, 31, 52, 52, 52, 159, 57, + 39, 40, 35, 72, 72, 72, 38, 53, 54, 42, + 70, 70, 70, 73, 73, 73, 158, 74, 74, 74, + + 77, 77, 77, 55, 65, 65, 65, 35, 35, 35, + 36, 71, 38, 39, 40, 82, 40, 157, 66, 75, + 67, 42, 42, 42, 89, 89, 89, 43, 50, 49, + 49, 49, 68, 156, 69, 155, 51, 90, 90, 154, + 91, 91, 91, 92, 92, 92, 97, 35, 93, 93, + 93, 98, 38, 94, 94, 94, 95, 95, 95, 114, + 81, 42, 96, 96, 96, 99, 99, 99, 100, 100, + 100, 101, 101, 101, 102, 102, 102, 103, 103, 103, + 104, 104, 104, 105, 105, 105, 106, 106, 106, 108, + 108, 108, 89, 89, 89, 91, 91, 91, 153, 51, + + 91, 91, 91, 121, 121, 121, 152, 107, 123, 123, + 123, 125, 125, 125, 133, 133, 133, 134, 134, 134, + 135, 135, 135, 150, 150, 150, 151, 151, 151, 34, + 34, 37, 37, 41, 41, 35, 35, 38, 38, 42, + 42, 149, 148, 147, 146, 145, 144, 143, 142, 141, + 140, 139, 138, 137, 136, 132, 131, 130, 129, 128, + 114, 127, 126, 124, 122, 120, 119, 118, 117, 116, + 115, 81, 113, 112, 111, 110, 109, 88, 87, 86, + 85, 84, 83, 81, 80, 32, 79, 78, 76, 62, + 59, 33, 33, 32, 160, 3, 160, 160, 160, 160, + + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160 + } ; + +static const flex_int16_t yy_chk[339] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8, 8, 8, 8, 9, 9, 9, 10, + 10, 10, 11, 18, 18, 10, 11, 12, 12, 12, + 15, 20, 20, 11, 11, 13, 13, 13, 13, 16, + 16, 16, 11, 13, 161, 14, 14, 14, 157, 15, + 38, 38, 8, 23, 23, 23, 9, 14, 14, 10, + 22, 22, 22, 24, 24, 24, 155, 25, 25, 25, + + 27, 27, 27, 14, 21, 21, 21, 34, 34, 34, + 34, 22, 37, 37, 37, 40, 40, 153, 21, 25, + 21, 41, 41, 41, 50, 50, 50, 41, 49, 49, + 49, 49, 21, 152, 21, 149, 49, 51, 51, 148, + 51, 51, 51, 53, 53, 53, 59, 34, 54, 54, + 54, 59, 37, 55, 55, 55, 56, 56, 56, 82, + 82, 41, 57, 57, 57, 60, 60, 60, 61, 61, + 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, + 66, 66, 66, 67, 67, 67, 68, 68, 68, 69, + 69, 69, 89, 89, 89, 90, 90, 90, 146, 89, + + 91, 91, 91, 98, 98, 98, 145, 68, 109, 109, + 109, 111, 111, 111, 120, 120, 120, 122, 122, 122, + 124, 124, 124, 143, 143, 143, 144, 144, 144, 162, + 162, 163, 163, 164, 164, 165, 165, 166, 166, 167, + 167, 142, 141, 140, 139, 138, 137, 136, 132, 131, + 130, 129, 128, 127, 126, 119, 118, 117, 116, 115, + 114, 113, 112, 110, 107, 97, 88, 87, 86, 85, + 84, 81, 79, 78, 76, 75, 71, 48, 47, 46, + 45, 44, 42, 39, 35, 32, 29, 28, 26, 19, + 17, 7, 6, 5, 3, 160, 160, 160, 160, 160, + + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "src/core/io/pajek-lexer.l" +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ +#line 24 "src/core/io/pajek-lexer.l" + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "config.h" +#include + +#include "pajek-header.h" +#include "pajek-parser.h" + +#define YY_EXTRA_TYPE igraph_i_pajek_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in Pajek parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +#line 843 "src/core/io/pajek-lexer.c" +#define YY_NO_INPUT 1 +#line 845 "src/core/io/pajek-lexer.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + yy_size_t yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yylval = yylval_param; + + yylloc = yylloc_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 77 "src/core/io/pajek-lexer.l" + + +#line 1130 "src/core/io/pajek-lexer.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 161 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 296 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 79 "src/core/io/pajek-lexer.l" +{ } + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 80 "src/core/io/pajek-lexer.l" +{ } + YY_BREAK +case 3: +/* rule 3 can match eol */ +YY_RULE_SETUP +#line 81 "src/core/io/pajek-lexer.l" +{ } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 82 "src/core/io/pajek-lexer.l" +{ return NETWORKLINE; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 83 "src/core/io/pajek-lexer.l" +{ return NETWORKLINE; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 84 "src/core/io/pajek-lexer.l" +{ return VERTICESLINE; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 85 "src/core/io/pajek-lexer.l" +{ return ARCSLINE; } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 86 "src/core/io/pajek-lexer.l" +{ return EDGESLINE; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 87 "src/core/io/pajek-lexer.l" +{ return ARCSLISTLINE; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 88 "src/core/io/pajek-lexer.l" +{ return EDGESLISTLINE; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 89 "src/core/io/pajek-lexer.l" +{ return MATRIXLINE; } + YY_BREAK +case 12: +/* rule 12 can match eol */ +YY_RULE_SETUP +#line 90 "src/core/io/pajek-lexer.l" +{ yyextra->mode=0; return NEWLINE; } + YY_BREAK +case 13: +/* rule 13 can match eol */ +YY_RULE_SETUP +#line 91 "src/core/io/pajek-lexer.l" +{ return QSTR; } + YY_BREAK +case 14: +/* rule 14 can match eol */ +YY_RULE_SETUP +#line 92 "src/core/io/pajek-lexer.l" +{ return PSTR; } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 93 "src/core/io/pajek-lexer.l" +{ + return NUM; } + YY_BREAK +case 16: +/* rule 16 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 6); +yyg->yy_c_buf_p = yy_cp = yy_bp + 6; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 96 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_X_FACT; } else { return ALNUM; } } + YY_BREAK +case 17: +/* rule 17 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 6); +yyg->yy_c_buf_p = yy_cp = yy_bp + 6; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 97 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_Y_FACT; } else { return ALNUM; } } + YY_BREAK +case 18: +/* rule 18 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 98 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_IC; } else { return ALNUM; } } + YY_BREAK +case 19: +/* rule 19 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 99 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_BC; } else { return ALNUM; } } + YY_BREAK +case 20: +/* rule 20 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 100 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_BW; } else { return ALNUM; } } + YY_BREAK +case 21: +/* rule 21 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 3); +yyg->yy_c_buf_p = yy_cp = yy_bp + 3; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 101 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_PHI; } else { return ALNUM; } } + YY_BREAK +case 22: +/* rule 22 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 102 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_R; } else { return ALNUM; } } + YY_BREAK +case 23: +/* rule 23 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 103 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_Q; } else { return ALNUM; } } + YY_BREAK +case 24: +/* rule 24 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 4); +yyg->yy_c_buf_p = yy_cp = yy_bp + 4; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 104 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_FONT; } else { return ALNUM; } } + YY_BREAK +case 25: +/* rule 25 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 3); +yyg->yy_c_buf_p = yy_cp = yy_bp + 3; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 105 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_URL; } else { return ALNUM; } } + YY_BREAK +case 26: +/* rule 26 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 107 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_C; } else { return ALNUM; } } + YY_BREAK +case 27: +/* rule 27 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 108 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_P; } else { return ALNUM; } } + YY_BREAK +case 28: +/* rule 28 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 109 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_S; } else { return ALNUM; } } + YY_BREAK +case 29: +/* rule 29 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 110 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_A; } else { return ALNUM; } } + YY_BREAK +case 30: +/* rule 30 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 111 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_W; } else { return ALNUM; } } + YY_BREAK +case 31: +/* rule 31 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 112 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_H1; } else { return ALNUM; } } + YY_BREAK +case 32: +/* rule 32 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 113 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_H2; } else { return ALNUM; } } + YY_BREAK +case 33: +/* rule 33 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 114 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_A1; } else { return ALNUM; } } + YY_BREAK +case 34: +/* rule 34 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 115 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_A2; } else { return ALNUM; } } + YY_BREAK +case 35: +/* rule 35 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 116 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_K1; } else { return ALNUM; } } + YY_BREAK +case 36: +/* rule 36 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 117 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_K2; } else { return ALNUM; } } + YY_BREAK +case 37: +/* rule 37 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 118 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_AP; } else { return ALNUM; } } + YY_BREAK +case 38: +/* rule 38 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 1); +yyg->yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 119 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_L; } else { return ALNUM; } } + YY_BREAK +case 39: +/* rule 39 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 120 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==2) { return EP_LP; } else { return ALNUM; } } + YY_BREAK +case 40: +/* rule 40 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 4); +yyg->yy_c_buf_p = yy_cp = yy_bp + 4; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 122 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_LPHI; } else + if (yyextra->mode==2) { return EP_LPHI; } else { return ALNUM; } } + YY_BREAK +case 41: +/* rule 41 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 124 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_LC; } else + if (yyextra->mode==2) { return EP_LC; } else { return ALNUM; } } + YY_BREAK +case 42: +/* rule 42 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 126 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_LR; } else + if (yyextra->mode==2) { return EP_LR; } else { return ALNUM; } } + YY_BREAK +case 43: +/* rule 43 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 2); +yyg->yy_c_buf_p = yy_cp = yy_bp + 2; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 128 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_LA; } else + if (yyextra->mode==2) { return EP_LA; } else { return ALNUM; } } + YY_BREAK +case 44: +/* rule 44 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 4); +yyg->yy_c_buf_p = yy_cp = yy_bp + 4; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 130 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_SIZE; } else + if (yyextra->mode==2) { return EP_SIZE; } else { return ALNUM; } } + YY_BREAK +case 45: +/* rule 45 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_bp + 3); +yyg->yy_c_buf_p = yy_cp = yy_bp + 3; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 132 "src/core/io/pajek-lexer.l" +{ if (yyextra->mode==1) { return VP_FOS; } else + if (yyextra->mode==2) { return EP_FOS; } else { return ALNUM; } } + YY_BREAK +case 46: +YY_RULE_SETUP +#line 135 "src/core/io/pajek-lexer.l" +{ return ALNUM; } + YY_BREAK +case YY_STATE_EOF(INITIAL): +#line 137 "src/core/io/pajek-lexer.l" +{ if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return NEWLINE; + } + } + YY_BREAK +case 47: +YY_RULE_SETUP +#line 145 "src/core/io/pajek-lexer.l" +{ return ERROR; } + YY_BREAK +case 48: +YY_RULE_SETUP +#line 147 "src/core/io/pajek-lexer.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1599 "src/core/io/pajek-lexer.c" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap( yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin , yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 42); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 161 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 42; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 161 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 160); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( yyscanner ) ) + return 0; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void __attribute__((unused)) yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +yy_size_t yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void yyset_column (int _column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = _column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int _bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 147 "src/core/io/pajek-lexer.l" + + diff --git a/src/rigraph/core/io/pajek-lexer.h b/src/rigraph/core/io/pajek-lexer.h new file mode 100644 index 0000000..647317b --- /dev/null +++ b/src/rigraph/core/io/pajek-lexer.h @@ -0,0 +1,736 @@ +#ifndef igraph_pajek_yyHEADER_H +#define igraph_pajek_yyHEADER_H 1 +#define igraph_pajek_yyIN_HEADER 1 + +#line 6 "src/core/io/pajek-lexer.h" + +#line 8 "src/core/io/pajek-lexer.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define igraph_pajek_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer igraph_pajek_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define igraph_pajek_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer igraph_pajek_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define igraph_pajek_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer igraph_pajek_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define igraph_pajek_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string igraph_pajek_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define igraph_pajek_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes igraph_pajek_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define igraph_pajek_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer igraph_pajek_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define igraph_pajek_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer igraph_pajek_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define igraph_pajek_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state igraph_pajek_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define igraph_pajek_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer igraph_pajek_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define igraph_pajek_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state igraph_pajek_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define igraph_pajek_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state igraph_pajek_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define igraph_pajek_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack igraph_pajek_yyensure_buffer_stack +#endif + +#ifdef yylex +#define igraph_pajek_yylex_ALREADY_DEFINED +#else +#define yylex igraph_pajek_yylex +#endif + +#ifdef yyrestart +#define igraph_pajek_yyrestart_ALREADY_DEFINED +#else +#define yyrestart igraph_pajek_yyrestart +#endif + +#ifdef yylex_init +#define igraph_pajek_yylex_init_ALREADY_DEFINED +#else +#define yylex_init igraph_pajek_yylex_init +#endif + +#ifdef yylex_init_extra +#define igraph_pajek_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra igraph_pajek_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define igraph_pajek_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy igraph_pajek_yylex_destroy +#endif + +#ifdef yyget_debug +#define igraph_pajek_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug igraph_pajek_yyget_debug +#endif + +#ifdef yyset_debug +#define igraph_pajek_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug igraph_pajek_yyset_debug +#endif + +#ifdef yyget_extra +#define igraph_pajek_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra igraph_pajek_yyget_extra +#endif + +#ifdef yyset_extra +#define igraph_pajek_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra igraph_pajek_yyset_extra +#endif + +#ifdef yyget_in +#define igraph_pajek_yyget_in_ALREADY_DEFINED +#else +#define yyget_in igraph_pajek_yyget_in +#endif + +#ifdef yyset_in +#define igraph_pajek_yyset_in_ALREADY_DEFINED +#else +#define yyset_in igraph_pajek_yyset_in +#endif + +#ifdef yyget_out +#define igraph_pajek_yyget_out_ALREADY_DEFINED +#else +#define yyget_out igraph_pajek_yyget_out +#endif + +#ifdef yyset_out +#define igraph_pajek_yyset_out_ALREADY_DEFINED +#else +#define yyset_out igraph_pajek_yyset_out +#endif + +#ifdef yyget_leng +#define igraph_pajek_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng igraph_pajek_yyget_leng +#endif + +#ifdef yyget_text +#define igraph_pajek_yyget_text_ALREADY_DEFINED +#else +#define yyget_text igraph_pajek_yyget_text +#endif + +#ifdef yyget_lineno +#define igraph_pajek_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno igraph_pajek_yyget_lineno +#endif + +#ifdef yyset_lineno +#define igraph_pajek_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno igraph_pajek_yyset_lineno +#endif + +#ifdef yyget_column +#define igraph_pajek_yyget_column_ALREADY_DEFINED +#else +#define yyget_column igraph_pajek_yyget_column +#endif + +#ifdef yyset_column +#define igraph_pajek_yyset_column_ALREADY_DEFINED +#else +#define yyset_column igraph_pajek_yyset_column +#endif + +#ifdef yywrap +#define igraph_pajek_yywrap_ALREADY_DEFINED +#else +#define yywrap igraph_pajek_yywrap +#endif + +#ifdef yyget_lval +#define igraph_pajek_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval igraph_pajek_yyget_lval +#endif + +#ifdef yyset_lval +#define igraph_pajek_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval igraph_pajek_yyset_lval +#endif + +#ifdef yyget_lloc +#define igraph_pajek_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc igraph_pajek_yyget_lloc +#endif + +#ifdef yyset_lloc +#define igraph_pajek_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc igraph_pajek_yyset_lloc +#endif + +#ifdef yyalloc +#define igraph_pajek_yyalloc_ALREADY_DEFINED +#else +#define yyalloc igraph_pajek_yyalloc +#endif + +#ifdef yyrealloc +#define igraph_pajek_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc igraph_pajek_yyrealloc +#endif + +#ifdef yyfree +#define igraph_pajek_yyfree_ALREADY_DEFINED +#else +#define yyfree igraph_pajek_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define igraph_pajek_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef igraph_pajek_yy_create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef igraph_pajek_yy_delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef igraph_pajek_yy_scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef igraph_pajek_yy_scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef igraph_pajek_yy_scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef igraph_pajek_yy_init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef igraph_pajek_yy_flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef igraph_pajek_yy_load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef igraph_pajek_yy_switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef igraph_pajek_yypush_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef igraph_pajek_yypop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef igraph_pajek_yyensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef igraph_pajek_yylex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef igraph_pajek_yyrestart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef igraph_pajek_yylex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef igraph_pajek_yylex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef igraph_pajek_yylex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef igraph_pajek_yyget_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef igraph_pajek_yyset_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef igraph_pajek_yyget_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef igraph_pajek_yyset_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef igraph_pajek_yyget_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef igraph_pajek_yyset_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef igraph_pajek_yyget_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef igraph_pajek_yyset_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef igraph_pajek_yyget_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef igraph_pajek_yyget_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef igraph_pajek_yyget_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef igraph_pajek_yyset_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef igraph_pajek_yyget_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef igraph_pajek_yyset_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef igraph_pajek_yywrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef igraph_pajek_yyget_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef igraph_pajek_yyset_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef igraph_pajek_yyget_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef igraph_pajek_yyset_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef igraph_pajek_yyalloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef igraph_pajek_yyrealloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef igraph_pajek_yyfree_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef igraph_pajek_yytext_ALREADY_DEFINED +#undef yytext +#endif +#ifndef igraph_pajek_yyleng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef igraph_pajek_yyin_ALREADY_DEFINED +#undef yyin +#endif +#ifndef igraph_pajek_yyout_ALREADY_DEFINED +#undef yyout +#endif +#ifndef igraph_pajek_yy_flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef igraph_pajek_yylineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef igraph_pajek_yytables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef igraph_pajek_yytables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef igraph_pajek_yyTABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 147 "src/core/io/pajek-lexer.l" + + +#line 735 "src/core/io/pajek-lexer.h" +#undef igraph_pajek_yyIN_HEADER +#endif /* igraph_pajek_yyHEADER_H */ diff --git a/src/rigraph/core/io/pajek-lexer.l b/src/rigraph/core/io/pajek-lexer.l new file mode 100644 index 0000000..e73b71e --- /dev/null +++ b/src/rigraph/core/io/pajek-lexer.l @@ -0,0 +1,147 @@ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "config.h" +#include + +#include "pajek-header.h" +#include "pajek-parser.h" + +#define YY_EXTRA_TYPE igraph_i_pajek_parsedata_t* +#define YY_USER_ACTION yylloc->first_line = yylineno; +#define YY_FATAL_ERROR(msg) IGRAPH_FATAL("Error in Pajek parser: " # msg) +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +%} + +%option noyywrap +%option prefix="igraph_pajek_yy" +%option nounput +%option noinput +%option nodefault +%option reentrant +%option bison-bridge +%option bison-locations + +digit [0-9] +word [^ \t\r\n\0] + +%% + +[ \t]+ { } +%[^\n]*\n[\r]* { } +%[^\n]*\r[\n]* { } +\*[Nn][eE][Tt] { return NETWORKLINE; } +\*[Nn][Ee][Tt][Ww][Oo][Rr][Kk] { return NETWORKLINE; } +\*[Vv][Ee][Rr][Tt][Ii][Cc][Ee][Ss] { return VERTICESLINE; } +\*[Aa][Rr][Cc][Ss] { return ARCSLINE; } +\*[Ee][Dd][Gg][Ee][Ss] { return EDGESLINE; } +\*[Aa][Rr][Cc][Ss][Ll][Ii][Ss][Tt] { return ARCSLISTLINE; } +\*[Ee][Dd][Gg][Ee][Ss][Ll][Ii][Ss][Tt] { return EDGESLISTLINE; } +\*[Mm][Aa][Tt][Rr][Ii][Xx] { return MATRIXLINE; } +\n\r|\r\n|\n|\r { yyextra->mode=0; return NEWLINE; } +\"[^\"]*\" { return QSTR; } +\([^\)]*\) { return PSTR; } +\-?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { + return NUM; } + +[Xx]_[Ff][Aa][Cc][Tt]/[ \t\n\r] { if (yyextra->mode==1) { return VP_X_FACT; } else { return ALNUM; } } +[Yy]_[Ff][Aa][Cc][Tt]/[ \t\n\r] { if (yyextra->mode==1) { return VP_Y_FACT; } else { return ALNUM; } } +[Ii][Cc]/[ \t\n\r] { if (yyextra->mode==1) { return VP_IC; } else { return ALNUM; } } +[Bb][Cc]/[ \t\n\r] { if (yyextra->mode==1) { return VP_BC; } else { return ALNUM; } } +[Bb][Ww]/[ \t\n\r] { if (yyextra->mode==1) { return VP_BW; } else { return ALNUM; } } +[Pp][Hh][Ii]/[ \t\n\r] { if (yyextra->mode==1) { return VP_PHI; } else { return ALNUM; } } +[Rr]/[ \t\n\r] { if (yyextra->mode==1) { return VP_R; } else { return ALNUM; } } +[Qq]/[ \t\n\r] { if (yyextra->mode==1) { return VP_Q; } else { return ALNUM; } } +[Ff][Oo][Nn][Tt]/[ \t\n\r] { if (yyextra->mode==1) { return VP_FONT; } else { return ALNUM; } } +[Uu][Rr][Ll]/[ \t\n\r] { if (yyextra->mode==1) { return VP_URL; } else { return ALNUM; } } + +[Cc]/[ \t\n\r] { if (yyextra->mode==2) { return EP_C; } else { return ALNUM; } } +[Pp]/[ \t\n\r] { if (yyextra->mode==2) { return EP_P; } else { return ALNUM; } } +[Ss]/[ \t\n\r] { if (yyextra->mode==2) { return EP_S; } else { return ALNUM; } } +[Aa]/[ \t\n\r] { if (yyextra->mode==2) { return EP_A; } else { return ALNUM; } } +[Ww]/[ \t\n\r] { if (yyextra->mode==2) { return EP_W; } else { return ALNUM; } } +[Hh]1/[ \t\n\r] { if (yyextra->mode==2) { return EP_H1; } else { return ALNUM; } } +[Hh]2/[ \t\n\r] { if (yyextra->mode==2) { return EP_H2; } else { return ALNUM; } } +[Aa]1/[ \t\n\r] { if (yyextra->mode==2) { return EP_A1; } else { return ALNUM; } } +[Aa]2/[ \t\n\r] { if (yyextra->mode==2) { return EP_A2; } else { return ALNUM; } } +[Kk]1/[ \t\n\r] { if (yyextra->mode==2) { return EP_K1; } else { return ALNUM; } } +[Kk]2/[ \t\n\r] { if (yyextra->mode==2) { return EP_K2; } else { return ALNUM; } } +[Aa][Pp]/[ \t\n\r] { if (yyextra->mode==2) { return EP_AP; } else { return ALNUM; } } +[Ll]/[ \t\n\r] { if (yyextra->mode==2) { return EP_L; } else { return ALNUM; } } +[Ll][Pp]/[ \t\n\r] { if (yyextra->mode==2) { return EP_LP; } else { return ALNUM; } } + +[Ll][Pp][Hh][Ii]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LPHI; } else + if (yyextra->mode==2) { return EP_LPHI; } else { return ALNUM; } } +[Ll][Cc]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LC; } else + if (yyextra->mode==2) { return EP_LC; } else { return ALNUM; } } +[Ll][Rr]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LR; } else + if (yyextra->mode==2) { return EP_LR; } else { return ALNUM; } } +[Ll][Aa]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LA; } else + if (yyextra->mode==2) { return EP_LA; } else { return ALNUM; } } +[Ss][Ii][Zz][Ee]/[ \t\n\r] { if (yyextra->mode==1) { return VP_SIZE; } else + if (yyextra->mode==2) { return EP_SIZE; } else { return ALNUM; } } +[Ff][Oo][Ss]/[ \t\n\r] { if (yyextra->mode==1) { return VP_FOS; } else + if (yyextra->mode==2) { return EP_FOS; } else { return ALNUM; } } + +{word}+ { return ALNUM; } + +<> { if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + return NEWLINE; + } + } + +. { return ERROR; } + +%% diff --git a/src/rigraph/core/io/pajek-parser.c b/src/rigraph/core/io/pajek-parser.c new file mode 100644 index 0000000..c1659c4 --- /dev/null +++ b/src/rigraph/core/io/pajek-parser.c @@ -0,0 +1,2882 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + 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 + (at your option) 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse igraph_pajek_yyparse +#define yylex igraph_pajek_yylex +#define yyerror igraph_pajek_yyerror +#define yydebug igraph_pajek_yydebug +#define yynerrs igraph_pajek_yynerrs + +/* First part of user prologue. */ +#line 23 "src/core/io/pajek-parser.y" + + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include +#include +#include + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "igraph_attributes.h" +#include "config.h" + +#include "../core/math.h" +#include "pajek-header.h" +#include "pajek-parser.h" /* it must come first because of YYSTYPE */ +#include "pajek-lexer.h" +//#include "internal/hacks.h" + +int igraph_pajek_yyerror(YYLTYPE* locp, + igraph_i_pajek_parsedata_t *context, + const char *s); + +int igraph_i_pajek_add_string_vertex_attribute(const char *name, + const char *value, + int len, + igraph_i_pajek_parsedata_t *context); +int igraph_i_pajek_add_string_edge_attribute(const char *name, + const char *value, + int len, + igraph_i_pajek_parsedata_t *context); +int igraph_i_pajek_add_numeric_vertex_attribute(const char *name, + igraph_real_t value, + igraph_i_pajek_parsedata_t *context); +int igraph_i_pajek_add_numeric_edge_attribute(const char *name, + igraph_real_t value, + igraph_i_pajek_parsedata_t *context); +int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, + igraph_vector_ptr_t *attrs, + long int count, + const char *attrname, + igraph_integer_t vid, + igraph_real_t number); +int igraph_i_pajek_add_string_attribute(igraph_trie_t *names, + igraph_vector_ptr_t *attrs, + long int count, + const char *attrname, + igraph_integer_t vid, + const char *str); + +int igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context); +int igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t *context); + +extern igraph_real_t igraph_pajek_get_number(const char *str, long int len); +extern long int igraph_i_pajek_actvertex; +extern long int igraph_i_pajek_actedge; + +#define scanner context->scanner + + +#line 157 "yy.tab.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_IGRAPH_PAJEK_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_PAJEK_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_pajek_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + NEWLINE = 258, + NUM = 259, + ALNUM = 260, + QSTR = 261, + PSTR = 262, + NETWORKLINE = 263, + VERTICESLINE = 264, + ARCSLINE = 265, + EDGESLINE = 266, + ARCSLISTLINE = 267, + EDGESLISTLINE = 268, + MATRIXLINE = 269, + ERROR = 270, + VP_X_FACT = 271, + VP_Y_FACT = 272, + VP_IC = 273, + VP_BC = 274, + VP_LC = 275, + VP_LR = 276, + VP_LPHI = 277, + VP_BW = 278, + VP_FOS = 279, + VP_PHI = 280, + VP_R = 281, + VP_Q = 282, + VP_LA = 283, + VP_FONT = 284, + VP_URL = 285, + VP_SIZE = 286, + EP_C = 287, + EP_S = 288, + EP_A = 289, + EP_W = 290, + EP_H1 = 291, + EP_H2 = 292, + EP_A1 = 293, + EP_A2 = 294, + EP_K1 = 295, + EP_K2 = 296, + EP_AP = 297, + EP_P = 298, + EP_L = 299, + EP_LP = 300, + EP_LR = 301, + EP_LPHI = 302, + EP_LC = 303, + EP_LA = 304, + EP_SIZE = 305, + EP_FOS = 306 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 115 "src/core/io/pajek-parser.y" + + long int intnum; + double realnum; + struct { + char *str; + int len; + } string; + +#line 270 "yy.tab.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_pajek_yyparse (igraph_i_pajek_parsedata_t* context); + +#endif /* !YY_IGRAPH_PAJEK_YY_YY_TAB_H_INCLUDED */ + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_uint8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 5 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 250 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 52 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 66 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 137 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 207 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 306 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 189, 189, 193, 193, 195, 197, 201, 207, 207, + 209, 210, 211, 211, 214, 216, 221, 222, 226, 232, + 232, 236, 236, 239, 240, 243, 246, 251, 256, 261, + 264, 267, 270, 273, 276, 279, 282, 285, 290, 290, + 294, 294, 298, 298, 302, 302, 307, 307, 314, 316, + 316, 316, 316, 316, 316, 318, 319, 321, 321, 323, + 324, 324, 330, 332, 334, 335, 337, 337, 339, 340, + 340, 346, 348, 350, 350, 354, 354, 357, 358, 363, + 366, 369, 372, 375, 378, 381, 384, 387, 390, 393, + 396, 399, 402, 405, 410, 410, 414, 414, 418, 418, + 422, 422, 426, 426, 432, 434, 436, 436, 438, 438, + 440, 440, 442, 444, 449, 451, 451, 453, 453, 455, + 455, 457, 459, 466, 468, 473, 473, 475, 477, 477, + 479, 499, 502, 505, 505, 507, 509, 511 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "NEWLINE", "NUM", "ALNUM", "QSTR", + "PSTR", "NETWORKLINE", "VERTICESLINE", "ARCSLINE", "EDGESLINE", + "ARCSLISTLINE", "EDGESLISTLINE", "MATRIXLINE", "ERROR", "VP_X_FACT", + "VP_Y_FACT", "VP_IC", "VP_BC", "VP_LC", "VP_LR", "VP_LPHI", "VP_BW", + "VP_FOS", "VP_PHI", "VP_R", "VP_Q", "VP_LA", "VP_FONT", "VP_URL", + "VP_SIZE", "EP_C", "EP_S", "EP_A", "EP_W", "EP_H1", "EP_H2", "EP_A1", + "EP_A2", "EP_K1", "EP_K2", "EP_AP", "EP_P", "EP_L", "EP_LP", "EP_LR", + "EP_LPHI", "EP_LC", "EP_LA", "EP_SIZE", "EP_FOS", "$accept", "input", + "nethead", "vertices", "verticeshead", "vertdefs", "vertexline", "$@1", + "vertex", "vertexid", "vertexcoords", "shape", "params", "param", + "vpword", "$@2", "$@3", "$@4", "$@5", "$@6", "vpwordpar", "edgeblock", + "arcs", "arcsdefs", "arcsline", "$@7", "arcfrom", "arcto", "edges", + "edgesdefs", "edgesline", "$@8", "edgefrom", "edgeto", "weight", + "edgeparams", "edgeparam", "epword", "$@9", "$@10", "$@11", "$@12", + "$@13", "epwordpar", "arcslist", "arcslistlines", "arclistline", + "arctolist", "arclistfrom", "arclistto", "edgeslist", "edgelistlines", + "edgelistline", "edgetolist", "edgelistfrom", "edgelistto", "adjmatrix", + "matrixline", "adjmatrixlines", "adjmatrixline", "adjmatrixnumbers", + "adjmatrixentry", "longint", "number", "words", "word", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306 +}; +# endif + +#define YYPACT_NINF (-167) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-129) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + -4, -167, 7, 36, 22, -167, 44, -167, 49, -167, + -167, -167, -167, -167, -167, 44, 10, -167, -167, 12, + 27, 51, 56, -167, -167, -167, -167, -167, -167, 58, + 29, -167, -167, 59, -167, 60, -167, -167, -167, -167, + -167, 61, -167, 31, -167, 33, -167, 35, 37, 39, + -167, 5, -167, -167, 44, -167, 31, -167, -167, 44, + -167, 33, -167, -167, -167, -167, -167, -167, -167, -167, + -167, 62, 65, -167, 65, -167, -167, -167, -167, -167, + 47, 53, -167, -167, 5, 65, 65, 65, -167, -167, + -167, -167, -167, -167, -167, -167, 65, -167, -167, -167, + 219, -167, 150, 170, -167, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, -167, -167, + 65, -167, -167, -167, 65, 65, -167, 65, 65, 65, + 65, 65, 65, 65, 65, -167, -167, 65, 65, 65, + -167, 65, 65, 65, -167, -167, -167, -167, -167, 5, + 65, 5, 65, 5, 65, -167, -167, -167, -167, -167, + -167, -167, -167, 5, 5, -167, 5, 65, -167, 5, + -167, -167, -167, -167, -167, -167, -167, -167, 5, 5, + -167, -167, -167, 5, -167, -167, -167, -167, -167, 65, + -167, 65, -167, 65, -167, -167, -167, -167, 65, -167, + -167, -167, -167, -167, -167, -167, -167 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 3, 133, 0, 0, 0, 1, 0, 49, 0, 4, + 136, 135, 137, 134, 131, 6, 2, 8, 7, 0, + 0, 0, 0, 124, 50, 51, 52, 53, 54, 0, + 5, 57, 132, 0, 66, 0, 106, 115, 125, 10, + 9, 12, 14, 55, 57, 64, 66, 105, 114, 123, + 11, 0, 59, 58, 0, 62, 56, 68, 67, 0, + 71, 65, 108, 107, 110, 112, 117, 116, 119, 121, + 126, 0, 128, 130, 16, 15, 60, 63, 69, 72, + 0, 0, 127, 129, 19, 0, 73, 73, 109, 111, + 113, 118, 120, 122, 21, 20, 17, 75, 74, 75, + 0, 18, 0, 0, 13, 0, 0, 42, 44, 46, + 0, 0, 0, 0, 0, 0, 0, 0, 38, 40, + 0, 22, 23, 61, 102, 0, 94, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 98, 0, 0, 0, + 100, 0, 0, 0, 76, 77, 70, 24, 25, 0, + 0, 0, 0, 0, 0, 29, 30, 31, 32, 33, + 34, 35, 36, 0, 0, 37, 0, 0, 79, 0, + 80, 81, 82, 83, 84, 85, 86, 87, 0, 0, + 88, 89, 90, 0, 91, 92, 93, 43, 48, 0, + 45, 0, 47, 0, 39, 41, 103, 104, 0, 95, + 97, 99, 101, 26, 27, 28, 78 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, + -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, + -145, -167, -167, 26, -167, -167, -167, -167, -167, 25, + -167, -167, -167, -167, -15, -26, -167, -167, -167, -167, + -167, -167, -167, -166, -167, -167, -167, -167, -167, -167, + -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, + 2, -167, -1, -19, -167, -2 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 2, 3, 7, 8, 30, 40, 51, 41, 74, + 84, 94, 100, 121, 122, 163, 164, 149, 151, 153, + 187, 16, 24, 43, 53, 86, 54, 76, 25, 45, + 58, 87, 59, 78, 97, 102, 144, 145, 169, 178, + 179, 183, 166, 196, 26, 47, 63, 80, 64, 89, + 27, 48, 67, 81, 68, 92, 28, 29, 49, 70, + 71, 72, 55, 73, 4, 188 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 33, 35, 13, 199, 1, 15, 190, 5, 192, 10, + 11, 12, 200, 201, 18, 31, 32, 202, 194, 195, + 19, 20, 21, 22, 23, 9, 10, 11, 12, 42, + 34, 32, 39, 14, 52, 14, 57, 14, 62, 14, + 66, 14, -128, 32, 60, 6, 65, 69, 14, 75, + 88, 14, 17, 77, 36, 85, 91, 14, 79, 37, + 60, 38, 44, 46, 50, 82, 96, 98, 98, 32, + 56, 61, 99, 103, 83, 0, 0, 101, 0, 90, + 93, 0, 95, 0, 0, 0, 147, 148, 150, 152, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 0, + 0, 165, 0, 0, 0, 167, 168, 0, 170, 171, + 172, 173, 174, 175, 176, 177, 0, 0, 180, 181, + 182, 0, 184, 185, 186, 0, 0, 0, 0, 0, + 0, 189, 0, 191, 0, 193, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 198, 0, + 0, 0, 0, 123, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 197, 0, 0, 197, 0, 0, + 203, 0, 204, 146, 205, 0, 197, 197, 0, 206, + 0, 197, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120 +}; + +static const yytype_int16 yycheck[] = +{ + 19, 20, 4, 169, 8, 6, 151, 0, 153, 4, + 5, 6, 178, 179, 15, 3, 4, 183, 163, 164, + 10, 11, 12, 13, 14, 3, 4, 5, 6, 30, + 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, + 3, 4, 3, 4, 45, 9, 47, 48, 4, 51, + 3, 4, 3, 54, 3, 74, 3, 4, 59, 3, + 61, 3, 3, 3, 3, 3, 85, 86, 87, 4, + 44, 46, 87, 99, 72, -1, -1, 96, -1, 80, + 81, -1, 84, -1, -1, -1, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, -1, + -1, 120, -1, -1, -1, 124, 125, -1, 127, 128, + 129, 130, 131, 132, 133, 134, -1, -1, 137, 138, + 139, -1, 141, 142, 143, -1, -1, -1, -1, -1, + -1, 150, -1, 152, -1, 154, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 167, -1, + -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 166, -1, -1, 169, -1, -1, + 189, -1, 191, 3, 193, -1, 178, 179, -1, 198, + -1, 183, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 3, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 8, 53, 54, 116, 0, 9, 55, 56, 3, + 4, 5, 6, 117, 4, 114, 73, 3, 114, 10, + 11, 12, 13, 14, 74, 80, 96, 102, 108, 109, + 57, 3, 4, 115, 3, 115, 3, 3, 3, 3, + 58, 60, 114, 75, 3, 81, 3, 97, 103, 110, + 3, 59, 3, 76, 78, 114, 75, 3, 82, 84, + 114, 81, 3, 98, 100, 114, 3, 104, 106, 114, + 111, 112, 113, 115, 61, 117, 79, 114, 85, 114, + 99, 105, 3, 112, 62, 115, 77, 83, 3, 101, + 114, 3, 107, 114, 63, 117, 115, 86, 115, 86, + 64, 115, 87, 87, 3, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 65, 66, 3, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 88, 89, 3, 115, 115, 69, + 115, 70, 115, 71, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 67, 68, 115, 94, 115, 115, 90, + 115, 115, 115, 115, 115, 115, 115, 115, 91, 92, + 115, 115, 115, 93, 115, 115, 115, 72, 117, 115, + 72, 115, 72, 115, 72, 72, 95, 117, 115, 95, + 95, 95, 95, 115, 115, 115, 115 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 52, 53, 54, 54, 55, 56, 56, 57, 57, + 58, 58, 59, 58, 60, 61, 62, 62, 62, 63, + 63, 64, 64, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 67, 66, + 68, 66, 69, 66, 70, 66, 71, 66, 72, 73, + 73, 73, 73, 73, 73, 74, 74, 75, 75, 76, + 77, 76, 78, 79, 80, 80, 81, 81, 82, 83, + 82, 84, 85, 86, 86, 87, 87, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 90, 89, 91, 89, 92, 89, + 93, 89, 94, 89, 95, 96, 97, 97, 98, 98, + 99, 99, 100, 101, 102, 103, 103, 104, 104, 105, + 105, 106, 107, 108, 109, 110, 110, 111, 112, 112, + 113, 114, 115, 116, 116, 117, 117, 117 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 3, 0, 3, 3, 2, 3, 0, 2, + 1, 2, 0, 7, 1, 1, 0, 2, 3, 0, + 1, 0, 2, 1, 2, 2, 4, 4, 4, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 0, 3, + 0, 3, 0, 3, 0, 3, 0, 3, 1, 0, + 2, 2, 2, 2, 2, 3, 4, 0, 2, 1, + 0, 6, 1, 1, 3, 4, 0, 2, 1, 0, + 6, 1, 1, 0, 1, 0, 2, 1, 4, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 0, 3, 0, 3, 0, 3, + 0, 3, 0, 3, 1, 3, 0, 2, 1, 3, + 0, 2, 1, 1, 3, 0, 2, 1, 3, 0, + 2, 1, 1, 3, 1, 0, 2, 2, 0, 2, + 1, 1, 1, 0, 2, 1, 1, 1 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_pajek_parsedata_t* context) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (yylocationp); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_pajek_parsedata_t* context) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_pajek_parsedata_t* context) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[+yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, context); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[+*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_pajek_parsedata_t* context) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (igraph_i_pajek_parsedata_t* context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + 'yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, &yylloc, scanner); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 189 "src/core/io/pajek-parser.y" + { + if (context->vcount2 > 0) { igraph_i_pajek_check_bipartite(context); } + } +#line 1741 "yy.tab.c" + break; + + case 6: +#line 197 "src/core/io/pajek-parser.y" + { + context->vcount=(yyvsp[0].intnum); + context->vcount2=0; + } +#line 1750 "yy.tab.c" + break; + + case 7: +#line 201 "src/core/io/pajek-parser.y" + { + context->vcount=(yyvsp[-1].intnum); + context->vcount2=(yyvsp[0].intnum); + igraph_i_pajek_add_bipartite_type(context); +} +#line 1760 "yy.tab.c" + break; + + case 12: +#line 211 "src/core/io/pajek-parser.y" + { context->actvertex=(yyvsp[0].intnum); } +#line 1766 "yy.tab.c" + break; + + case 13: +#line 211 "src/core/io/pajek-parser.y" + { } +#line 1772 "yy.tab.c" + break; + + case 14: +#line 214 "src/core/io/pajek-parser.y" + { (yyval.intnum)=(yyvsp[0].intnum); context->mode=1; } +#line 1778 "yy.tab.c" + break; + + case 15: +#line 216 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_string_vertex_attribute("id", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + igraph_i_pajek_add_string_vertex_attribute("name", (yyvsp[0].string).str, (yyvsp[0].string).len, context); +} +#line 1787 "yy.tab.c" + break; + + case 17: +#line 222 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("x", (yyvsp[-1].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("y", (yyvsp[0].realnum), context); + } +#line 1796 "yy.tab.c" + break; + + case 18: +#line 226 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("x", (yyvsp[-2].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("y", (yyvsp[-1].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("z", (yyvsp[0].realnum), context); + } +#line 1806 "yy.tab.c" + break; + + case 20: +#line 232 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_string_vertex_attribute("shape", (yyvsp[0].string).str, (yyvsp[0].string).len, context); +} +#line 1814 "yy.tab.c" + break; + + case 24: +#line 240 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("xfact", (yyvsp[0].realnum), context); + } +#line 1822 "yy.tab.c" + break; + + case 25: +#line 243 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("yfact", (yyvsp[0].realnum), context); + } +#line 1830 "yy.tab.c" + break; + + case 26: +#line 246 "src/core/io/pajek-parser.y" + { /* RGB color */ + igraph_i_pajek_add_numeric_vertex_attribute("color-red", (yyvsp[-2].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("color-green", (yyvsp[-1].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("color-blue", (yyvsp[0].realnum), context); + } +#line 1840 "yy.tab.c" + break; + + case 27: +#line 251 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("framecolor-red", (yyvsp[-2].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("framecolor-green", (yyvsp[-1].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("framecolor-blue", (yyvsp[0].realnum), context); + } +#line 1850 "yy.tab.c" + break; + + case 28: +#line 256 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-red", (yyvsp[-2].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-green", (yyvsp[-1].realnum), context); + igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-blue", (yyvsp[0].realnum), context); + } +#line 1860 "yy.tab.c" + break; + + case 29: +#line 261 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("labeldist", (yyvsp[0].realnum), context); + } +#line 1868 "yy.tab.c" + break; + + case 30: +#line 264 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("labeldegree2", (yyvsp[0].realnum), context); + } +#line 1876 "yy.tab.c" + break; + + case 31: +#line 267 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("framewidth", (yyvsp[0].realnum), context); + } +#line 1884 "yy.tab.c" + break; + + case 32: +#line 270 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("fontsize", (yyvsp[0].realnum), context); + } +#line 1892 "yy.tab.c" + break; + + case 33: +#line 273 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("rotation", (yyvsp[0].realnum), context); + } +#line 1900 "yy.tab.c" + break; + + case 34: +#line 276 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("radius", (yyvsp[0].realnum), context); + } +#line 1908 "yy.tab.c" + break; + + case 35: +#line 279 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("diamondratio", (yyvsp[0].realnum), context); + } +#line 1916 "yy.tab.c" + break; + + case 36: +#line 282 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("labeldegree", (yyvsp[0].realnum), context); + } +#line 1924 "yy.tab.c" + break; + + case 37: +#line 285 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_vertex_attribute("vertexsize", (yyvsp[0].realnum), context); + } +#line 1932 "yy.tab.c" + break; + + case 38: +#line 290 "src/core/io/pajek-parser.y" + { context->mode=3; } +#line 1938 "yy.tab.c" + break; + + case 39: +#line 290 "src/core/io/pajek-parser.y" + { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("font", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 1947 "yy.tab.c" + break; + + case 40: +#line 294 "src/core/io/pajek-parser.y" + { context->mode=3; } +#line 1953 "yy.tab.c" + break; + + case 41: +#line 294 "src/core/io/pajek-parser.y" + { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("url", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 1962 "yy.tab.c" + break; + + case 42: +#line 298 "src/core/io/pajek-parser.y" + { context->mode=3; } +#line 1968 "yy.tab.c" + break; + + case 43: +#line 298 "src/core/io/pajek-parser.y" + { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("color", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 1977 "yy.tab.c" + break; + + case 44: +#line 302 "src/core/io/pajek-parser.y" + { context->mode=3; } +#line 1983 "yy.tab.c" + break; + + case 45: +#line 302 "src/core/io/pajek-parser.y" + { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("framecolor", + (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 1993 "yy.tab.c" + break; + + case 46: +#line 307 "src/core/io/pajek-parser.y" + { context->mode=3; } +#line 1999 "yy.tab.c" + break; + + case 47: +#line 307 "src/core/io/pajek-parser.y" + { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("labelcolor", + (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 2009 "yy.tab.c" + break; + + case 48: +#line 314 "src/core/io/pajek-parser.y" + { (yyval.string)=(yyvsp[0].string); } +#line 2015 "yy.tab.c" + break; + + case 55: +#line 318 "src/core/io/pajek-parser.y" + { context->directed=1; } +#line 2021 "yy.tab.c" + break; + + case 56: +#line 319 "src/core/io/pajek-parser.y" + { context->directed=1; } +#line 2027 "yy.tab.c" + break; + + case 60: +#line 324 "src/core/io/pajek-parser.y" + { context->actedge++; + context->mode=2; } +#line 2034 "yy.tab.c" + break; + + case 61: +#line 325 "src/core/io/pajek-parser.y" + { + igraph_vector_push_back(context->vector, (yyvsp[-5].intnum)-1); + igraph_vector_push_back(context->vector, (yyvsp[-4].intnum)-1); } +#line 2042 "yy.tab.c" + break; + + case 64: +#line 334 "src/core/io/pajek-parser.y" + { context->directed=0; } +#line 2048 "yy.tab.c" + break; + + case 65: +#line 335 "src/core/io/pajek-parser.y" + { context->directed=0; } +#line 2054 "yy.tab.c" + break; + + case 69: +#line 340 "src/core/io/pajek-parser.y" + { context->actedge++; + context->mode=2; } +#line 2061 "yy.tab.c" + break; + + case 70: +#line 341 "src/core/io/pajek-parser.y" + { + igraph_vector_push_back(context->vector, (yyvsp[-5].intnum)-1); + igraph_vector_push_back(context->vector, (yyvsp[-4].intnum)-1); } +#line 2069 "yy.tab.c" + break; + + case 74: +#line 350 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("weight", (yyvsp[0].realnum), context); +} +#line 2077 "yy.tab.c" + break; + + case 78: +#line 358 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("color-red", (yyvsp[-2].realnum), context); + igraph_i_pajek_add_numeric_edge_attribute("color-green", (yyvsp[-1].realnum), context); + igraph_i_pajek_add_numeric_edge_attribute("color-blue", (yyvsp[0].realnum), context); + } +#line 2087 "yy.tab.c" + break; + + case 79: +#line 363 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("arrowsize", (yyvsp[0].realnum), context); + } +#line 2095 "yy.tab.c" + break; + + case 80: +#line 366 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("edgewidth", (yyvsp[0].realnum), context); + } +#line 2103 "yy.tab.c" + break; + + case 81: +#line 369 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("hook1", (yyvsp[0].realnum), context); + } +#line 2111 "yy.tab.c" + break; + + case 82: +#line 372 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("hook2", (yyvsp[0].realnum), context); + } +#line 2119 "yy.tab.c" + break; + + case 83: +#line 375 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("angle1", (yyvsp[0].realnum), context); + } +#line 2127 "yy.tab.c" + break; + + case 84: +#line 378 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("angle2", (yyvsp[0].realnum), context); + } +#line 2135 "yy.tab.c" + break; + + case 85: +#line 381 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("velocity1", (yyvsp[0].realnum), context); + } +#line 2143 "yy.tab.c" + break; + + case 86: +#line 384 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("velocity2", (yyvsp[0].realnum), context); + } +#line 2151 "yy.tab.c" + break; + + case 87: +#line 387 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("arrowpos", (yyvsp[0].realnum), context); + } +#line 2159 "yy.tab.c" + break; + + case 88: +#line 390 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("labelpos", (yyvsp[0].realnum), context); + } +#line 2167 "yy.tab.c" + break; + + case 89: +#line 393 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("labelangle", (yyvsp[0].realnum), context); + } +#line 2175 "yy.tab.c" + break; + + case 90: +#line 396 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("labelangle2", (yyvsp[0].realnum), context); + } +#line 2183 "yy.tab.c" + break; + + case 91: +#line 399 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("labeldegree", (yyvsp[0].realnum), context); + } +#line 2191 "yy.tab.c" + break; + + case 92: +#line 402 "src/core/io/pajek-parser.y" + { /* what is this??? */ + igraph_i_pajek_add_numeric_edge_attribute("arrowsize", (yyvsp[0].realnum), context); + } +#line 2199 "yy.tab.c" + break; + + case 93: +#line 405 "src/core/io/pajek-parser.y" + { + igraph_i_pajek_add_numeric_edge_attribute("fontsize", (yyvsp[0].realnum), context); + } +#line 2207 "yy.tab.c" + break; + + case 94: +#line 410 "src/core/io/pajek-parser.y" + { context->mode=4; } +#line 2213 "yy.tab.c" + break; + + case 95: +#line 410 "src/core/io/pajek-parser.y" + { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("arrowtype", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 2222 "yy.tab.c" + break; + + case 96: +#line 414 "src/core/io/pajek-parser.y" + { context->mode=4; } +#line 2228 "yy.tab.c" + break; + + case 97: +#line 414 "src/core/io/pajek-parser.y" + { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("linepattern", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 2237 "yy.tab.c" + break; + + case 98: +#line 418 "src/core/io/pajek-parser.y" + { context->mode=4; } +#line 2243 "yy.tab.c" + break; + + case 99: +#line 418 "src/core/io/pajek-parser.y" + { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("label", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 2252 "yy.tab.c" + break; + + case 100: +#line 422 "src/core/io/pajek-parser.y" + { context->mode=4; } +#line 2258 "yy.tab.c" + break; + + case 101: +#line 422 "src/core/io/pajek-parser.y" + { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("labelcolor", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 2267 "yy.tab.c" + break; + + case 102: +#line 426 "src/core/io/pajek-parser.y" + { context->mode=4; } +#line 2273 "yy.tab.c" + break; + + case 103: +#line 426 "src/core/io/pajek-parser.y" + { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("color", (yyvsp[0].string).str, (yyvsp[0].string).len, context); + } +#line 2282 "yy.tab.c" + break; + + case 104: +#line 432 "src/core/io/pajek-parser.y" + { context->mode=2; (yyval.string)=(yyvsp[0].string); } +#line 2288 "yy.tab.c" + break; + + case 105: +#line 434 "src/core/io/pajek-parser.y" + { context->directed=1; } +#line 2294 "yy.tab.c" + break; + + case 112: +#line 442 "src/core/io/pajek-parser.y" + { context->mode=0; context->actfrom=labs((yyvsp[0].intnum))-1; } +#line 2300 "yy.tab.c" + break; + + case 113: +#line 444 "src/core/io/pajek-parser.y" + { + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, labs((yyvsp[0].intnum))-1); +} +#line 2309 "yy.tab.c" + break; + + case 114: +#line 449 "src/core/io/pajek-parser.y" + { context->directed=0; } +#line 2315 "yy.tab.c" + break; + + case 121: +#line 457 "src/core/io/pajek-parser.y" + { context->mode=0; context->actfrom=labs((yyvsp[0].intnum))-1; } +#line 2321 "yy.tab.c" + break; + + case 122: +#line 459 "src/core/io/pajek-parser.y" + { + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, labs((yyvsp[0].intnum))-1); +} +#line 2330 "yy.tab.c" + break; + + case 124: +#line 468 "src/core/io/pajek-parser.y" + { context->actfrom=0; + context->actto=0; + context->directed=(context->vcount2==0); + } +#line 2339 "yy.tab.c" + break; + + case 127: +#line 475 "src/core/io/pajek-parser.y" + { context->actfrom++; context->actto=0; } +#line 2345 "yy.tab.c" + break; + + case 130: +#line 479 "src/core/io/pajek-parser.y" + { + if ((yyvsp[0].realnum) != 0) { + if (context->vcount2==0) { + context->actedge++; + igraph_i_pajek_add_numeric_edge_attribute("weight", (yyvsp[0].realnum), context); + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, context->actto); + } else if (context->vcount2 + context->actto < context->vcount) { + context->actedge++; + igraph_i_pajek_add_numeric_edge_attribute("weight", (yyvsp[0].realnum), context); + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, + context->vcount2+context->actto); + } + } + context->actto++; +} +#line 2367 "yy.tab.c" + break; + + case 131: +#line 499 "src/core/io/pajek-parser.y" + { (yyval.intnum)=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner), + igraph_pajek_yyget_leng(scanner)); } +#line 2374 "yy.tab.c" + break; + + case 132: +#line 502 "src/core/io/pajek-parser.y" + { (yyval.realnum)=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner), + igraph_pajek_yyget_leng(scanner)); } +#line 2381 "yy.tab.c" + break; + + case 135: +#line 507 "src/core/io/pajek-parser.y" + { (yyval.string).str=igraph_pajek_yyget_text(scanner); + (yyval.string).len=igraph_pajek_yyget_leng(scanner); } +#line 2388 "yy.tab.c" + break; + + case 136: +#line 509 "src/core/io/pajek-parser.y" + { (yyval.string).str=igraph_pajek_yyget_text(scanner); + (yyval.string).len=igraph_pajek_yyget_leng(scanner); } +#line 2395 "yy.tab.c" + break; + + case 137: +#line 511 "src/core/io/pajek-parser.y" + { (yyval.string).str=igraph_pajek_yyget_text(scanner)+1; + (yyval.string).len=igraph_pajek_yyget_leng(scanner)-2; } +#line 2402 "yy.tab.c" + break; + + +#line 2406 "yy.tab.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[+*yyssp], yyvsp, yylsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} +#line 514 "src/core/io/pajek-parser.y" + + +int igraph_pajek_yyerror(YYLTYPE* locp, + igraph_i_pajek_parsedata_t *context, + const char *s) { + snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, + "Parse error in Pajek file, line %i (%s)", + locp->first_line, s); + return 0; +} + +igraph_real_t igraph_pajek_get_number(const char *str, long int length) { + igraph_real_t num; + char *tmp=IGRAPH_CALLOC(length+1, char); + + strncpy(tmp, str, length); + tmp[length]='\0'; + sscanf(tmp, "%lf", &num); + IGRAPH_FREE(tmp); + return num; +} + +/* TODO: NA's */ + +int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, + igraph_vector_ptr_t *attrs, + long int count, + const char *attrname, + igraph_integer_t vid, + igraph_real_t number) { + long int attrsize=igraph_trie_size(names); + long int id; + igraph_vector_t *na; + igraph_attribute_record_t *rec; + + igraph_trie_get(names, attrname, &id); + if (id == attrsize) { + /* add a new attribute */ + rec=IGRAPH_CALLOC(1, igraph_attribute_record_t); + na=IGRAPH_CALLOC(1, igraph_vector_t); + igraph_vector_init(na, count); + rec->name=strdup(attrname); + rec->type=IGRAPH_ATTRIBUTE_NUMERIC; + rec->value=na; + igraph_vector_ptr_push_back(attrs, rec); + } + rec=VECTOR(*attrs)[id]; + na=(igraph_vector_t*)rec->value; + if (igraph_vector_size(na) == vid) { + IGRAPH_CHECK(igraph_vector_push_back(na, number)); + } else if (igraph_vector_size(na) < vid) { + long int origsize=igraph_vector_size(na); + IGRAPH_CHECK(igraph_vector_resize(na, (long int)vid+1)); + for (;origsizename=strdup(attrname); + rec->type=IGRAPH_ATTRIBUTE_STRING; + rec->value=na; + igraph_vector_ptr_push_back(attrs, rec); + } + rec=VECTOR(*attrs)[id]; + na=(igraph_strvector_t*)rec->value; + if (igraph_strvector_size(na) <= vid) { + long int origsize=igraph_strvector_size(na); + IGRAPH_CHECK(igraph_strvector_resize(na, vid+1)); + for (;origsizevertex_attribute_names, + context->vertex_attributes, + context->vcount, + name, context->actvertex-1, + tmp); + + IGRAPH_FREE(tmp); + IGRAPH_FINALLY_CLEAN(1); + + return ret; +} + +int igraph_i_pajek_add_string_edge_attribute(const char *name, + const char *value, + int len, + igraph_i_pajek_parsedata_t *context) { + char *tmp; + int ret; + + tmp=IGRAPH_CALLOC(len+1, char); + if (tmp==0) { + IGRAPH_ERROR("cannot add element to hash table", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, tmp); + strncpy(tmp, value, len); + tmp[len]='\0'; + + ret=igraph_i_pajek_add_string_attribute(context->edge_attribute_names, + context->edge_attributes, + context->actedge, + name, context->actedge-1, + tmp); + + IGRAPH_FREE(tmp); + IGRAPH_FINALLY_CLEAN(1); + + return ret; +} + +int igraph_i_pajek_add_numeric_vertex_attribute(const char *name, + igraph_real_t value, + igraph_i_pajek_parsedata_t *context) { + + return + igraph_i_pajek_add_numeric_attribute(context->vertex_attribute_names, + context->vertex_attributes, + context->vcount, + name, context->actvertex-1, + value); +} + +int igraph_i_pajek_add_numeric_edge_attribute(const char *name, + igraph_real_t value, + igraph_i_pajek_parsedata_t *context) { + + return + igraph_i_pajek_add_numeric_attribute(context->edge_attribute_names, + context->edge_attributes, + context->actedge, + name, context->actedge-1, + value); +} + +int igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context) { + + const char *attrname="type"; + igraph_trie_t *names=context->vertex_attribute_names; + igraph_vector_ptr_t *attrs=context->vertex_attributes; + int i, n=context->vcount, n1=context->vcount2; + long int attrid, attrsize=igraph_trie_size(names); + igraph_attribute_record_t *rec; + igraph_vector_t *na; + + if (n1 > n) { + IGRAPH_ERROR("Invalid number of vertices in bipartite Pajek file", + IGRAPH_PARSEERROR); + } + + igraph_trie_get(names, attrname, &attrid); + if (attrid != attrsize) { + IGRAPH_ERROR("Duplicate 'type' attribute in Pajek file, " + "this should not happen", IGRAPH_EINTERNAL); + } + + /* add a new attribute */ + rec=IGRAPH_CALLOC(1, igraph_attribute_record_t); + na=IGRAPH_CALLOC(1, igraph_vector_t); + igraph_vector_init(na, n); + rec->name=strdup(attrname); + rec->type=IGRAPH_ATTRIBUTE_NUMERIC; + rec->value=na; + igraph_vector_ptr_push_back(attrs, rec); + + for (i=0; ivector; + int i, n1=context->vcount2; + int ne=igraph_vector_size(edges); + + for (i=0; i n1 && v2 > n1) ) { + IGRAPH_WARNING("Invalid edge in bipartite graph"); + } + } + + return 0; +} diff --git a/src/rigraph/core/io/pajek-parser.h b/src/rigraph/core/io/pajek-parser.h new file mode 100644 index 0000000..a6c29aa --- /dev/null +++ b/src/rigraph/core/io/pajek-parser.h @@ -0,0 +1,143 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + 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 + (at your option) 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_IGRAPH_PAJEK_YY_YY_TAB_H_INCLUDED +# define YY_IGRAPH_PAJEK_YY_YY_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int igraph_pajek_yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + NEWLINE = 258, + NUM = 259, + ALNUM = 260, + QSTR = 261, + PSTR = 262, + NETWORKLINE = 263, + VERTICESLINE = 264, + ARCSLINE = 265, + EDGESLINE = 266, + ARCSLISTLINE = 267, + EDGESLISTLINE = 268, + MATRIXLINE = 269, + ERROR = 270, + VP_X_FACT = 271, + VP_Y_FACT = 272, + VP_IC = 273, + VP_BC = 274, + VP_LC = 275, + VP_LR = 276, + VP_LPHI = 277, + VP_BW = 278, + VP_FOS = 279, + VP_PHI = 280, + VP_R = 281, + VP_Q = 282, + VP_LA = 283, + VP_FONT = 284, + VP_URL = 285, + VP_SIZE = 286, + EP_C = 287, + EP_S = 288, + EP_A = 289, + EP_W = 290, + EP_H1 = 291, + EP_H2 = 292, + EP_A1 = 293, + EP_A2 = 294, + EP_K1 = 295, + EP_K2 = 296, + EP_AP = 297, + EP_P = 298, + EP_L = 299, + EP_LP = 300, + EP_LR = 301, + EP_LPHI = 302, + EP_LC = 303, + EP_LA = 304, + EP_SIZE = 305, + EP_FOS = 306 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 115 "src/core/io/pajek-parser.y" + + long int intnum; + double realnum; + struct { + char *str; + int len; + } string; + +#line 118 "yy.tab.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int igraph_pajek_yyparse (igraph_i_pajek_parsedata_t* context); + +#endif /* !YY_IGRAPH_PAJEK_YY_YY_TAB_H_INCLUDED */ diff --git a/src/rigraph/core/io/pajek-parser.y b/src/rigraph/core/io/pajek-parser.y new file mode 100644 index 0000000..e57c9e6 --- /dev/null +++ b/src/rigraph/core/io/pajek-parser.y @@ -0,0 +1,752 @@ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +%{ + +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include +#include +#include + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "igraph_attributes.h" +#include "config.h" + +#include "../core/math.h" +#include "pajek-header.h" +#include "pajek-parser.h" /* it must come first because of YYSTYPE */ +#include "pajek-lexer.h" +//#include "internal/hacks.h" + +int igraph_pajek_yyerror(YYLTYPE* locp, + igraph_i_pajek_parsedata_t *context, + const char *s); + +int igraph_i_pajek_add_string_vertex_attribute(const char *name, + const char *value, + int len, + igraph_i_pajek_parsedata_t *context); +int igraph_i_pajek_add_string_edge_attribute(const char *name, + const char *value, + int len, + igraph_i_pajek_parsedata_t *context); +int igraph_i_pajek_add_numeric_vertex_attribute(const char *name, + igraph_real_t value, + igraph_i_pajek_parsedata_t *context); +int igraph_i_pajek_add_numeric_edge_attribute(const char *name, + igraph_real_t value, + igraph_i_pajek_parsedata_t *context); +int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, + igraph_vector_ptr_t *attrs, + long int count, + const char *attrname, + igraph_integer_t vid, + igraph_real_t number); +int igraph_i_pajek_add_string_attribute(igraph_trie_t *names, + igraph_vector_ptr_t *attrs, + long int count, + const char *attrname, + igraph_integer_t vid, + const char *str); + +int igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context); +int igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t *context); + +extern igraph_real_t igraph_pajek_get_number(const char *str, long int len); +extern long int igraph_i_pajek_actvertex; +extern long int igraph_i_pajek_actedge; + +#define scanner context->scanner + +%} + +%pure-parser +/* bison: do not remove the equals sign; macOS XCode ships with bison 2.3, which + * needs the equals sign */ +%name-prefix="igraph_pajek_yy" +%defines +%locations +%error-verbose +%parse-param { igraph_i_pajek_parsedata_t* context } +%lex-param { void *scanner } + +%union { + long int intnum; + double realnum; + struct { + char *str; + int len; + } string; +} + +%type longint; +%type arcfrom; +%type arcto; +%type edgefrom; +%type edgeto; +%type number; +%type word; +%type vpwordpar; +%type epwordpar; +%type vertex; + +%token NEWLINE +%token NUM +%token ALNUM +%token QSTR +%token PSTR +%token NETWORKLINE +%token VERTICESLINE +%token ARCSLINE +%token EDGESLINE +%token ARCSLISTLINE +%token EDGESLISTLINE +%token MATRIXLINE +%token ERROR + +%token VP_X_FACT +%token VP_Y_FACT +%token VP_IC +%token VP_BC +%token VP_LC +%token VP_LR +%token VP_LPHI +%token VP_BW +%token VP_FOS +%token VP_PHI +%token VP_R +%token VP_Q +%token VP_LA +%token VP_FONT +%token VP_URL +%token VP_SIZE + +%token EP_C +%token EP_S +%token EP_A +%token EP_W +%token EP_H1 +%token EP_H2 +%token EP_A1 +%token EP_A2 +%token EP_K1 +%token EP_K2 +%token EP_AP +%token EP_P +%token EP_L +%token EP_LP +%token EP_LR +%token EP_LPHI +%token EP_LC +%token EP_LA +%token EP_SIZE +%token EP_FOS + +%% + +input: nethead vertices edgeblock { + if (context->vcount2 > 0) { igraph_i_pajek_check_bipartite(context); } + }; + +nethead: /* empty */ | NETWORKLINE words NEWLINE; + +vertices: verticeshead NEWLINE vertdefs; + +verticeshead: VERTICESLINE longint { + context->vcount=$2; + context->vcount2=0; + } + | VERTICESLINE longint longint { + context->vcount=$2; + context->vcount2=$3; + igraph_i_pajek_add_bipartite_type(context); +}; + +vertdefs: /* empty */ | vertdefs vertexline; + +vertexline: NEWLINE | + vertex NEWLINE | + vertex { context->actvertex=$1; } vertexid vertexcoords shape params NEWLINE { } +; + +vertex: longint { $$=$1; context->mode=1; }; + +vertexid: word { + igraph_i_pajek_add_string_vertex_attribute("id", $1.str, $1.len, context); + igraph_i_pajek_add_string_vertex_attribute("name", $1.str, $1.len, context); +}; + +vertexcoords: /* empty */ + | number number { + igraph_i_pajek_add_numeric_vertex_attribute("x", $1, context); + igraph_i_pajek_add_numeric_vertex_attribute("y", $2, context); + } + | number number number { + igraph_i_pajek_add_numeric_vertex_attribute("x", $1, context); + igraph_i_pajek_add_numeric_vertex_attribute("y", $2, context); + igraph_i_pajek_add_numeric_vertex_attribute("z", $3, context); + }; + +shape: /* empty */ | word { + igraph_i_pajek_add_string_vertex_attribute("shape", $1.str, $1.len, context); +}; + +params: /* empty */ | params param; + +param: + vpword + | VP_X_FACT number { + igraph_i_pajek_add_numeric_vertex_attribute("xfact", $2, context); + } + | VP_Y_FACT number { + igraph_i_pajek_add_numeric_vertex_attribute("yfact", $2, context); + } + | VP_IC number number number { /* RGB color */ + igraph_i_pajek_add_numeric_vertex_attribute("color-red", $2, context); + igraph_i_pajek_add_numeric_vertex_attribute("color-green", $3, context); + igraph_i_pajek_add_numeric_vertex_attribute("color-blue", $4, context); + } + | VP_BC number number number { + igraph_i_pajek_add_numeric_vertex_attribute("framecolor-red", $2, context); + igraph_i_pajek_add_numeric_vertex_attribute("framecolor-green", $3, context); + igraph_i_pajek_add_numeric_vertex_attribute("framecolor-blue", $4, context); + } + | VP_LC number number number { + igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-red", $2, context); + igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-green", $3, context); + igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-blue", $4, context); + } + | VP_LR number { + igraph_i_pajek_add_numeric_vertex_attribute("labeldist", $2, context); + } + | VP_LPHI number { + igraph_i_pajek_add_numeric_vertex_attribute("labeldegree2", $2, context); + } + | VP_BW number { + igraph_i_pajek_add_numeric_vertex_attribute("framewidth", $2, context); + } + | VP_FOS number { + igraph_i_pajek_add_numeric_vertex_attribute("fontsize", $2, context); + } + | VP_PHI number { + igraph_i_pajek_add_numeric_vertex_attribute("rotation", $2, context); + } + | VP_R number { + igraph_i_pajek_add_numeric_vertex_attribute("radius", $2, context); + } + | VP_Q number { + igraph_i_pajek_add_numeric_vertex_attribute("diamondratio", $2, context); + } + | VP_LA number { + igraph_i_pajek_add_numeric_vertex_attribute("labeldegree", $2, context); + } + | VP_SIZE number { + igraph_i_pajek_add_numeric_vertex_attribute("vertexsize", $2, context); + } +; + +vpword: VP_FONT { context->mode=3; } vpwordpar { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("font", $3.str, $3.len, context); + } + | VP_URL { context->mode=3; } vpwordpar { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("url", $3.str, $3.len, context); + } + | VP_IC { context->mode=3; } vpwordpar { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("color", $3.str, $3.len, context); + } + | VP_BC { context->mode=3; } vpwordpar { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("framecolor", + $3.str, $3.len, context); + } + | VP_LC { context->mode=3; } vpwordpar { + context->mode=1; + igraph_i_pajek_add_string_vertex_attribute("labelcolor", + $3.str, $3.len, context); + } +; + +vpwordpar: word { $$=$1; }; + +edgeblock: /* empty */ | edgeblock arcs | edgeblock edges | edgeblock arcslist | edgeblock edgeslist | edgeblock adjmatrix; + +arcs: ARCSLINE NEWLINE arcsdefs { context->directed=1; } + | ARCSLINE number NEWLINE arcsdefs { context->directed=1; }; + +arcsdefs: /* empty */ | arcsdefs arcsline; + +arcsline: NEWLINE | + arcfrom arcto { context->actedge++; + context->mode=2; } weight edgeparams NEWLINE { + igraph_vector_push_back(context->vector, $1-1); + igraph_vector_push_back(context->vector, $2-1); } +; + +arcfrom: longint; + +arcto: longint; + +edges: EDGESLINE NEWLINE edgesdefs { context->directed=0; } + | EDGESLINE number NEWLINE edgesdefs { context->directed=0; } + +edgesdefs: /* empty */ | edgesdefs edgesline; + +edgesline: NEWLINE | + edgefrom edgeto { context->actedge++; + context->mode=2; } weight edgeparams NEWLINE { + igraph_vector_push_back(context->vector, $1-1); + igraph_vector_push_back(context->vector, $2-1); } +; + +edgefrom: longint; + +edgeto: longint; + +weight: /* empty */ | number { + igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context); +}; + +edgeparams: /* empty */ | edgeparams edgeparam; + +edgeparam: + epword + | EP_C number number number { + igraph_i_pajek_add_numeric_edge_attribute("color-red", $2, context); + igraph_i_pajek_add_numeric_edge_attribute("color-green", $3, context); + igraph_i_pajek_add_numeric_edge_attribute("color-blue", $4, context); + } + | EP_S number { + igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context); + } + | EP_W number { + igraph_i_pajek_add_numeric_edge_attribute("edgewidth", $2, context); + } + | EP_H1 number { + igraph_i_pajek_add_numeric_edge_attribute("hook1", $2, context); + } + | EP_H2 number { + igraph_i_pajek_add_numeric_edge_attribute("hook2", $2, context); + } + | EP_A1 number { + igraph_i_pajek_add_numeric_edge_attribute("angle1", $2, context); + } + | EP_A2 number { + igraph_i_pajek_add_numeric_edge_attribute("angle2", $2, context); + } + | EP_K1 number { + igraph_i_pajek_add_numeric_edge_attribute("velocity1", $2, context); + } + | EP_K2 number { + igraph_i_pajek_add_numeric_edge_attribute("velocity2", $2, context); + } + | EP_AP number { + igraph_i_pajek_add_numeric_edge_attribute("arrowpos", $2, context); + } + | EP_LP number { + igraph_i_pajek_add_numeric_edge_attribute("labelpos", $2, context); + } + | EP_LR number { + igraph_i_pajek_add_numeric_edge_attribute("labelangle", $2, context); + } + | EP_LPHI number { + igraph_i_pajek_add_numeric_edge_attribute("labelangle2", $2, context); + } + | EP_LA number { + igraph_i_pajek_add_numeric_edge_attribute("labeldegree", $2, context); + } + | EP_SIZE number { /* what is this??? */ + igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context); + } + | EP_FOS number { + igraph_i_pajek_add_numeric_edge_attribute("fontsize", $2, context); + } +; + +epword: EP_A { context->mode=4; } epwordpar { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("arrowtype", $3.str, $3.len, context); + } + | EP_P { context->mode=4; } epwordpar { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("linepattern", $3.str, $3.len, context); + } + | EP_L { context->mode=4; } epwordpar { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("label", $3.str, $3.len, context); + } + | EP_LC { context->mode=4; } epwordpar { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("labelcolor", $3.str, $3.len, context); + } + | EP_C { context->mode=4; } epwordpar { + context->mode=2; + igraph_i_pajek_add_string_edge_attribute("color", $3.str, $3.len, context); + } +; + +epwordpar: word { context->mode=2; $$=$1; }; + +arcslist: ARCSLISTLINE NEWLINE arcslistlines { context->directed=1; }; + +arcslistlines: /* empty */ | arcslistlines arclistline; + +arclistline: NEWLINE | arclistfrom arctolist NEWLINE; + +arctolist: /* empty */ | arctolist arclistto; + +arclistfrom: longint { context->mode=0; context->actfrom=labs($1)-1; }; + +arclistto: longint { + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, labs($1)-1); +}; + +edgeslist: EDGESLISTLINE NEWLINE edgelistlines { context->directed=0; }; + +edgelistlines: /* empty */ | edgelistlines edgelistline; + +edgelistline: NEWLINE | edgelistfrom edgetolist NEWLINE; + +edgetolist: /* empty */ | edgetolist edgelistto; + +edgelistfrom: longint { context->mode=0; context->actfrom=labs($1)-1; }; + +edgelistto: longint { + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, labs($1)-1); +}; + +/* -----------------------------------------------------*/ + +adjmatrix: matrixline NEWLINE adjmatrixlines; + +matrixline: MATRIXLINE { context->actfrom=0; + context->actto=0; + context->directed=(context->vcount2==0); + }; + +adjmatrixlines: /* empty */ | adjmatrixlines adjmatrixline; + +adjmatrixline: adjmatrixnumbers NEWLINE { context->actfrom++; context->actto=0; }; + +adjmatrixnumbers: /* empty */ | adjmatrixentry adjmatrixnumbers; + +adjmatrixentry: number { + if ($1 != 0) { + if (context->vcount2==0) { + context->actedge++; + igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context); + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, context->actto); + } else if (context->vcount2 + context->actto < context->vcount) { + context->actedge++; + igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context); + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, + context->vcount2+context->actto); + } + } + context->actto++; +}; + +/* -----------------------------------------------------*/ + +longint: NUM { $$=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner), + igraph_pajek_yyget_leng(scanner)); }; + +number: NUM { $$=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner), + igraph_pajek_yyget_leng(scanner)); }; + +words: /* empty */ | words word; + +word: ALNUM { $$.str=igraph_pajek_yyget_text(scanner); + $$.len=igraph_pajek_yyget_leng(scanner); } + | NUM { $$.str=igraph_pajek_yyget_text(scanner); + $$.len=igraph_pajek_yyget_leng(scanner); } + | QSTR { $$.str=igraph_pajek_yyget_text(scanner)+1; + $$.len=igraph_pajek_yyget_leng(scanner)-2; }; + +%% + +int igraph_pajek_yyerror(YYLTYPE* locp, + igraph_i_pajek_parsedata_t *context, + const char *s) { + snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, + "Parse error in Pajek file, line %i (%s)", + locp->first_line, s); + return 0; +} + +igraph_real_t igraph_pajek_get_number(const char *str, long int length) { + igraph_real_t num; + char *tmp=IGRAPH_CALLOC(length+1, char); + + strncpy(tmp, str, length); + tmp[length]='\0'; + sscanf(tmp, "%lf", &num); + IGRAPH_FREE(tmp); + return num; +} + +/* TODO: NA's */ + +int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, + igraph_vector_ptr_t *attrs, + long int count, + const char *attrname, + igraph_integer_t vid, + igraph_real_t number) { + long int attrsize=igraph_trie_size(names); + long int id; + igraph_vector_t *na; + igraph_attribute_record_t *rec; + + igraph_trie_get(names, attrname, &id); + if (id == attrsize) { + /* add a new attribute */ + rec=IGRAPH_CALLOC(1, igraph_attribute_record_t); + na=IGRAPH_CALLOC(1, igraph_vector_t); + igraph_vector_init(na, count); + rec->name=strdup(attrname); + rec->type=IGRAPH_ATTRIBUTE_NUMERIC; + rec->value=na; + igraph_vector_ptr_push_back(attrs, rec); + } + rec=VECTOR(*attrs)[id]; + na=(igraph_vector_t*)rec->value; + if (igraph_vector_size(na) == vid) { + IGRAPH_CHECK(igraph_vector_push_back(na, number)); + } else if (igraph_vector_size(na) < vid) { + long int origsize=igraph_vector_size(na); + IGRAPH_CHECK(igraph_vector_resize(na, (long int)vid+1)); + for (;origsizename=strdup(attrname); + rec->type=IGRAPH_ATTRIBUTE_STRING; + rec->value=na; + igraph_vector_ptr_push_back(attrs, rec); + } + rec=VECTOR(*attrs)[id]; + na=(igraph_strvector_t*)rec->value; + if (igraph_strvector_size(na) <= vid) { + long int origsize=igraph_strvector_size(na); + IGRAPH_CHECK(igraph_strvector_resize(na, vid+1)); + for (;origsizevertex_attribute_names, + context->vertex_attributes, + context->vcount, + name, context->actvertex-1, + tmp); + + IGRAPH_FREE(tmp); + IGRAPH_FINALLY_CLEAN(1); + + return ret; +} + +int igraph_i_pajek_add_string_edge_attribute(const char *name, + const char *value, + int len, + igraph_i_pajek_parsedata_t *context) { + char *tmp; + int ret; + + tmp=IGRAPH_CALLOC(len+1, char); + if (tmp==0) { + IGRAPH_ERROR("cannot add element to hash table", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, tmp); + strncpy(tmp, value, len); + tmp[len]='\0'; + + ret=igraph_i_pajek_add_string_attribute(context->edge_attribute_names, + context->edge_attributes, + context->actedge, + name, context->actedge-1, + tmp); + + IGRAPH_FREE(tmp); + IGRAPH_FINALLY_CLEAN(1); + + return ret; +} + +int igraph_i_pajek_add_numeric_vertex_attribute(const char *name, + igraph_real_t value, + igraph_i_pajek_parsedata_t *context) { + + return + igraph_i_pajek_add_numeric_attribute(context->vertex_attribute_names, + context->vertex_attributes, + context->vcount, + name, context->actvertex-1, + value); +} + +int igraph_i_pajek_add_numeric_edge_attribute(const char *name, + igraph_real_t value, + igraph_i_pajek_parsedata_t *context) { + + return + igraph_i_pajek_add_numeric_attribute(context->edge_attribute_names, + context->edge_attributes, + context->actedge, + name, context->actedge-1, + value); +} + +int igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context) { + + const char *attrname="type"; + igraph_trie_t *names=context->vertex_attribute_names; + igraph_vector_ptr_t *attrs=context->vertex_attributes; + int i, n=context->vcount, n1=context->vcount2; + long int attrid, attrsize=igraph_trie_size(names); + igraph_attribute_record_t *rec; + igraph_vector_t *na; + + if (n1 > n) { + IGRAPH_ERROR("Invalid number of vertices in bipartite Pajek file", + IGRAPH_PARSEERROR); + } + + igraph_trie_get(names, attrname, &attrid); + if (attrid != attrsize) { + IGRAPH_ERROR("Duplicate 'type' attribute in Pajek file, " + "this should not happen", IGRAPH_EINTERNAL); + } + + /* add a new attribute */ + rec=IGRAPH_CALLOC(1, igraph_attribute_record_t); + na=IGRAPH_CALLOC(1, igraph_vector_t); + igraph_vector_init(na, n); + rec->name=strdup(attrname); + rec->type=IGRAPH_ATTRIBUTE_NUMERIC; + rec->value=na; + igraph_vector_ptr_push_back(attrs, rec); + + for (i=0; ivector; + int i, n1=context->vcount2; + int ne=igraph_vector_size(edges); + + for (i=0; i n1 && v2 > n1) ) { + IGRAPH_WARNING("Invalid edge in bipartite graph"); + } + } + + return 0; +} diff --git a/src/rigraph/core/io/pajek.c b/src/rigraph/core/io/pajek.c new file mode 100644 index 0000000..998352b --- /dev/null +++ b/src/rigraph/core/io/pajek.c @@ -0,0 +1,771 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "../graph/attributes.h" + +#include "pajek-header.h" + +#include +#include + +int igraph_pajek_yylex_init_extra(igraph_i_pajek_parsedata_t* user_defined, + void* scanner); +void igraph_pajek_yylex_destroy (void *scanner ); +int igraph_pajek_yyparse (igraph_i_pajek_parsedata_t* context); +void igraph_pajek_yyset_in (FILE * in_str, void* yyscanner ); + +/** + * \function igraph_read_graph_pajek + * \brief Reads a file in Pajek format + * + * \param graph Pointer to an uninitialized graph object. + * \param file An already opened file handler. + * \return Error code. + * + * + * Only a subset of the Pajek format is implemented. This is partially + * because this format is not very well documented, but also because + * igraph does not support some Pajek features, like + * multigraphs. + * + * + * Starting from version 0.6.1 igraph reads bipartite (two-mode) + * graphs from Pajek files and add the \c type vertex attribute for them. + * Warnings are given for invalid edges, i.e. edges connecting + * vertices of the same type. + * + * + * The list of the current limitations: + * \olist + * \oli Only .net files are supported, Pajek + * project files (.paj) are not. These might be + * supported in the future if there is need for it. + * \oli Time events networks are not supported. + * \oli Hypergraphs (i.e. graphs with non-binary edges) are not + * supported. + * \oli Graphs with both directed and non-directed edges are not + * supported, are they cannot be represented in + * igraph. + * \oli Only Pajek networks are supported, permutations, hierarchies, + * clusters and vectors are not. + * \oli Graphs with multiple edge sets are not supported. + * \endolist + * + * + * If there are attribute handlers installed, + * igraph also reads the vertex and edge attributes + * from the file. Most attributes are renamed to be more informative: + * \c color instead of \c c, \c xfact instead of \c x_fact, + * \c yfact instead of y_fact, \c labeldist instead of \c lr, + * \c labeldegree2 instead of \c lphi, \c framewidth instead of \c bw, + * \c fontsize + * instead of \c fos, \c rotation instead of \c phi, \c radius instead + * of \c r, + * \c diamondratio instead of \c q, \c labeldegree instead of \c la, + * \c vertexsize + * instead of \c size, \c color instead of \c ic, \c framecolor instead of + * \c bc, \c labelcolor instead of \c lc, these belong to vertices. + * + * + * Edge attributes are also renamed, \c s to \c arrowsize, \c w + * to \c edgewidth, \c h1 to \c hook1, \c h2 to \c hook2, + * \c a1 to \c angle1, \c a2 to \c angle2, \c k1 to + * \c velocity1, \c k2 to \c velocity2, \c ap to \c + * arrowpos, \c lp to \c labelpos, \c lr to + * \c labelangle, \c lphi to \c labelangle2, \c la to \c + * labeldegree, \c fos to + * \c fontsize, \c a to \c arrowtype, \c p to \c + * linepattern, \c l to \c label, \c lc to + * \c labelcolor, \c c to \c color. + * + * + * In addition the following vertex attributes might be added: \c id + * if there are vertex ids in the file, \c x and \c y or \c x + * and \c y and \c z if there are vertex coordinates in the file. + * + * The \c weight edge attribute might be + * added if there are edge weights present. + * + * + * See the pajek homepage: + * http://vlado.fmf.uni-lj.si/pub/networks/pajek/ for more info on + * Pajek and the Pajek manual: + * http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/pajekman.pdf for + * information on the Pajek file format. + * + * + * Time complexity: O(|V|+|E|+|A|), |V| is the number of vertices, |E| + * the number of edges, |A| the number of attributes (vertex + edge) + * in the graph if there are attribute handlers installed. + * + * \sa \ref igraph_write_graph_pajek() for writing Pajek files, \ref + * igraph_read_graph_graphml() for reading GraphML files. + * + * \example examples/simple/foreign.c + */ + +int igraph_read_graph_pajek(igraph_t *graph, FILE *instream) { + + igraph_vector_t edges; + igraph_trie_t vattrnames; + igraph_vector_ptr_t vattrs; + igraph_trie_t eattrnames; + igraph_vector_ptr_t eattrs; + long int i, j; + igraph_i_pajek_parsedata_t context; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + IGRAPH_TRIE_INIT_FINALLY(&vattrnames, 1); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&vattrs, 0); + IGRAPH_TRIE_INIT_FINALLY(&eattrnames, 1); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&eattrs, 0); + + context.vector = &edges; + context.mode = 0; + context.vcount = -1; + context.vertexid = 0; + context.vertex_attribute_names = &vattrnames; + context.vertex_attributes = &vattrs; + context.edge_attribute_names = &eattrnames; + context.edge_attributes = &eattrs; + context.actedge = 0; + context.eof = 0; + + igraph_pajek_yylex_init_extra(&context, &context.scanner); + IGRAPH_FINALLY(igraph_pajek_yylex_destroy, context.scanner); + + igraph_pajek_yyset_in(instream, context.scanner); + + if (igraph_pajek_yyparse(&context)) { + if (context.errmsg[0] != 0) { + IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else { + IGRAPH_ERROR("Cannot read Pajek file", IGRAPH_PARSEERROR); + } + } + + if (context.vcount < 0) { + IGRAPH_ERROR("invalid vertex count in Pajek file", IGRAPH_EINVAL); + } + if (context.vcount2 < 0) { + IGRAPH_ERROR("invalid 2-mode vertex count in Pajek file", IGRAPH_EINVAL); + } + + for (i = 0; i < igraph_vector_ptr_size(&eattrs); i++) { + igraph_attribute_record_t *rec = VECTOR(eattrs)[i]; + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *vec = (igraph_vector_t*)rec->value; + long int origsize = igraph_vector_size(vec); + igraph_vector_resize(vec, context.actedge); + for (j = origsize; j < context.actedge; j++) { + VECTOR(*vec)[j] = IGRAPH_NAN; + } + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *strvec = (igraph_strvector_t*)rec->value; + long int origsize = igraph_strvector_size(strvec); + igraph_strvector_resize(strvec, context.actedge); + for (j = origsize; j < context.actedge; j++) { + igraph_strvector_set(strvec, j, ""); + } + } + } + + IGRAPH_CHECK(igraph_empty(graph, 0, context.directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + IGRAPH_CHECK(igraph_add_vertices(graph, context.vcount, &vattrs)); + IGRAPH_CHECK(igraph_add_edges(graph, &edges, &eattrs)); + + for (i = 0; i < igraph_vector_ptr_size(&vattrs); i++) { + igraph_attribute_record_t *rec = VECTOR(vattrs)[i]; + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *vec = (igraph_vector_t*) rec->value; + igraph_vector_destroy(vec); + IGRAPH_FREE(vec); + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *strvec = (igraph_strvector_t *)rec->value; + igraph_strvector_destroy(strvec); + IGRAPH_FREE(strvec); + } + igraph_free( (char*)(rec->name)); + IGRAPH_FREE(rec); + } + + for (i = 0; i < igraph_vector_ptr_size(&eattrs); i++) { + igraph_attribute_record_t *rec = VECTOR(eattrs)[i]; + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *vec = (igraph_vector_t*) rec->value; + igraph_vector_destroy(vec); + IGRAPH_FREE(vec); + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *strvec = (igraph_strvector_t *)rec->value; + igraph_strvector_destroy(strvec); + IGRAPH_FREE(strvec); + } + igraph_free( (char*)(rec->name)); + IGRAPH_FREE(rec); + } + + igraph_vector_destroy(&edges); + igraph_vector_ptr_destroy(&eattrs); + igraph_trie_destroy(&eattrnames); + igraph_vector_ptr_destroy(&vattrs); + igraph_trie_destroy(&vattrnames); + igraph_pajek_yylex_destroy(context.scanner); + + IGRAPH_FINALLY_CLEAN(7); + return 0; +} + +/* Order matters here! */ +#define V_ID 0 +#define V_X 1 +#define V_Y 2 +#define V_Z 3 +#define V_SHAPE 4 +#define V_XFACT 5 +#define V_YFACT 6 +#define V_COLOR_RED 7 +#define V_COLOR_GREEN 8 +#define V_COLOR_BLUE 9 +#define V_FRAMECOLOR_RED 10 +#define V_FRAMECOLOR_GREEN 11 +#define V_FRAMECOLOR_BLUE 12 +#define V_LABELCOLOR_RED 13 +#define V_LABELCOLOR_GREEN 14 +#define V_LABELCOLOR_BLUE 15 +#define V_LABELDIST 16 +#define V_LABELDEGREE2 17 +#define V_FRAMEWIDTH 18 +#define V_FONTSIZE 19 +#define V_ROTATION 20 +#define V_RADIUS 21 +#define V_DIAMONDRATIO 22 +#define V_LABELDEGREE 23 +#define V_VERTEXSIZE 24 +#define V_FONT 25 +#define V_URL 26 +#define V_COLOR 27 +#define V_FRAMECOLOR 28 +#define V_LABELCOLOR 29 +#define V_LAST 30 + +#define E_WEIGHT 0 +#define E_COLOR_RED 1 +#define E_COLOR_GREEN 2 +#define E_COLOR_BLUE 3 +#define E_ARROWSIZE 4 +#define E_EDGEWIDTH 5 +#define E_HOOK1 6 +#define E_HOOK2 7 +#define E_ANGLE1 8 +#define E_ANGLE2 9 +#define E_VELOCITY1 10 +#define E_VELOCITY2 11 +#define E_ARROWPOS 12 +#define E_LABELPOS 13 +#define E_LABELANGLE 14 +#define E_LABELANGLE2 15 +#define E_LABELDEGREE 16 +#define E_FONTSIZE 17 +#define E_ARROWTYPE 18 +#define E_LINEPATTERN 19 +#define E_LABEL 20 +#define E_LABELCOLOR 21 +#define E_COLOR 22 +#define E_LAST 23 + +static int igraph_i_pajek_escape(char* src, char** dest) { + long int destlen = 0; + igraph_bool_t need_escape = 0; + + /* Determine whether the string contains characters to be escaped */ + char *s, *d; + for (s = src; *s; s++, destlen++) { + if (*s == '\\') { + need_escape = 1; + destlen++; + } else if (*s == '"') { + need_escape = 1; + destlen++; + } else if (!isalnum(*s)) { + need_escape = 1; + } + } + + if (!need_escape) { + /* At this point, we know that the string does not contain any chars + * that would warrant escaping. Therefore, we simply quote it and + * return the quoted string. This is necessary because Pajek uses some + * reserved words in its format (like 'c' standing for color) and they + * have to be quoted as well. + */ + *dest = IGRAPH_CALLOC(destlen + 3, char); + if (!*dest) { + IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); + } + + d = *dest; + strcpy(d + 1, src); + d[0] = d[destlen + 1] = '"'; + d[destlen + 2] = 0; + return IGRAPH_SUCCESS; + } + + *dest = IGRAPH_CALLOC(destlen + 3, char); + if (!*dest) { + IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); + } + + d = *dest; + *d = '"'; d++; + + for (s = src; *s; s++, d++) { + switch (*s) { + case '\\': + case '"': + *d = '\\'; d++; + *d = *s; + break; + default: + *d = *s; + } + } + *d = '"'; d++; *d = 0; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_write_graph_pajek + * \brief Writes a graph to a file in Pajek format. + * + * + * The Pajek vertex and edge parameters (like color) are determined by + * the attributes of the vertices and edges, of course this requires + * an attribute handler to be installed. The names of the + * corresponding vertex and edge attributes are listed at \ref + * igraph_read_graph_pajek(), e.g. the \c color vertex attributes + * determines the color (\c c in Pajek) parameter. + * + * + * As of version 0.6.1 igraph writes bipartite graphs into Pajek files + * correctly, i.e. they will be also bipartite when read into Pajek. + * As Pajek is less flexible for bipartite graphs (the numeric IDs of + * the vertices must be sorted according to vertex type), igraph might + * need to reorder the vertices when writing a bipartite Pajek file. + * This effectively means that numeric vertex IDs usually change when + * a bipartite graph is written to a Pajek file, and then read back + * into igraph. + * + * + * Early versions of Pajek supported only Windows-style line endings + * in Pajek files, but recent versions support both Windows and Unix + * line endings. igraph therefore uses the platform-native line endings + * when the input file is opened in text mode, and uses Unix-style + * line endings when the input file is opened in binary mode. If you + * are using an old version of Pajek, you are on Unix and you are having + * problems reading files written by igraph on a Windows machine, convert the + * line endings manually with a text editor or with \c unix2dos or \c iconv + * from the command line). + * + * \param graph The graph object to write. + * \param outstream The file to write to. It should be opened and + * writable. Make sure that you open the file in binary format if you use MS Windows, + * otherwise end of line characters will be messed up. (igraph will be able + * to read back these messed up files, but Pajek won't.) + * \return Error code. + * + * Time complexity: O(|V|+|E|+|A|), |V| is the number of vertices, |E| + * is the number of edges, |A| the number of attributes (vertex + + * edge) in the graph if there are attribute handlers installed. + * + * \sa \ref igraph_read_graph_pajek() for reading Pajek graphs, \ref + * igraph_write_graph_graphml() for writing a graph in GraphML format, + * this suites igraph graphs better. + * + * \example examples/simple/igraph_write_graph_pajek.c + */ + +int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) { + long int no_of_nodes = igraph_vcount(graph); + long int i, j; + + igraph_attribute_type_t vtypes[V_LAST], etypes[E_LAST]; + igraph_bool_t write_vertex_attrs = 0; + + /* Same order as the #define's */ + const char *vnames[] = { "id", "x", "y", "z", "shape", "xfact", "yfact", + "", "", "", "", "", "", "", "", "", + "labeldist", "labeldegree2", "framewidth", + "fontsize", "rotation", "radius", + "diamondratio", "labeldegree", "vertexsize", + "font", "url", "color", "framecolor", + "labelcolor" + }; + + const char *vnumnames[] = { "xfact", "yfact", "labeldist", + "labeldegree2", "framewidth", "fontsize", + "rotation", "radius", "diamondratio", + "labeldegree", "vertexsize" + }; + const char *vnumnames2[] = { "x_fact", "y_fact", "lr", "lphi", "bw", + "fos", "phi", "r", "q", "la", "size" + }; + const char *vstrnames[] = { "font", "url", "color", "framecolor", + "labelcolor" + }; + const char *vstrnames2[] = { "font", "url", "ic", "bc", "lc" }; + + const char *enames[] = { "weight", "", "", "", + "arrowsize", "edgewidth", "hook1", "hook2", + "angle1", "angle2", "velocity1", "velocity2", + "arrowpos", "labelpos", "labelangle", + "labelangle2", "labeldegree", "fontsize", + "arrowtype", "linepattern", "label", "labelcolor", + "color" + }; + const char *enumnames[] = { "arrowsize", "edgewidth", "hook1", "hook2", + "angle1", "angle2", "velocity1", "velocity2", + "arrowpos", "labelpos", "labelangle", + "labelangle2", "labeldegree", "fontsize" + }; + const char *enumnames2[] = { "s", "w", "h1", "h2", "a1", "a2", "k1", "k2", + "ap", "lp", "lr", "lphi", "la", "fos" + }; + const char *estrnames[] = { "arrowtype", "linepattern", "label", + "labelcolor", "color" + }; + const char *estrnames2[] = { "a", "p", "l", "lc", "c" }; + + /* Newer Pajek versions support both Unix and Windows-style line endings, + * so we just use Unix style. This will get converted to CRLF on Windows + * when the file is opened in text mode */ + const char *newline = "\n"; + + igraph_es_t es; + igraph_eit_t eit; + + igraph_vector_t numv; + igraph_strvector_t strv; + + igraph_vector_t ex_numa; + igraph_vector_t ex_stra; + igraph_vector_t vx_numa; + igraph_vector_t vx_stra; + + char *s, *escaped; + + igraph_bool_t bipartite = 0; + igraph_vector_int_t bip_index, bip_index2; + igraph_vector_bool_t bvec; + long int notop = 0, nobottom = 0; + + IGRAPH_VECTOR_INIT_FINALLY(&numv, 1); + IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1); + + IGRAPH_VECTOR_INIT_FINALLY(&ex_numa, 0); + IGRAPH_VECTOR_INIT_FINALLY(&ex_stra, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vx_numa, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vx_stra, 0); + + /* Check if graph is bipartite */ + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, "type")) { + igraph_attribute_type_t type_type; + igraph_i_attribute_gettype(graph, &type_type, IGRAPH_ATTRIBUTE_VERTEX, + "type"); + if (type_type == IGRAPH_ATTRIBUTE_BOOLEAN) { + int bptr = 0, tptr = 0; + bipartite = 1; write_vertex_attrs = 1; + /* Count top and bottom vertices, we go over them twice, + because we want to keep their original order */ + IGRAPH_CHECK(igraph_vector_int_init(&bip_index, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &bip_index); + IGRAPH_CHECK(igraph_vector_int_init(&bip_index2, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &bip_index2); + IGRAPH_CHECK(igraph_vector_bool_init(&bvec, 1)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &bvec); + for (i = 0; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph, + "type", igraph_vss_1((igraph_integer_t) i), &bvec)); + if (VECTOR(bvec)[0]) { + notop++; + } else { + nobottom++; + } + } + for (i = 0, bptr = 0, tptr = (int) nobottom; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph, + "type", igraph_vss_1((igraph_integer_t) i), &bvec)); + if (VECTOR(bvec)[0]) { + VECTOR(bip_index)[tptr] = (int) i; + VECTOR(bip_index2)[i] = tptr; + tptr++; + } else { + VECTOR(bip_index)[bptr] = (int) i; + VECTOR(bip_index2)[i] = bptr; + bptr++; + } + } + igraph_vector_bool_destroy(&bvec); + IGRAPH_FINALLY_CLEAN(1); + } + } + + /* Write header */ + if (bipartite) { + if (fprintf(outstream, "*Vertices %li %li%s", no_of_nodes, nobottom, + newline) < 0) { + IGRAPH_ERROR("Cannot write pajek file", IGRAPH_EFILE); + } + } else { + if (fprintf(outstream, "*Vertices %li%s", no_of_nodes, newline) < 0) { + IGRAPH_ERROR("Cannot write pajek file", IGRAPH_EFILE); + } + } + + /* Check the vertex attributes */ + memset(vtypes, 0, sizeof(vtypes[0])*V_LAST); + for (i = 0; i < V_LAST; i++) { + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, + vnames[i])) { + igraph_i_attribute_gettype(graph, &vtypes[i], IGRAPH_ATTRIBUTE_VERTEX, + vnames[i]); + write_vertex_attrs = 1; + } else { + vtypes[i] = (igraph_attribute_type_t) -1; + } + } + for (i = 0; i < (long int) (sizeof(vnumnames) / sizeof(const char*)); i++) { + igraph_attribute_type_t type; + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, + vnumnames[i])) { + igraph_i_attribute_gettype(graph, &type, IGRAPH_ATTRIBUTE_VERTEX, + vnumnames[i]); + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_vector_push_back(&vx_numa, i)); + } + } + } + for (i = 0; i < (long int) (sizeof(vstrnames) / sizeof(const char*)); i++) { + igraph_attribute_type_t type; + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, + vstrnames[i])) { + igraph_i_attribute_gettype(graph, &type, IGRAPH_ATTRIBUTE_VERTEX, + vstrnames[i]); + if (type == IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_CHECK(igraph_vector_push_back(&vx_stra, i)); + } + } + } + + /* Write vertices */ + if (write_vertex_attrs) { + for (i = 0; i < no_of_nodes; i++) { + long int id = bipartite ? VECTOR(bip_index)[i] : i; + + /* vertex id */ + fprintf(outstream, "%li", i + 1); + if (vtypes[V_ID] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_i_attribute_get_numeric_vertex_attr(graph, vnames[V_ID], + igraph_vss_1((igraph_integer_t) id), &numv); + fputs(" \"", outstream); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + fputc('"', outstream); + } else if (vtypes[V_ID] == IGRAPH_ATTRIBUTE_STRING) { + igraph_i_attribute_get_string_vertex_attr(graph, vnames[V_ID], + igraph_vss_1((igraph_integer_t) id), &strv); + igraph_strvector_get(&strv, 0, &s); + IGRAPH_CHECK(igraph_i_pajek_escape(s, &escaped)); + fprintf(outstream, " %s", escaped); + IGRAPH_FREE(escaped); + } else { + fprintf(outstream, " \"%li\"", id + 1); + } + + /* coordinates */ + if (vtypes[V_X] == IGRAPH_ATTRIBUTE_NUMERIC && + vtypes[V_Y] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_i_attribute_get_numeric_vertex_attr(graph, vnames[V_X], + igraph_vss_1((igraph_integer_t) id), &numv); + fputc(' ', outstream); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + igraph_i_attribute_get_numeric_vertex_attr(graph, vnames[V_Y], + igraph_vss_1((igraph_integer_t) id), &numv); + fputc(' ', outstream); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + if (vtypes[V_Z] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_i_attribute_get_numeric_vertex_attr(graph, vnames[V_Z], + igraph_vss_1((igraph_integer_t) id), &numv); + fputc(' ', outstream); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + } + } + + /* shape */ + if (vtypes[V_SHAPE] == IGRAPH_ATTRIBUTE_STRING) { + igraph_i_attribute_get_string_vertex_attr(graph, vnames[V_SHAPE], + igraph_vss_1((igraph_integer_t) id), &strv); + igraph_strvector_get(&strv, 0, &s); + IGRAPH_CHECK(igraph_i_pajek_escape(s, &escaped)); + fprintf(outstream, " %s", escaped); + IGRAPH_FREE(escaped); + } + + /* numeric parameters */ + for (j = 0; j < igraph_vector_size(&vx_numa); j++) { + int idx = (int) VECTOR(vx_numa)[j]; + igraph_i_attribute_get_numeric_vertex_attr(graph, vnumnames[idx], + igraph_vss_1((igraph_integer_t) id), &numv); + fprintf(outstream, " %s ", vnumnames2[idx]); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + } + + /* string parameters */ + for (j = 0; j < igraph_vector_size(&vx_stra); j++) { + int idx = (int) VECTOR(vx_stra)[j]; + igraph_i_attribute_get_string_vertex_attr(graph, vstrnames[idx], + igraph_vss_1((igraph_integer_t) id), &strv); + igraph_strvector_get(&strv, 0, &s); + IGRAPH_CHECK(igraph_i_pajek_escape(s, &escaped)); + fprintf(outstream, " %s %s", vstrnames2[idx], escaped); + IGRAPH_FREE(escaped); + } + + /* trailing newline */ + fprintf(outstream, "%s", newline); + } + } + + /* edges header */ + if (igraph_is_directed(graph)) { + fprintf(outstream, "*Arcs%s", newline); + } else { + fprintf(outstream, "*Edges%s", newline); + } + + IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_ID)); + IGRAPH_FINALLY(igraph_es_destroy, &es); + IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + /* Check edge attributes */ + for (i = 0; i < E_LAST; i++) { + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, + enames[i])) { + igraph_i_attribute_gettype(graph, &etypes[i], IGRAPH_ATTRIBUTE_EDGE, + enames[i]); + } else { + etypes[i] = (igraph_attribute_type_t) -1; + } + } + for (i = 0; i < (long int) (sizeof(enumnames) / sizeof(const char*)); i++) { + igraph_attribute_type_t type; + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, + enumnames[i])) { + igraph_i_attribute_gettype(graph, &type, IGRAPH_ATTRIBUTE_EDGE, + enumnames[i]); + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_vector_push_back(&ex_numa, i)); + } + } + } + for (i = 0; i < (long int) (sizeof(estrnames) / sizeof(const char*)); i++) { + igraph_attribute_type_t type; + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, + estrnames[i])) { + igraph_i_attribute_gettype(graph, &type, IGRAPH_ATTRIBUTE_EDGE, + estrnames[i]); + if (type == IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_CHECK(igraph_vector_push_back(&ex_stra, i)); + } + } + } + + for (i = 0; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit), i++) { + long int edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from, to; + igraph_edge(graph, (igraph_integer_t) edge, &from, &to); + if (bipartite) { + from = VECTOR(bip_index2)[from]; + to = VECTOR(bip_index2)[to]; + } + fprintf(outstream, "%li %li", (long int) from + 1, (long int) to + 1); + + /* Weights */ + if (etypes[E_WEIGHT] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_i_attribute_get_numeric_edge_attr(graph, enames[E_WEIGHT], + igraph_ess_1((igraph_integer_t) edge), &numv); + fputc(' ', outstream); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + } + + /* numeric parameters */ + for (j = 0; j < igraph_vector_size(&ex_numa); j++) { + int idx = (int) VECTOR(ex_numa)[j]; + igraph_i_attribute_get_numeric_edge_attr(graph, enumnames[idx], + igraph_ess_1((igraph_integer_t) edge), &numv); + fprintf(outstream, " %s ", enumnames2[idx]); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + } + + /* string parameters */ + for (j = 0; j < igraph_vector_size(&ex_stra); j++) { + int idx = (int) VECTOR(ex_stra)[j]; + igraph_i_attribute_get_string_edge_attr(graph, estrnames[idx], + igraph_ess_1((igraph_integer_t) edge), &strv); + igraph_strvector_get(&strv, 0, &s); + IGRAPH_CHECK(igraph_i_pajek_escape(s, &escaped)); + fprintf(outstream, " %s %s", estrnames2[idx], escaped); + IGRAPH_FREE(escaped); + } + + /* trailing newline */ + fprintf(outstream, "%s", newline); + } + + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + IGRAPH_FINALLY_CLEAN(2); + + if (bipartite) { + igraph_vector_int_destroy(&bip_index2); + igraph_vector_int_destroy(&bip_index); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_vector_destroy(&ex_numa); + igraph_vector_destroy(&ex_stra); + igraph_vector_destroy(&vx_numa); + igraph_vector_destroy(&vx_stra); + igraph_strvector_destroy(&strv); + igraph_vector_destroy(&numv); + IGRAPH_FINALLY_CLEAN(6); + return 0; +} diff --git a/src/rigraph/core/isomorphism/bliss.cc b/src/rigraph/core/isomorphism/bliss.cc new file mode 100644 index 0000000..f7611a2 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss.cc @@ -0,0 +1,608 @@ +/* + Copyright (C) 2003-2006 Tommi Junttila + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ + +#include "bliss/graph.hh" + +#include "igraph_topology.h" +#include "igraph_conversion.h" +#include "igraph_interface.h" +#include "igraph_interrupt.h" +#include "igraph_memory.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" + +#include "../core/exceptions.h" + +using namespace bliss; +using namespace std; + +/** + * \section about_bliss + * + * + * Bliss is a successor of the famous NAUTY algorithm and + * implementation. While using the same ideas in general, with better + * heuristics and data structures Bliss outperforms NAUTY on most + * graphs. + * + * + * + * Bliss was developed and implemented by Tommi Junttila and Petteri Kaski at + * Helsinki University of Technology, Finland. For more information, + * see the Bliss homepage at https://users.aalto.fi/~tjunttil/bliss/ and the following + * publication: + * + * + * + * Tommi Junttila and Petteri Kaski: "Engineering an Efficient Canonical Labeling + * Tool for Large and Sparse Graphs" In ALENEX 2007, pages 135–149, 2007 + * https://doi.org/10.1137/1.9781611972870.13 + * + * + * + * Tommi Junttila and Petteri Kaski: "Conflict Propagation and Component Recursion + * for Canonical Labeling" in TAPAS 2011, pages 151–162, 2011. + * https://doi.org/10.1007/978-3-642-19754-3_16 + * + * + * + * Bliss works with both directed graphs and undirected graphs. It supports graphs with + * self-loops, but not graphs with multi-edges. + * + * + * + * Bliss version 0.75 is included in igraph. + * + */ + +namespace { // unnamed namespace + +inline AbstractGraph *bliss_from_igraph(const igraph_t *graph) { + unsigned int nof_vertices = (unsigned int)igraph_vcount(graph); + unsigned int nof_edges = (unsigned int)igraph_ecount(graph); + + AbstractGraph *g; + + if (igraph_is_directed(graph)) { + g = new Digraph(nof_vertices); + } else { + g = new Graph(nof_vertices); + } + + /* g->set_verbose_level(0); */ + + for (unsigned int i = 0; i < nof_edges; i++) { + g->add_edge((unsigned int)IGRAPH_FROM(graph, i), (unsigned int)IGRAPH_TO(graph, i)); + } + + return g; +} + + +void bliss_free_graph(AbstractGraph *g) { + delete g; +} + + +inline int bliss_set_sh(AbstractGraph *g, igraph_bliss_sh_t sh, bool directed) { + if (directed) { + Digraph::SplittingHeuristic gsh = Digraph::shs_fsm; + switch (sh) { + case IGRAPH_BLISS_F: gsh = Digraph::shs_f; break; + case IGRAPH_BLISS_FL: gsh = Digraph::shs_fl; break; + case IGRAPH_BLISS_FS: gsh = Digraph::shs_fs; break; + case IGRAPH_BLISS_FM: gsh = Digraph::shs_fm; break; + case IGRAPH_BLISS_FLM: gsh = Digraph::shs_flm; break; + case IGRAPH_BLISS_FSM: gsh = Digraph::shs_fsm; break; + default: IGRAPH_ERROR("Invalid splitting heuristic.", IGRAPH_EINVAL); + } + static_cast(g)->set_splitting_heuristic(gsh); + } else { + Graph::SplittingHeuristic gsh = Graph::shs_fsm; + switch (sh) { + case IGRAPH_BLISS_F: gsh = Graph::shs_f; break; + case IGRAPH_BLISS_FL: gsh = Graph::shs_fl; break; + case IGRAPH_BLISS_FS: gsh = Graph::shs_fs; break; + case IGRAPH_BLISS_FM: gsh = Graph::shs_fm; break; + case IGRAPH_BLISS_FLM: gsh = Graph::shs_flm; break; + case IGRAPH_BLISS_FSM: gsh = Graph::shs_fsm; break; + default: IGRAPH_ERROR("Invalid splitting heuristic.", IGRAPH_EINVAL); + } + static_cast(g)->set_splitting_heuristic(gsh); + } + return IGRAPH_SUCCESS; +} + + +inline int bliss_set_colors(AbstractGraph *g, const igraph_vector_int_t *colors) { + if (colors == NULL) { + return IGRAPH_SUCCESS; + } + const int n = g->get_nof_vertices(); + if (n != igraph_vector_int_size(colors)) { + IGRAPH_ERROR("Invalid vertex color vector length.", IGRAPH_EINVAL); + } + for (int i = 0; i < n; ++i) { + g->change_color(i, VECTOR(*colors)[i]); + } + return IGRAPH_SUCCESS; +} + + +inline int bliss_info_to_igraph(igraph_bliss_info_t *info, const Stats &stats) { + if (info) { + size_t group_size_strlen; + + info->max_level = stats.get_max_level(); + info->nof_nodes = stats.get_nof_nodes(); + info->nof_leaf_nodes = stats.get_nof_leaf_nodes(); + info->nof_bad_nodes = stats.get_nof_bad_nodes(); + info->nof_canupdates = stats.get_nof_canupdates(); + info->nof_generators = stats.get_nof_generators(); + + mpz_t group_size; + mpz_init(group_size); + stats.get_group_size().get(group_size); + group_size_strlen = mpz_sizeinbase(group_size, /* base */ 10) + 2; + info->group_size = IGRAPH_CALLOC(group_size_strlen, char); + if (! info->group_size) { + IGRAPH_ERROR("Insufficient memory to retrieve automotphism group size.", IGRAPH_ENOMEM); + } + mpz_get_str(info->group_size, /* base */ 10, group_size); + mpz_clear(group_size); + } + + return IGRAPH_SUCCESS; +} + + +// This is the callback function that can tell Bliss to terminate early. +struct AbortChecker { + bool aborted; + + AbortChecker() : aborted(false) { } + bool operator()() { + if (igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) { + aborted = true; + return true; + } + return false; + } +}; + + +// This is the callback function used with AbstractGraph::find_automorphisms(). +// It collects the automorphism group generators into a pointer vector. +class AutCollector { + igraph_vector_ptr_t *generators; + +public: + AutCollector(igraph_vector_ptr_t *generators_) : generators(generators_) { } + + void operator ()(unsigned int n, const unsigned int *aut) { + int err; + igraph_vector_t *newvector = IGRAPH_CALLOC(1, igraph_vector_t); + if (! newvector) { + throw bad_alloc(); + } + err = igraph_vector_init(newvector, n); + if (err) { + throw bad_alloc(); + } + copy(aut, aut + n, newvector->stor_begin); // takes care of unsigned int -> double conversion + err = igraph_vector_ptr_push_back(generators, newvector); + if (err) { + throw bad_alloc(); + } + } +}; + +} // end unnamed namespace + + +/** + * \function igraph_canonical_permutation + * \brief Canonical permutation using Bliss. + * + * This function computes the vertex permutation which transforms + * the graph into a canonical form, using the Bliss algorithm. + * Two graphs have the same canonical form if and only if they + * are isomorphic. Use \ref igraph_is_same_graph() to compare + * two canonical forms. + * + * \param graph The input graph. Multiple edges between the same nodes + * are not supported and will cause an incorrect result to be returned. + * \param colors An optional vertex color vector for the graph. Supply a + * null pointer is the graph is not colored. + * \param labeling Pointer to a vector, the result is stored here. The + * permutation takes vertex 0 to the first element of the vector, + * vertex 1 to the second, etc. The vector will be resized as + * needed. + * \param sh The splitting heuristics to be used in Bliss. See \ref + * igraph_bliss_sh_t. + * \param info If not \c NULL then information on Bliss internals is + * stored here. The memory used by this structure must to be freed + * when no longer needed, see \ref igraph_bliss_info_t. + * \return Error code. + * + * \sa igraph_is_same_graph() + * + * Time complexity: exponential, in practice it is fast for many graphs. + */ +int igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_vector_t *labeling, igraph_bliss_sh_t sh, igraph_bliss_info_t *info) { + IGRAPH_HANDLE_EXCEPTIONS( + AbstractGraph *g = bliss_from_igraph(graph); + IGRAPH_FINALLY(bliss_free_graph, g); + const unsigned int N = g->get_nof_vertices(); + + IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph))); + IGRAPH_CHECK(bliss_set_colors(g, colors)); + + Stats stats; + AbortChecker checker; + const unsigned int *cl = g->canonical_form(stats, /* report */ nullptr, /* terminate */ checker); + if (checker.aborted) { + return IGRAPH_INTERRUPTED; + } + + IGRAPH_CHECK(igraph_vector_resize(labeling, N)); + for (unsigned int i = 0; i < N; i++) { + VECTOR(*labeling)[i] = cl[i]; + } + + IGRAPH_CHECK(bliss_info_to_igraph(info, stats)); + + delete g; + IGRAPH_FINALLY_CLEAN(1); + ); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_automorphisms + * \brief Number of automorphisms using Bliss. + * + * The number of automorphisms of a graph is computed using Bliss. The + * result is returned as part of the \p info structure, in tag \c + * group_size. It is returned as a string, as it can be very high even + * for relatively small graphs. If the GNU MP library is used then + * this number is exact, otherwise a long double is used + * and it is only approximate. See also \ref igraph_bliss_info_t. + * + * \param graph The input graph. Multiple edges between the same nodes + * are not supported and will cause an incorrect result to be returned. + * \param colors An optional vertex color vector for the graph. Supply a + * null pointer is the graph is not colored. + * \param sh The splitting heuristics to be used in Bliss. See \ref + * igraph_bliss_sh_t. + * \param info The result is stored here, in particular in the \c + * group_size tag of \p info. The memory used by this structure must be + * released when no longer needed, see \ref igraph_bliss_info_t. + * \return Error code. + * + * Time complexity: exponential, in practice it is fast for many graphs. + */ +int igraph_automorphisms(const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_bliss_sh_t sh, igraph_bliss_info_t *info) { + IGRAPH_HANDLE_EXCEPTIONS( + AbstractGraph *g = bliss_from_igraph(graph); + IGRAPH_FINALLY(bliss_free_graph, g); + + IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph))); + IGRAPH_CHECK(bliss_set_colors(g, colors)); + + Stats stats; + AbortChecker checker; + g->find_automorphisms(stats, /* report */ nullptr, /* terminate */ checker); + if (checker.aborted) { + return IGRAPH_INTERRUPTED; + } + + IGRAPH_CHECK(bliss_info_to_igraph(info, stats)); + + delete g; + IGRAPH_FINALLY_CLEAN(1); + ); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_automorphism_group + * \brief Automorphism group generators using Bliss. + * + * The generators of the automorphism group of a graph are computed + * using Bliss. The generator set may not be minimal and may depend on + * the splitting heuristics. The generators are permutations represented + * using zero-based indexing. + * + * \param graph The input graph. Multiple edges between the same nodes + * are not supported and will cause an incorrect result to be returned. + * \param colors An optional vertex color vector for the graph. Supply a + * null pointer is the graph is not colored. + * \param generators Must be an initialized pointer vector. It will + * contain pointers to \ref igraph_vector_t objects + * representing generators of the automorphism group. + * \param sh The splitting heuristics to be used in Bliss. See \ref + * igraph_bliss_sh_t. + * \param info If not \c NULL then information on Bliss internals is + * stored here. The memory used by this structure must to be freed + * when no longer needed, see \ref igraph_bliss_info_t. + * \return Error code. + * + * Time complexity: exponential, in practice it is fast for many graphs. + */ +int igraph_automorphism_group( + const igraph_t *graph, const igraph_vector_int_t *colors, igraph_vector_ptr_t *generators, + igraph_bliss_sh_t sh, igraph_bliss_info_t *info) { + IGRAPH_HANDLE_EXCEPTIONS( + AbstractGraph *g = bliss_from_igraph(graph); + IGRAPH_FINALLY(bliss_free_graph, g); + + IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph))); + IGRAPH_CHECK(bliss_set_colors(g, colors)); + + Stats stats; + igraph_vector_ptr_resize(generators, 0); + AutCollector collector(generators); + AbortChecker checker; + g->find_automorphisms(stats, collector, checker); + if (checker.aborted) { + return IGRAPH_INTERRUPTED; + } + IGRAPH_CHECK(bliss_info_to_igraph(info, stats)); + + delete g; + IGRAPH_FINALLY_CLEAN(1); + ); + + return IGRAPH_SUCCESS; +} + + +/* The following license notice applies to the rest of this file */ + +/* + IGraph library. + Copyright (C) 2006-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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 . +*/ + +/** + * \function igraph_isomorphic_bliss + * \brief Graph isomorphism via Bliss. + * + * This function uses the Bliss graph isomorphism algorithm, a + * successor of the famous NAUTY algorithm and implementation. Bliss + * is open source and licensed according to the GNU LGPL. See + * https://users.aalto.fi/~tjunttil/bliss/ for + * details. Currently the 0.75 version of Bliss is included in igraph. + * + * + * Isomorphism testing is implemented by producing the canonical form + * of both graphs using \ref igraph_canonical_permutation() and + * comparing them. + * + * \param graph1 The first input graph. Multiple edges between the same nodes + * are not supported and will cause an incorrect result to be returned. + * \param graph2 The second input graph. Multiple edges between the same nodes + * are not supported and will cause an incorrect result to be returned. + * \param colors1 An optional vertex color vector for the first graph. Supply a + * null pointer if your graph is not colored. + * \param colors2 An optional vertex color vector for the second graph. Supply a + * null pointer if your graph is not colored. + * \param iso Pointer to a boolean, the result is stored here. + * \param map12 A vector or \c NULL pointer. If not \c NULL then an + * isomorphic mapping from \p graph1 to \p graph2 is stored here. + * If the input graphs are not isomorphic then this vector is + * cleared, i.e. it will have length zero. + * \param map21 Similar to \p map12, but for the mapping from \p + * graph2 to \p graph1. + * \param sh Splitting heuristics to be used for the graphs. See + * \ref igraph_bliss_sh_t. + * \param info1 If not \c NULL, information about the canonization of + * the first input graph is stored here. Note that if the two graphs + * have different number of vertices or edges, then this is only + * partially filled. The memory used by this structure should be + * released when no longer needed, see \ref igraph_bliss_info_t + * for details. + * \param info2 Same as \p info1, but for the second graph. + * \return Error code. + * + * Time complexity: exponential, but in practice it is quite fast. + */ +int igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2, + const igraph_vector_int_t *colors1, const igraph_vector_int_t *colors2, + igraph_bool_t *iso, igraph_vector_t *map12, + igraph_vector_t *map21, igraph_bliss_sh_t sh, + igraph_bliss_info_t *info1, igraph_bliss_info_t *info2) { + + long int no_of_nodes = igraph_vcount(graph1); + long int no_of_edges = igraph_ecount(graph1); + igraph_vector_t perm1, perm2; + igraph_vector_t vmap12, *mymap12 = &vmap12; + igraph_vector_t from, to, index; + igraph_vector_t from2, to2, index2; + igraph_bool_t directed; + long int i, j; + + *iso = 0; + if (info1) { + info1->nof_nodes = info1->nof_leaf_nodes = info1->nof_bad_nodes = + info1->nof_canupdates = info1->max_level = info1->nof_generators = 0; + info1->group_size = 0; + } + if (info2) { + info2->nof_nodes = info2->nof_leaf_nodes = info2->nof_bad_nodes = + info2->nof_canupdates = info2->max_level = info2->nof_generators = 0; + info2->group_size = 0; + } + + directed = igraph_is_directed(graph1); + if (igraph_is_directed(graph2) != directed) { + IGRAPH_ERROR("Cannot compare directed and undirected graphs.", + IGRAPH_EINVAL); + } + if ((colors1 == NULL || colors2 == NULL) && colors1 != colors2) { + IGRAPH_WARNING("Only one of the graphs is vertex colored, colors will be ignored."); + colors1 = NULL; colors2 = NULL; + } + + if (no_of_nodes != igraph_vcount(graph2) || + no_of_edges != igraph_ecount(graph2)) { + if (map12) { + igraph_vector_clear(map12); + } + if (map21) { + igraph_vector_clear(map21); + } + return 0; + } + + if (map12) { + mymap12 = map12; + } else { + IGRAPH_VECTOR_INIT_FINALLY(mymap12, 0); + } + + IGRAPH_VECTOR_INIT_FINALLY(&perm1, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&perm2, no_of_nodes); + + IGRAPH_CHECK(igraph_canonical_permutation(graph1, colors1, &perm1, sh, info1)); + IGRAPH_CHECK(igraph_canonical_permutation(graph2, colors2, &perm2, sh, info2)); + + IGRAPH_CHECK(igraph_vector_resize(mymap12, no_of_nodes)); + + /* The inverse of perm2 is produced in mymap12 */ + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*mymap12)[ (long int)VECTOR(perm2)[i] ] = i; + } + /* Now we produce perm2^{-1} o perm1 in perm2 */ + for (i = 0; i < no_of_nodes; i++) { + VECTOR(perm2)[i] = VECTOR(*mymap12)[ (long int) VECTOR(perm1)[i] ]; + } + /* Copy it to mymap12 */ + igraph_vector_update(mymap12, &perm2); + + igraph_vector_destroy(&perm1); + igraph_vector_destroy(&perm2); + IGRAPH_FINALLY_CLEAN(2); + + /* Check isomorphism, we apply the permutation in mymap12 to graph1 + and should get graph2 */ + + IGRAPH_VECTOR_INIT_FINALLY(&from, no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&to, no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&index, no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&from2, no_of_edges * 2); + IGRAPH_VECTOR_INIT_FINALLY(&to2, no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&index2, no_of_edges); + + for (i = 0; i < no_of_edges; i++) { + VECTOR(from)[i] = VECTOR(*mymap12)[ (long int) IGRAPH_FROM(graph1, i) ]; + VECTOR(to)[i] = VECTOR(*mymap12)[ (long int) IGRAPH_TO (graph1, i) ]; + if (! directed && VECTOR(from)[i] < VECTOR(to)[i]) { + igraph_real_t tmp = VECTOR(from)[i]; + VECTOR(from)[i] = VECTOR(to)[i]; + VECTOR(to)[i] = tmp; + } + } + igraph_vector_order(&from, &to, &index, no_of_nodes); + + igraph_get_edgelist(graph2, &from2, /*bycol=*/ 1); + for (i = 0, j = no_of_edges; i < no_of_edges; i++, j++) { + VECTOR(to2)[i] = VECTOR(from2)[j]; + if (! directed && VECTOR(from2)[i] < VECTOR(to2)[i]) { + igraph_real_t tmp = VECTOR(from2)[i]; + VECTOR(from2)[i] = VECTOR(to2)[i]; + VECTOR(to2)[i] = tmp; + } + } + igraph_vector_resize(&from2, no_of_edges); + igraph_vector_order(&from2, &to2, &index2, no_of_nodes); + + *iso = 1; + for (i = 0; i < no_of_edges; i++) { + long int i1 = (long int) VECTOR(index)[i]; + long int i2 = (long int) VECTOR(index2)[i]; + if (VECTOR(from)[i1] != VECTOR(from2)[i2] || + VECTOR(to)[i1] != VECTOR(to2)[i2]) { + *iso = 0; + break; + } + } + + /* If the graphs are coloured, we also need to check that applying the + permutation mymap12 to colors1 gives colors2. */ + + if (*iso && colors1 != NULL) { + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*colors1)[i] != VECTOR(*colors2)[(long int) VECTOR(*mymap12)[i] ]) { + *iso = 0; + break; + } + } + } + + igraph_vector_destroy(&index2); + igraph_vector_destroy(&to2); + igraph_vector_destroy(&from2); + igraph_vector_destroy(&index); + igraph_vector_destroy(&to); + igraph_vector_destroy(&from); + IGRAPH_FINALLY_CLEAN(6); + + if (*iso) { + /* The inverse of mymap12 */ + if (map21) { + IGRAPH_CHECK(igraph_vector_resize(map21, no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*map21)[ (long int) VECTOR(*mymap12)[i] ] = i; + } + } + } else { + if (map12) { + igraph_vector_clear(map12); + } + if (map21) { + igraph_vector_clear(map21); + } + } + + if (!map12) { + igraph_vector_destroy(mymap12); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} diff --git a/src/rigraph/core/isomorphism/bliss/bignum.hh b/src/rigraph/core/isomorphism/bliss/bignum.hh new file mode 100644 index 0000000..0e4ec82 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/bignum.hh @@ -0,0 +1,103 @@ +#ifndef BLISS_BIGNUM_HH +#define BLISS_BIGNUM_HH + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +#define BLISS_USE_GMP + +#if defined(BLISS_USE_GMP) +#include "../../internal/gmp_internal.h" +#endif + +#include +#include "defs.hh" + +namespace bliss { + +/** + * \brief A simple wrapper class for big integers (or approximation of them). + * + * If the compile time flag BLISS_USE_GMP is set, + * then the GNU Multiple Precision Arithmetic library (GMP) is used to + * obtain arbitrary precision, otherwise "long double" is used to + * approximate big integers. + */ + +#if defined(BLISS_USE_GMP) + +class BigNum +{ + mpz_t v; +public: + /** + * \brief Create a new big number and set it to zero. + */ + BigNum() {mpz_init(v); } + + /** + * \brief Destroy the number. + */ + ~BigNum() {mpz_clear(v); } + + /** + * \brief Set the number to \a n. + */ + void assign(const int n) {mpz_set_si(v, n); } + + /** + * \brief Multiply the number with \a n. + */ + void multiply(const int n) {mpz_mul_si(v, v, n); } + + /** + * Get a copy of the internal GNU GMP integer. + * The caller is responsible for calling mpz_init before, + * and mpz_clear afterwards on the \a result variable. + */ + void get(mpz_t& result) const {mpz_set(result, v); } +}; + +#else + +class BigNum +{ + long double v; +public: + /** + * \brief Create a new big number and set it to zero. + */ + BigNum(): v(0.0) {} + + /** + * \brief Set the number to \a n. + */ + void assign(const int n) {v = (long double)n; } + + /** + * \brief Multiply the number with \a n. + */ + void multiply(const int n) {v *= (long double)n; } +}; + +#endif + +} //namespace bliss + +#endif // BLISS_BIGNUM_HH diff --git a/src/rigraph/core/isomorphism/bliss/defs.cc b/src/rigraph/core/isomorphism/bliss/defs.cc new file mode 100644 index 0000000..9760006 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/defs.cc @@ -0,0 +1,32 @@ +#include "igraph_error.h" + +#include "defs.hh" + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +namespace bliss { + +void +fatal_error(const char* reason) +{ + IGRAPH_FATAL(reason); +} + +} diff --git a/src/rigraph/core/isomorphism/bliss/defs.hh b/src/rigraph/core/isomorphism/bliss/defs.hh new file mode 100644 index 0000000..51b8246 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/defs.hh @@ -0,0 +1,90 @@ +#ifndef BLISS_DEFS_HH +#define BLISS_DEFS_HH + +#include +#include + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +/** \file + * \brief Some common definitions. + */ + +namespace bliss { + +/** \brief The version number of bliss. */ +static const char * const version = "0.75"; + +/** + * If a fatal internal error is encountered, + * this function is called. + * There should no return from this function, but an exit or + * a jump/throw to code that deallocates the AbstractGraph instance calling this. + */ +void fatal_error(const char* fmt); + + +#if defined(BLISS_DEBUG) +#define BLISS_CONSISTENCY_CHECKS +#define BLISS_EXPENSIVE_CONSISTENCY_CHECKS +#endif + + +#if defined(BLISS_CONSISTENCY_CHECKS) +/* Force a check that the found automorphisms are valid */ +#define BLISS_VERIFY_AUTOMORPHISMS +#endif + + +#if defined(BLISS_CONSISTENCY_CHECKS) +/* Force a check that the generated partitions are equitable */ +#define BLISS_VERIFY_EQUITABLEDNESS +#endif + +} // namespace bliss + + +/*! \mainpage Outline + * + * This is the C++ API documentation of bliss, + * produced by running doxygen in + * the source directory. + * + * The algorithms and data structures used in bliss, + * the graph file format, as well as the compilation process + * can be found at the + * bliss web site. + * + * The C++ language API is the main API to bliss. + * It basically consists of the public methods in the classes + * * bliss::Graph and + * * bliss::Digraph. + * + * For an example of its use, + * see the \ref executable "source of the bliss executable". + * + * \section capi_sec The C language API + * + * The C language API is given in the file bliss_C.h. + * It is currently only a subset of the C++ API, + * so consider using the C++ API whenever possible. + */ + +#endif // BLISS_DEFS_HH diff --git a/src/rigraph/core/isomorphism/bliss/graph.cc b/src/rigraph/core/isomorphism/bliss/graph.cc new file mode 100644 index 0000000..7260423 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/graph.cc @@ -0,0 +1,5035 @@ +#include "igraph_error.h" + +#include +#include +#include +#include +#include +// #include +#include +#include + +#include "defs.hh" +#include "graph.hh" +#include "partition.hh" +#include "utils.hh" + +/* Allow using 'and' instead of '&&' with MSVC */ +#if _MSC_VER +#include +#endif + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + + +namespace bliss { + +#define _INTERNAL_ERROR() IGRAPH_FATAL("Bliss internal error") + +/*------------------------------------------------------------------------- + * + * Constructor and destructor routines for the abstract graph class + * + *-------------------------------------------------------------------------*/ + + +AbstractGraph::AbstractGraph() +{ + /* Initialize stuff */ + first_path_labeling = nullptr; + first_path_labeling_inv = nullptr; + best_path_labeling = nullptr; + best_path_labeling_inv = nullptr; + first_path_automorphism = nullptr; + best_path_automorphism = nullptr; + in_search = false; + + /* Default value for using "long prune" */ + opt_use_long_prune = true; + /* Default value for using failure recording */ + opt_use_failure_recording = true; + /* Default value for using component recursion */ + opt_use_comprec = true; + + + /* + verbose_level = 0; + verbstr = stdout; + */ +} + + +AbstractGraph::~AbstractGraph() +{ + delete[] first_path_labeling; first_path_labeling = nullptr; + delete[] first_path_labeling_inv; first_path_labeling_inv = nullptr; + delete[] first_path_automorphism; first_path_automorphism = nullptr; + + delete[] best_path_labeling; best_path_labeling = nullptr; + delete[] best_path_labeling_inv; best_path_labeling_inv = nullptr; + delete[] best_path_automorphism; best_path_automorphism = nullptr; +} + + + +/*------------------------------------------------------------------------- + * + * Verbose output management routines + * + *-------------------------------------------------------------------------*/ + +/* +void +AbstractGraph::set_verbose_level(const unsigned int level) +{ + verbose_level = level; +} + +void +AbstractGraph::set_verbose_file(FILE* const fp) +{ + verbstr = fp; +} +*/ + + + +/*------------------------------------------------------------------------- + * + * Routines for refinement to equitable partition + * + *-------------------------------------------------------------------------*/ + +void +AbstractGraph::refine_to_equitable() +{ + + /* Start refinement from all cells -> push 'em all in the splitting queue */ + for(Partition::Cell* cell = p.first_cell; cell; cell = cell->next) + p.splitting_queue_add(cell); + + do_refine_to_equitable(); + +} + +void +AbstractGraph::refine_to_equitable(Partition::Cell* const unit_cell) +{ + + p.splitting_queue_add(unit_cell); + + do_refine_to_equitable(); +} + + + +void +AbstractGraph::refine_to_equitable(Partition::Cell* const unit_cell1, + Partition::Cell* const unit_cell2) +{ + + p.splitting_queue_add(unit_cell1); + p.splitting_queue_add(unit_cell2); + + do_refine_to_equitable(); +} + + + +bool +AbstractGraph::do_refine_to_equitable() +{ + + eqref_hash.reset(); + + while(!p.splitting_queue_is_empty()) + { + Partition::Cell* const cell = p.splitting_queue_pop(); + + if(cell->is_unit()) + { + if(in_search) { + const unsigned int index = cell->first; + if(first_path_automorphism) + { + /* Build the (potential) automorphism on-the-fly */ + first_path_automorphism[first_path_labeling_inv[index]] = + p.elements[index]; + } + if(best_path_automorphism) + { + /* Build the (potential) automorphism on-the-fly */ + best_path_automorphism[best_path_labeling_inv[index]] = + p.elements[index]; + } + } + const bool worse = split_neighbourhood_of_unit_cell(cell); + if(in_search and worse) + goto worse_exit; + } + else + { + const bool worse = split_neighbourhood_of_cell(cell); + if(in_search and worse) + goto worse_exit; + } + } + + return true; + + worse_exit: + /* Clear splitting_queue */ + p.splitting_queue_clear(); + return false; +} + + + + + + + + + + + + + + + + +/*------------------------------------------------------------------------- + * + * Routines for handling the canonical labeling + * + *-------------------------------------------------------------------------*/ + +/** \internal + * Assign the labeling induced by the current partition 'this.p' to + * \a labeling. + * That is, if the partition is [[2,0],[1]], + * then \a labeling will map 0 to 1, 1 to 2, and 2 to 0. + */ +void +AbstractGraph::update_labeling(unsigned int* const labeling) +{ + const unsigned int N = get_nof_vertices(); + unsigned int* ep = p.elements; + for(unsigned int i = 0; i < N; i++, ep++) + labeling[*ep] = i; +} + +/** \internal + * The same as update_labeling() except that the inverse of the labeling + * is also produced and assigned to \a labeling_inv. + */ +void +AbstractGraph::update_labeling_and_its_inverse(unsigned int* const labeling, + unsigned int* const labeling_inv) +{ + const unsigned int N = get_nof_vertices(); + unsigned int* ep = p.elements; + unsigned int* clip = labeling_inv; + + for(unsigned int i = 0; i < N; ) { + labeling[*ep] = i; + i++; + *clip = *ep; + ep++; + clip++; + } +} + + + + + +/*------------------------------------------------------------------------- + * + * Routines for handling automorphisms + * + *-------------------------------------------------------------------------*/ + + +/** \internal + * Reset the permutation \a perm to the identity permutation. + */ +void +AbstractGraph::reset_permutation(unsigned int* perm) +{ + const unsigned int N = get_nof_vertices(); + for(unsigned int i = 0; i < N; i++, perm++) + *perm = i; +} + +/* +bool +AbstractGraph::is_automorphism(unsigned int* const perm) +{ + _INTERNAL_ERROR(); + return false; +} +*/ + +/* +bool +AbstractGraph::is_automorphism(const std::vector& perm) const +{ + _INTERNAL_ERROR(); + return false; +} +*/ + + + +/*------------------------------------------------------------------------- + * + * Certificate building + * + *-------------------------------------------------------------------------*/ + +void +AbstractGraph::cert_add(const unsigned int v1, + const unsigned int v2, + const unsigned int v3) +{ + if(refine_compare_certificate) + { + if(refine_equal_to_first) + { + /* So far equivalent to the first path... */ + unsigned int index = certificate_current_path.size(); + if(index >= refine_first_path_subcertificate_end) + { + refine_equal_to_first = false; + } + else if(certificate_first_path[index] != v1) + { + refine_equal_to_first = false; + } + else if(certificate_first_path[++index] != v2) + { + refine_equal_to_first = false; + } + else if(certificate_first_path[++index] != v3) + { + refine_equal_to_first = false; + } + if(opt_use_failure_recording and !refine_equal_to_first) + { + /* We just became different from the first path, + * remember the deviation point tree-specific invariant + * for the use of failure recording */ + UintSeqHash h; + h.update(v1); + h.update(v2); + h.update(v3); + h.update(index); + h.update(eqref_hash.get_value()); + failure_recording_fp_deviation = h.get_value(); + } + } + if(refine_cmp_to_best == 0) + { + /* So far equivalent to the current best path... */ + unsigned int index = certificate_current_path.size(); + if(index >= refine_best_path_subcertificate_end) + { + refine_cmp_to_best = 1; + } + else if(v1 > certificate_best_path[index]) + { + refine_cmp_to_best = 1; + } + else if(v1 < certificate_best_path[index]) + { + refine_cmp_to_best = -1; + } + else if(v2 > certificate_best_path[++index]) + { + refine_cmp_to_best = 1; + } + else if(v2 < certificate_best_path[index]) + { + refine_cmp_to_best = -1; + } + else if(v3 > certificate_best_path[++index]) + { + refine_cmp_to_best = 1; + } + else if(v3 < certificate_best_path[index]) + { + refine_cmp_to_best = -1; + } + } + if((refine_equal_to_first == false) and + (refine_cmp_to_best < 0)) + return; + } + /* Update the current path certificate */ + certificate_current_path.push_back(v1); + certificate_current_path.push_back(v2); + certificate_current_path.push_back(v3); +} + + +void +AbstractGraph::cert_add_redundant(const unsigned int v1, + const unsigned int v2, + const unsigned int v3) +{ + return cert_add(v1, v2, v3); +} + + + + + + + + + + + +/*------------------------------------------------------------------------- + * + * Long prune code + * + *-------------------------------------------------------------------------*/ + +void +AbstractGraph::long_prune_init() +{ + const unsigned int N = get_nof_vertices(); + long_prune_temp.clear(); + long_prune_temp.resize(N); + /* Of how many automorphisms we can store information in + the predefined, fixed amount of memory? */ + const unsigned int nof_fitting_in_max_mem = + (long_prune_options_max_mem * 1024 * 1024) / (((N * 2) / 8)+1); + long_prune_max_stored_autss = long_prune_options_max_stored_auts; + /* Had some problems with g++ in using (a* tmp = long_prune_fixed[real_i]; + long_prune_fixed[real_i] = long_prune_fixed[real_j]; + long_prune_fixed[real_j] = tmp; + tmp = long_prune_mcrs[real_i]; + long_prune_mcrs[real_i] = long_prune_mcrs[real_j]; + long_prune_mcrs[real_j] = tmp; +} + +std::vector& +AbstractGraph::long_prune_allocget_fixed(const unsigned int index) +{ + const unsigned int i = index % long_prune_max_stored_autss; + if(!long_prune_fixed[i]) + long_prune_fixed[i] = new std::vector(get_nof_vertices()); + return *long_prune_fixed[i]; +} + +std::vector& +AbstractGraph::long_prune_get_fixed(const unsigned int index) +{ + return *long_prune_fixed[index % long_prune_max_stored_autss]; +} + +std::vector& +AbstractGraph::long_prune_allocget_mcrs(const unsigned int index) +{ + const unsigned int i = index % long_prune_max_stored_autss; + if(!long_prune_mcrs[i]) + long_prune_mcrs[i] = new std::vector(get_nof_vertices()); + return *long_prune_mcrs[i]; +} + +std::vector& +AbstractGraph::long_prune_get_mcrs(const unsigned int index) +{ + return *long_prune_mcrs[index % long_prune_max_stored_autss]; +} + +void +AbstractGraph::long_prune_add_automorphism(const unsigned int* aut) +{ + if(long_prune_max_stored_autss == 0) + return; + + const unsigned int N = get_nof_vertices(); + + + /* If the buffer of stored auts is full, remove the oldest aut */ + if(long_prune_end - long_prune_begin == long_prune_max_stored_autss) + { + long_prune_begin++; + } + long_prune_end++; + std::vector& fixed = long_prune_allocget_fixed(long_prune_end-1); + std::vector& mcrs = long_prune_allocget_mcrs(long_prune_end-1); + /* Mark nodes that are (i) fixed or (ii) minimal orbit representatives + * under the automorphism 'aut' */ + for(unsigned int i = 0; i < N; i++) + { + fixed[i] = (aut[i] == i); + if(long_prune_temp[i] == false) + { + mcrs[i] = true; + unsigned int j = aut[i]; + while(j != i) + { + long_prune_temp[j] = true; + j = aut[j]; + } + } + else + { + mcrs[i] = false; + } + /* Clear the temp array on-the-fly... */ + long_prune_temp[i] = false; + } + + +} + + + +/*------------------------------------------------------------------------- + * + * Routines for handling orbit information + * + *-------------------------------------------------------------------------*/ + +void +AbstractGraph::update_orbit_information(Orbit& o, const unsigned int* perm) +{ + const unsigned int N = get_nof_vertices(); + for(unsigned int i = 0; i < N; i++) + if(perm[i] != i) + o.merge_orbits(i, perm[i]); +} + + + + + + + + +/*------------------------------------------------------------------------- + * + * The actual backtracking search + * + *-------------------------------------------------------------------------*/ + +/** \internal \brief Search tree node information. + */ +class TreeNode +{ + //friend class AbstractGraph; +public: + unsigned int split_cell_first; + + int split_element; + static const int SPLIT_START = -1; + static const int SPLIT_END = -2; + + Partition::BacktrackPoint partition_bt_point; + + unsigned int certificate_index; + + static const char NO = -1; + static const char MAYBE = 0; + static const char YES = 1; + + /* First path stuff */ + bool fp_on; + bool fp_cert_equal; + char fp_extendable; + + /* Best path stuff */ + bool in_best_path; + int cmp_to_best_path; + + unsigned int failure_recording_ival; + + /* Component recursion related data */ + unsigned int cr_cep_stack_size; + unsigned int cr_cep_index; + unsigned int cr_level; + + bool needs_long_prune = false; /* igraph-specific patch: initialize to false to silence UBSan */ + unsigned int long_prune_begin; + std::set > long_prune_redundant; + + UintSeqHash eqref_hash; + unsigned int subcertificate_length; +}; + + + + + +void +AbstractGraph::search(const bool canonical, + Stats& stats, + const std::function& report, + const std::function& terminate) +{ + const unsigned int N = get_nof_vertices(); + + unsigned int all_same_level = UINT_MAX; + + p.graph = this; + + /* + * Must be done! + */ + remove_duplicate_edges(); + + /* + * Reset search statistics + */ + stats.reset(); + stats.nof_nodes = 1; + stats.nof_leaf_nodes = 1; + + /* Free old first path data structures */ + delete[] first_path_labeling; first_path_labeling = nullptr; + delete[] first_path_labeling_inv; first_path_labeling_inv = nullptr; + delete[] first_path_automorphism; first_path_automorphism = nullptr; + + /* Free old best path data structures */ + delete[] best_path_labeling; best_path_labeling = nullptr; + delete[] best_path_labeling_inv; best_path_labeling_inv = nullptr; + delete[] best_path_automorphism; best_path_automorphism = nullptr; + + if(N == 0) + { + /* Nothing to do, return... */ + return; + } + + /* Initialize the partition ... */ + p.init(N); + /* ... and the component recursion data structures in the partition */ + if(opt_use_comprec) + p.cr_init(); + + neighbour_heap.init(N); + + in_search = false; + /* Do not compute certificate when building the initial partition */ + refine_compare_certificate = false; + /* The 'eqref_hash' hash value is not computed when building + * the initial partition as it is not used for anything at the moment. + * This saves some cycles. */ + compute_eqref_hash = false; + + make_initial_equitable_partition(); + + /* + * Allocate space for the "first path" and "best path" labelings + */ + delete[] first_path_labeling; + first_path_labeling = new unsigned int[N]; + + delete[] best_path_labeling; + best_path_labeling = new unsigned int[N]; + for(unsigned int i = 0; i < N; i++) best_path_labeling[i] = i; + + /* + * Is the initial partition discrete? + */ + if(p.is_discrete()) + { + /* Make the best path labeling i.e. the canonical labeling */ + update_labeling(best_path_labeling); + /* Update statistics */ + stats.nof_leaf_nodes = 1; + /* Release component recursion data in partition */ + if(opt_use_comprec) + p.cr_free(); + return; + } + + /* + * Allocate the inverses of the "first path" and "best path" labelings + */ + delete[] first_path_labeling_inv; + first_path_labeling_inv = new unsigned int[N]; + std::fill_n(first_path_labeling_inv, N, 0); + delete[] best_path_labeling_inv; + best_path_labeling_inv = new unsigned int[N]; + std::fill_n(best_path_labeling_inv, N, 0); + + /* + * Allocate space for the automorphisms + */ + delete[] first_path_automorphism; + first_path_automorphism = new unsigned int[N]; + delete[] best_path_automorphism; + best_path_automorphism = new unsigned int[N]; + + /* + * Initialize orbit information so that all vertices are in their own orbits + */ + first_path_orbits.init(N); + best_path_orbits.init(N); + + /* + * Initialize certificate memory + */ + initialize_certificate(); + + std::vector search_stack; + std::vector first_path_info; + std::vector best_path_info; + + search_stack.clear(); + + /* Initialize "long prune" data structures */ + if(opt_use_long_prune) + long_prune_init(); + + /* + * Initialize failure recording data structures + */ + typedef std::set > FailureRecordingSet; + std::vector failure_recording_hashes; + + /* + * Initialize component recursion data structures + */ + cr_cep_stack.clear(); + unsigned int cr_cep_index = 0; + { + /* Inset a sentinel "component end point" */ + CR_CEP sentinel; + sentinel.creation_level = 0; + sentinel.discrete_cell_limit = get_nof_vertices(); + sentinel.next_cr_level = 0; + sentinel.next_cep_index = 0; + sentinel.first_checked = false; + sentinel.best_checked = false; + cr_cep_index = 0; + cr_cep_stack.push_back(sentinel); + } + cr_level = 0; + if(opt_use_comprec and + nucr_find_first_component(cr_level) == true and + p.nof_discrete_cells() + cr_component_elements < + cr_cep_stack[cr_cep_index].discrete_cell_limit) + { + cr_level = p.cr_split_level(0, cr_component); + CR_CEP cep; + cep.creation_level = 0; + cep.discrete_cell_limit = p.nof_discrete_cells() + cr_component_elements; + cep.next_cr_level = 0; + cep.next_cep_index = cr_cep_index; + cep.first_checked = false; + cep.best_checked = false; + cr_cep_index = cr_cep_stack.size(); + cr_cep_stack.push_back(cep); + } + + /* + * Build the root node of the search tree + */ + { + TreeNode root; + Partition::Cell* split_cell = find_next_cell_to_be_splitted(p.first_cell); + root.split_cell_first = split_cell->first; + root.split_element = TreeNode::SPLIT_START; + root.partition_bt_point = p.set_backtrack_point(); + root.certificate_index = 0; + root.fp_on = true; + root.fp_cert_equal = true; + root.fp_extendable = TreeNode::MAYBE; + root.in_best_path = false; + root.cmp_to_best_path = 0; + root.long_prune_begin = 0; + + root.failure_recording_ival = 0; + + /* Save component recursion info for backtracking */ + root.cr_level = cr_level; + root.cr_cep_stack_size = cr_cep_stack.size(); + root.cr_cep_index = cr_cep_index; + search_stack.push_back(root); + } + + /* + * Set status and global flags for search related procedures + */ + in_search = true; + /* Do not compare certificates during refinement until the first path has been traversed to the leaf */ + refine_compare_certificate = false; + + + + + /* + * The actual backtracking search + */ + while(!search_stack.empty()) + { + if(terminate and terminate()) { + break; + } + TreeNode& current_node = search_stack.back(); + const unsigned int current_level = (unsigned int)search_stack.size()-1; + + + if(opt_use_comprec) + { + CR_CEP& cep = cr_cep_stack[current_node.cr_cep_index]; + if(cep.first_checked == true and + current_node.fp_extendable == TreeNode::MAYBE and + !search_stack[cep.creation_level].fp_on) + { + current_node.fp_extendable = TreeNode::NO; + } + } + + if(current_node.fp_on) + { + if(current_node.split_element == TreeNode::SPLIT_END) + { + search_stack.pop_back(); + continue; + } + } + else + { + if(current_node.fp_extendable == TreeNode::YES) + { + search_stack.pop_back(); + continue; + } + if(current_node.split_element == TreeNode::SPLIT_END) + { + if(opt_use_failure_recording) + { + TreeNode& parent_node = search_stack[current_level-1]; + if(parent_node.fp_on) + failure_recording_hashes[current_level-1].insert(current_node.failure_recording_ival); + } + search_stack.pop_back(); + continue; + } + if(current_node.fp_extendable == TreeNode::NO and + (!canonical or current_node.cmp_to_best_path < 0)) + { + if(opt_use_failure_recording) + { + TreeNode& parent_node = search_stack[current_level-1]; + if(parent_node.fp_on) + failure_recording_hashes[current_level-1].insert(current_node.failure_recording_ival); + } + search_stack.pop_back(); + continue; + } + } + + /* Restore partition ... */ + p.goto_backtrack_point(current_node.partition_bt_point); + /* ... and re-remember backtracking point */ + current_node.partition_bt_point = p.set_backtrack_point(); + + /* Restore current path certificate */ + certificate_index = current_node.certificate_index; + refine_current_path_certificate_index = current_node.certificate_index; + certificate_current_path.resize(certificate_index); + + /* Fetch split cell information */ + Partition::Cell * const cell = + p.get_cell(p.elements[current_node.split_cell_first]); + + /* Restore component recursion information */ + cr_level = current_node.cr_level; + cr_cep_stack.resize(current_node.cr_cep_stack_size); + cr_cep_index = current_node.cr_cep_index; + + + /* + * Update long prune redundancy sets + */ + if(opt_use_long_prune and current_level >= 1 and !current_node.fp_on) + { + unsigned int begin = (current_node.long_prune_begin>long_prune_begin)?current_node.long_prune_begin:long_prune_begin; + for(unsigned int i = begin; i < long_prune_end; i++) + { + const std::vector& fixed = long_prune_get_fixed(i); +#if defined(BLISS_CONSISTENCY_CHECKS) + for(unsigned int l = 0; l < search_stack.size()-2; l++) + assert(fixed[search_stack[l].split_element]); +#endif + if(fixed[search_stack[search_stack.size()-1-1].split_element] == + false) + { + long_prune_swap(begin, i); + begin++; + current_node.long_prune_begin = begin; + continue; + } + } + + if(current_node.split_element == TreeNode::SPLIT_START) + { + current_node.needs_long_prune = true; + } + else if(current_node.needs_long_prune) + { + current_node.needs_long_prune = false; + unsigned int begin = (current_node.long_prune_begin>long_prune_begin)?current_node.long_prune_begin:long_prune_begin; + for(unsigned int i = begin; i < long_prune_end; i++) + { + const std::vector& fixed = long_prune_get_fixed(i); +#if defined(BLISS_CONSISTENCY_CHECKS) + for(unsigned int l = 0; l < search_stack.size()-2; l++) + assert(fixed[search_stack[l].split_element]); +#endif + assert(fixed[search_stack[current_level-1].split_element] == true); + if(fixed[search_stack[current_level-1].split_element] == false) + { + long_prune_swap(begin, i); + begin++; + current_node.long_prune_begin = begin; + continue; + } + const std::vector& mcrs = long_prune_get_mcrs(i); + unsigned int* ep = p.elements + cell->first; + for(unsigned int j = cell->length; j > 0; j--, ep++) { + if(mcrs[*ep] == false) + current_node.long_prune_redundant.insert(*ep); + } + } + } + } + + + /* + * Find the next smallest, non-isomorphic element in the cell and + * store it in current_node.split_element + */ + { + unsigned int next_split_element = UINT_MAX; + //unsigned int* next_split_element_pos = 0; + unsigned int* ep = p.elements + cell->first; + if(current_node.fp_on) + { + /* Find the next larger splitting element that is + * a minimal orbit representative w.r.t. first_path_orbits */ + for(unsigned int i = cell->length; i > 0; i--, ep++) { + if((int)(*ep) > current_node.split_element and + *ep < next_split_element and + first_path_orbits.is_minimal_representative(*ep)) { + next_split_element = *ep; + //next_split_element_pos = ep; + } + } + } + else if(current_node.in_best_path) + { + /* Find the next larger splitting element that is + * a minimal orbit representative w.r.t. best_path_orbits */ + for(unsigned int i = cell->length; i > 0; i--, ep++) { + if((int)(*ep) > current_node.split_element and + *ep < next_split_element and + best_path_orbits.is_minimal_representative(*ep) and + (!opt_use_long_prune or + current_node.long_prune_redundant.find(*ep) == + current_node.long_prune_redundant.end())) { + next_split_element = *ep; + //next_split_element_pos = ep; + } + } + } + else + { + /* Find the next larger splitting element */ + for(unsigned int i = cell->length; i > 0; i--, ep++) { + if((int)(*ep) > current_node.split_element and + *ep < next_split_element and + (!opt_use_long_prune or + current_node.long_prune_redundant.find(*ep) == + current_node.long_prune_redundant.end())) { + next_split_element = *ep; + //next_split_element_pos = ep; + } + } + } + if(next_split_element == UINT_MAX) + { + /* No more (unexplored children) in the cell */ + current_node.split_element = TreeNode::SPLIT_END; + if(current_node.fp_on) + { + /* Update group size */ + const unsigned int index = first_path_orbits.orbit_size(first_path_info[search_stack.size()-1].splitting_element); + stats.group_size.multiply(index); + stats.group_size_approx *= (long double)index; + /* + * Update all_same_level + */ + if(index == cell->length and all_same_level == current_level+1) + all_same_level = current_level; + /* + if(verbstr and verbose_level >= 2) { + fprintf(verbstr, + "Level %u: orbits=%u, index=%u/%u, all_same_level=%u\n", + current_level, + first_path_orbits.nof_orbits(), + index, cell->length, + all_same_level); + fflush(verbstr); + } + */ + } + continue; + } + + /* Split on smallest */ + current_node.split_element = next_split_element; + } + + const unsigned int child_level = current_level+1; + /* Update some statistics */ + stats.nof_nodes++; + if(search_stack.size() > stats.max_level) + stats.max_level = search_stack.size(); + + + + /* Set flags and indices for the refiner certificate builder */ + refine_equal_to_first = current_node.fp_cert_equal; + refine_cmp_to_best = current_node.cmp_to_best_path; + if(!first_path_info.empty()) + { + if(refine_equal_to_first) + refine_first_path_subcertificate_end = + first_path_info[search_stack.size()-1].certificate_index + + first_path_info[search_stack.size()-1].subcertificate_length; + if(canonical) + { + if(refine_cmp_to_best == 0) + refine_best_path_subcertificate_end = + best_path_info[search_stack.size()-1].certificate_index + + best_path_info[search_stack.size()-1].subcertificate_length; + } + else + refine_cmp_to_best = -1; + } + + const bool was_fp_cert_equal = current_node.fp_cert_equal; + + /* Individualize, i.e. split the cell in two, the latter new cell + * will be a unit one containing info.split_element */ + Partition::Cell* const new_cell = + p.individualize(cell, current_node.split_element); + + /* + * Refine the new partition to equitable + */ + if(cell->is_unit()) + refine_to_equitable(cell, new_cell); + else + refine_to_equitable(new_cell); + + + + + /* Update statistics */ + if(p.is_discrete()) + stats.nof_leaf_nodes++; + + + if(!first_path_info.empty()) + { + /* We are no longer on the first path */ + const unsigned int subcertificate_length = + certificate_current_path.size() - certificate_index; + if(refine_equal_to_first) + { + /* Was equal to the first path so far */ + PathInfo& first_pinfo = first_path_info[current_level]; + assert(first_pinfo.certificate_index == certificate_index); + if(subcertificate_length != first_pinfo.subcertificate_length) + { + refine_equal_to_first = false; + if(opt_use_failure_recording) + failure_recording_fp_deviation = subcertificate_length; + } + else if(first_pinfo.eqref_hash.cmp(eqref_hash) != 0) + { + refine_equal_to_first = false; + if(opt_use_failure_recording) + failure_recording_fp_deviation = eqref_hash.get_value(); + } + } + if(canonical and (refine_cmp_to_best == 0)) + { + /* Was equal to the best path so far */ + PathInfo& bestp_info = best_path_info[current_level]; + assert(bestp_info.certificate_index == certificate_index); + if(subcertificate_length < bestp_info.subcertificate_length) + { + refine_cmp_to_best = -1; + } + else if(subcertificate_length > bestp_info.subcertificate_length) + { + refine_cmp_to_best = 1; + } + else if(bestp_info.eqref_hash.cmp(eqref_hash) > 0) + { + refine_cmp_to_best = -1; + } + else if(bestp_info.eqref_hash.cmp(eqref_hash) < 0) + { + refine_cmp_to_best = 1; + } + } + + if(opt_use_failure_recording and + was_fp_cert_equal and + !refine_equal_to_first) + { + UintSeqHash k; + k.update(failure_recording_fp_deviation); + k.update(eqref_hash.get_value()); + failure_recording_fp_deviation = k.get_value(); + + if(current_node.fp_on) + failure_recording_hashes[current_level].insert(failure_recording_fp_deviation); + else + { + for(unsigned int i = current_level; i > 0; i--) + { + if(search_stack[i].fp_on) + break; + const FailureRecordingSet& s = failure_recording_hashes[i]; + if(i == current_level and + s.find(failure_recording_fp_deviation) != s.end()) + break; + if(s.find(0) != s.end()) + break; + search_stack[i].fp_extendable = TreeNode::NO; + } + } + } + + + /* Check if no longer equal to the first path and, + * if canonical labeling is desired, also worse than the + * current best path */ + if(refine_equal_to_first == false and + (!canonical or (refine_cmp_to_best < 0))) + { + /* Yes, backtrack */ + stats.nof_bad_nodes++; + if(current_node.fp_cert_equal == true and + current_level+1 > all_same_level) + { + assert(all_same_level >= 1); + for(unsigned int i = all_same_level; + i < search_stack.size(); + i++) + { + search_stack[i].fp_extendable = TreeNode::NO; + } + } + + continue; + } + } + +#if defined(BLISS_VERIFY_EQUITABLEDNESS) + /* The new partition should be equitable */ + if(!is_equitable()) + fatal_error("consistency check failed - partition after refinement is not equitable"); +#endif + + /* + * Next level search tree node info + */ + TreeNode child_node; + + /* No more in the first path */ + child_node.fp_on = false; + /* No more in the best path */ + child_node.in_best_path = false; + + child_node.fp_cert_equal = refine_equal_to_first; + if(current_node.fp_extendable == TreeNode::NO or + (current_node.fp_extendable == TreeNode::MAYBE and + child_node.fp_cert_equal == false)) + child_node.fp_extendable = TreeNode::NO; + else + child_node.fp_extendable = TreeNode::MAYBE; + child_node.cmp_to_best_path = refine_cmp_to_best; + + child_node.failure_recording_ival = 0; + child_node.cr_cep_stack_size = current_node.cr_cep_stack_size; + child_node.cr_cep_index = current_node.cr_cep_index; + child_node.cr_level = current_node.cr_level; + + certificate_index = certificate_current_path.size(); + + current_node.eqref_hash = eqref_hash; + current_node.subcertificate_length = + certificate_index - current_node.certificate_index; + + + /* + * The first encountered leaf node at the end of the "first path"? + */ + if(p.is_discrete() and first_path_info.empty()) + { + //fprintf(stdout, "Level %u: FIRST\n", child_level); fflush(stdout); + stats.nof_canupdates++; + /* + * Update labelings and their inverses + */ + update_labeling_and_its_inverse(first_path_labeling, + first_path_labeling_inv); + update_labeling_and_its_inverse(best_path_labeling, + best_path_labeling_inv); + /* + * Reset automorphism array + */ + reset_permutation(first_path_automorphism); + reset_permutation(best_path_automorphism); + /* + * Reset orbit information + */ + first_path_orbits.reset(); + best_path_orbits.reset(); + /* + * Reset group size + */ + stats.group_size.assign(1); + stats.group_size_approx = 1.0; + /* + * Reset all_same_level + */ + all_same_level = child_level; + /* + * Mark the current path to be the first and best one and save it + */ + const unsigned int base_size = search_stack.size(); + best_path_info.clear(); + //fprintf(stdout, " New base is: "); + for(unsigned int i = 0; i < base_size; i++) { + search_stack[i].fp_on = true; + search_stack[i].fp_cert_equal = true; + search_stack[i].fp_extendable = TreeNode::YES; + search_stack[i].in_best_path = true; + search_stack[i].cmp_to_best_path = 0; + PathInfo path_info; + path_info.splitting_element = search_stack[i].split_element; + path_info.certificate_index = search_stack[i].certificate_index; + path_info.eqref_hash = search_stack[i].eqref_hash; + path_info.subcertificate_length = search_stack[i].subcertificate_length; + first_path_info.push_back(path_info); + best_path_info.push_back(path_info); + //fprintf(stdout, "%u ", search_stack[i].split_element); + } + //fprintf(stdout, "\n"); fflush(stdout); + /* Copy certificates */ + certificate_first_path = certificate_current_path; + certificate_best_path = certificate_current_path; + + /* From now on, compare certificates when refining */ + refine_compare_certificate = true; + + if(opt_use_failure_recording) + failure_recording_hashes.resize(base_size); + + /* + for(unsigned int j = 0; j < search_stack.size(); j++) + fprintf(stderr, "%u ", search_stack[j].split_element); + fprintf(stderr, "\n"); + p.print(stderr); fprintf(stderr, "\n"); + */ + + /* + * Backtrack to the previous level + */ + continue; + } + + + if(p.is_discrete() and child_node.fp_cert_equal) + { + /* + * A leaf node that is equal to the first one. + * An automorphism found: aut[i] = elements[first_path_labeling[i]] + */ + goto handle_first_path_automorphism; + } + + + if(!p.is_discrete()) + { + Partition::Cell* next_split_cell = 0; + /* + * An internal, non-leaf node + */ + if(opt_use_comprec) + { + assert(p.nof_discrete_cells() <= + cr_cep_stack[cr_cep_index].discrete_cell_limit); + assert(cr_level == child_node.cr_level); + + + if(p.nof_discrete_cells() == + cr_cep_stack[cr_cep_index].discrete_cell_limit) + { + /* We have reached the end of a component */ + assert(cr_cep_index != 0); + CR_CEP& cep = cr_cep_stack[cr_cep_index]; + + /* First, compare with respect to the first path */ + if(first_path_info.empty() or child_node.fp_cert_equal) { + if(cep.first_checked == false) + { + /* First time, go to the next component */ + cep.first_checked = true; + } + else + { + assert(!first_path_info.empty()); + assert(cep.creation_level < search_stack.size()); + TreeNode& old_info = search_stack[cep.creation_level]; + /* If the component was found when on the first path, + * handle the found automorphism as the other + * first path automorphisms */ + if(old_info.fp_on) + goto handle_first_path_automorphism; + } + } + + if(canonical and + !first_path_info.empty() and + child_node.cmp_to_best_path >= 0) { + if(cep.best_checked == false) + { + /* First time, go to the next component */ + cep.best_checked = true; + } + else + { + assert(cep.creation_level < search_stack.size()); + TreeNode& old_info = search_stack[cep.creation_level]; + if(child_node.cmp_to_best_path == 0) { + /* If the component was found when on the best path, + * handle the found automorphism as the other + * best path automorphisms */ + if(old_info.in_best_path) + goto handle_best_path_automorphism; + /* Otherwise, we do not remember the automorhism as + * we didn't memorize the path that was invariant + * equal to the best one and passed through the + * component. + * Thus we can only backtrack to the previous level */ + child_node.cmp_to_best_path = -1; + if(!child_node.fp_cert_equal) + { + continue; + } + } + else { + assert(child_node.cmp_to_best_path > 0); + if(old_info.in_best_path) + { + stats.nof_canupdates++; + /* + * Update canonical labeling and its inverse + */ + for(unsigned int i = 0; i < N; i++) { + if(p.get_cell(p.elements[i])->is_unit()) { + best_path_labeling[p.elements[i]] = i; + best_path_labeling_inv[i] = p.elements[i]; + } + } + //update_labeling_and_its_inverse(best_path_labeling, best_path_labeling_inv); + /* Reset best path automorphism */ + reset_permutation(best_path_automorphism); + /* Reset best path orbit structure */ + best_path_orbits.reset(); + /* Mark to be the best one and save prefix */ + unsigned int postfix_start = cep.creation_level; + assert(postfix_start < best_path_info.size()); + while(p.get_cell(best_path_info[postfix_start].splitting_element)->is_unit()) { + postfix_start++; + assert(postfix_start < best_path_info.size()); + } + unsigned int postfix_start_cert = best_path_info[postfix_start].certificate_index; + std::vector best_path_temp = best_path_info; + best_path_info.clear(); + for(unsigned int i = 0; i < search_stack.size(); i++) { + TreeNode& ss_info = search_stack[i]; + PathInfo bp_info; + ss_info.cmp_to_best_path = 0; + ss_info.in_best_path = true; + bp_info.splitting_element = ss_info.split_element; + bp_info.certificate_index = ss_info.certificate_index; + bp_info.subcertificate_length = ss_info.subcertificate_length; + bp_info.eqref_hash = ss_info.eqref_hash; + best_path_info.push_back(bp_info); + } + /* Copy the postfix of the previous best path */ + for(unsigned int i = postfix_start; + i < best_path_temp.size(); + i++) + { + best_path_info.push_back(best_path_temp[i]); + best_path_info[best_path_info.size()-1].certificate_index = + best_path_info[best_path_info.size()-2].certificate_index + + best_path_info[best_path_info.size()-2].subcertificate_length; + } + std::vector certificate_best_path_old = certificate_best_path; + certificate_best_path = certificate_current_path; + for(unsigned int i = postfix_start_cert; i < certificate_best_path_old.size(); i++) + certificate_best_path.push_back(certificate_best_path_old[i]); + assert(certificate_best_path.size() == best_path_info.back().certificate_index + best_path_info.back().subcertificate_length); + /* Backtrack to the previous level */ + continue; + } + } + } + } + + /* No backtracking performed, go to next componenet */ + cr_level = cep.next_cr_level; + cr_cep_index = cep.next_cep_index; + } + + /* Check if the current component has been split into + * new non-uniformity subcomponents */ + //if(nucr_find_first_component(cr_level) == true and + // p.nof_discrete_cells() + cr_component_elements < + // cr_cep_stack[cr_cep_index].discrete_cell_limit) + if(nucr_find_first_component(cr_level, cr_component, + cr_component_elements, + next_split_cell) == true and + p.nof_discrete_cells() + cr_component_elements < + cr_cep_stack[cr_cep_index].discrete_cell_limit) + { + const unsigned int next_cr_level = + p.cr_split_level(cr_level, cr_component); + CR_CEP cep; + cep.creation_level = search_stack.size(); + cep.discrete_cell_limit = + p.nof_discrete_cells() + cr_component_elements; + cep.next_cr_level = cr_level; + cep.next_cep_index = cr_cep_index; + cep.first_checked = false; + cep.best_checked = false; + cr_cep_index = cr_cep_stack.size(); + cr_cep_stack.push_back(cep); + cr_level = next_cr_level; + } + } + + + /* + * Build the next node info + */ + /* Find the next cell to be splitted */ + if(!next_split_cell) + next_split_cell = find_next_cell_to_be_splitted(p.get_cell(p.elements[current_node.split_cell_first])); + //Partition::Cell * const next_split_cell = find_next_cell_to_be_splitted(p.get_cell(p.elements[current_node.split_cell_first])); + child_node.split_cell_first = next_split_cell->first; + child_node.split_element = TreeNode::SPLIT_START; + child_node.certificate_index = certificate_index; + child_node.partition_bt_point = p.set_backtrack_point(); + child_node.long_prune_redundant.clear(); + child_node.long_prune_begin = current_node.long_prune_begin; + + /* Save component recursion info for backtracking */ + child_node.cr_level = cr_level; + child_node.cr_cep_stack_size = cr_cep_stack.size(); + child_node.cr_cep_index = cr_cep_index; + + search_stack.push_back(child_node); + continue; + } + + /* + * A leaf node not in the first path or equivalent to the first path + */ + + + + if(child_node.cmp_to_best_path > 0) + { + /* + * A new, better representative found + */ + //fprintf(stdout, "Level %u: NEW BEST\n", child_level); fflush(stdout); + stats.nof_canupdates++; + /* + * Update canonical labeling and its inverse + */ + update_labeling_and_its_inverse(best_path_labeling, + best_path_labeling_inv); + /* Reset best path automorphism */ + reset_permutation(best_path_automorphism); + /* Reset best path orbit structure */ + best_path_orbits.reset(); + /* + * Mark the current path to be the best one and save it + */ + const unsigned int base_size = search_stack.size(); + assert(current_level+1 == base_size); + best_path_info.clear(); + for(unsigned int i = 0; i < base_size; i++) { + search_stack[i].cmp_to_best_path = 0; + search_stack[i].in_best_path = true; + PathInfo path_info; + path_info.splitting_element = search_stack[i].split_element; + path_info.certificate_index = search_stack[i].certificate_index; + path_info.subcertificate_length = search_stack[i].subcertificate_length; + path_info.eqref_hash = search_stack[i].eqref_hash; + best_path_info.push_back(path_info); + } + certificate_best_path = certificate_current_path; + /* + * Backtrack to the previous level + */ + continue; + } + + + handle_best_path_automorphism: + /* + * + * Best path automorphism handling + * + */ + { + + /* + * Equal to the previous best path + */ + if(p.is_discrete()) + { +#if defined(BLISS_CONSISTENCY_CHECKS) + /* Verify that the automorphism is correctly built */ + for(unsigned int i = 0; i < N; i++) + assert(best_path_automorphism[i] == + p.elements[best_path_labeling[i]]); +#endif + } + else + { + /* An automorphism that was found before the partition was discrete. + * Set the image of all elements in non-disrete cells accordingly */ + for(Partition::Cell* c = p.first_nonsingleton_cell; c; + c = c->next_nonsingleton) { + for(unsigned int i = c->first; i < c->first+c->length; i++) + if(p.get_cell(p.elements[best_path_labeling[p.elements[i]]])->is_unit()) + best_path_automorphism[p.elements[best_path_labeling[p.elements[i]]]] = p.elements[i]; + else + best_path_automorphism[p.elements[i]] = p.elements[i]; + } + } + +#if defined(BLISS_VERIFY_AUTOMORPHISMS) + /* Verify that it really is an automorphism */ + if(!is_automorphism(best_path_automorphism)) + fatal_error("Best path automorhism validation check failed"); +#endif + + unsigned int gca_level_with_first = 0; + for(unsigned int i = search_stack.size(); i > 0; i--) { + if((int)first_path_info[gca_level_with_first].splitting_element != + search_stack[gca_level_with_first].split_element) + break; + gca_level_with_first++; + } + + unsigned int gca_level_with_best = 0; + for(unsigned int i = search_stack.size(); i > 0; i--) { + if((int)best_path_info[gca_level_with_best].splitting_element != + search_stack[gca_level_with_best].split_element) + break; + gca_level_with_best++; + } + + if(opt_use_long_prune) + { + /* Record automorphism */ + long_prune_add_automorphism(best_path_automorphism); + } + + /* + * Update orbit information + */ + update_orbit_information(best_path_orbits, best_path_automorphism); + + /* + * Update orbit information + */ + const unsigned int nof_old_orbits = first_path_orbits.nof_orbits(); + update_orbit_information(first_path_orbits, best_path_automorphism); + if(nof_old_orbits != first_path_orbits.nof_orbits()) + { + /* Some orbits were merged */ + /* Report automorphism */ + if(report) + report(get_nof_vertices(), best_path_automorphism); + /* Update statistics */ + stats.nof_generators++; + } + + /* + * Compute backjumping level + */ + unsigned int backjumping_level = current_level+1-1; + if(!first_path_orbits.is_minimal_representative(search_stack[gca_level_with_first].split_element)) + { + backjumping_level = gca_level_with_first; + } + else + { + assert(!best_path_orbits.is_minimal_representative(search_stack[gca_level_with_best].split_element)); + backjumping_level = gca_level_with_best; + } + /* Backtrack */ + search_stack.resize(backjumping_level + 1); + continue; + } + + + _INTERNAL_ERROR(); + + + handle_first_path_automorphism: + /* + * + * A first-path automorphism: aut[i] = elements[first_path_labeling[i]] + * + */ + + + if(p.is_discrete()) + { +#if defined(BLISS_CONSISTENCY_CHECKS) + /* Verify that the complete automorphism is correctly built */ + for(unsigned int i = 0; i < N; i++) + assert(first_path_automorphism[i] == + p.elements[first_path_labeling[i]]); +#endif + } + else + { + /* An automorphism that was found before the partition was discrete. + * Set the image of all elements in non-disrete cells accordingly */ + for(Partition::Cell* c = p.first_nonsingleton_cell; c; + c = c->next_nonsingleton) { + for(unsigned int i = c->first; i < c->first+c->length; i++) + if(p.get_cell(p.elements[first_path_labeling[p.elements[i]]])->is_unit()) + first_path_automorphism[p.elements[first_path_labeling[p.elements[i]]]] = p.elements[i]; + else + first_path_automorphism[p.elements[i]] = p.elements[i]; + } + } + +#if defined(BLISS_VERIFY_AUTOMORPHISMS) + /* Verify that it really is an automorphism */ + if(!is_automorphism(first_path_automorphism)) + fatal_error("First path automorphism validation check failed"); +#endif + + if(opt_use_long_prune) + { + long_prune_add_automorphism(first_path_automorphism); + } + + /* + * Update orbit information + */ + update_orbit_information(first_path_orbits, first_path_automorphism); + + /* + * Compute backjumping level + */ + for(unsigned int i = 0; i < search_stack.size(); i++) { + TreeNode& n = search_stack[i]; + if(n.fp_on) { + ; + } else { + n.fp_extendable = TreeNode::YES; + } + } + + /* Report automorphism by calling the user defined hook function */ + if(report) + report(get_nof_vertices(), first_path_automorphism); + /* Update statistics */ + stats.nof_generators++; + continue; + + } /* while(!search_stack.empty()) */ + + + + + /* Free "long prune" technique memory */ + if(opt_use_long_prune) + long_prune_deallocate(); + + /* Release component recursion data in partition */ + if(opt_use_comprec) + p.cr_free(); +} + + + + +void +AbstractGraph::find_automorphisms(Stats& stats, + const std::function& report, + const std::function& terminate) +{ + search(false, stats, report, terminate); + + delete[] first_path_labeling; first_path_labeling = nullptr; + delete[] best_path_labeling; best_path_labeling = nullptr; +} + + +const unsigned int * +AbstractGraph::canonical_form(Stats& stats, + const std::function& report, + const std::function& terminate) +{ + search(true, stats, report, terminate); + + return best_path_labeling; +} + + + + +/*------------------------------------------------------------------------- + * + * Routines for directed graphs + * + *-------------------------------------------------------------------------*/ + +Digraph::Vertex::Vertex() +{ + color = 0; +} + + +Digraph::Vertex::~Vertex() +{ + ; +} + + +void +Digraph::Vertex::add_edge_to(const unsigned int other_vertex) +{ + edges_out.push_back(other_vertex); +} + + +void +Digraph::Vertex::add_edge_from(const unsigned int other_vertex) +{ + edges_in.push_back(other_vertex); +} + + +void +Digraph::Vertex::remove_duplicate_edges(std::vector& tmp) +{ +#if defined(BLISS_CONSISTENCY_CHECKS) + /* Pre-conditions */ + for(unsigned int i = 0; i < tmp.size(); i++) assert(tmp[i] == false); +#endif + for(std::vector::iterator iter = edges_out.begin(); + iter != edges_out.end(); ) + { + const unsigned int dest_vertex = *iter; + if(tmp[dest_vertex] == true) + { + /* A duplicate edge found! */ + iter = edges_out.erase(iter); + } + else + { + /* Not seen earlier, mark as seen */ + tmp[dest_vertex] = true; + iter++; + } + } + + /* Clear tmp */ + for(std::vector::iterator iter = edges_out.begin(); + iter != edges_out.end(); + iter++) + { + tmp[*iter] = false; + } + + for(std::vector::iterator iter = edges_in.begin(); + iter != edges_in.end(); ) + { + const unsigned int dest_vertex = *iter; + if(tmp[dest_vertex] == true) + { + /* A duplicate edge found! */ + iter = edges_in.erase(iter); + } + else + { + /* Not seen earlier, mark as seen */ + tmp[dest_vertex] = true; + iter++; + } + } + + /* Clear tmp */ + for(std::vector::iterator iter = edges_in.begin(); + iter != edges_in.end(); + iter++) + { + tmp[*iter] = false; + } +#if defined(BLISS_CONSISTENCY_CHECKS) + /* Post-conditions */ + for(unsigned int i = 0; i < tmp.size(); i++) assert(tmp[i] == false); +#endif +} + + +/** + * Sort the edges entering and leaving the vertex according to + * the vertex number of the other edge end. + * Time complexity: O(e log(e)), where e is the number of edges + * entering/leaving the vertex. + */ +void +Digraph::Vertex::sort_edges() +{ + std::sort(edges_in.begin(), edges_in.end()); + std::sort(edges_out.begin(), edges_out.end()); +} + + + + + +/*------------------------------------------------------------------------- + * + * Constructor and destructor for directed graphs + * + *-------------------------------------------------------------------------*/ + + +Digraph::Digraph(const unsigned int nof_vertices) +{ + vertices.resize(nof_vertices); + sh = shs_flm; +} + + +Digraph::~Digraph() +{ + ; +} + + +unsigned int +Digraph::add_vertex(const unsigned int color) +{ + const unsigned int new_vertex_num = vertices.size(); + vertices.resize(new_vertex_num + 1); + vertices.back().color = color; + return new_vertex_num; +} + + +void +Digraph::add_edge(const unsigned int vertex1, const unsigned int vertex2) +{ + if(vertex1 >= vertices.size() or vertex2 >= vertices.size()) + throw std::runtime_error("out of bounds vertex number"); + //assert(vertex1 < get_nof_vertices()); + //assert(vertex2 < get_nof_vertices()); + vertices[vertex1].add_edge_to(vertex2); + vertices[vertex2].add_edge_from(vertex1); +} + + +void +Digraph::change_color(const unsigned int vertex, const unsigned int new_color) +{ + assert(vertex < get_nof_vertices()); + vertices[vertex].color = new_color; +} + + +void +Digraph::sort_edges() +{ + for(unsigned int i = 0; i < get_nof_vertices(); i++) + vertices[i].sort_edges(); +} + + +int +Digraph::cmp(Digraph& other) +{ + /* Compare the numbers of vertices */ + if(get_nof_vertices() < other.get_nof_vertices()) + return -1; + if(get_nof_vertices() > other.get_nof_vertices()) + return 1; + /* Compare vertex colors */ + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + if(vertices[i].color < other.vertices[i].color) + return -1; + if(vertices[i].color > other.vertices[i].color) + return 1; + } + /* Compare vertex degrees */ + remove_duplicate_edges(); + other.remove_duplicate_edges(); + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + if(vertices[i].nof_edges_in() < other.vertices[i].nof_edges_in()) + return -1; + if(vertices[i].nof_edges_in() > other.vertices[i].nof_edges_in()) + return 1; + if(vertices[i].nof_edges_out() < other.vertices[i].nof_edges_out()) + return -1; + if(vertices[i].nof_edges_out() > other.vertices[i].nof_edges_out()) + return 1; + } + /* Compare edges */ + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + Vertex& v1 = vertices[i]; + Vertex& v2 = other.vertices[i]; + v1.sort_edges(); + v2.sort_edges(); + std::vector::const_iterator ei1 = v1.edges_in.begin(); + std::vector::const_iterator ei2 = v2.edges_in.begin(); + while(ei1 != v1.edges_in.end()) + { + if(*ei1 < *ei2) + return -1; + if(*ei1 > *ei2) + return 1; + ei1++; + ei2++; + } + ei1 = v1.edges_out.begin(); + ei2 = v2.edges_out.begin(); + while(ei1 != v1.edges_out.end()) + { + if(*ei1 < *ei2) + return -1; + if(*ei1 > *ei2) + return 1; + ei1++; + ei2++; + } + } + return 0; +} + + + + +Digraph* +Digraph::permute(const std::vector& perm) const +{ + Digraph* const g = new Digraph(get_nof_vertices()); + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + const Vertex& v = vertices[i]; + g->change_color(perm[i], v.color); + for(std::vector::const_iterator ei = v.edges_out.begin(); + ei != v.edges_out.end(); + ei++) + { + g->add_edge(perm[i], perm[*ei]); + } + } + g->sort_edges(); + return g; +} + + +Digraph* +Digraph::permute(const unsigned int* const perm) const +{ + Digraph* const g = new Digraph(get_nof_vertices()); + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + const Vertex &v = vertices[i]; + g->change_color(perm[i], v.color); + for(std::vector::const_iterator ei = v.edges_out.begin(); + ei != v.edges_out.end(); + ei++) + { + g->add_edge(perm[i], perm[*ei]); + } + } + g->sort_edges(); + return g; +} + + +void +Digraph::remove_duplicate_edges() +{ + std::vector tmp(get_nof_vertices(), false); + + for(std::vector::iterator vi = vertices.begin(); + vi != vertices.end(); + vi++) + { +#if defined(BLISS_EXPENSIVE_CONSISTENCY_CHECKS) + for(unsigned int i = 0; i < tmp.size(); i++) assert(tmp[i] == false); +#endif + (*vi).remove_duplicate_edges(tmp); + } +} + + + + + +/*------------------------------------------------------------------------- + * + * Get a hash value for the graph. + * + *-------------------------------------------------------------------------*/ + +unsigned int +Digraph::get_hash() +{ + remove_duplicate_edges(); + sort_edges(); + + UintSeqHash h; + + h.update(get_nof_vertices()); + + /* Hash the color of each vertex */ + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + h.update(vertices[i].color); + } + + /* Hash the edges */ + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + Vertex &v = vertices[i]; + for(std::vector::const_iterator ei = v.edges_out.begin(); + ei != v.edges_out.end(); + ei++) + { + h.update(i); + h.update(*ei); + } + } + + return h.get_value(); +} + + +/*------------------------------------------------------------------------- + * + * Partition independent invariants + * + *-------------------------------------------------------------------------*/ + +unsigned int +Digraph::vertex_color_invariant(const Digraph* const g, const unsigned int vnum) +{ + return g->vertices[vnum].color; +} + +unsigned int +Digraph::indegree_invariant(const Digraph* const g, const unsigned int vnum) +{ + return g->vertices[vnum].nof_edges_in(); +} + +unsigned int +Digraph::outdegree_invariant(const Digraph* const g, const unsigned int vnum) +{ + return g->vertices[vnum].nof_edges_out(); +} + +unsigned int +Digraph::selfloop_invariant(const Digraph* const g, const unsigned int vnum) +{ + /* Quite inefficient but luckily not in the critical path */ + const Vertex& v = g->vertices[vnum]; + for(std::vector::const_iterator ei = v.edges_out.begin(); + ei != v.edges_out.end(); + ei++) + { + if(*ei == vnum) + return 1; + } + return 0; +} + + + + + +/*------------------------------------------------------------------------- + * + * Refine the partition p according to a partition independent invariant + * + *-------------------------------------------------------------------------*/ + +bool +Digraph::refine_according_to_invariant(unsigned int (*inv)(const Digraph* const g, + const unsigned int v)) +{ + bool refined = false; + + for(Partition::Cell* cell = p.first_nonsingleton_cell; cell; ) + { + + Partition::Cell* const next_cell = cell->next_nonsingleton; + const unsigned int* ep = p.elements + cell->first; + for(unsigned int i = cell->length; i > 0; i--, ep++) + { + unsigned int ival = inv(this, *ep); + p.invariant_values[*ep] = ival; + if(ival > cell->max_ival) { + cell->max_ival = ival; + cell->max_ival_count = 1; + } + else if(ival == cell->max_ival) { + cell->max_ival_count++; + } + } + Partition::Cell* const last_new_cell = p.zplit_cell(cell, true); + refined |= (last_new_cell != cell); + cell = next_cell; + } + + return refined; +} + + + + + +/*------------------------------------------------------------------------- + * + * Split the neighbourhood of a cell according to the equitable invariant + * + *-------------------------------------------------------------------------*/ + +bool +Digraph::split_neighbourhood_of_cell(Partition::Cell* const cell) +{ + + + const bool was_equal_to_first = refine_equal_to_first; + + if(compute_eqref_hash) + { + eqref_hash.update(cell->first); + eqref_hash.update(cell->length); + } + + const unsigned int* ep = p.elements + cell->first; + for(unsigned int i = cell->length; i > 0; i--) + { + const Vertex& v = vertices[*ep++]; + + std::vector::const_iterator ei = v.edges_out.begin(); + for(unsigned int j = v.nof_edges_out(); j != 0; j--) + { + const unsigned int dest_vertex = *ei++; + Partition::Cell* const neighbour_cell = p.get_cell(dest_vertex); + if(neighbour_cell->is_unit()) + continue; + const unsigned int ival = ++p.invariant_values[dest_vertex]; + if(ival > neighbour_cell->max_ival) { + neighbour_cell->max_ival = ival; + neighbour_cell->max_ival_count = 1; + if(ival == 1) + neighbour_heap.insert(neighbour_cell->first); + } + else if(ival == neighbour_cell->max_ival) { + neighbour_cell->max_ival_count++; + } + } + } + + while(!neighbour_heap.is_empty()) + { + const unsigned int start = neighbour_heap.remove(); + Partition::Cell* const neighbour_cell = p.get_cell(p.elements[start]); + + if(compute_eqref_hash) + { + eqref_hash.update(neighbour_cell->first); + eqref_hash.update(neighbour_cell->length); + eqref_hash.update(neighbour_cell->max_ival); + eqref_hash.update(neighbour_cell->max_ival_count); + } + + + Partition::Cell* const last_new_cell = p.zplit_cell(neighbour_cell, true); + + /* Update certificate and hash if needed */ + const Partition::Cell* c = neighbour_cell; + while(1) + { + if(in_search) + { + /* Build certificate */ + cert_add_redundant(CERT_SPLIT, c->first, c->length); + /* No need to continue? */ + if(refine_compare_certificate and + (refine_equal_to_first == false) and + (refine_cmp_to_best < 0)) + goto worse_exit; + } + if(compute_eqref_hash) + { + eqref_hash.update(c->first); + eqref_hash.update(c->length); + } + if(c == last_new_cell) + break; + c = c->next; + } + } + + if(cell->is_in_splitting_queue()) + { + return false; + } + + + ep = p.elements + cell->first; + for(unsigned int i = cell->length; i > 0; i--) + { + const Vertex& v = vertices[*ep++]; + + std::vector::const_iterator ei = v.edges_in.begin(); + for(unsigned int j = v.nof_edges_in(); j > 0; j--) + { + const unsigned int dest_vertex = *ei++; + Partition::Cell* const neighbour_cell = p.get_cell(dest_vertex); + if(neighbour_cell->is_unit()) + continue; + const unsigned int ival = ++p.invariant_values[dest_vertex]; + if(ival > neighbour_cell->max_ival) + { + neighbour_cell->max_ival = ival; + neighbour_cell->max_ival_count = 1; + if(ival == 1) + neighbour_heap.insert(neighbour_cell->first); + } + else if(ival == neighbour_cell->max_ival) { + neighbour_cell->max_ival_count++; + } + } + } + + while(!neighbour_heap.is_empty()) + { + const unsigned int start = neighbour_heap.remove(); + Partition::Cell* const neighbour_cell = p.get_cell(p.elements[start]); + + if(compute_eqref_hash) + { + eqref_hash.update(neighbour_cell->first); + eqref_hash.update(neighbour_cell->length); + eqref_hash.update(neighbour_cell->max_ival); + eqref_hash.update(neighbour_cell->max_ival_count); + } + + Partition::Cell* const last_new_cell = p.zplit_cell(neighbour_cell, true); + + /* Update certificate and hash if needed */ + const Partition::Cell* c = neighbour_cell; + while(1) + { + if(in_search) + { + /* Build certificate */ + cert_add_redundant(CERT_SPLIT, c->first, c->length); + /* No need to continue? */ + if(refine_compare_certificate and + (refine_equal_to_first == false) and + (refine_cmp_to_best < 0)) + goto worse_exit; + } + if(compute_eqref_hash) + { + eqref_hash.update(c->first); + eqref_hash.update(c->length); + } + if(c == last_new_cell) + break; + c = c->next; + } + } + + + if(refine_compare_certificate and + (refine_equal_to_first == false) and + (refine_cmp_to_best < 0)) + return true; + + return false; + + worse_exit: + /* Clear neighbour heap */ + UintSeqHash rest; + while(!neighbour_heap.is_empty()) + { + const unsigned int start = neighbour_heap.remove(); + Partition::Cell* const neighbour_cell = p.get_cell(p.elements[start]); + if(opt_use_failure_recording and was_equal_to_first) + { + rest.update(neighbour_cell->first); + rest.update(neighbour_cell->length); + rest.update(neighbour_cell->max_ival); + rest.update(neighbour_cell->max_ival_count); + } + neighbour_cell->max_ival = 0; + neighbour_cell->max_ival_count = 0; + p.clear_ivs(neighbour_cell); + } + if(opt_use_failure_recording and was_equal_to_first) + { + for(unsigned int i = p.splitting_queue.size(); i > 0; i--) + { + Partition::Cell* const cell = p.splitting_queue.pop_front(); + rest.update(cell->first); + rest.update(cell->length); + p.splitting_queue.push_back(cell); + } + rest.update(failure_recording_fp_deviation); + failure_recording_fp_deviation = rest.get_value(); + } + + return true; +} + + +bool +Digraph::split_neighbourhood_of_unit_cell(Partition::Cell* const unit_cell) +{ + + + const bool was_equal_to_first = refine_equal_to_first; + + if(compute_eqref_hash) + { + eqref_hash.update(0x87654321); + eqref_hash.update(unit_cell->first); + eqref_hash.update(1); + } + + const Vertex& v = vertices[p.elements[unit_cell->first]]; + + /* + * Phase 1 + * Refine neighbours according to the edges that leave the vertex v + */ + std::vector::const_iterator ei = v.edges_out.begin(); + for(unsigned int j = v.nof_edges_out(); j > 0; j--) + { + const unsigned int dest_vertex = *ei++; + Partition::Cell* const neighbour_cell = p.get_cell(dest_vertex); + + if(neighbour_cell->is_unit()) { + if(in_search) { + /* Remember neighbour in order to generate certificate */ + neighbour_heap.insert(neighbour_cell->first); + } + continue; + } + if(neighbour_cell->max_ival_count == 0) + { + neighbour_heap.insert(neighbour_cell->first); + } + neighbour_cell->max_ival_count++; + + unsigned int* const swap_position = + p.elements + neighbour_cell->first + neighbour_cell->length - + neighbour_cell->max_ival_count; + *p.in_pos[dest_vertex] = *swap_position; + p.in_pos[*swap_position] = p.in_pos[dest_vertex]; + *swap_position = dest_vertex; + p.in_pos[dest_vertex] = swap_position; + } + + while(!neighbour_heap.is_empty()) + { + const unsigned int start = neighbour_heap.remove(); + Partition::Cell* neighbour_cell = p.get_cell(p.elements[start]); + +#if defined(BLISS_CONSISTENCY_CHECKS) + assert(neighbour_cell->first == start); + if(neighbour_cell->is_unit()) { + assert(neighbour_cell->max_ival_count == 0); + } else { + assert(neighbour_cell->max_ival_count > 0); + assert(neighbour_cell->max_ival_count <= neighbour_cell->length); + } +#endif + + if(compute_eqref_hash) + { + eqref_hash.update(neighbour_cell->first); + eqref_hash.update(neighbour_cell->length); + eqref_hash.update(neighbour_cell->max_ival_count); + } + + if(neighbour_cell->length > 1 and + neighbour_cell->max_ival_count != neighbour_cell->length) + { + + Partition::Cell* const new_cell = + p.aux_split_in_two(neighbour_cell, + neighbour_cell->length - + neighbour_cell->max_ival_count); + unsigned int* ep = p.elements + new_cell->first; + unsigned int* const lp = p.elements+new_cell->first+new_cell->length; + while(ep < lp) + { + p.element_to_cell_map[*ep] = new_cell; + ep++; + } + neighbour_cell->max_ival_count = 0; + + + if(compute_eqref_hash) + { + /* Update hash */ + eqref_hash.update(neighbour_cell->first); + eqref_hash.update(neighbour_cell->length); + eqref_hash.update(0); + eqref_hash.update(new_cell->first); + eqref_hash.update(new_cell->length); + eqref_hash.update(1); + } + + /* Add cells in splitting_queue */ + if(neighbour_cell->is_in_splitting_queue()) { + /* Both cells must be included in splitting_queue in order + to have refinement to equitable partition */ + p.splitting_queue_add(new_cell); + } else { + Partition::Cell *min_cell, *max_cell; + if(neighbour_cell->length <= new_cell->length) { + min_cell = neighbour_cell; + max_cell = new_cell; + } else { + min_cell = new_cell; + max_cell = neighbour_cell; + } + /* Put the smaller cell in splitting_queue */ + p.splitting_queue_add(min_cell); + if(max_cell->is_unit()) { + /* Put the "larger" cell also in splitting_queue */ + p.splitting_queue_add(max_cell); + } + } + /* Update pointer for certificate generation */ + neighbour_cell = new_cell; + } + else + { + neighbour_cell->max_ival_count = 0; + } + + /* + * Build certificate if required + */ + if(in_search) + { + for(unsigned int i = neighbour_cell->first, + j = neighbour_cell->length; + j > 0; + j--, i++) + { + /* Build certificate */ + cert_add(CERT_EDGE, unit_cell->first, i); + /* No need to continue? */ + if(refine_compare_certificate and + (refine_equal_to_first == false) and + (refine_cmp_to_best < 0)) + goto worse_exit; + } + } /* if(in_search) */ + } /* while(!neighbour_heap.is_empty()) */ + + /* + * Phase 2 + * Refine neighbours according to the edges that enter the vertex v + */ + ei = v.edges_in.begin(); + for(unsigned int j = v.nof_edges_in(); j > 0; j--) + { + const unsigned int dest_vertex = *ei++; + Partition::Cell* const neighbour_cell = p.get_cell(dest_vertex); + + if(neighbour_cell->is_unit()) { + if(in_search) { + neighbour_heap.insert(neighbour_cell->first); + } + continue; + } + if(neighbour_cell->max_ival_count == 0) + { + neighbour_heap.insert(neighbour_cell->first); + } + neighbour_cell->max_ival_count++; + + unsigned int* const swap_position = + p.elements + neighbour_cell->first + neighbour_cell->length - + neighbour_cell->max_ival_count; + *p.in_pos[dest_vertex] = *swap_position; + p.in_pos[*swap_position] = p.in_pos[dest_vertex]; + *swap_position = dest_vertex; + p.in_pos[dest_vertex] = swap_position; + } + + while(!neighbour_heap.is_empty()) + { + const unsigned int start = neighbour_heap.remove(); + Partition::Cell* neighbour_cell = p.get_cell(p.elements[start]); + +#if defined(BLISS_CONSISTENCY_CHECKS) + assert(neighbour_cell->first == start); + if(neighbour_cell->is_unit()) { + assert(neighbour_cell->max_ival_count == 0); + } else { + assert(neighbour_cell->max_ival_count > 0); + assert(neighbour_cell->max_ival_count <= neighbour_cell->length); + } +#endif + + if(compute_eqref_hash) + { + eqref_hash.update(neighbour_cell->first); + eqref_hash.update(neighbour_cell->length); + eqref_hash.update(neighbour_cell->max_ival_count); + } + + if(neighbour_cell->length > 1 and + neighbour_cell->max_ival_count != neighbour_cell->length) + { + Partition::Cell* const new_cell = + p.aux_split_in_two(neighbour_cell, + neighbour_cell->length - + neighbour_cell->max_ival_count); + unsigned int* ep = p.elements + new_cell->first; + unsigned int* const lp = p.elements+new_cell->first+new_cell->length; + while(ep < lp) { + p.element_to_cell_map[*ep] = new_cell; + ep++; + } + neighbour_cell->max_ival_count = 0; + + + if(compute_eqref_hash) + { + eqref_hash.update(neighbour_cell->first); + eqref_hash.update(neighbour_cell->length); + eqref_hash.update(0); + eqref_hash.update(new_cell->first); + eqref_hash.update(new_cell->length); + eqref_hash.update(1); + } + + /* Add cells in splitting_queue */ + if(neighbour_cell->is_in_splitting_queue()) { + /* Both cells must be included in splitting_queue in order + to have refinement to equitable partition */ + p.splitting_queue_add(new_cell); + } else { + Partition::Cell *min_cell, *max_cell; + if(neighbour_cell->length <= new_cell->length) { + min_cell = neighbour_cell; + max_cell = new_cell; + } else { + min_cell = new_cell; + max_cell = neighbour_cell; + } + /* Put the smaller cell in splitting_queue */ + p.splitting_queue_add(min_cell); + if(max_cell->is_unit()) { + /* Put the "larger" cell also in splitting_queue */ + p.splitting_queue_add(max_cell); + } + } + /* Update pointer for certificate generation */ + neighbour_cell = new_cell; + } + else + { + neighbour_cell->max_ival_count = 0; + } + + /* + * Build certificate if required + */ + if(in_search) + { + for(unsigned int i = neighbour_cell->first, + j = neighbour_cell->length; + j > 0; + j--, i++) + { + /* Build certificate */ + cert_add(CERT_EDGE, i, unit_cell->first); + /* No need to continue? */ + if(refine_compare_certificate and + (refine_equal_to_first == false) and + (refine_cmp_to_best < 0)) + goto worse_exit; + } + } /* if(in_search) */ + } /* while(!neighbour_heap.is_empty()) */ + + if(refine_compare_certificate and + (refine_equal_to_first == false) and + (refine_cmp_to_best < 0)) + return true; + + return false; + + worse_exit: + /* Clear neighbour heap */ + UintSeqHash rest; + while(!neighbour_heap.is_empty()) + { + const unsigned int start = neighbour_heap.remove(); + Partition::Cell* const neighbour_cell = p.get_cell(p.elements[start]); + if(opt_use_failure_recording and was_equal_to_first) + { + rest.update(neighbour_cell->first); + rest.update(neighbour_cell->length); + rest.update(neighbour_cell->max_ival_count); + } + neighbour_cell->max_ival_count = 0; + } + if(opt_use_failure_recording and was_equal_to_first) + { + rest.update(failure_recording_fp_deviation); + failure_recording_fp_deviation = rest.get_value(); + } + return true; +} + + + + + +/*------------------------------------------------------------------------- + * + * Check whether the current partition p is equitable. + * Performance: very slow, use only for debugging purposes. + * + *-------------------------------------------------------------------------*/ + +bool +Digraph::is_equitable() const +{ + const unsigned int N = get_nof_vertices(); + if(N == 0) + return true; + + std::vector first_count = std::vector(N, 0); + std::vector other_count = std::vector(N, 0); + + /* + * Check equitabledness w.r.t. outgoing edges + */ + for(Partition::Cell* cell = p.first_cell; cell; cell = cell->next) + { + if(cell->is_unit()) + continue; + + unsigned int* ep = p.elements + cell->first; + const Vertex& first_vertex = vertices[*ep++]; + + /* Count outgoing edges of the first vertex for cells */ + for(std::vector::const_iterator ei = + first_vertex.edges_out.begin(); + ei != first_vertex.edges_out.end(); + ei++) + { + first_count[p.get_cell(*ei)->first]++; + } + + /* Count and compare outgoing edges of the other vertices */ + for(unsigned int i = cell->length; i > 1; i--) + { + const Vertex &vertex = vertices[*ep++]; + for(std::vector::const_iterator ei = + vertex.edges_out.begin(); + ei != vertex.edges_out.end(); + ei++) + { + other_count[p.get_cell(*ei)->first]++; + } + for(Partition::Cell *cell2 = p.first_cell; + cell2; + cell2 = cell2->next) + { + if(first_count[cell2->first] != other_count[cell2->first]) + { + /* Not equitable */ + return false; + } + other_count[cell2->first] = 0; + } + } + /* Reset first_count */ + for(unsigned int i = 0; i < N; i++) + first_count[i] = 0; + } + + + /* + * Check equitabledness w.r.t. incoming edges + */ + for(Partition::Cell* cell = p.first_cell; cell; cell = cell->next) + { + if(cell->is_unit()) + continue; + + unsigned int* ep = p.elements + cell->first; + const Vertex& first_vertex = vertices[*ep++]; + + /* Count incoming edges of the first vertex for cells */ + for(std::vector::const_iterator ei = + first_vertex.edges_in.begin(); + ei != first_vertex.edges_in.end(); + ei++) + { + first_count[p.get_cell(*ei)->first]++; + } + + /* Count and compare incoming edges of the other vertices */ + for(unsigned int i = cell->length; i > 1; i--) + { + const Vertex &vertex = vertices[*ep++]; + for(std::vector::const_iterator ei = + vertex.edges_in.begin(); + ei != vertex.edges_in.end(); + ei++) + { + other_count[p.get_cell(*ei)->first]++; + } + for(Partition::Cell *cell2 = p.first_cell; + cell2; + cell2 = cell2->next) + { + if(first_count[cell2->first] != other_count[cell2->first]) + { + /* Not equitable */ + return false; + } + other_count[cell2->first] = 0; + } + } + /* Reset first_count */ + for(unsigned int i = 0; i < N; i++) + first_count[i] = 0; + } + return true; +} + + + + + +/*------------------------------------------------------------------------- + * + * Build the initial equitable partition + * + *-------------------------------------------------------------------------*/ + +void +Digraph::make_initial_equitable_partition() +{ + refine_according_to_invariant(&vertex_color_invariant); + p.splitting_queue_clear(); + //p.print_signature(stderr); fprintf(stderr, "\n"); + + refine_according_to_invariant(&selfloop_invariant); + p.splitting_queue_clear(); + //p.print_signature(stderr); fprintf(stderr, "\n"); + + refine_according_to_invariant(&outdegree_invariant); + p.splitting_queue_clear(); + //p.print_signature(stderr); fprintf(stderr, "\n"); + + refine_according_to_invariant(&indegree_invariant); + p.splitting_queue_clear(); + //p.print_signature(stderr); fprintf(stderr, "\n"); + + refine_to_equitable(); + //p.print_signature(stderr); fprintf(stderr, "\n"); +} + + + + + +/*------------------------------------------------------------------------- + * + * Find the next cell to be splitted + * + *-------------------------------------------------------------------------*/ + +Partition::Cell* +Digraph::find_next_cell_to_be_splitted(Partition::Cell* cell) +{ + switch(sh) { + case shs_f: return sh_first(); + case shs_fs: return sh_first_smallest(); + case shs_fl: return sh_first_largest(); + case shs_fm: return sh_first_max_neighbours(); + case shs_fsm: return sh_first_smallest_max_neighbours(); + case shs_flm: return sh_first_largest_max_neighbours(); + default: + fatal_error("Internal error - unknown splitting heuristics"); + return 0; + } +} + +/** \internal + * A splitting heuristic. + * Returns the first nonsingleton cell in the current partition. + * The argument \a cell is ignored. + */ +Partition::Cell* +Digraph::sh_first() +{ + Partition::Cell* best_cell = 0; + for(Partition::Cell* cell = p.first_nonsingleton_cell; + cell; + cell = cell->next_nonsingleton) + { + if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level) + continue; + best_cell = cell; + break; + } + return best_cell; +} + +/** \internal + * A splitting heuristic. + * Returns the first smallest nonsingleton cell in the current partition. + * The argument \a cell is ignored. + */ +Partition::Cell* +Digraph::sh_first_smallest() +{ + Partition::Cell* best_cell = 0; + unsigned int best_size = UINT_MAX; + for(Partition::Cell* cell = p.first_nonsingleton_cell; + cell; + cell = cell->next_nonsingleton) + { + if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level) + continue; + if(cell->length < best_size) + { + best_size = cell->length; + best_cell = cell; + } + } + return best_cell; +} + +/** \internal + * A splitting heuristic. + * Returns the first largest nonsingleton cell in the current partition. + * The argument \a cell is ignored. + */ +Partition::Cell* +Digraph::sh_first_largest() +{ + Partition::Cell* best_cell = 0; + unsigned int best_size = 0; + for(Partition::Cell* cell = p.first_nonsingleton_cell; + cell; + cell = cell->next_nonsingleton) + { + if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level) + continue; + if(cell->length > best_size) + { + best_size = cell->length; + best_cell = cell; + } + } + return best_cell; +} + +/** \internal + * A splitting heuristic. + * Returns the first nonsingleton cell with max number of neighbouring + * nonsingleton cells. + * Assumes that the partition p is equitable. + * Assumes that the max_ival fields of the cells are all 0. + */ +Partition::Cell* +Digraph::sh_first_max_neighbours() +{ + Partition::Cell* best_cell = 0; + int best_value = -1; + KStack neighbour_cells_visited; + neighbour_cells_visited.init(get_nof_vertices()); + for(Partition::Cell* cell = p.first_nonsingleton_cell; + cell; + cell = cell->next_nonsingleton) + { + if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level) + continue; + int value = 0; + const Vertex &v = vertices[p.elements[cell->first]]; + std::vector::const_iterator ei; + ei = v.edges_in.begin(); + for(unsigned int j = v.nof_edges_in(); j > 0; j--) + { + Partition::Cell * const neighbour_cell = p.get_cell(*ei++); + if(neighbour_cell->is_unit()) + continue; + neighbour_cell->max_ival++; + if(neighbour_cell->max_ival == 1) + neighbour_cells_visited.push(neighbour_cell); + } + while(!neighbour_cells_visited.is_empty()) + { + Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop(); + if(neighbour_cell->max_ival != neighbour_cell->length) + value++; + neighbour_cell->max_ival = 0; + } + + ei = v.edges_out.begin(); + for(unsigned int j = v.nof_edges_out(); j > 0; j--) + { + Partition::Cell * const neighbour_cell = p.get_cell(*ei++); + if(neighbour_cell->is_unit()) + continue; + neighbour_cell->max_ival++; + if(neighbour_cell->max_ival == 1) + neighbour_cells_visited.push(neighbour_cell); + } + while(!neighbour_cells_visited.is_empty()) + { + Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop(); + if(neighbour_cell->max_ival != neighbour_cell->length) + value++; + neighbour_cell->max_ival = 0; + } + + if(value > best_value) + { + best_value = value; + best_cell = cell; + } + } + return best_cell; +} + +/** \internal + * A splitting heuristic. + * Returns the first smallest nonsingleton cell with max number of neighbouring + * nonsingleton cells. + * Assumes that the partition p is equitable. + * Assumes that the max_ival fields of the cells are all 0. + */ +Partition::Cell* +Digraph::sh_first_smallest_max_neighbours() +{ + Partition::Cell* best_cell = 0; + int best_value = -1; + unsigned int best_size = UINT_MAX; + KStack neighbour_cells_visited; + neighbour_cells_visited.init(get_nof_vertices()); + for(Partition::Cell* cell = p.first_nonsingleton_cell; + cell; + cell = cell->next_nonsingleton) + { + + if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level) + continue; + + int value = 0; + const Vertex& v = vertices[p.elements[cell->first]]; + std::vector::const_iterator ei; + + ei = v.edges_in.begin(); + for(unsigned int j = v.nof_edges_in(); j > 0; j--) + { + Partition::Cell * const neighbour_cell = p.get_cell(*ei++); + if(neighbour_cell->is_unit()) + continue; + neighbour_cell->max_ival++; + if(neighbour_cell->max_ival == 1) + neighbour_cells_visited.push(neighbour_cell); + } + while(!neighbour_cells_visited.is_empty()) + { + Partition::Cell * const neighbour_cell = neighbour_cells_visited.pop(); + if(neighbour_cell->max_ival != neighbour_cell->length) + value++; + neighbour_cell->max_ival = 0; + } + + ei = v.edges_out.begin(); + for(unsigned int j = v.nof_edges_out(); j > 0; j--) + { + Partition::Cell * const neighbour_cell = p.get_cell(*ei++); + if(neighbour_cell->is_unit()) + continue; + neighbour_cell->max_ival++; + if(neighbour_cell->max_ival == 1) + neighbour_cells_visited.push(neighbour_cell); + } + while(!neighbour_cells_visited.is_empty()) + { + Partition::Cell * const neighbour_cell = neighbour_cells_visited.pop(); + if(neighbour_cell->max_ival != neighbour_cell->length) + value++; + neighbour_cell->max_ival = 0; + } + + if((value > best_value) or + (value == best_value and cell->length < best_size)) + { + best_value = value; + best_size = cell->length; + best_cell = cell; + } + } + return best_cell; +} + +/** \internal + * A splitting heuristic. + * Returns the first largest nonsingleton cell with max number of neighbouring + * nonsingleton cells. + * Assumes that the partition p is equitable. + * Assumes that the max_ival fields of the cells are all 0. + */ +Partition::Cell* +Digraph::sh_first_largest_max_neighbours() +{ + Partition::Cell* best_cell = 0; + int best_value = -1; + unsigned int best_size = 0; + KStack neighbour_cells_visited; + neighbour_cells_visited.init(get_nof_vertices()); + for(Partition::Cell* cell = p.first_nonsingleton_cell; + cell; + cell = cell->next_nonsingleton) + { + + if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level) + continue; + + int value = 0; + const Vertex &v = vertices[p.elements[cell->first]]; + std::vector::const_iterator ei; + + ei = v.edges_in.begin(); + for(unsigned int j = v.nof_edges_in(); j > 0; j--) + { + Partition::Cell* const neighbour_cell = p.get_cell(*ei++); + if(neighbour_cell->is_unit()) + continue; + neighbour_cell->max_ival++; + if(neighbour_cell->max_ival == 1) + neighbour_cells_visited.push(neighbour_cell); + } + while(!neighbour_cells_visited.is_empty()) + { + Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop(); + if(neighbour_cell->max_ival != neighbour_cell->length) + value++; + neighbour_cell->max_ival = 0; + } + + ei = v.edges_out.begin(); + for(unsigned int j = v.nof_edges_out(); j > 0; j--) + { + Partition::Cell* const neighbour_cell = p.get_cell(*ei++); + if(neighbour_cell->is_unit()) + continue; + neighbour_cell->max_ival++; + if(neighbour_cell->max_ival == 1) + neighbour_cells_visited.push(neighbour_cell); + } + while(!neighbour_cells_visited.is_empty()) + { + Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop(); + if(neighbour_cell->max_ival != neighbour_cell->length) + value++; + neighbour_cell->max_ival = 0; + } + + if((value > best_value) || + (value == best_value && cell->length > best_size)) + { + best_value = value; + best_size = cell->length; + best_cell = cell; + } + } + return best_cell; +} + + + + + + +/*------------------------------------------------------------------------ + * + * Initialize the certificate size and memory + * + *-------------------------------------------------------------------------*/ + +void +Digraph::initialize_certificate() +{ + certificate_index = 0; + certificate_current_path.clear(); + certificate_first_path.clear(); + certificate_best_path.clear(); +} + + + +/* + * Check whether perm is an automorphism. + * Slow, mainly for debugging and validation purposes. + */ +bool +Digraph::is_automorphism(unsigned int* const perm) const +{ + std::set > edges1; + std::set > edges2; + +#if defined(BLISS_CONSISTENCY_CHECKS) + if(!is_permutation(get_nof_vertices(), perm)) + _INTERNAL_ERROR(); +#endif + + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + const Vertex& v1 = vertices[i]; + const Vertex& v2 = vertices[perm[i]]; + + edges1.clear(); + for(std::vector::const_iterator ei = v1.edges_in.cbegin(); + ei != v1.edges_in.cend(); + ei++) + edges1.insert(perm[*ei]); + edges2.clear(); + for(std::vector::const_iterator ei = v2.edges_in.cbegin(); + ei != v2.edges_in.cend(); + ei++) + edges2.insert(*ei); + if(!(edges1 == edges2)) + return false; + + edges1.clear(); + for(std::vector::const_iterator ei = v1.edges_out.cbegin(); + ei != v1.edges_out.cend(); + ei++) + edges1.insert(perm[*ei]); + edges2.clear(); + for(std::vector::const_iterator ei = v2.edges_out.cbegin(); + ei != v2.edges_out.cend(); + ei++) + edges2.insert(*ei); + if(!(edges1 == edges2)) + return false; + } + + return true; +} + +bool +Digraph::is_automorphism(const std::vector& perm) const +{ + + if(!(perm.size() == get_nof_vertices() and is_permutation(perm))) + return false; + + std::set > edges1; + std::set > edges2; + + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + const Vertex& v1 = vertices[i]; + const Vertex& v2 = vertices[perm[i]]; + + edges1.clear(); + for(std::vector::const_iterator ei = v1.edges_in.begin(); + ei != v1.edges_in.end(); + ei++) + edges1.insert(perm[*ei]); + edges2.clear(); + for(std::vector::const_iterator ei = v2.edges_in.begin(); + ei != v2.edges_in.end(); + ei++) + edges2.insert(*ei); + if(!(edges1 == edges2)) + return false; + + edges1.clear(); + for(std::vector::const_iterator ei = v1.edges_out.begin(); + ei != v1.edges_out.end(); + ei++) + edges1.insert(perm[*ei]); + edges2.clear(); + for(std::vector::const_iterator ei = v2.edges_out.begin(); + ei != v2.edges_out.end(); + ei++) + edges2.insert(*ei); + if(!(edges1 == edges2)) + return false; + } + + return true; +} + + + + +bool +Digraph::nucr_find_first_component(const unsigned int level) +{ + + cr_component.clear(); + cr_component_elements = 0; + + /* Find first non-discrete cell in the component level */ + Partition::Cell* first_cell = p.first_nonsingleton_cell; + while(first_cell) + { + if(p.cr_get_level(first_cell->first) == level) + break; + first_cell = first_cell->next_nonsingleton; + } + + /* The component is discrete, return false */ + if(!first_cell) + return false; + + std::vector component; + first_cell->max_ival = 1; + component.push_back(first_cell); + + for(unsigned int i = 0; i < component.size(); i++) + { + Partition::Cell* const cell = component[i]; + + const Vertex& v = vertices[p.elements[cell->first]]; + std::vector::const_iterator ei; + + ei = v.edges_out.begin(); + for(unsigned int j = v.nof_edges_out(); j > 0; j--) + { + const unsigned int neighbour = *ei++; + Partition::Cell* const neighbour_cell = p.get_cell(neighbour); + + /* Skip unit neighbours */ + if(neighbour_cell->is_unit()) + continue; + /* Already marked to be in the same component? */ + if(neighbour_cell->max_ival == 1) + continue; + /* Is the neighbour at the same component recursion level? */ + if(p.cr_get_level(neighbour_cell->first) != level) + continue; + + if(neighbour_cell->max_ival_count == 0) + neighbour_heap.insert(neighbour_cell->first); + neighbour_cell->max_ival_count++; + } + while(!neighbour_heap.is_empty()) + { + const unsigned int start = neighbour_heap.remove(); + Partition::Cell* const neighbour_cell = + p.get_cell(p.elements[start]); + + /* Skip saturated neighbour cells */ + if(neighbour_cell->max_ival_count == neighbour_cell->length) + { + neighbour_cell->max_ival_count = 0; + continue; + } + neighbour_cell->max_ival_count = 0; + neighbour_cell->max_ival = 1; + component.push_back(neighbour_cell); + } + + ei = v.edges_in.begin(); + for(unsigned int j = v.nof_edges_in(); j > 0; j--) + { + const unsigned int neighbour = *ei++; + + Partition::Cell* const neighbour_cell = p.get_cell(neighbour); + + /* Skip unit neighbours */ + if(neighbour_cell->is_unit()) + continue; + /* Already marked to be in the same component? */ + if(neighbour_cell->max_ival == 1) + continue; + /* Is the neighbour at the same component recursion level? */ + if(p.cr_get_level(neighbour_cell->first) != level) + continue; + + if(neighbour_cell->max_ival_count == 0) + neighbour_heap.insert(neighbour_cell->first); + neighbour_cell->max_ival_count++; + } + while(!neighbour_heap.is_empty()) + { + const unsigned int start = neighbour_heap.remove(); + Partition::Cell* const neighbour_cell = + p.get_cell(p.elements[start]); + + /* Skip saturated neighbour cells */ + if(neighbour_cell->max_ival_count == neighbour_cell->length) + { + neighbour_cell->max_ival_count = 0; + continue; + } + neighbour_cell->max_ival_count = 0; + neighbour_cell->max_ival = 1; + component.push_back(neighbour_cell); + } + } + + for(unsigned int i = 0; i < component.size(); i++) + { + Partition::Cell* const cell = component[i]; + cell->max_ival = 0; + cr_component.push_back(cell->first); + cr_component_elements += cell->length; + } + + /* + if(verbstr and verbose_level > 2) { + fprintf(verbstr, "NU-component with %lu cells and %u vertices\n", + (long unsigned)cr_component.size(), cr_component_elements); + fflush(verbstr); + } + */ + + return true; +} + + + + + +bool +Digraph::nucr_find_first_component(const unsigned int level, + std::vector& component, + unsigned int& component_elements, + Partition::Cell*& sh_return) +{ + + component.clear(); + component_elements = 0; + sh_return = 0; + unsigned int sh_first = 0; + unsigned int sh_size = 0; + unsigned int sh_nuconn = 0; + + /* Find first non-discrete cell in the component level */ + Partition::Cell* first_cell = p.first_nonsingleton_cell; + while(first_cell) + { + if(p.cr_get_level(first_cell->first) == level) + break; + first_cell = first_cell->next_nonsingleton; + } + + if(!first_cell) + { + /* The component is discrete, return false */ + return false; + } + + std::vector comp; + KStack neighbours; + neighbours.init(get_nof_vertices()); + + first_cell->max_ival = 1; + comp.push_back(first_cell); + + for(unsigned int i = 0; i < comp.size(); i++) + { + Partition::Cell* const cell = comp[i]; + + unsigned int nuconn = 1; + + const Vertex& v = vertices[p.elements[cell->first]]; + std::vector::const_iterator ei; + + /*| Phase 1: outgoing edges */ + ei = v.edges_out.begin(); + for(unsigned int j = v.nof_edges_out(); j > 0; j--) + { + const unsigned int neighbour = *ei++; + + Partition::Cell* const neighbour_cell = p.get_cell(neighbour); + + /* Skip unit neighbours */ + if(neighbour_cell->is_unit()) + continue; + /* Is the neighbour at the same component recursion level? */ + //if(p.cr_get_level(neighbour_cell->first) != level) + // continue; + if(neighbour_cell->max_ival_count == 0) + neighbours.push(neighbour_cell); + neighbour_cell->max_ival_count++; + } + while(!neighbours.is_empty()) + { + Partition::Cell* const neighbour_cell = neighbours.pop(); + /* Skip saturated neighbour cells */ + if(neighbour_cell->max_ival_count == neighbour_cell->length) + { + neighbour_cell->max_ival_count = 0; + continue; + } + nuconn++; + neighbour_cell->max_ival_count = 0; + if(neighbour_cell->max_ival == 0) { + comp.push_back(neighbour_cell); + neighbour_cell->max_ival = 1; + } + } + + /*| Phase 2: incoming edges */ + ei = v.edges_in.begin(); + for(unsigned int j = v.nof_edges_in(); j > 0; j--) + { + const unsigned int neighbour = *ei++; + Partition::Cell* const neighbour_cell = p.get_cell(neighbour); + /*| Skip unit neighbours */ + if(neighbour_cell->is_unit()) + continue; + /* Is the neighbour at the same component recursion level? */ + //if(p.cr_get_level(neighbour_cell->first) != level) + // continue; + if(neighbour_cell->max_ival_count == 0) + neighbours.push(neighbour_cell); + neighbour_cell->max_ival_count++; + } + while(!neighbours.is_empty()) + { + Partition::Cell* const neighbour_cell = neighbours.pop(); + /* Skip saturated neighbour cells */ + if(neighbour_cell->max_ival_count == neighbour_cell->length) + { + neighbour_cell->max_ival_count = 0; + continue; + } + nuconn++; + neighbour_cell->max_ival_count = 0; + if(neighbour_cell->max_ival == 0) { + comp.push_back(neighbour_cell); + neighbour_cell->max_ival = 1; + } + } + + /*| Phase 3: splitting heuristics */ + switch(sh) { + case shs_f: + if(sh_return == 0 or + cell->first <= sh_first) { + sh_return = cell; + sh_first = cell->first; + } + break; + case shs_fs: + if(sh_return == 0 or + cell->length < sh_size or + (cell->length == sh_size and cell->first <= sh_first)) { + sh_return = cell; + sh_first = cell->first; + sh_size = cell->length; + } + break; + case shs_fl: + if(sh_return == 0 or + cell->length > sh_size or + (cell->length == sh_size and cell->first <= sh_first)) { + sh_return = cell; + sh_first = cell->first; + sh_size = cell->length; + } + break; + case shs_fm: + if(sh_return == 0 or + nuconn > sh_nuconn or + (nuconn == sh_nuconn and cell->first <= sh_first)) { + sh_return = cell; + sh_first = cell->first; + sh_nuconn = nuconn; + } + break; + case shs_fsm: + if(sh_return == 0 or + nuconn > sh_nuconn or + (nuconn == sh_nuconn and + (cell->length < sh_size or + (cell->length == sh_size and cell->first <= sh_first)))) { + sh_return = cell; + sh_first = cell->first; + sh_size = cell->length; + sh_nuconn = nuconn; + } + break; + case shs_flm: + if(sh_return == 0 or + nuconn > sh_nuconn or + (nuconn == sh_nuconn and + (cell->length > sh_size or + (cell->length == sh_size and cell->first <= sh_first)))) { + sh_return = cell; + sh_first = cell->first; + sh_size = cell->length; + sh_nuconn = nuconn; + } + break; + default: + fatal_error("Internal error - unknown splitting heuristics"); + return 0; + } + } + assert(sh_return); + + for(unsigned int i = 0; i < comp.size(); i++) + { + Partition::Cell* const cell = comp[i]; + cell->max_ival = 0; + component.push_back(cell->first); + component_elements += cell->length; + } + + /* + if(verbstr and verbose_level > 2) { + fprintf(verbstr, "NU-component with %lu cells and %u vertices\n", + (long unsigned)component.size(), component_elements); + fflush(verbstr); + } + */ + + return true; +} + + + + +/*------------------------------------------------------------------------- + * + * Routines for undirected graphs + * + *-------------------------------------------------------------------------*/ + +Graph::Vertex::Vertex() +{ + color = 0; +} + + +Graph::Vertex::~Vertex() +{ + ; +} + + +void +Graph::Vertex::add_edge(const unsigned int other_vertex) +{ + edges.push_back(other_vertex); +} + + +void +Graph::Vertex::remove_duplicate_edges(std::vector& tmp) +{ +#if defined(BLISS_CONSISTENCY_CHECKS) + /* Pre-conditions */ + for(unsigned int i = 0; i < tmp.size(); i++) assert(tmp[i] == false); +#endif + for(std::vector::iterator iter = edges.begin(); + iter != edges.end(); ) + { + const unsigned int dest_vertex = *iter; + if(tmp[dest_vertex] == true) + { + /* A duplicate edge found! */ + iter = edges.erase(iter); + } + else + { + /* Not seen earlier, mark as seen */ + tmp[dest_vertex] = true; + iter++; + } + } + + /* Clear tmp */ + for(std::vector::iterator iter = edges.begin(); + iter != edges.end(); + iter++) + { + tmp[*iter] = false; + } +#if defined(BLISS_CONSISTENCY_CHECKS) + /* Post-conditions */ + for(unsigned int i = 0; i < tmp.size(); i++) assert(tmp[i] == false); +#endif +} + + +/** + * Sort the edges leaving the vertex according to + * the vertex number of the other edge end. + * Time complexity: O(e log(e)), where e is the number of edges + * leaving the vertex. + */ +void +Graph::Vertex::sort_edges() +{ + std::sort(edges.begin(), edges.end()); +} + + + +/*------------------------------------------------------------------------- + * + * Constructor and destructor for undirected graphs + * + *-------------------------------------------------------------------------*/ + + +Graph::Graph(const unsigned int nof_vertices) +{ + vertices.resize(nof_vertices); + sh = shs_flm; +} + + +Graph::~Graph() +{ + ; +} + + +unsigned int +Graph::add_vertex(const unsigned int color) +{ + const unsigned int vertex_num = vertices.size(); + vertices.resize(vertex_num + 1); + vertices.back().color = color; + return vertex_num; +} + + +void +Graph::add_edge(const unsigned int vertex1, const unsigned int vertex2) +{ + //fprintf(stderr, "(%u,%u) ", vertex1, vertex2); + if(vertex1 >= vertices.size() or vertex2 >= vertices.size()) + throw std::runtime_error("out of bounds vertex number"); + vertices[vertex1].add_edge(vertex2); + vertices[vertex2].add_edge(vertex1); +} + + +void +Graph::change_color(const unsigned int vertex, const unsigned int color) +{ + vertices[vertex].color = color; +} + + +void +Graph::sort_edges() +{ + for(unsigned int i = 0; i < get_nof_vertices(); i++) + vertices[i].sort_edges(); +} + + +int +Graph::cmp(Graph& other) +{ + /* Compare the numbers of vertices */ + if(get_nof_vertices() < other.get_nof_vertices()) + return -1; + if(get_nof_vertices() > other.get_nof_vertices()) + return 1; + /* Compare vertex colors */ + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + if(vertices[i].color < other.vertices[i].color) + return -1; + if(vertices[i].color > other.vertices[i].color) + return 1; + } + /* Compare vertex degrees */ + remove_duplicate_edges(); + other.remove_duplicate_edges(); + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + if(vertices[i].nof_edges() < other.vertices[i].nof_edges()) + return -1; + if(vertices[i].nof_edges() > other.vertices[i].nof_edges()) + return 1; + } + /* Compare edges */ + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + Vertex &v1 = vertices[i]; + Vertex &v2 = other.vertices[i]; + v1.sort_edges(); + v2.sort_edges(); + std::vector::const_iterator ei1 = v1.edges.begin(); + std::vector::const_iterator ei2 = v2.edges.begin(); + while(ei1 != v1.edges.end()) + { + if(*ei1 < *ei2) + return -1; + if(*ei1 > *ei2) + return 1; + ei1++; + ei2++; + } + } + return 0; +} + + +Graph* +Graph::permute(const std::vector& perm) const +{ +#if defined(BLISS_CONSISTENCY_CHECKS) +#endif + + Graph* const g = new Graph(get_nof_vertices()); + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + const Vertex& v = vertices[i]; + Vertex& permuted_v = g->vertices[perm[i]]; + permuted_v.color = v.color; + for(std::vector::const_iterator ei = v.edges.begin(); + ei != v.edges.end(); + ei++) + { + const unsigned int dest_v = *ei; + permuted_v.add_edge(perm[dest_v]); + } + permuted_v.sort_edges(); + } + return g; +} + +Graph* +Graph::permute(const unsigned int* perm) const +{ +#if defined(BLISS_CONSISTENCY_CHECKS) + if(!is_permutation(get_nof_vertices(), perm)) + _INTERNAL_ERROR(); +#endif + + Graph* const g = new Graph(get_nof_vertices()); + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + const Vertex& v = vertices[i]; + Vertex& permuted_v = g->vertices[perm[i]]; + permuted_v.color = v.color; + for(std::vector::const_iterator ei = v.edges.begin(); + ei != v.edges.end(); + ei++) + { + const unsigned int dest_v = *ei; + permuted_v.add_edge(perm[dest_v]); + } + permuted_v.sort_edges(); + } + return g; +} + + +/*------------------------------------------------------------------------- + * + * Get a hash value for the graph. + * + *-------------------------------------------------------------------------*/ + +unsigned int +Graph::get_hash() +{ + remove_duplicate_edges(); + sort_edges(); + + UintSeqHash h; + + h.update(get_nof_vertices()); + + /* Hash the color of each vertex */ + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + h.update(vertices[i].color); + } + + /* Hash the edges */ + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + Vertex &v = vertices[i]; + for(std::vector::const_iterator ei = v.edges.begin(); + ei != v.edges.end(); + ei++) + { + const unsigned int dest_i = *ei; + if(dest_i < i) + continue; + h.update(i); + h.update(dest_i); + } + } + + return h.get_value(); +} + + + + + +void +Graph::remove_duplicate_edges() +{ + std::vector tmp(vertices.size(), false); + + for(std::vector::iterator vi = vertices.begin(); + vi != vertices.end(); + vi++) + { +#if defined(BLISS_EXPENSIVE_CONSISTENCY_CHECKS) + for(unsigned int i = 0; i < tmp.size(); i++) assert(tmp[i] == false); +#endif + (*vi).remove_duplicate_edges(tmp); + } +} + + + + + +/*------------------------------------------------------------------------- + * + * Partition independent invariants + * + *-------------------------------------------------------------------------*/ + +/* + * Return the color of the vertex. + * Time complexity: O(1) + */ +unsigned int +Graph::vertex_color_invariant(const Graph* const g, const unsigned int v) +{ + return g->vertices[v].color; +} + +/* + * Return the degree of the vertex. + * Time complexity: O(1) + */ +unsigned int +Graph::degree_invariant(const Graph* const g, const unsigned int v) +{ + return g->vertices[v].nof_edges(); +} + +/* + * Return 1 if the vertex v has a self-loop, 0 otherwise + * Time complexity: O(E_v), where E_v is the number of edges leaving v + */ +unsigned int +Graph::selfloop_invariant(const Graph* const g, const unsigned int v) +{ + const Vertex& vertex = g->vertices[v]; + for(std::vector::const_iterator ei = vertex.edges.begin(); + ei != vertex.edges.end(); + ei++) + { + if(*ei == v) + return 1; + } + return 0; +} + + + +/*------------------------------------------------------------------------- + * + * Refine the partition p according to a partition independent invariant + * + *-------------------------------------------------------------------------*/ + +bool +Graph::refine_according_to_invariant(unsigned int (*inv)(const Graph* const g, + const unsigned int v)) +{ + bool refined = false; + + for(Partition::Cell* cell = p.first_nonsingleton_cell; cell; ) + { + + Partition::Cell* const next_cell = cell->next_nonsingleton; + + const unsigned int* ep = p.elements + cell->first; + for(unsigned int i = cell->length; i > 0; i--, ep++) + { + const unsigned int ival = inv(this, *ep); + p.invariant_values[*ep] = ival; + if(ival > cell->max_ival) + { + cell->max_ival = ival; + cell->max_ival_count = 1; + } + else if(ival == cell->max_ival) + { + cell->max_ival_count++; + } + } + Partition::Cell* const last_new_cell = p.zplit_cell(cell, true); + refined |= (last_new_cell != cell); + cell = next_cell; + } + + return refined; +} + + + + + + + + + + + + +/*------------------------------------------------------------------------- + * + * Split the neighbourhood of a cell according to the equitable invariant + * + *-------------------------------------------------------------------------*/ + +bool +Graph::split_neighbourhood_of_cell(Partition::Cell* const cell) +{ + + + const bool was_equal_to_first = refine_equal_to_first; + + if(compute_eqref_hash) + { + eqref_hash.update(cell->first); + eqref_hash.update(cell->length); + } + + const unsigned int* ep = p.elements + cell->first; + for(unsigned int i = cell->length; i > 0; i--) + { + const Vertex& v = vertices[*ep++]; + + std::vector::const_iterator ei = v.edges.begin(); + for(unsigned int j = v.nof_edges(); j != 0; j--) + { + const unsigned int dest_vertex = *ei++; + Partition::Cell * const neighbour_cell = p.get_cell(dest_vertex); + if(neighbour_cell->is_unit()) + continue; + const unsigned int ival = ++p.invariant_values[dest_vertex]; + if(ival > neighbour_cell->max_ival) + { + neighbour_cell->max_ival = ival; + neighbour_cell->max_ival_count = 1; + if(ival == 1) { + neighbour_heap.insert(neighbour_cell->first); + } + } + else if(ival == neighbour_cell->max_ival) { + neighbour_cell->max_ival_count++; + } + } + } + + while(!neighbour_heap.is_empty()) + { + const unsigned int start = neighbour_heap.remove(); + Partition::Cell * const neighbour_cell = p.get_cell(p.elements[start]); + + if(compute_eqref_hash) + { + eqref_hash.update(neighbour_cell->first); + eqref_hash.update(neighbour_cell->length); + eqref_hash.update(neighbour_cell->max_ival); + eqref_hash.update(neighbour_cell->max_ival_count); + } + + + Partition::Cell* const last_new_cell = p.zplit_cell(neighbour_cell, true); + + /* Update certificate and hash if needed */ + const Partition::Cell* c = neighbour_cell; + while(1) + { + if(in_search) + { + /* Build certificate */ + cert_add_redundant(CERT_SPLIT, c->first, c->length); + /* No need to continue? */ + if(refine_compare_certificate and + (refine_equal_to_first == false) and + (refine_cmp_to_best < 0)) + goto worse_exit; + } + if(compute_eqref_hash) + { + eqref_hash.update(c->first); + eqref_hash.update(c->length); + } + if(c == last_new_cell) + break; + c = c->next; + } + } + + if(refine_compare_certificate and + (refine_equal_to_first == false) and + (refine_cmp_to_best < 0)) + return true; + + return false; + + worse_exit: + /* Clear neighbour heap */ + UintSeqHash rest; + while(!neighbour_heap.is_empty()) + { + const unsigned int start = neighbour_heap.remove(); + Partition::Cell * const neighbour_cell = p.get_cell(p.elements[start]); + if(opt_use_failure_recording and was_equal_to_first) + { + rest.update(neighbour_cell->first); + rest.update(neighbour_cell->length); + rest.update(neighbour_cell->max_ival); + rest.update(neighbour_cell->max_ival_count); + } + neighbour_cell->max_ival = 0; + neighbour_cell->max_ival_count = 0; + p.clear_ivs(neighbour_cell); + } + if(opt_use_failure_recording and was_equal_to_first) + { + for(unsigned int i = p.splitting_queue.size(); i > 0; i--) + { + Partition::Cell* const cell = p.splitting_queue.pop_front(); + rest.update(cell->first); + rest.update(cell->length); + p.splitting_queue.push_back(cell); + } + rest.update(failure_recording_fp_deviation); + failure_recording_fp_deviation = rest.get_value(); + } + + return true; +} + + + +bool +Graph::split_neighbourhood_of_unit_cell(Partition::Cell* const unit_cell) +{ + + + const bool was_equal_to_first = refine_equal_to_first; + + if(compute_eqref_hash) + { + eqref_hash.update(0x87654321); + eqref_hash.update(unit_cell->first); + eqref_hash.update(1); + } + + const Vertex& v = vertices[p.elements[unit_cell->first]]; + + std::vector::const_iterator ei = v.edges.begin(); + for(unsigned int j = v.nof_edges(); j > 0; j--) + { + const unsigned int dest_vertex = *ei++; + Partition::Cell * const neighbour_cell = p.get_cell(dest_vertex); + + if(neighbour_cell->is_unit()) { + if(in_search) { + /* Remember neighbour in order to generate certificate */ + neighbour_heap.insert(neighbour_cell->first); + } + continue; + } + if(neighbour_cell->max_ival_count == 0) + { + neighbour_heap.insert(neighbour_cell->first); + } + neighbour_cell->max_ival_count++; + + unsigned int * const swap_position = + p.elements + neighbour_cell->first + neighbour_cell->length - + neighbour_cell->max_ival_count; + *p.in_pos[dest_vertex] = *swap_position; + p.in_pos[*swap_position] = p.in_pos[dest_vertex]; + *swap_position = dest_vertex; + p.in_pos[dest_vertex] = swap_position; + } + + while(!neighbour_heap.is_empty()) + { + const unsigned int start = neighbour_heap.remove(); + Partition::Cell* neighbour_cell = p.get_cell(p.elements[start]); + +#if defined(BLISS_CONSISTENCY_CHECKS) + if(neighbour_cell->is_unit()) { + } else { + } +#endif + + if(compute_eqref_hash) + { + eqref_hash.update(neighbour_cell->first); + eqref_hash.update(neighbour_cell->length); + eqref_hash.update(neighbour_cell->max_ival_count); + } + + if(neighbour_cell->length > 1 and + neighbour_cell->max_ival_count != neighbour_cell->length) + { + Partition::Cell * const new_cell = + p.aux_split_in_two(neighbour_cell, + neighbour_cell->length - + neighbour_cell->max_ival_count); + unsigned int *ep = p.elements + new_cell->first; + unsigned int * const lp = p.elements+new_cell->first+new_cell->length; + while(ep < lp) + { + p.element_to_cell_map[*ep] = new_cell; + ep++; + } + neighbour_cell->max_ival_count = 0; + + + if(compute_eqref_hash) + { + /* Update hash */ + eqref_hash.update(neighbour_cell->first); + eqref_hash.update(neighbour_cell->length); + eqref_hash.update(0); + eqref_hash.update(new_cell->first); + eqref_hash.update(new_cell->length); + eqref_hash.update(1); + } + + /* Add cells in splitting_queue */ + if(neighbour_cell->is_in_splitting_queue()) { + /* Both cells must be included in splitting_queue in order + to ensure refinement into equitable partition */ + p.splitting_queue_add(new_cell); + } else { + Partition::Cell *min_cell, *max_cell; + if(neighbour_cell->length <= new_cell->length) { + min_cell = neighbour_cell; + max_cell = new_cell; + } else { + min_cell = new_cell; + max_cell = neighbour_cell; + } + /* Put the smaller cell in splitting_queue */ + p.splitting_queue_add(min_cell); + if(max_cell->is_unit()) { + /* Put the "larger" cell also in splitting_queue */ + p.splitting_queue_add(max_cell); + } + } + /* Update pointer for certificate generation */ + neighbour_cell = new_cell; + } + else + { + /* neighbour_cell->length == 1 || + neighbour_cell->max_ival_count == neighbour_cell->length */ + neighbour_cell->max_ival_count = 0; + } + + /* + * Build certificate if required + */ + if(in_search) + { + for(unsigned int i = neighbour_cell->first, + j = neighbour_cell->length; + j > 0; + j--, i++) + { + /* Build certificate */ + cert_add(CERT_EDGE, unit_cell->first, i); + /* No need to continue? */ + if(refine_compare_certificate and + (refine_equal_to_first == false) and + (refine_cmp_to_best < 0)) + goto worse_exit; + } + } /* if(in_search) */ + } /* while(!neighbour_heap.is_empty()) */ + + if(refine_compare_certificate and + (refine_equal_to_first == false) and + (refine_cmp_to_best < 0)) + return true; + + return false; + + worse_exit: + /* Clear neighbour heap */ + UintSeqHash rest; + while(!neighbour_heap.is_empty()) + { + const unsigned int start = neighbour_heap.remove(); + Partition::Cell * const neighbour_cell = p.get_cell(p.elements[start]); + if(opt_use_failure_recording and was_equal_to_first) + { + rest.update(neighbour_cell->first); + rest.update(neighbour_cell->length); + rest.update(neighbour_cell->max_ival_count); + } + neighbour_cell->max_ival_count = 0; + } + if(opt_use_failure_recording and was_equal_to_first) + { + rest.update(failure_recording_fp_deviation); + failure_recording_fp_deviation = rest.get_value(); + } + return true; +} + + + + + + + + + +/*------------------------------------------------------------------------- + * + * Check whether the current partition p is equitable. + * Performance: very slow, use only for debugging purposes. + * + *-------------------------------------------------------------------------*/ + +bool Graph::is_equitable() const +{ + const unsigned int N = get_nof_vertices(); + if(N == 0) + return true; + + std::vector first_count = std::vector(N, 0); + std::vector other_count = std::vector(N, 0); + + for(Partition::Cell *cell = p.first_cell; cell; cell = cell->next) + { + if(cell->is_unit()) + continue; + + unsigned int *ep = p.elements + cell->first; + const Vertex &first_vertex = vertices[*ep++]; + + /* Count how many edges lead from the first vertex to + * the neighbouring cells */ + for(std::vector::const_iterator ei = + first_vertex.edges.begin(); + ei != first_vertex.edges.end(); + ei++) + { + first_count[p.get_cell(*ei)->first]++; + } + + /* Count and compare to the edges of the other vertices */ + for(unsigned int i = cell->length; i > 1; i--) + { + const Vertex &vertex = vertices[*ep++]; + for(std::vector::const_iterator ei = + vertex.edges.begin(); + ei != vertex.edges.end(); + ei++) + { + other_count[p.get_cell(*ei)->first]++; + } + for(Partition::Cell *cell2 = p.first_cell; + cell2; + cell2 = cell2->next) + { + if(first_count[cell2->first] != other_count[cell2->first]) + { + /* Not equitable */ + return false; + } + other_count[cell2->first] = 0; + } + } + /* Reset first_count */ + for(unsigned int i = 0; i < N; i++) + first_count[i] = 0; + } + return true; +} + + + + + +/*------------------------------------------------------------------------- + * + * Build the initial equitable partition + * + *-------------------------------------------------------------------------*/ + +void Graph::make_initial_equitable_partition() +{ + refine_according_to_invariant(&vertex_color_invariant); + p.splitting_queue_clear(); + //p.print_signature(stderr); fprintf(stderr, "\n"); + + refine_according_to_invariant(&selfloop_invariant); + p.splitting_queue_clear(); + //p.print_signature(stderr); fprintf(stderr, "\n"); + + refine_according_to_invariant(°ree_invariant); + p.splitting_queue_clear(); + //p.print_signature(stderr); fprintf(stderr, "\n"); + + refine_to_equitable(); + //p.print_signature(stderr); fprintf(stderr, "\n"); + + +} + + + + + + + +/*------------------------------------------------------------------------- + * + * Find the next cell to be splitted + * + *-------------------------------------------------------------------------*/ + + +Partition::Cell* +Graph::find_next_cell_to_be_splitted(Partition::Cell* cell) +{ + switch(sh) { + case shs_f: return sh_first(); + case shs_fs: return sh_first_smallest(); + case shs_fl: return sh_first_largest(); + case shs_fm: return sh_first_max_neighbours(); + case shs_fsm: return sh_first_smallest_max_neighbours(); + case shs_flm: return sh_first_largest_max_neighbours(); + default: + fatal_error("Internal error - unknown splitting heuristics"); + return 0; + } +} + +/** \internal + * A splitting heuristic. + * Returns the first nonsingleton cell in the current partition. + */ +Partition::Cell* +Graph::sh_first() +{ + Partition::Cell* best_cell = 0; + for(Partition::Cell* cell = p.first_nonsingleton_cell; + cell; + cell = cell->next_nonsingleton) + { + if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level) + continue; + best_cell = cell; + break; + } + return best_cell; +} + +/** \internal + * A splitting heuristic. + * Returns the first smallest nonsingleton cell in the current partition. + */ +Partition::Cell* +Graph::sh_first_smallest() +{ + Partition::Cell* best_cell = 0; + unsigned int best_size = UINT_MAX; + for(Partition::Cell* cell = p.first_nonsingleton_cell; + cell; + cell = cell->next_nonsingleton) + { + if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level) + continue; + if(cell->length < best_size) + { + best_size = cell->length; + best_cell = cell; + } + } + return best_cell; +} + +/** \internal + * A splitting heuristic. + * Returns the first largest nonsingleton cell in the current partition. + */ +Partition::Cell* +Graph::sh_first_largest() +{ + Partition::Cell* best_cell = 0; + unsigned int best_size = 0; + for(Partition::Cell* cell = p.first_nonsingleton_cell; + cell; + cell = cell->next_nonsingleton) + { + if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level) + continue; + if(cell->length > best_size) + { + best_size = cell->length; + best_cell = cell; + } + } + return best_cell; +} + +/** \internal + * A splitting heuristic. + * Returns the first nonsingleton cell with max number of neighbouring + * nonsingleton cells. + * Assumes that the partition p is equitable. + * Assumes that the max_ival fields of the cells are all 0. + */ +Partition::Cell* +Graph::sh_first_max_neighbours() +{ + Partition::Cell* best_cell = 0; + int best_value = -1; + KStack neighbour_cells_visited; + neighbour_cells_visited.init(get_nof_vertices()); + for(Partition::Cell* cell = p.first_nonsingleton_cell; + cell; + cell = cell->next_nonsingleton) + { + if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level) + continue; + const Vertex& v = vertices[p.elements[cell->first]]; + std::vector::const_iterator ei = v.edges.begin(); + for(unsigned int j = v.nof_edges(); j > 0; j--) + { + Partition::Cell * const neighbour_cell = p.get_cell(*ei++); + if(neighbour_cell->is_unit()) + continue; + neighbour_cell->max_ival++; + if(neighbour_cell->max_ival == 1) + neighbour_cells_visited.push(neighbour_cell); + } + int value = 0; + while(!neighbour_cells_visited.is_empty()) + { + Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop(); + if(neighbour_cell->max_ival != neighbour_cell->length) + value++; + neighbour_cell->max_ival = 0; + } + if(value > best_value) + { + best_value = value; + best_cell = cell; + } + } + return best_cell; +} + +/** \internal + * A splitting heuristic. + * Returns the first smallest nonsingleton cell with max number of neighbouring + * nonsingleton cells. + * Assumes that the partition p is equitable. + * Assumes that the max_ival fields of the cells are all 0. + */ +Partition::Cell* +Graph::sh_first_smallest_max_neighbours() +{ + Partition::Cell* best_cell = 0; + int best_value = -1; + unsigned int best_size = UINT_MAX; + KStack neighbour_cells_visited; + neighbour_cells_visited.init(get_nof_vertices()); + for(Partition::Cell* cell = p.first_nonsingleton_cell; + cell; + cell = cell->next_nonsingleton) + { + + if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level) + continue; + + const Vertex& v = vertices[p.elements[cell->first]]; + std::vector::const_iterator ei = v.edges.begin(); + for(unsigned int j = v.nof_edges(); j > 0; j--) + { + Partition::Cell* const neighbour_cell = p.get_cell(*ei++); + if(neighbour_cell->is_unit()) + continue; + neighbour_cell->max_ival++; + if(neighbour_cell->max_ival == 1) + neighbour_cells_visited.push(neighbour_cell); + } + int value = 0; + while(!neighbour_cells_visited.is_empty()) + { + Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop(); + if(neighbour_cell->max_ival != neighbour_cell->length) + value++; + neighbour_cell->max_ival = 0; + } + if((value > best_value) or + (value == best_value and cell->length < best_size)) + { + best_value = value; + best_size = cell->length; + best_cell = cell; + } + } + return best_cell; +} + +/** \internal + * A splitting heuristic. + * Returns the first largest nonsingleton cell with max number of neighbouring + * nonsingleton cells. + * Assumes that the partition p is equitable. + * Assumes that the max_ival fields of the cells are all 0. + */ +Partition::Cell* +Graph::sh_first_largest_max_neighbours() +{ + Partition::Cell* best_cell = 0; + int best_value = -1; + unsigned int best_size = 0; + KStack neighbour_cells_visited; + neighbour_cells_visited.init(get_nof_vertices()); + for(Partition::Cell* cell = p.first_nonsingleton_cell; + cell; + cell = cell->next_nonsingleton) + { + + if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level) + continue; + const Vertex& v = vertices[p.elements[cell->first]]; + std::vector::const_iterator ei = v.edges.begin(); + for(unsigned int j = v.nof_edges(); j > 0; j--) + { + Partition::Cell* const neighbour_cell = p.get_cell(*ei++); + if(neighbour_cell->is_unit()) + continue; + neighbour_cell->max_ival++; + if(neighbour_cell->max_ival == 1) + neighbour_cells_visited.push(neighbour_cell); + } + int value = 0; + while(!neighbour_cells_visited.is_empty()) + { + Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop(); + if(neighbour_cell->max_ival != neighbour_cell->length) + value++; + neighbour_cell->max_ival = 0; + } + if((value > best_value) or + (value == best_value and cell->length > best_size)) + { + best_value = value; + best_size = cell->length; + best_cell = cell; + } + } + return best_cell; +} + + + + + + + + + + + + + + + + + + + + +/*------------------------------------------------------------------------- + * + * Initialize the certificate size and memory + * + *-------------------------------------------------------------------------*/ + +void +Graph::initialize_certificate() +{ + certificate_index = 0; + certificate_current_path.clear(); + certificate_first_path.clear(); + certificate_best_path.clear(); +} + + + + + +/*------------------------------------------------------------------------- + * + * Check whether perm is an automorphism. + * Slow, mainly for debugging and validation purposes. + * + *-------------------------------------------------------------------------*/ + +bool +Graph::is_automorphism(unsigned int* const perm) const +{ + std::set > edges1; + std::set > edges2; + +#if defined(BLISS_CONSISTENCY_CHECKS) + if(!is_permutation(get_nof_vertices(), perm)) + _INTERNAL_ERROR(); +#endif + + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + const Vertex& v1 = vertices[i]; + edges1.clear(); + for(std::vector::const_iterator ei = v1.edges.cbegin(); + ei != v1.edges.cend(); + ei++) + edges1.insert(perm[*ei]); + + const Vertex& v2 = vertices[perm[i]]; + edges2.clear(); + for(std::vector::const_iterator ei = v2.edges.cbegin(); + ei != v2.edges.cend(); + ei++) + edges2.insert(*ei); + + if(!(edges1 == edges2)) + return false; + } + + return true; +} + + + + +bool +Graph::is_automorphism(const std::vector& perm) const +{ + + if(!(perm.size() == get_nof_vertices() and is_permutation(perm))) + return false; + + std::set > edges1; + std::set > edges2; + + for(unsigned int i = 0; i < get_nof_vertices(); i++) + { + const Vertex& v1 = vertices[i]; + edges1.clear(); + for(std::vector::const_iterator ei = v1.edges.begin(); + ei != v1.edges.end(); + ei++) + edges1.insert(perm[*ei]); + + const Vertex& v2 = vertices[perm[i]]; + edges2.clear(); + for(std::vector::const_iterator ei = v2.edges.begin(); + ei != v2.edges.end(); + ei++) + edges2.insert(*ei); + + if(!(edges1 == edges2)) + return false; + } + + return true; +} + + + + + + + +bool +Graph::nucr_find_first_component(const unsigned int level) +{ + + cr_component.clear(); + cr_component_elements = 0; + + /* Find first non-discrete cell in the component level */ + Partition::Cell* first_cell = p.first_nonsingleton_cell; + while(first_cell) + { + if(p.cr_get_level(first_cell->first) == level) + break; + first_cell = first_cell->next_nonsingleton; + } + + /* The component is discrete, return false */ + if(!first_cell) + return false; + + std::vector component; + first_cell->max_ival = 1; + component.push_back(first_cell); + + for(unsigned int i = 0; i < component.size(); i++) + { + Partition::Cell* const cell = component[i]; + + const Vertex& v = vertices[p.elements[cell->first]]; + std::vector::const_iterator ei = v.edges.begin(); + for(unsigned int j = v.nof_edges(); j > 0; j--) + { + const unsigned int neighbour = *ei++; + + Partition::Cell* const neighbour_cell = p.get_cell(neighbour); + + /* Skip unit neighbours */ + if(neighbour_cell->is_unit()) + continue; + /* Already marked to be in the same component? */ + if(neighbour_cell->max_ival == 1) + continue; + /* Is the neighbour at the same component recursion level? */ + if(p.cr_get_level(neighbour_cell->first) != level) + continue; + + if(neighbour_cell->max_ival_count == 0) + neighbour_heap.insert(neighbour_cell->first); + neighbour_cell->max_ival_count++; + } + while(!neighbour_heap.is_empty()) + { + const unsigned int start = neighbour_heap.remove(); + Partition::Cell* const neighbour_cell = + p.get_cell(p.elements[start]); + + /* Skip saturated neighbour cells */ + if(neighbour_cell->max_ival_count == neighbour_cell->length) + { + neighbour_cell->max_ival_count = 0; + continue; + } + neighbour_cell->max_ival_count = 0; + neighbour_cell->max_ival = 1; + component.push_back(neighbour_cell); + } + } + + for(unsigned int i = 0; i < component.size(); i++) + { + Partition::Cell* const cell = component[i]; + cell->max_ival = 0; + cr_component.push_back(cell->first); + cr_component_elements += cell->length; + } + + /* + if(verbstr and verbose_level > 2) { + fprintf(verbstr, "NU-component with %lu cells and %u vertices\n", + (long unsigned)cr_component.size(), cr_component_elements); + fflush(verbstr); + } + */ + + return true; +} + + + + +bool +Graph::nucr_find_first_component(const unsigned int level, + std::vector& component, + unsigned int& component_elements, + Partition::Cell*& sh_return) +{ + + component.clear(); + component_elements = 0; + sh_return = 0; + unsigned int sh_first = 0; + unsigned int sh_size = 0; + unsigned int sh_nuconn = 0; + + /* Find first non-discrete cell in the component level */ + Partition::Cell* first_cell = p.first_nonsingleton_cell; + while(first_cell) + { + if(p.cr_get_level(first_cell->first) == level) + break; + first_cell = first_cell->next_nonsingleton; + } + + if(!first_cell) + { + /* The component is discrete, return false */ + return false; + } + + std::vector comp; + KStack neighbours; + neighbours.init(get_nof_vertices()); + + first_cell->max_ival = 1; + comp.push_back(first_cell); + + for(unsigned int i = 0; i < comp.size(); i++) + { + Partition::Cell* const cell = comp[i]; + + const Vertex& v = vertices[p.elements[cell->first]]; + std::vector::const_iterator ei = v.edges.begin(); + for(unsigned int j = v.nof_edges(); j > 0; j--) + { + const unsigned int neighbour = *ei++; + + Partition::Cell* const neighbour_cell = p.get_cell(neighbour); + + /* Skip unit neighbours */ + if(neighbour_cell->is_unit()) + continue; + /* Is the neighbour at the same component recursion level? */ + //if(p.cr_get_level(neighbour_cell->first) != level) + // continue; + if(neighbour_cell->max_ival_count == 0) + neighbours.push(neighbour_cell); + neighbour_cell->max_ival_count++; + } + unsigned int nuconn = 1; + while(!neighbours.is_empty()) + { + Partition::Cell* const neighbour_cell = neighbours.pop(); + //neighbours.pop_back(); + + /* Skip saturated neighbour cells */ + if(neighbour_cell->max_ival_count == neighbour_cell->length) + { + neighbour_cell->max_ival_count = 0; + continue; + } + nuconn++; + neighbour_cell->max_ival_count = 0; + if(neighbour_cell->max_ival == 0) { + comp.push_back(neighbour_cell); + neighbour_cell->max_ival = 1; + } + } + + switch(sh) { + case shs_f: + if(sh_return == 0 or + cell->first <= sh_first) { + sh_return = cell; + sh_first = cell->first; + } + break; + case shs_fs: + if(sh_return == 0 or + cell->length < sh_size or + (cell->length == sh_size and cell->first <= sh_first)) { + sh_return = cell; + sh_first = cell->first; + sh_size = cell->length; + } + break; + case shs_fl: + if(sh_return == 0 or + cell->length > sh_size or + (cell->length == sh_size and cell->first <= sh_first)) { + sh_return = cell; + sh_first = cell->first; + sh_size = cell->length; + } + break; + case shs_fm: + if(sh_return == 0 or + nuconn > sh_nuconn or + (nuconn == sh_nuconn and cell->first <= sh_first)) { + sh_return = cell; + sh_first = cell->first; + sh_nuconn = nuconn; + } + break; + case shs_fsm: + if(sh_return == 0 or + nuconn > sh_nuconn or + (nuconn == sh_nuconn and + (cell->length < sh_size or + (cell->length == sh_size and cell->first <= sh_first)))) { + sh_return = cell; + sh_first = cell->first; + sh_size = cell->length; + sh_nuconn = nuconn; + } + break; + case shs_flm: + if(sh_return == 0 or + nuconn > sh_nuconn or + (nuconn == sh_nuconn and + (cell->length > sh_size or + (cell->length == sh_size and cell->first <= sh_first)))) { + sh_return = cell; + sh_first = cell->first; + sh_size = cell->length; + sh_nuconn = nuconn; + } + break; + default: + fatal_error("Internal error - unknown splitting heuristics"); + return 0; + } + } + assert(sh_return); + + for(unsigned int i = 0; i < comp.size(); i++) + { + Partition::Cell* const cell = comp[i]; + cell->max_ival = 0; + component.push_back(cell->first); + component_elements += cell->length; + } + + /* + if(verbstr and verbose_level > 2) { + fprintf(verbstr, "NU-component with %lu cells and %u vertices\n", + (long unsigned)component.size(), component_elements); + fflush(verbstr); + } + */ + + return true; +} + + + + +} diff --git a/src/rigraph/core/isomorphism/bliss/graph.hh b/src/rigraph/core/isomorphism/bliss/graph.hh new file mode 100644 index 0000000..c4d30b3 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/graph.hh @@ -0,0 +1,872 @@ +#ifndef BLISS_GRAPH_HH +#define BLISS_GRAPH_HH + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +/** + * \namespace bliss + * The namespace bliss contains all the classes and functions of the bliss + * tool except for the C programming language API. + */ +namespace bliss { + class AbstractGraph; +} + +// #include +#include +#include +#include "stats.hh" +#include "kstack.hh" +#include "kqueue.hh" +#include "heap.hh" +#include "orbit.hh" +#include "partition.hh" +#include "uintseqhash.hh" + +namespace bliss { + + + + + +/** + * \brief An abstract base class for different types of graphs. + */ +class AbstractGraph +{ + friend class Partition; + +public: + AbstractGraph(); + virtual ~AbstractGraph(); + +#if 0 + /** + * Set the verbose output level for the algorithms. + * \param level the level of verbose output, 0 means no verbose output + */ + void set_verbose_level(const unsigned int level); + + /** + * Set the file stream for the verbose output. + * \param fp the file stream; if null, no verbose output is written + */ + void set_verbose_file(FILE * const fp); +#endif + + /** + * Add a new vertex with color \a color in the graph and return its index. + */ + virtual unsigned int add_vertex(const unsigned int color = 0) = 0; + + /** + * Add an edge between vertices \a source and \a target. + * Duplicate edges between vertices are ignored but try to avoid introducing + * them in the first place as they are not ignored immediately but will + * consume memory and computation resources for a while. + */ + virtual void add_edge(const unsigned int source, const unsigned int target) = 0; + + /** + * Change the color of the vertex \a vertex to \a color. + */ + virtual void change_color(const unsigned int vertex, const unsigned int color) = 0; + + /** + * Check whether \a perm is an automorphism of this graph. + * Unoptimized, mainly for debugging purposes. + */ + virtual bool is_automorphism(const std::vector& perm) const = 0; + + + /** Activate/deactivate failure recording. + * May not be called during the search, i.e. from an automorphism reporting + * hook function. + * \param active if true, activate failure recording, deactivate otherwise + */ + void set_failure_recording(const bool active) {assert(!in_search); opt_use_failure_recording = active;} + + /** Activate/deactivate component recursion. + * The choice affects the computed canonical labelings; + * therefore, if you want to compare whether two graphs are isomorphic by + * computing and comparing (for equality) their canonical versions, + * be sure to use the same choice for both graphs. + * May not be called during the search, i.e. from an automorphism reporting + * hook function. + * \param active if true, activate component recursion, deactivate otherwise + */ + void set_component_recursion(const bool active) {assert(!in_search); opt_use_comprec = active;} + + + + /** + * Return the number of vertices in the graph. + */ + virtual unsigned int get_nof_vertices() const = 0; + + /** + * Return a new graph that is the result of applying the permutation \a perm + * to this graph. This graph is not modified. + * \a perm must contain N=this.get_nof_vertices() elements and be a bijection + * on {0,1,...,N-1}, otherwise the result is undefined or a segfault. + */ + virtual AbstractGraph* permute(const unsigned int* const perm) const = 0; + virtual AbstractGraph* permute(const std::vector& perm) const = 0; + + /** + * Find a set of generators for the automorphism group of the graph. + * The function \a report (if non-null) is called each time a new generator + * for the automorphism group is found. + * The first argument \a n for the function + * is the length of the automorphism (equal to get_nof_vertices()), and + * the second argument \a aut is the automorphism + * (a bijection on {0,...,get_nof_vertices()-1}). + * The memory for the automorphism \a aut will be invalidated immediately + * after the return from the \a report function; + * if you want to use the automorphism later, you have to take a copy of it. + * Do not call any member functions from the \a report function. + * + * The search statistics are copied in \a stats. + * + * If the \a terminate function argument is given, + * it is called in each search tree node: if the function returns true, + * then the search is terminated and thus not all the automorphisms + * may have been generated. + * The \a terminate function may be used to limit the time spent in bliss + * in case the graph is too difficult under the available time constraints. + * If used, keep the function simple to evaluate so that + * it does not consume too much time. + */ + void find_automorphisms(Stats& stats, + const std::function& report = nullptr, + const std::function& terminate = nullptr); + + /** + * Otherwise the same as find_automorphisms() except that + * a canonical labeling of the graph (a bijection on + * {0,...,get_nof_vertices()-1}) is returned. + * The memory allocated for the returned canonical labeling will remain + * valid only until the next call to a member function with the exception + * that constant member functions (for example, bliss::Graph::permute()) can + * be called without invalidating the labeling. + * To compute the canonical version of an undirected graph, call this + * function and then bliss::Graph::permute() with the returned canonical + * labeling. + * Note that the computed canonical version may depend on the applied version + * of bliss as well as on some other options (for instance, the splitting + * heuristic selected with bliss::Graph::set_splitting_heuristic()). + * + * If the \a terminate function argument is given, + * it is called in each search tree node: if the function returns true, + * then the search is terminated and thus (i) not all the automorphisms + * may have been generated and (ii) the returned labeling may not + * be canonical. + * The \a terminate function may be used to limit the time spent in bliss + * in case the graph is too difficult under the available time constraints. + * If used, keep the function simple to evaluate so that + * it does not consume too much time. + */ + const unsigned int* canonical_form(Stats& stats, + const std::function& report = nullptr, + const std::function& terminate = nullptr); + + /** + * Get a hash value for the graph. + * \return the hash value + */ + virtual unsigned int get_hash() = 0; + + /** + * Disable/enable the "long prune" method. + * The choice affects the computed canonical labelings; + * therefore, if you want to compare whether two graphs are isomorphic by + * computing and comparing (for equality) their canonical versions, + * be sure to use the same choice for both graphs. + * May not be called during the search, i.e. from an automorphism reporting + * hook function. + * \param active if true, activate "long prune", deactivate otherwise + */ + void set_long_prune_activity(const bool active) { + assert(!in_search); + opt_use_long_prune = active; + } + + + +protected: + /** \internal + * How much verbose output is produced (0 means none) */ + /* unsigned int verbose_level; */ + /** \internal + * The output stream for verbose output. */ + /* FILE *verbstr; */ +protected: + + /** \internal + * The ordered partition used in the search algorithm. */ + Partition p; + + /** \internal + * Whether the search for automorphisms and a canonical labeling is + * in progress. + */ + bool in_search; + + /** \internal + * Is failure recording in use? + */ + bool opt_use_failure_recording; + /* The "tree-specific" invariant value for the point when current path + * got different from the first path */ + unsigned int failure_recording_fp_deviation; + + /** \internal + * Is component recursion in use? + */ + bool opt_use_comprec; + + + unsigned int refine_current_path_certificate_index; + bool refine_compare_certificate = false; + bool refine_equal_to_first = false; + unsigned int refine_first_path_subcertificate_end; + int refine_cmp_to_best; + unsigned int refine_best_path_subcertificate_end; + + static const unsigned int CERT_SPLIT = 0; //UINT_MAX; + static const unsigned int CERT_EDGE = 1; //UINT_MAX-1; + /** \internal + * Add a triple (v1,v2,v3) in the certificate. + * May modify refine_equal_to_first and refine_cmp_to_best. + * May also update eqref_hash and failure_recording_fp_deviation. */ + void cert_add(const unsigned int v1, + const unsigned int v2, + const unsigned int v3); + + /** \internal + * Add a redundant triple (v1,v2,v3) in the certificate. + * Can also just dicard the triple. + * May modify refine_equal_to_first and refine_cmp_to_best. + * May also update eqref_hash and failure_recording_fp_deviation. */ + void cert_add_redundant(const unsigned int x, + const unsigned int y, + const unsigned int z); + + /**\internal + * Is the long prune method in use? + */ + bool opt_use_long_prune; + /**\internal + * Maximum amount of memory (in megabytes) available for + * the long prune method + */ + static const unsigned int long_prune_options_max_mem = 50; + /**\internal + * Maximum amount of automorphisms stored for the long prune method; + * less than this is stored if the memory limit above is reached first + */ + static const unsigned int long_prune_options_max_stored_auts = 100; + + unsigned int long_prune_max_stored_autss; + std::vector *> long_prune_fixed; + std::vector *> long_prune_mcrs; + std::vector long_prune_temp; + unsigned int long_prune_begin; + unsigned int long_prune_end; + /** \internal + * Initialize the "long prune" data structures. + */ + void long_prune_init(); + /** \internal + * Release the memory allocated for "long prune" data structures. + */ + void long_prune_deallocate(); + void long_prune_add_automorphism(const unsigned int *aut); + std::vector& long_prune_get_fixed(const unsigned int index); + std::vector& long_prune_allocget_fixed(const unsigned int index); + std::vector& long_prune_get_mcrs(const unsigned int index); + std::vector& long_prune_allocget_mcrs(const unsigned int index); + /** \internal + * Swap the i:th and j:th stored automorphism information; + * i and j must be "in window, i.e. in [long_prune_begin,long_prune_end[ + */ + void long_prune_swap(const unsigned int i, const unsigned int j); + + /* + * Data structures and routines for refining the partition p into equitable + */ + Heap neighbour_heap; + virtual bool split_neighbourhood_of_unit_cell(Partition::Cell * const) = 0; + virtual bool split_neighbourhood_of_cell(Partition::Cell * const) = 0; + void refine_to_equitable(); + void refine_to_equitable(Partition::Cell * const unit_cell); + void refine_to_equitable(Partition::Cell * const unit_cell1, + Partition::Cell * const unit_cell2); + + + /** \internal + * \return false if it was detected that the current certificate + * is different from the first and/or best (whether this is checked + * depends on in_search and refine_compare_certificate flags. + */ + bool do_refine_to_equitable(); + + unsigned int eqref_max_certificate_index; + /** \internal + * Whether eqref_hash is updated during equitable refinement process. + */ + bool compute_eqref_hash; + UintSeqHash eqref_hash; + + + /** \internal + * Check whether the current partition p is equitable. + * Performance: very slow, use only for debugging purposes. + */ + virtual bool is_equitable() const = 0; + + unsigned int *first_path_labeling; + unsigned int *first_path_labeling_inv; + Orbit first_path_orbits; + unsigned int *first_path_automorphism; + + unsigned int *best_path_labeling; + unsigned int *best_path_labeling_inv; + Orbit best_path_orbits; + unsigned int *best_path_automorphism; + + void update_labeling(unsigned int * const lab); + void update_labeling_and_its_inverse(unsigned int * const lab, + unsigned int * const lab_inv); + void update_orbit_information(Orbit &o, const unsigned int *perm); + + void reset_permutation(unsigned int *perm); + + /* Mainly for debugging purposes */ + virtual bool is_automorphism(unsigned int* const perm) const = 0; + + std::vector certificate_current_path; + std::vector certificate_first_path; + std::vector certificate_best_path; + + unsigned int certificate_index; + virtual void initialize_certificate() = 0; + + virtual void remove_duplicate_edges() = 0; + virtual void make_initial_equitable_partition() = 0; + virtual Partition::Cell* find_next_cell_to_be_splitted(Partition::Cell *cell) = 0; + + + /** \struct PathInfo + * + * A structure for holding first, current, and best path information. + */ + typedef struct { + unsigned int splitting_element; + unsigned int certificate_index; + unsigned int subcertificate_length; + UintSeqHash eqref_hash; + } PathInfo; + + void search(const bool canonical, Stats &stats, + const std::function& report_function = nullptr, + const std::function& terminate = nullptr); + + + void (*report_hook)(void *user_param, + unsigned int n, + const unsigned int *aut); + void *report_user_param; + + + /* + * + * Nonuniform component recursion (NUCR) + * + */ + + /* The currently traversed component */ + unsigned int cr_level; + + /** @internal @class CR_CEP + * The "Component End Point" data structure + */ + class CR_CEP { + public: + /** At which level in the search was this CEP created */ + unsigned int creation_level; + /** The current component has been fully traversed when the partition has + * this many discrete cells left */ + unsigned int discrete_cell_limit; + /** The component to be traversed after the current one */ + unsigned int next_cr_level; + /** The next component end point */ + unsigned int next_cep_index; + bool first_checked; + bool best_checked; + }; + /** \internal + * A stack for storing Component End Points + */ + std::vector cr_cep_stack; + + /** \internal + * Find the first non-uniformity component at the component recursion + * level \a level. + * The component is stored in \a cr_component. + * If no component is found, \a cr_component is empty. + * Returns false if all the cells in the component recursion level \a level + * were discrete. + * Modifies the max_ival and max_ival_count fields of Partition:Cell + * (assumes that they are 0 when called and + * quarantees that they are 0 when returned). + */ + virtual bool nucr_find_first_component(const unsigned int level) = 0; + virtual bool nucr_find_first_component(const unsigned int level, + std::vector& component, + unsigned int& component_elements, + Partition::Cell*& sh_return) = 0; + /** \internal + * The non-uniformity component found by nucr_find_first_component() + * is stored here. + */ + std::vector cr_component; + /** \internal + * The number of vertices in the component \a cr_component + */ + unsigned int cr_component_elements; + + + + + + +}; + + + +/** + * \brief The class for undirected, vertex colored graphs. + * + * Multiple edges between vertices are not allowed (i.e., are ignored). + */ +class Graph : public AbstractGraph +{ +public: + /** + * The possible splitting heuristics. + * The selected splitting heuristics affects the computed canonical + * labelings; therefore, if you want to compare whether two graphs + * are isomorphic by computing and comparing (for equality) their + * canonical versions, be sure to use the same splitting heuristics + * for both graphs. + */ + typedef enum { + /** First non-unit cell. + * Very fast but may result in large search spaces on difficult graphs. + * Use for large but easy graphs. */ + shs_f = 0, + /** First smallest non-unit cell. + * Fast, should usually produce smaller search spaces than shs_f. */ + shs_fs, + /** First largest non-unit cell. + * Fast, should usually produce smaller search spaces than shs_f. */ + shs_fl, + /** First maximally non-trivially connected non-unit cell. + * Not so fast, should usually produce smaller search spaces than shs_f, + * shs_fs, and shs_fl. */ + shs_fm, + /** First smallest maximally non-trivially connected non-unit cell. + * Not so fast, should usually produce smaller search spaces than shs_f, + * shs_fs, and shs_fl. */ + shs_fsm, + /** First largest maximally non-trivially connected non-unit cell. + * Not so fast, should usually produce smaller search spaces than shs_f, + * shs_fs, and shs_fl. */ + shs_flm + } SplittingHeuristic; + +protected: + class Vertex { + public: + Vertex(); + ~Vertex(); + void add_edge(const unsigned int other_vertex); + void remove_duplicate_edges(std::vector& tmp); + void sort_edges(); + + unsigned int color; + std::vector edges; + unsigned int nof_edges() const {return edges.size(); } + }; + std::vector vertices; + void sort_edges(); + void remove_duplicate_edges(); + + /** \internal + * Partition independent invariant. + * Returns the color of the vertex. + * Time complexity: O(1). + */ + static unsigned int vertex_color_invariant(const Graph* const g, + const unsigned int v); + /** \internal + * Partition independent invariant. + * Returns the degree of the vertex. + * DUPLICATE EDGES MUST HAVE BEEN REMOVED BEFORE. + * Time complexity: O(1). + */ + static unsigned int degree_invariant(const Graph* const g, + const unsigned int v); + /** \internal + * Partition independent invariant. + * Returns 1 if there is an edge from the vertex to itself, 0 if not. + * Time complexity: O(k), where k is the number of edges leaving the vertex. + */ + static unsigned int selfloop_invariant(const Graph* const g, + const unsigned int v); + + + bool refine_according_to_invariant(unsigned int (*inv)(const Graph* const g, + const unsigned int v)); + + /* + * Routines needed when refining the partition p into equitable + */ + bool split_neighbourhood_of_unit_cell(Partition::Cell * const); + bool split_neighbourhood_of_cell(Partition::Cell * const); + + /** \internal + * \copydoc AbstractGraph::is_equitable() const + */ + bool is_equitable() const; + + /* Splitting heuristics, documented in more detail in graph.cc */ + SplittingHeuristic sh; + Partition::Cell* find_next_cell_to_be_splitted(Partition::Cell *cell); + Partition::Cell* sh_first(); + Partition::Cell* sh_first_smallest(); + Partition::Cell* sh_first_largest(); + Partition::Cell* sh_first_max_neighbours(); + Partition::Cell* sh_first_smallest_max_neighbours(); + Partition::Cell* sh_first_largest_max_neighbours(); + + + void make_initial_equitable_partition(); + + void initialize_certificate(); + + bool is_automorphism(unsigned int* const perm) const; + + + bool nucr_find_first_component(const unsigned int level); + bool nucr_find_first_component(const unsigned int level, + std::vector& component, + unsigned int& component_elements, + Partition::Cell*& sh_return); + + + + +public: + /** + * Create a new graph with \a N vertices and no edges. + */ + Graph(const unsigned int N = 0); + + /** + * Destroy the graph. + */ + ~Graph(); + + /** + * \copydoc AbstractGraph::is_automorphism(const std::vector& perm) const + */ + bool is_automorphism(const std::vector& perm) const; + + + /** + * \copydoc AbstractGraph::get_hash() + */ + virtual unsigned int get_hash(); + + /** + * Return the number of vertices in the graph. + */ + unsigned int get_nof_vertices() const {return vertices.size(); } + + /** + * \copydoc AbstractGraph::permute(const unsigned int* const perm) const + */ + Graph* permute(const unsigned int* const perm) const; + Graph* permute(const std::vector& perm) const; + + /** + * Add a new vertex with color \a color in the graph and return its index. + */ + unsigned int add_vertex(const unsigned int color = 0); + + /** + * Add an edge between vertices \a v1 and \a v2. + * Duplicate edges between vertices are ignored but try to avoid introducing + * them in the first place as they are not ignored immediately but will + * consume memory and computation resources for a while. + */ + void add_edge(const unsigned int v1, const unsigned int v2); + + /** + * Change the color of the vertex \a vertex to \a color. + */ + void change_color(const unsigned int vertex, const unsigned int color); + + /** + * Compare this graph with the graph \a other. + * Returns 0 if the graphs are equal, and a negative (positive) integer + * if this graph is "smaller than" ("greater than", resp.) than \a other. + */ + int cmp(Graph& other); + + /** + * Set the splitting heuristic used by the automorphism and canonical + * labeling algorithm. + * The selected splitting heuristics affects the computed canonical + * labelings; therefore, if you want to compare whether two graphs + * are isomorphic by computing and comparing (for equality) their + * canonical versions, be sure to use the same splitting heuristics + * for both graphs. + */ + void set_splitting_heuristic(const SplittingHeuristic shs) {sh = shs; } + + +}; + + + +/** + * \brief The class for directed, vertex colored graphs. + * + * Multiple edges between vertices are not allowed (i.e., are ignored). + */ +class Digraph : public AbstractGraph +{ +public: + /** + * The possible splitting heuristics. + * The selected splitting heuristics affects the computed canonical + * labelings; therefore, if you want to compare whether two graphs + * are isomorphic by computing and comparing (for equality) their + * canonical versions, be sure to use the same splitting heuristics + * for both graphs. + */ + typedef enum { + /** First non-unit cell. + * Very fast but may result in large search spaces on difficult graphs. + * Use for large but easy graphs. */ + shs_f = 0, + /** First smallest non-unit cell. + * Fast, should usually produce smaller search spaces than shs_f. */ + shs_fs, + /** First largest non-unit cell. + * Fast, should usually produce smaller search spaces than shs_f. */ + shs_fl, + /** First maximally non-trivially connected non-unit cell. + * Not so fast, should usually produce smaller search spaces than shs_f, + * shs_fs, and shs_fl. */ + shs_fm, + /** First smallest maximally non-trivially connected non-unit cell. + * Not so fast, should usually produce smaller search spaces than shs_f, + * shs_fs, and shs_fl. */ + shs_fsm, + /** First largest maximally non-trivially connected non-unit cell. + * Not so fast, should usually produce smaller search spaces than shs_f, + * shs_fs, and shs_fl. */ + shs_flm + } SplittingHeuristic; + +protected: + class Vertex { + public: + Vertex(); + ~Vertex(); + void add_edge_to(const unsigned int dest_vertex); + void add_edge_from(const unsigned int source_vertex); + void remove_duplicate_edges(std::vector& tmp); + void sort_edges(); + unsigned int color; + std::vector edges_out; + std::vector edges_in; + unsigned int nof_edges_in() const {return edges_in.size(); } + unsigned int nof_edges_out() const {return edges_out.size(); } + }; + std::vector vertices; + void remove_duplicate_edges(); + + /** \internal + * Partition independent invariant. + * Returns the color of the vertex. + * Time complexity: O(1). + */ + static unsigned int vertex_color_invariant(const Digraph* const g, + const unsigned int v); + /** \internal + * Partition independent invariant. + * Returns the indegree of the vertex. + * DUPLICATE EDGES MUST HAVE BEEN REMOVED BEFORE. + * Time complexity: O(1). + */ + static unsigned int indegree_invariant(const Digraph* const g, + const unsigned int v); + /** \internal + * Partition independent invariant. + * Returns the outdegree of the vertex. + * DUPLICATE EDGES MUST HAVE BEEN REMOVED BEFORE. + * Time complexity: O(1). + */ + static unsigned int outdegree_invariant(const Digraph* const g, + const unsigned int v); + /** \internal + * Partition independent invariant. + * Returns 1 if there is an edge from the vertex to itself, 0 if not. + * Time complexity: O(k), where k is the number of edges leaving the vertex. + */ + static unsigned int selfloop_invariant(const Digraph* const g, + const unsigned int v); + + /** \internal + * Refine the partition \a p according to + * the partition independent invariant \a inv. + */ + bool refine_according_to_invariant(unsigned int (*inv)(const Digraph* const g, + const unsigned int v)); + + /* + * Routines needed when refining the partition p into equitable + */ + bool split_neighbourhood_of_unit_cell(Partition::Cell* const); + bool split_neighbourhood_of_cell(Partition::Cell* const); + + + /** \internal + * \copydoc AbstractGraph::is_equitable() const + */ + bool is_equitable() const; + + /* Splitting heuristics, documented in more detail in the cc-file. */ + SplittingHeuristic sh; + Partition::Cell* find_next_cell_to_be_splitted(Partition::Cell *cell); + Partition::Cell* sh_first(); + Partition::Cell* sh_first_smallest(); + Partition::Cell* sh_first_largest(); + Partition::Cell* sh_first_max_neighbours(); + Partition::Cell* sh_first_smallest_max_neighbours(); + Partition::Cell* sh_first_largest_max_neighbours(); + + void make_initial_equitable_partition(); + + void initialize_certificate(); + + bool is_automorphism(unsigned int* const perm) const; + + void sort_edges(); + + bool nucr_find_first_component(const unsigned int level); + bool nucr_find_first_component(const unsigned int level, + std::vector& component, + unsigned int& component_elements, + Partition::Cell*& sh_return); + +public: + /** + * Create a new directed graph with \a N vertices and no edges. + */ + Digraph(const unsigned int N = 0); + + /** + * Destroy the graph. + */ + ~Digraph(); + + + /** + * \copydoc AbstractGraph::is_automorphism(const std::vector& perm) const + */ + bool is_automorphism(const std::vector& perm) const; + + + + /** + * \copydoc AbstractGraph::get_hash() + */ + virtual unsigned int get_hash(); + + /** + * Return the number of vertices in the graph. + */ + unsigned int get_nof_vertices() const {return vertices.size(); } + + /** + * Add a new vertex with color 'color' in the graph and return its index. + */ + unsigned int add_vertex(const unsigned int color = 0); + + /** + * Add an edge from the vertex \a source to the vertex \a target. + * Duplicate edges are ignored but try to avoid introducing + * them in the first place as they are not ignored immediately but will + * consume memory and computation resources for a while. + */ + void add_edge(const unsigned int source, const unsigned int target); + + /** + * Change the color of the vertex 'vertex' to 'color'. + */ + void change_color(const unsigned int vertex, const unsigned int color); + + /** + * Compare this graph with the graph \a other. + * Returns 0 if the graphs are equal, and a negative (positive) integer + * if this graph is "smaller than" ("greater than", resp.) than \a other. + */ + int cmp(Digraph& other); + + /** + * Set the splitting heuristic used by the automorphism and canonical + * labeling algorithm. + * The selected splitting heuristics affects the computed canonical + * labelings; therefore, if you want to compare whether two graphs + * are isomorphic by computing and comparing (for equality) their + * canonical versions, be sure to use the same splitting heuristics + * for both graphs. + */ + void set_splitting_heuristic(SplittingHeuristic shs) {sh = shs; } + + /** + * \copydoc AbstractGraph::permute(const unsigned int* const perm) const + */ + Digraph* permute(const unsigned int* const perm) const; + Digraph* permute(const std::vector& perm) const; +}; + + + + +} // namespace bliss + +#endif // BLISS_GRAPH_HH diff --git a/src/rigraph/core/isomorphism/bliss/heap.cc b/src/rigraph/core/isomorphism/bliss/heap.cc new file mode 100644 index 0000000..0e57893 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/heap.cc @@ -0,0 +1,111 @@ +#include "heap.hh" + +#include +#include + +/* Allow using 'and' instead of '&&' with MSVC */ +#if _MSC_VER +#include +#endif + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +namespace bliss { + +Heap::Heap() { + array = nullptr; + n = 0; + N = 0; +} + +Heap::~Heap() +{ + delete[] array; + array = nullptr; + n = 0; + N = 0; +} + +void Heap::upheap(unsigned int index) +{ + assert(n >= 1); + assert(index >= 1 and index <= n); + const unsigned int v = array[index]; + array[0] = 0; + while(array[index/2] > v) + { + array[index] = array[index/2]; + index = index/2; + } + array[index] = v; +} + +void Heap::downheap(unsigned int index) +{ + const unsigned int v = array[index]; + const unsigned int lim = n/2; + while(index <= lim) + { + unsigned int new_index = index + index; + if((new_index < n) and (array[new_index] > array[new_index+1])) + new_index++; + if(v <= array[new_index]) + break; + array[index] = array[new_index]; + index = new_index; + } + array[index] = v; +} + +void Heap::init(const unsigned int size) +{ + assert(size > 0); + if(size > N) + { + delete[] array; + array = new unsigned int[size + 1]; + N = size; + } + n = 0; +} + +void Heap::insert(const unsigned int v) +{ + assert(n < N); + array[++n] = v; + upheap(n); +} + +unsigned int Heap::smallest() const +{ + assert(n >= 1 and n <= N); + return array[1]; +} + +unsigned int Heap::remove() +{ + assert(n >= 1 and n <= N); + const unsigned int v = array[1]; + array[1] = array[n--]; + downheap(1); + return v; +} + +} // namespace bliss diff --git a/src/rigraph/core/isomorphism/bliss/heap.hh b/src/rigraph/core/isomorphism/bliss/heap.hh new file mode 100644 index 0000000..bb84cd4 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/heap.hh @@ -0,0 +1,89 @@ +#ifndef BLISS_HEAP_HH +#define BLISS_HEAP_HH + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +namespace bliss { + +/** + * \brief A capacity bounded heap data structure. + */ + +class Heap +{ + unsigned int N; + unsigned int n; + unsigned int *array; + void upheap(unsigned int k); + void downheap(unsigned int k); +public: + /** + * Create a new heap. + * init() must be called after this. + */ + Heap(); + ~Heap(); + + /** + * Initialize the heap to have the capacity to hold \e size elements. + */ + void init(const unsigned int size); + + /** + * Is the heap empty? + * Time complexity is O(1). + */ + bool is_empty() const {return n == 0; } + + /** + * Remove all the elements in the heap. + * Time complexity is O(1). + */ + void clear() {n = 0; } + + /** + * Insert the element \a e in the heap. + * Time complexity is O(log(N)), where N is the number of elements + * currently in the heap. + */ + void insert(const unsigned int e); + + /** + * Return the smallest element in the heap. + * Time complexity is O(1). + */ + unsigned int smallest() const; + + /** + * Remove and return the smallest element in the heap. + * Time complexity is O(log(N)), where N is the number of elements + * currently in the heap. + */ + unsigned int remove(); + + /** + * Get the number of elements in the heap. + */ + unsigned int size() const {return n; } +}; + +} // namespace bliss + +#endif // BLISS_HEAP_HH diff --git a/src/rigraph/core/isomorphism/bliss/igraph-changes.md b/src/rigraph/core/isomorphism/bliss/igraph-changes.md new file mode 100644 index 0000000..3c7058b --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/igraph-changes.md @@ -0,0 +1,34 @@ +This file lists changes that were made to the original Bliss package (version 0.75) to integrate it into igraph. + +Exclude `CMakeLists.txt`, `Doxyfile`, `Makefile-manual`, `readme.txt`. Make sure not to accidentally overwrite igraph's own `bliss/CMakeLists.txt`. + +Removed `bliss.cc`, `bliss_C.cc`, `bliss_C.h`. + +Remove `timer.hh`. Remove references to `timer.hh` and `Timer` class in `graph.cc`. + +Replace `#pragma once` by traditional header guards in all headers. + +### In `bignum.hh`: + +Replace `#include ` by `#include "internal/gmp_internal.h"`. + +At the beginning, add `#define BLISS_USE_GMP`. Verify that this macro is only used in this file. + +### In `defs.cc` and `defs.hh`: + +Remove the `...` argument from `fatal_error` for simplicity, and make the function simply invoke `IGRAPH_FATAL`. + +### In `graph.cc`: + +Define `_INTERNAL_ERROR` in terms of `IGRAPH_FATAL`. + +### MSVC compatibility + +Bliss uses `and`, `or`, etc. instead of `&&`, `||`, etc. These are not supported by MSVC by default. Bliss 0.74 uses the `/permissive` option to enable support in MSVC, but this option is only supported wit VS2019. Instead, in igraph we add the following where relevant: + +``` +/* Allow using 'and' instead of '&&' with MSVC */ +#if _MSC_VER +#include +#endif +``` diff --git a/src/rigraph/core/isomorphism/bliss/kqueue.hh b/src/rigraph/core/isomorphism/bliss/kqueue.hh new file mode 100644 index 0000000..06de3b4 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/kqueue.hh @@ -0,0 +1,168 @@ +#ifndef BLISS_KQUEUE_HH +#define BLISS_KQUEUE_HH + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +#include +#include + +namespace bliss { + +/** + * \brief A simple implementation of queues with fixed maximum capacity. + */ +template +class KQueue +{ +public: + /** + * Create a new queue with capacity zero. + * The function init() should be called next. + */ + KQueue(); + + ~KQueue(); + + /** + * Initialize the queue to have the capacity to hold at most \a N elements. + */ + void init(const unsigned int N); + + /** Is the queue empty? */ + bool is_empty() const; + + /** Return the number of elements in the queue. */ + unsigned int size() const; + + /** Remove all the elements in the queue. */ + void clear(); + + /** Return (but don't remove) the first element in the queue. */ + Type front() const; + + /** Remove and return the first element of the queue. */ + Type pop_front(); + + /** Push the element \a e in the front of the queue. */ + void push_front(Type e); + + /** Remove and return the last element of the queue. */ + Type pop_back(); + + /** Push the element \a e in the back of the queue. */ + void push_back(Type e); +private: + Type *entries, *end; + Type *head, *tail; +}; + +template +KQueue::KQueue() +{ + entries = nullptr; + end = nullptr; + head = nullptr; + tail = nullptr; +} + +template +KQueue::~KQueue() +{ + delete[] entries; + entries = nullptr; + end = nullptr; + head = nullptr; + tail = nullptr; +} + +template +void KQueue::init(const unsigned int k) +{ + assert(k > 0); + delete[] entries; + entries = new Type[k+1]; + end = entries + k + 1; + head = entries; + tail = head; +} + +template +void KQueue::clear() +{ + head = entries; + tail = head; +} + +template +bool KQueue::is_empty() const +{ + return head == tail; +} + +template +unsigned int KQueue::size() const +{ + if(tail >= head) + return(tail - head); + return (end - head) + (tail - entries); +} + +template +Type KQueue::front() const +{ + assert(head != tail); + return *head; +} + +template +Type KQueue::pop_front() +{ + assert(head != tail); + Type *old_head = head; + head++; + if(head == end) + head = entries; + return *old_head; +} + +template +void KQueue::push_front(Type e) +{ + if(head == entries) + head = end - 1; + else + head--; + assert(head != tail); + *head = e; +} + +template +void KQueue::push_back(Type e) +{ + *tail = e; + tail++; + if(tail == end) + tail = entries; + assert(head != tail); +} + +} // namespace bliss + +#endif // BLISS_KQUEUE_HH diff --git a/src/rigraph/core/isomorphism/bliss/kstack.hh b/src/rigraph/core/isomorphism/bliss/kstack.hh new file mode 100644 index 0000000..ecb9b83 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/kstack.hh @@ -0,0 +1,145 @@ +#ifndef BLISS_KSTACK_HH +#define BLISS_KSTACK_HH + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +#include +#include + +namespace bliss { + +/** + * \brief A simple implementation of a stack with fixed maximum capacity. + */ +template +class KStack { +public: + /** + * Create a new stack with zero capacity. + * The function init() should be called next. + */ + KStack(); + + /** + * Create a new stack with the capacity to hold at most \a N elements. + */ + KStack(int N); + + ~KStack(); + + /** + * Initialize the stack to have the capacity to hold at most \a N elements. + */ + void init(int N); + + /** + * Is the stack empty? + */ + bool is_empty() const {return cursor == entries; } + + /** + * Return (but don't remove) the top element of the stack. + */ + Type top() const {assert(cursor > entries); return *cursor; } + + /** + * Pop (remove) the top element of the stack. + */ + Type pop() + { + assert(cursor > entries); + return *cursor--; + } + + /** + * Push the element \a e in the stack. + */ + void push(Type e) + { + assert(cursor < entries + kapacity); + *(++cursor) = e; + } + + /** Remove all the elements in the stack. */ + void clean() {cursor = entries; } + + /** + * Get the number of elements in the stack. + */ + unsigned int size() const {return cursor - entries; } + + /** + * Return the i:th element in the stack, where \a i is in the range + * 0,...,this.size()-1; the 0:th element is the bottom element + * in the stack. + */ + Type element_at(unsigned int i) + { + assert(i < size()); + return entries[i+1]; + } + + /** Return the capacity (NOT the number of elements) of the stack. */ + int capacity() const {return kapacity; } +private: + int kapacity; + Type *entries; + Type *cursor; +}; + +template +KStack::KStack() +{ + kapacity = 0; + entries = nullptr; + cursor = nullptr; +} + +template +KStack::KStack(int k) +{ + assert(k > 0); + kapacity = k; + entries = new Type[k+1]; + cursor = entries; +} + +template +void KStack::init(int k) +{ + assert(k > 0); + delete[] entries; + kapacity = k; + entries = new Type[k+1]; + cursor = entries; +} + +template +KStack::~KStack() +{ + delete[] entries; + kapacity = 0; + entries = nullptr; + cursor = nullptr; +} + +} // namespace bliss + +#endif // BLISS_KSTACK_HH diff --git a/src/rigraph/core/isomorphism/bliss/orbit.cc b/src/rigraph/core/isomorphism/bliss/orbit.cc new file mode 100644 index 0000000..cb73862 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/orbit.cc @@ -0,0 +1,151 @@ +#include +#include "orbit.hh" + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +namespace bliss { + +Orbit::Orbit() +{ + orbits = 0; + in_orbit = 0; + nof_elements = 0; +} + + +Orbit::~Orbit() +{ + delete[] orbits; + orbits = 0; + /* + if(orbits) + { + free(orbits); + orbits = 0; + } + */ + delete[] in_orbit; + in_orbit = 0; + /* + if(in_orbit) + { + free(in_orbit); + in_orbit = 0; + } + */ + nof_elements = 0; + _nof_orbits = 0; +} + + +void Orbit::init(const unsigned int n) +{ + assert(n > 0); + if(orbits) delete[] orbits; + orbits = new OrbitEntry[n]; + delete[] in_orbit; + in_orbit = new OrbitEntry*[n]; + nof_elements = n; + + reset(); +} + + +void Orbit::reset() +{ + assert(orbits); + assert(in_orbit); + + for(unsigned int i = 0; i < nof_elements; i++) + { + orbits[i].element = i; + orbits[i].next = 0; + orbits[i].size = 1; + in_orbit[i] = &orbits[i]; + } + _nof_orbits = nof_elements; +} + + +void Orbit::merge_orbits(OrbitEntry *orbit1, OrbitEntry *orbit2) +{ + + if(orbit1 != orbit2) + { + _nof_orbits--; + /* Only update the elements in the smaller orbit */ + if(orbit1->size > orbit2->size) + { + OrbitEntry * const temp = orbit2; + orbit2 = orbit1; + orbit1 = temp; + } + /* Link the elements of orbit1 to the almost beginning of orbit2 */ + OrbitEntry *e = orbit1; + while(e->next) + { + in_orbit[e->element] = orbit2; + e = e->next; + } + in_orbit[e->element] = orbit2; + e->next = orbit2->next; + orbit2->next = orbit1; + /* Keep the minimal orbit representative in the beginning */ + if(orbit1->element < orbit2->element) + { + const unsigned int temp = orbit1->element; + orbit1->element = orbit2->element; + orbit2->element = temp; + } + orbit2->size += orbit1->size; + } +} + + +void Orbit::merge_orbits(unsigned int e1, unsigned int e2) +{ + + merge_orbits(in_orbit[e1], in_orbit[e2]); +} + + +bool Orbit::is_minimal_representative(unsigned int element) const +{ + return(get_minimal_representative(element) == element); +} + + +unsigned int Orbit::get_minimal_representative(unsigned int element) const +{ + + OrbitEntry * const orbit = in_orbit[element]; + + return(orbit->element); +} + + +unsigned int Orbit::orbit_size(unsigned int element) const +{ + + return(in_orbit[element]->size); +} + + +} // namespace bliss diff --git a/src/rigraph/core/isomorphism/bliss/orbit.hh b/src/rigraph/core/isomorphism/bliss/orbit.hh new file mode 100644 index 0000000..1d59445 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/orbit.hh @@ -0,0 +1,112 @@ +#ifndef BLISS_ORBIT_HH +#define BLISS_ORBIT_HH + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +namespace bliss { + +/** + * \brief A class for representing orbit information. + * + * Given a set {0,...,N-1} of N elements, represent equivalence + * classes (that is, unordered partitions) of the elements. + * Supports only equivalence class merging, not splitting. + * Merging two classes requires time O(k), where k is the number of + * the elements in the smaller of the merged classes. + * Getting the smallest representative in a class + * (and thus testing whether two elements belong to the same class) + * is a constant time operation. + */ +class Orbit +{ + class OrbitEntry + { + public: + unsigned int element; + OrbitEntry *next; + unsigned int size; + }; + + OrbitEntry *orbits; + OrbitEntry **in_orbit; + unsigned int nof_elements; + unsigned int _nof_orbits; + void merge_orbits(OrbitEntry *o1, OrbitEntry *o2); + +public: + /** + * Create a new orbit information object. + * The init() function must be called next to actually initialize + * the object. + */ + Orbit(); + ~Orbit(); + + /** + * Initialize the orbit information to consider sets of \a N elements. + * It is required that \a N > 0. + * The orbit information is reset so that each element forms + * an orbit of its own. + * Time complexity is O(N). + * \sa reset() + */ + void init(const unsigned int N); + + /** + * Reset the orbits so that each element forms an orbit of its own. + * Time complexity is O(N). + */ + void reset(); + + /** + * Merge the orbits of the elements \a e1 and \a e2. + * Time complexity is O(k), where k is the number of elements in + * the smaller of the merged orbits. + */ + void merge_orbits(unsigned int e1, unsigned int e2); + + /** + * Is the element \a e the smallest element in its orbit? + * Time complexity is O(1). + */ + bool is_minimal_representative(unsigned int e) const; + + /** + * Get the smallest element in the orbit of the element \a e. + * Time complexity is O(1). + */ + unsigned int get_minimal_representative(unsigned int e) const; + + /** + * Get the number of elements in the orbit of the element \a e. + * Time complexity is O(1). + */ + unsigned int orbit_size(unsigned int e) const; + + /** + * Get the number of orbits. + * Time complexity is O(1). + */ + unsigned int nof_orbits() const {return _nof_orbits; } +}; + +} // namespace bliss + +#endif // BLISS_ORBIT_HH diff --git a/src/rigraph/core/isomorphism/bliss/partition.cc b/src/rigraph/core/isomorphism/bliss/partition.cc new file mode 100644 index 0000000..d0b2d52 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/partition.cc @@ -0,0 +1,1127 @@ +#include +#include + +#include "graph.hh" +#include "partition.hh" + +#include "igraph_decls.h" + +/* Allow using 'and' instead of '&&' with MSVC */ +#if _MSC_VER +#include +#endif + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +namespace bliss { + +Partition::Partition() +{ + N = 0; + elements = 0; + in_pos = 0; + invariant_values = 0; + cells = 0; + free_cells = 0; + element_to_cell_map = 0; + graph = 0; + discrete_cell_count = 0; + /* Initialize a distribution count sorting array. */ + for(unsigned int i = 0; i < 256; i++) + dcs_count[i] = 0; + + cr_enabled = false; + cr_cells = 0; + cr_levels = 0; +} + + + +Partition::~Partition() +{ + delete[] elements; elements = nullptr; + delete[] cells; cells = nullptr; + delete[] element_to_cell_map; element_to_cell_map = nullptr; + delete[] in_pos; in_pos = nullptr; + delete[] invariant_values; invariant_values = nullptr; + N = 0; +} + + + +void Partition::init(const unsigned int M) +{ + assert(M > 0); + N = M; + + delete[] elements; + elements = new unsigned int[N]; + for(unsigned int i = 0; i < N; i++) + elements[i] = i; + + delete[] in_pos; + in_pos = new unsigned int*[N]; + for(unsigned int i = 0; i < N; i++) + in_pos[i] = elements + i; + + delete[] invariant_values; + invariant_values = new unsigned int[N]; + for(unsigned int i = 0; i < N; i++) + invariant_values[i] = 0; + + delete[] cells; + cells = new Cell[N]; + + cells[0].first = 0; + cells[0].length = N; + cells[0].max_ival = 0; + cells[0].max_ival_count = 0; + cells[0].in_splitting_queue = false; + cells[0].in_neighbour_heap = false; + cells[0].prev = 0; + cells[0].next = 0; + cells[0].next_nonsingleton = 0; + cells[0].prev_nonsingleton = 0; + cells[0].split_level = 0; + first_cell = &cells[0]; + if(N == 1) + { + first_nonsingleton_cell = 0; + discrete_cell_count = 1; + } + else + { + first_nonsingleton_cell = &cells[0]; + discrete_cell_count = 0; + } + + for(unsigned int i = 1; i < N; i++) + { + cells[i].first = 0; + cells[i].length = 0; + cells[i].max_ival = 0; + cells[i].max_ival_count = 0; + cells[i].in_splitting_queue = false; + cells[i].in_neighbour_heap = false; + cells[i].prev = 0; + cells[i].next = (i < N-1)?&cells[i+1]:0; + cells[i].next_nonsingleton = 0; + cells[i].prev_nonsingleton = 0; + } + if(N > 1) + free_cells = &cells[1]; + else + free_cells = 0; + + delete[] element_to_cell_map; + element_to_cell_map = new Cell*[N]; + for(unsigned int i = 0; i < N; i++) + element_to_cell_map[i] = first_cell; + + splitting_queue.init(N); + refinement_stack.init(N); + + /* Reset the main backtracking stack */ + bt_stack.clear(); +} + + + + + + +Partition::BacktrackPoint +Partition::set_backtrack_point() +{ + BacktrackInfo info; + info.refinement_stack_size = refinement_stack.size(); + if(cr_enabled) + info.cr_backtrack_point = cr_get_backtrack_point(); + BacktrackPoint p = bt_stack.size(); + bt_stack.push_back(info); + return p; +} + + + +void +Partition::goto_backtrack_point(BacktrackPoint p) +{ + assert(p < bt_stack.size()); + BacktrackInfo info = bt_stack[p]; + bt_stack.resize(p); + + if(cr_enabled) + cr_goto_backtrack_point(info.cr_backtrack_point); + + const unsigned int dest_refinement_stack_size = info.refinement_stack_size; + + assert(refinement_stack.size() >= dest_refinement_stack_size); + while(refinement_stack.size() > dest_refinement_stack_size) + { + RefInfo i = refinement_stack.pop(); + const unsigned int first = i.split_cell_first; + Cell* cell = get_cell(elements[first]); + + if(cell->first != first) + { + assert(cell->first < first); + assert(cell->split_level <= dest_refinement_stack_size); + goto done; + } + assert(cell->split_level > dest_refinement_stack_size); + + while(cell->split_level > dest_refinement_stack_size) + { + assert(cell->prev); + cell = cell->prev; + } + while(cell->next and + cell->next->split_level > dest_refinement_stack_size) + { + /* Merge next cell */ + Cell* const next_cell = cell->next; + if(cell->length == 1) + discrete_cell_count--; + if(next_cell->length == 1) + discrete_cell_count--; + /* Update element_to_cell_map values of elements added in cell */ + unsigned int* ep = elements + next_cell->first; + unsigned int* const lp = ep + next_cell->length; + for( ; ep < lp; ep++) + element_to_cell_map[*ep] = cell; + /* Update cell parameters */ + cell->length += next_cell->length; + if(next_cell->next) + next_cell->next->prev = cell; + cell->next = next_cell->next; + /* (Pseudo)free next_cell */ + next_cell->first = 0; + next_cell->length = 0; + next_cell->prev = 0; + next_cell->next = free_cells; + free_cells = next_cell; + } + + done: + if(i.prev_nonsingleton_first >= 0) + { + Cell* const prev_cell = get_cell(elements[i.prev_nonsingleton_first]); + assert(prev_cell->length > 1); + cell->prev_nonsingleton = prev_cell; + prev_cell->next_nonsingleton = cell; + } + else + { + //assert(cell->prev_nonsingleton == 0); + cell->prev_nonsingleton = 0; + first_nonsingleton_cell = cell; + } + + if(i.next_nonsingleton_first >= 0) + { + Cell* const next_cell = get_cell(elements[i.next_nonsingleton_first]); + assert(next_cell->length > 1); + cell->next_nonsingleton = next_cell; + next_cell->prev_nonsingleton = cell; + } + else + { + //assert(cell->next_nonsingleton == 0); + cell->next_nonsingleton = 0; + } + } + +} + + + +Partition::Cell* +Partition::individualize(Partition::Cell * const cell, + const unsigned int element) +{ + assert(!cell->is_unit()); + + unsigned int * const pos = in_pos[element]; + assert((unsigned int)(pos - elements) >= cell->first); + assert((unsigned int)(pos - elements) < cell->first + cell->length); + assert(*pos == element); + + const unsigned int last = cell->first + cell->length - 1; + *pos = elements[last]; + in_pos[*pos] = pos; + elements[last] = element; + in_pos[element] = elements + last; + + Partition::Cell * const new_cell = aux_split_in_two(cell, cell->length-1); + assert(elements[new_cell->first] == element); + element_to_cell_map[element] = new_cell; + + return new_cell; +} + + + +Partition::Cell* +Partition::aux_split_in_two(Partition::Cell* const cell, + const unsigned int first_half_size) +{ + RefInfo i; + + assert(0 < first_half_size && first_half_size < cell->length); + + /* (Pseudo)allocate new cell */ + Cell * const new_cell = free_cells; + assert(new_cell != 0); + free_cells = new_cell->next; + /* Update new cell parameters */ + new_cell->first = cell->first + first_half_size; + new_cell->length = cell->length - first_half_size; + new_cell->next = cell->next; + if(new_cell->next) + new_cell->next->prev = new_cell; + new_cell->prev = cell; + new_cell->split_level = refinement_stack.size()+1; + /* Update old, splitted cell parameters */ + cell->length = first_half_size; + cell->next = new_cell; + /* CR */ + if(cr_enabled) + cr_create_at_level_trailed(new_cell->first, cr_get_level(cell->first)); + + /* Add cell in refinement_stack for backtracking */ + i.split_cell_first = new_cell->first; + if(cell->prev_nonsingleton) + i.prev_nonsingleton_first = cell->prev_nonsingleton->first; + else + i.prev_nonsingleton_first = -1; + if(cell->next_nonsingleton) + i.next_nonsingleton_first = cell->next_nonsingleton->first; + else + i.next_nonsingleton_first = -1; + refinement_stack.push(i); + + /* Modify nonsingleton cell list */ + if(new_cell->length > 1) + { + new_cell->prev_nonsingleton = cell; + new_cell->next_nonsingleton = cell->next_nonsingleton; + if(new_cell->next_nonsingleton) + new_cell->next_nonsingleton->prev_nonsingleton = new_cell; + cell->next_nonsingleton = new_cell; + } + else + { + new_cell->next_nonsingleton = 0; + new_cell->prev_nonsingleton = 0; + discrete_cell_count++; + } + + if(cell->is_unit()) + { + if(cell->prev_nonsingleton) + cell->prev_nonsingleton->next_nonsingleton = cell->next_nonsingleton; + else + first_nonsingleton_cell = cell->next_nonsingleton; + if(cell->next_nonsingleton) + cell->next_nonsingleton->prev_nonsingleton = cell->prev_nonsingleton; + cell->next_nonsingleton = 0; + cell->prev_nonsingleton = 0; + discrete_cell_count++; + } + + return new_cell; +} + + + +void +Partition::splitting_queue_add(Cell* const cell) +{ + static const unsigned int smallish_cell_threshold = 1; + assert(!cell->in_splitting_queue); + cell->in_splitting_queue = true; + if(cell->length <= smallish_cell_threshold) + splitting_queue.push_front(cell); + else + splitting_queue.push_back(cell); +} + + + +void +Partition::splitting_queue_clear() +{ + while(!splitting_queue_is_empty()) + splitting_queue_pop(); +} + + + + + +/* + * Assumes that the invariant values are NOT the same + * and that the cell contains more than one element + */ +Partition::Cell* +Partition::sort_and_split_cell1(Partition::Cell* const cell) +{ +#if defined(BLISS_EXPENSIVE_CONSISTENCY_CHECKS) + assert(cell->length > 1); + assert(cell->first + cell->length <= N); + unsigned int nof_0_found = 0; + unsigned int nof_1_found = 0; + for(unsigned int i = cell->first; i < cell->first + cell->length; i++) + { + const unsigned int ival = invariant_values[elements[i]]; + assert(ival == 0 or ival == 1); + if(ival == 0) nof_0_found++; + else nof_1_found++; + } + assert(nof_0_found > 0); + assert(nof_1_found > 0); + assert(nof_1_found == cell->max_ival_count); + assert(nof_0_found + nof_1_found == cell->length); + assert(cell->max_ival == 1); +#endif + + + /* (Pseudo)allocate new cell */ + Cell* const new_cell = free_cells; + assert(new_cell != 0); + free_cells = new_cell->next; + +#define NEW_SORT1 +#ifdef NEW_SORT1 + unsigned int *ep0 = elements + cell->first; + unsigned int *ep1 = ep0 + cell->length - cell->max_ival_count; + if(cell->max_ival_count > cell->length / 2) + { + /* There are more ones than zeros, only move zeros */ + unsigned int * const end = ep0 + cell->length; + while(ep1 < end) + { + while(invariant_values[*ep1] == 0) + { + const unsigned int tmp = *ep1; + *ep1 = *ep0; + *ep0 = tmp; + in_pos[tmp] = ep0; + in_pos[*ep1] = ep1; + ep0++; + } + element_to_cell_map[*ep1] = new_cell; + invariant_values[*ep1] = 0; + ep1++; + } + } + else + { + /* There are more zeros than ones, only move ones */ + unsigned int * const end = ep1; + while(ep0 < end) + { + while(invariant_values[*ep0] != 0) + { + const unsigned int tmp = *ep0; + *ep0 = *ep1; + *ep1 = tmp; + in_pos[tmp] = ep1; + in_pos[*ep0] = ep0; + ep1++; + } + ep0++; + } + ep1 = end; + while(ep1 < elements + cell->first + cell->length) + { + element_to_cell_map[*ep1] = new_cell; + invariant_values[*ep1] = 0; + ep1++; + } + } + /* Update new cell parameters */ + new_cell->first = cell->first + cell->length - cell->max_ival_count; + new_cell->length = cell->length - (new_cell->first - cell->first); + new_cell->next = cell->next; + if(new_cell->next) + new_cell->next->prev = new_cell; + new_cell->prev = cell; + new_cell->split_level = refinement_stack.size()+1; + /* Update old, splitted cell parameters */ + cell->length = new_cell->first - cell->first; + cell->next = new_cell; + /* CR */ + if(cr_enabled) + cr_create_at_level_trailed(new_cell->first, cr_get_level(cell->first)); + +#else + /* Sort vertices in the cell according to the invariant values */ + unsigned int *ep0 = elements + cell->first; + unsigned int *ep1 = ep0 + cell->length; + while(ep1 > ep0) + { + const unsigned int element = *ep0; + const unsigned int ival = invariant_values[element]; + invariant_values[element] = 0; + assert(ival <= 1); + assert(element_to_cell_map[element] == cell); + assert(in_pos[element] == ep0); + if(ival == 0) + { + ep0++; + } + else + { + ep1--; + *ep0 = *ep1; + *ep1 = element; + element_to_cell_map[element] = new_cell; + in_pos[element] = ep1; + in_pos[*ep0] = ep0; + } + } + + assert(ep1 != elements + cell->first); + assert(ep0 != elements + cell->first + cell->length); + + /* Update new cell parameters */ + new_cell->first = ep1 - elements; + new_cell->length = cell->length - (new_cell->first - cell->first); + new_cell->next = cell->next; + if(new_cell->next) + new_cell->next->prev = new_cell; + new_cell->prev = cell; + new_cell->split_level = cell->split_level; + /* Update old, splitted cell parameters */ + cell->length = new_cell->first - cell->first; + cell->next = new_cell; + cell->split_level = refinement_stack.size()+1; + /* CR */ + if(cr_enabled) + cr_create_at_level_trailed(new_cell->first, cr_get_level(cell->first)); + +#endif /* ifdef NEW_SORT1*/ + + /* Add cell in refinement stack for backtracking */ + { + RefInfo i; + i.split_cell_first = new_cell->first; + if(cell->prev_nonsingleton) + i.prev_nonsingleton_first = cell->prev_nonsingleton->first; + else + i.prev_nonsingleton_first = -1; + if(cell->next_nonsingleton) + i.next_nonsingleton_first = cell->next_nonsingleton->first; + else + i.next_nonsingleton_first = -1; + /* Modify nonsingleton cell list */ + if(new_cell->length > 1) + { + new_cell->prev_nonsingleton = cell; + new_cell->next_nonsingleton = cell->next_nonsingleton; + if(new_cell->next_nonsingleton) + new_cell->next_nonsingleton->prev_nonsingleton = new_cell; + cell->next_nonsingleton = new_cell; + } + else + { + new_cell->next_nonsingleton = 0; + new_cell->prev_nonsingleton = 0; + discrete_cell_count++; + } + if(cell->is_unit()) + { + if(cell->prev_nonsingleton) + cell->prev_nonsingleton->next_nonsingleton = cell->next_nonsingleton; + else + first_nonsingleton_cell = cell->next_nonsingleton; + if(cell->next_nonsingleton) + cell->next_nonsingleton->prev_nonsingleton = cell->prev_nonsingleton; + cell->next_nonsingleton = 0; + cell->prev_nonsingleton = 0; + discrete_cell_count++; + } + refinement_stack.push(i); + } + + + /* Add cells in splitting queue */ + assert(!new_cell->in_splitting_queue); + if(cell->in_splitting_queue) { + /* Both cells must be included in splitting_queue in order to have + refinement to equitable partition */ + splitting_queue_add(new_cell); + } else { + Cell *min_cell, *max_cell; + if(cell->length <= new_cell->length) { + min_cell = cell; + max_cell = new_cell; + } else { + min_cell = new_cell; + max_cell = cell; + } + /* Put the smaller cell in splitting_queue */ + splitting_queue_add(min_cell); + if(max_cell->is_unit()) { + /* Put the "larger" cell also in splitting_queue */ + splitting_queue_add(max_cell); + } + } + + + return new_cell; +} + + + + + +/** + * An auxiliary function for distribution count sorting. + * Build start array so that + * dcs_start[0] = 0 and dcs_start[i+1] = dcs_start[i] + dcs_count[i]. + */ +void +Partition::dcs_cumulate_count(const unsigned int max) +{ + assert(max <= 255); + unsigned int* count_p = dcs_count; + unsigned int* start_p = dcs_start; + unsigned int sum = 0; + for(unsigned int i = max+1; i > 0; i--) + { + *start_p = sum; + start_p++; + sum += *count_p; + count_p++; + } +} + + +/** + * Distribution count sorting of cells with invariant values less than 256. + */ +Partition::Cell* +Partition::sort_and_split_cell255(Partition::Cell* const cell, + const unsigned int max_ival) +{ + assert(max_ival <= 255); + + if(cell->is_unit()) + { + /* Reset invariant value */ + invariant_values[elements[cell->first]] = 0; + return cell; + } + +#ifdef BLISS_CONSISTENCY_CHECKS + for(unsigned int i = 0; i < 256; i++) + assert(dcs_count[i] == 0); +#endif + + /* + * Compute the distribution of invariant values to the count array + */ + { + const unsigned int *ep = elements + cell->first; + assert(element_to_cell_map[*ep] == cell); + const unsigned int ival = invariant_values[*ep]; + assert(ival <= 255); + dcs_count[ival]++; + ep++; +#if defined(BLISS_CONSISTENCY_CHECKS) + bool equal_invariant_values = true; +#endif + for(unsigned int i = cell->length - 1; i != 0; i--) + { + assert(element_to_cell_map[*ep] == cell); + const unsigned int ival2 = invariant_values[*ep]; + assert(ival2 <= 255); + assert(ival2 <= max_ival); + dcs_count[ival2]++; +#if defined(BLISS_CONSISTENCY_CHECKS) + if(ival2 != ival) { + equal_invariant_values = false; + } +#endif + ep++; + } +#if defined(BLISS_CONSISTENCY_CHECKS) + assert(!equal_invariant_values); + if(equal_invariant_values) { + assert(dcs_count[ival] == cell->length); + dcs_count[ival] = 0; + clear_ivs(cell); + return cell; + } +#endif + } + + /* Build start array */ + dcs_cumulate_count(max_ival); + + //assert(dcs_start[255] + dcs_count[255] == cell->length); + assert(dcs_start[max_ival] + dcs_count[max_ival] == cell->length); + + /* Do the sorting */ + for(unsigned int i = 0; i <= max_ival; i++) + { + unsigned int *ep = elements + cell->first + dcs_start[i]; + for(unsigned int j = dcs_count[i]; j > 0; j--) + { + while(true) + { + const unsigned int element = *ep; + const unsigned int ival = invariant_values[element]; + if(ival == i) + break; + assert(ival > i); + assert(dcs_count[ival] > 0); + *ep = elements[cell->first + dcs_start[ival]]; + elements[cell->first + dcs_start[ival]] = element; + dcs_start[ival]++; + dcs_count[ival]--; + } + ep++; + } + dcs_count[i] = 0; + } + +#if defined(BLISS_CONSISTENCY_CHECKS) + for(unsigned int i = 0; i < 256; i++) + assert(dcs_count[i] == 0); +#endif + + /* split cell */ + Cell* const new_cell = split_cell(cell); + assert(new_cell != cell); + return new_cell; +} + + + +/* + * Sort the elements in a cell according to their invariant values. + * The invariant values are not cleared. + * Warning: the in_pos array is left in incorrect state. + */ +bool +Partition::shellsort_cell(Partition::Cell* const cell) +{ + unsigned int h; + unsigned int* ep; + + //assert(cell->first + cell->length <= N); + + if(cell->is_unit()) + return false; + + /* Check whether all the elements have the same invariant value */ + bool equal_invariant_values = true; + { + ep = elements + cell->first; + const unsigned int ival = invariant_values[*ep]; + assert(element_to_cell_map[*ep] == cell); + ep++; + for(unsigned int i = cell->length - 1; i > 0; i--) + { + assert(element_to_cell_map[*ep] == cell); + if(invariant_values[*ep] != ival) { + equal_invariant_values = false; + break; + } + ep++; + } + } + if(equal_invariant_values) + return false; + + ep = elements + cell->first; + + for(h = 1; h <= cell->length/9; h = 3*h + 1) + ; + for( ; h > 0; h = h/3) { + for(unsigned int i = h; i < cell->length; i++) { + const unsigned int element = ep[i]; + const unsigned int ival = invariant_values[element]; + unsigned int j = i; + while(j >= h and invariant_values[ep[j-h]] > ival) { + ep[j] = ep[j-h]; + j -= h; + } + ep[j] = element; + } + } + return true; +} + + + +void +Partition::clear_ivs(Cell* const cell) +{ + unsigned int* ep = elements + cell->first; + for(unsigned int i = cell->length; i > 0; i--, ep++) + invariant_values[*ep] = 0; +} + + +/* + * Assumes that the elements in the cell are sorted according to their + * invariant values. + */ +Partition::Cell* +Partition::split_cell(Partition::Cell* const original_cell) +{ + Cell* cell = original_cell; + const bool original_cell_was_in_splitting_queue = + original_cell->in_splitting_queue; + Cell* largest_new_cell = 0; + + while(true) + { + unsigned int* ep = elements + cell->first; + const unsigned int* const lp = ep + cell->length; + const unsigned int ival = invariant_values[*ep]; + invariant_values[*ep] = 0; + element_to_cell_map[*ep] = cell; + in_pos[*ep] = ep; + ep++; + while(ep < lp) + { + const unsigned int e = *ep; + if(invariant_values[e] != ival) + break; + invariant_values[e] = 0; + in_pos[e] = ep; + ep++; + element_to_cell_map[e] = cell; + } + if(ep == lp) + break; + + Cell* const new_cell = aux_split_in_two(cell, + (ep - elements) - cell->first); + + if(graph and graph->compute_eqref_hash) + { + graph->eqref_hash.update(new_cell->first); + graph->eqref_hash.update(new_cell->length); + graph->eqref_hash.update(ival); + } + + /* Add cells in splitting_queue */ + assert(!new_cell->is_in_splitting_queue()); + if(original_cell_was_in_splitting_queue) + { + /* In this case, all new cells are inserted in splitting_queue */ + assert(cell->is_in_splitting_queue()); + splitting_queue_add(new_cell); + } + else + { + /* Otherwise, we can omit one new cell from splitting_queue */ + assert(!cell->is_in_splitting_queue()); + if(largest_new_cell == 0) { + largest_new_cell = cell; + } else { + assert(!largest_new_cell->is_in_splitting_queue()); + if(cell->length > largest_new_cell->length) { + splitting_queue_add(largest_new_cell); + largest_new_cell = cell; + } else { + splitting_queue_add(cell); + } + } + } + /* Process the rest of the cell */ + cell = new_cell; + } + + + if(original_cell == cell) { + /* All the elements in cell had the same invariant value */ + return cell; + } + + /* Add cells in splitting_queue */ + if(!original_cell_was_in_splitting_queue) + { + /* Also consider the last new cell */ + assert(largest_new_cell); + if(cell->length > largest_new_cell->length) + { + splitting_queue_add(largest_new_cell); + largest_new_cell = cell; + } + else + { + splitting_queue_add(cell); + } + if(largest_new_cell->is_unit()) + { + /* Needed in certificate computation */ + splitting_queue_add(largest_new_cell); + } + } + + return cell; +} + + +Partition::Cell* +Partition::zplit_cell(Partition::Cell* const cell, + const bool max_ival_info_ok) +{ + assert(cell != 0); + + Cell* last_new_cell = cell; + + if(!max_ival_info_ok) + { + /* Compute max_ival info */ + assert(cell->max_ival == 0); + assert(cell->max_ival_count == 0); + unsigned int *ep = elements + cell->first; + for(unsigned int i = cell->length; i > 0; i--, ep++) + { + const unsigned int ival = invariant_values[*ep]; + if(ival > cell->max_ival) + { + cell->max_ival = ival; + cell->max_ival_count = 1; + } + else if(ival == cell->max_ival) + { + cell->max_ival_count++; + } + } + } + +#ifdef BLISS_CONSISTENCY_CHECKS + /* Verify max_ival info */ + { + unsigned int nof_zeros = 0; + unsigned int max_ival = 0; + unsigned int max_ival_count = 0; + unsigned int *ep = elements + cell->first; + for(unsigned int i = cell->length; i > 0; i--, ep++) + { + const unsigned int ival = invariant_values[*ep]; + if(ival == 0) + nof_zeros++; + if(ival > max_ival) + { + max_ival = ival; + max_ival_count = 1; + } + else if(ival == max_ival) + max_ival_count++; + } + assert(max_ival == cell->max_ival); + assert(max_ival_count == cell->max_ival_count); + } +#endif + + /* max_ival info has been computed */ + + if(cell->max_ival_count == cell->length) + { + /* All invariant values are the same, clear 'em */ + if(cell->max_ival > 0) + clear_ivs(cell); + } + else + { + /* All invariant values are not the same */ + if(cell->max_ival == 1) + { + /* Specialized splitting for cells with binary invariant values */ + last_new_cell = sort_and_split_cell1(cell); + } + else if(cell->max_ival < 256) + { + /* Specialized splitting for cells with invariant values < 256 */ + last_new_cell = sort_and_split_cell255(cell, cell->max_ival); + } + else + { + /* Generic sorting and splitting */ + const bool sorted = shellsort_cell(cell); + assert(sorted); + IGRAPH_UNUSED(sorted); + last_new_cell = split_cell(cell); + } + } + cell->max_ival = 0; + cell->max_ival_count = 0; + return last_new_cell; +} + + + +/* + * + * Component recursion specific code + * + */ +void +Partition::cr_init() +{ + assert(bt_stack.empty()); + + cr_enabled = true; + + delete[] cr_cells; + cr_cells = new CRCell[N]; + + delete[] cr_levels; + cr_levels = new CRCell*[N]; + + for(unsigned int i = 0; i < N; i++) { + cr_levels[i] = 0; + cr_cells[i].level = UINT_MAX; + cr_cells[i].next = 0; + cr_cells[i].prev_next_ptr = 0; + } + + for(const Cell *cell = first_cell; cell; cell = cell->next) + cr_create_at_level_trailed(cell->first, 0); + + cr_max_level = 0; +} + + +void +Partition::cr_free() +{ + delete[] cr_cells; cr_cells = nullptr; + delete[] cr_levels; cr_levels = nullptr; + + cr_created_trail.clear(); + cr_splitted_level_trail.clear(); + cr_bt_info.clear(); + cr_max_level = 0; + + cr_enabled = false; +} + + +unsigned int +Partition::cr_split_level(const unsigned int level, + const std::vector& splitted_cells) +{ + assert(cr_enabled); + assert(level <= cr_max_level); + cr_levels[++cr_max_level] = 0; + cr_splitted_level_trail.push_back(level); + + for(unsigned int i = 0; i < splitted_cells.size(); i++) + { + const unsigned int cell_index = splitted_cells[i]; + assert(cell_index < N); + CRCell& cr_cell = cr_cells[cell_index]; + assert(cr_cell.level == level); + cr_cell.detach(); + cr_create_at_level(cell_index, cr_max_level); + } + + return cr_max_level; +} + + +unsigned int +Partition::cr_get_backtrack_point() +{ + assert(cr_enabled); + CR_BTInfo info; + info.created_trail_index = cr_created_trail.size(); + info.splitted_level_trail_index = cr_splitted_level_trail.size(); + cr_bt_info.push_back(info); + return cr_bt_info.size()-1; +} + + +void +Partition::cr_goto_backtrack_point(const unsigned int btpoint) +{ + assert(cr_enabled); + assert(btpoint < cr_bt_info.size()); + while(cr_created_trail.size() > cr_bt_info[btpoint].created_trail_index) + { + const unsigned int cell_index = cr_created_trail.back(); + cr_created_trail.pop_back(); + CRCell& cr_cell = cr_cells[cell_index]; + assert(cr_cell.level != UINT_MAX); + assert(cr_cell.prev_next_ptr); + cr_cell.detach(); + } + + while(cr_splitted_level_trail.size() > + cr_bt_info[btpoint].splitted_level_trail_index) + { + const unsigned int dest_level = cr_splitted_level_trail.back(); + cr_splitted_level_trail.pop_back(); + assert(cr_max_level > 0); + assert(dest_level < cr_max_level); + while(cr_levels[cr_max_level]) { + CRCell *cr_cell = cr_levels[cr_max_level]; + cr_cell->detach(); + cr_create_at_level(cr_cell - cr_cells, dest_level); + } + cr_max_level--; + } + cr_bt_info.resize(btpoint); +} + + +void +Partition::cr_create_at_level(const unsigned int cell_index, + const unsigned int level) +{ + assert(cr_enabled); + assert(cell_index < N); + assert(level < N); + CRCell& cr_cell = cr_cells[cell_index]; + assert(cr_cell.level == UINT_MAX); + assert(cr_cell.next == 0); + assert(cr_cell.prev_next_ptr == 0); + if(cr_levels[level]) + cr_levels[level]->prev_next_ptr = &(cr_cell.next); + cr_cell.next = cr_levels[level]; + cr_levels[level] = &cr_cell; + cr_cell.prev_next_ptr = &cr_levels[level]; + cr_cell.level = level; +} + + +void +Partition::cr_create_at_level_trailed(const unsigned int cell_index, + const unsigned int level) +{ + assert(cr_enabled); + cr_create_at_level(cell_index, level); + cr_created_trail.push_back(cell_index); +} + + +} // namespace bliss diff --git a/src/rigraph/core/isomorphism/bliss/partition.hh b/src/rigraph/core/isomorphism/bliss/partition.hh new file mode 100644 index 0000000..52cefda --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/partition.hh @@ -0,0 +1,299 @@ +#ifndef BLISS_PARTITION_HH +#define BLISS_PARTITION_HH + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +namespace bliss { + class Partition; +} + +#include +#include +#include "kstack.hh" +#include "kqueue.hh" +#include "graph.hh" + + +namespace bliss { + +/** + * \brief A class for refinable, backtrackable ordered partitions. + * + * This is rather a data structure with some helper functions than + * a proper self-contained class. + * That is, for efficiency reasons the fields of this class are directly + * manipulated from bliss::AbstractGraph and its subclasses. + * Conversely, some methods of this class modify the fields of + * bliss::AbstractGraph, too. + */ +class Partition +{ +public: + /** + * \brief Data structure for holding information about a cell in a Partition. + */ + class Cell + { + friend class Partition; + public: + unsigned int length; + /* Index of the first element of the cell in + the Partition::elements array */ + unsigned int first; + unsigned int max_ival; + unsigned int max_ival_count; + private: + bool in_splitting_queue; + public: + bool in_neighbour_heap; + /* Pointer to the next cell, null if this is the last one. */ + Cell* next; + Cell* prev; + Cell* next_nonsingleton; + Cell* prev_nonsingleton; + unsigned int split_level; + /** Is this a unit cell? */ + bool is_unit() const {return(length == 1); } + /** Is this cell in splitting queue? */ + bool is_in_splitting_queue() const {return(in_splitting_queue); } + }; + + +private: + + /** \internal + * Data structure for remembering information about splits in order to + * perform efficient backtracking over the splits. + */ + class RefInfo { + public: + unsigned int split_cell_first; + int prev_nonsingleton_first; + int next_nonsingleton_first; + }; + /** \internal + * A stack for remembering the splits, used for backtracking. + */ + KStack refinement_stack; + + class BacktrackInfo { + public: + unsigned int refinement_stack_size; + unsigned int cr_backtrack_point; + }; + + /** \internal + * The main stack for enabling backtracking. + */ + std::vector bt_stack; + +public: + AbstractGraph* graph; + + /* Used during equitable partition refinement */ + KQueue splitting_queue; + void splitting_queue_add(Cell* const cell); + Cell* splitting_queue_pop(); + bool splitting_queue_is_empty() const; + void splitting_queue_clear(); + + + /** Type for backtracking points. */ + typedef unsigned int BacktrackPoint; + + /** + * Get a new backtrack point for the current partition + */ + BacktrackPoint set_backtrack_point(); + + /** + * Backtrack to the point \a p and remove it. + */ + void goto_backtrack_point(BacktrackPoint p); + + /** + * Split the non-unit Cell \a cell = {\a element,e1,e2,...,en} containing + * the element \a element in two: + * \a cell = {e1,...,en} and \a newcell = {\a element}. + * @param cell a non-unit Cell + * @param element an element in \a cell + * @return the new unit Cell \a newcell + */ + Cell* individualize(Cell* const cell, + const unsigned int element); + + Cell* aux_split_in_two(Cell* const cell, + const unsigned int first_half_size); + + +private: + unsigned int N; + Cell* cells; + Cell* free_cells; + unsigned int discrete_cell_count; +public: + Cell* first_cell; + Cell* first_nonsingleton_cell; + unsigned int *elements; + /* invariant_values[e] gives the invariant value of the element e */ + unsigned int *invariant_values; + /* element_to_cell_map[e] gives the cell of the element e */ + Cell **element_to_cell_map; + /** Get the cell of the element \a e */ + Cell* get_cell(const unsigned int e) const { + assert(e < N); + return element_to_cell_map[e]; + } + /* in_pos[e] points to the elements array s.t. *in_pos[e] = e */ + unsigned int **in_pos; + + Partition(); + ~Partition(); + + /** + * Initialize the partition to the unit partition (all elements in one cell) + * over the \a N > 0 elements {0,...,\a N-1}. + */ + void init(const unsigned int N); + + /** + * Returns true iff the partition is discrete, meaning that all + * the elements are in their own cells. + */ + bool is_discrete() const {return(free_cells == 0); } + + unsigned int nof_discrete_cells() const {return(discrete_cell_count); } + + /* + * Splits the Cell \a cell into [cell_1,...,cell_n] + * according to the invariant_values of the elements in \a cell. + * After splitting, cell_1 == \a cell. + * Returns the pointer to the Cell cell_n; + * cell_n != cell iff the Cell \a cell was actually splitted. + * The flag \a max_ival_info_ok indicates whether the max_ival and + * max_ival_count fields of the Cell \a cell have consistent values + * when the method is called. + * Clears the invariant values of elements in the Cell \a cell as well as + * the max_ival and max_ival_count fields of the Cell \a cell. + */ + Cell *zplit_cell(Cell * const cell, const bool max_ival_info_ok); + + /* + * Routines for component recursion + */ + void cr_init(); + void cr_free(); + unsigned int cr_get_level(const unsigned int cell_index) const; + unsigned int cr_split_level(const unsigned int level, + const std::vector& cells); + + /** Clear the invariant_values of the elements in the Cell \a cell. */ + void clear_ivs(Cell* const cell); + +private: + /* + * Component recursion data structures + */ + + /* Is component recursion support in use? */ + bool cr_enabled; + + class CRCell { + public: + unsigned int level; + CRCell* next; + CRCell** prev_next_ptr; + void detach() { + if(next) + next->prev_next_ptr = prev_next_ptr; + *(prev_next_ptr) = next; + level = UINT_MAX; + next = 0; + prev_next_ptr = 0; + } + }; + CRCell* cr_cells; + CRCell** cr_levels; + class CR_BTInfo { + public: + unsigned int created_trail_index; + unsigned int splitted_level_trail_index; + }; + std::vector cr_created_trail; + std::vector cr_splitted_level_trail; + std::vector cr_bt_info; + unsigned int cr_max_level; + void cr_create_at_level(const unsigned int cell_index, unsigned int level); + void cr_create_at_level_trailed(const unsigned int cell_index, unsigned int level); + unsigned int cr_get_backtrack_point(); + void cr_goto_backtrack_point(const unsigned int btpoint); + + + /* + * + * Auxiliary routines for sorting and splitting cells + * + */ + Cell* sort_and_split_cell1(Cell* cell); + Cell* sort_and_split_cell255(Cell* const cell, const unsigned int max_ival); + bool shellsort_cell(Cell* cell); + Cell* split_cell(Cell* const cell); + + /* + * Some auxiliary stuff needed for distribution count sorting. + * To make the code thread-safe (modulo the requirement that each graph is + * only accessed in one thread at a time), the arrays are owned by + * the partition instance, not statically defined. + */ + unsigned int dcs_count[256]; + unsigned int dcs_start[256]; + void dcs_cumulate_count(const unsigned int max); +}; + + +inline Partition::Cell* +Partition::splitting_queue_pop() +{ + assert(!splitting_queue.is_empty()); + Cell* const cell = splitting_queue.pop_front(); + assert(cell->in_splitting_queue); + cell->in_splitting_queue = false; + return cell; +} + +inline bool +Partition::splitting_queue_is_empty() const +{ + return splitting_queue.is_empty(); +} + + +inline unsigned int +Partition::cr_get_level(const unsigned int cell_index) const +{ + assert(cr_enabled); + assert(cell_index < N); + assert(cr_cells[cell_index].level != UINT_MAX); + return(cr_cells[cell_index].level); +} + +} // namespace bliss + +#endif // BLISS_PARTITION_HH diff --git a/src/rigraph/core/isomorphism/bliss/stats.hh b/src/rigraph/core/isomorphism/bliss/stats.hh new file mode 100644 index 0000000..5294581 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/stats.hh @@ -0,0 +1,87 @@ +#ifndef BLISS_STATS_HH +#define BLISS_STATS_HH + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +#include "graph.hh" +#include "bignum.hh" + +namespace bliss { + +/** + * \brief Statistics returned by the bliss search algorithm. + */ +class Stats +{ + friend class AbstractGraph; + /** \internal The size of the automorphism group. */ + BigNum group_size; + /** \internal An approximation (due to possible overflows) of + * the size of the automorphism group. */ + long double group_size_approx; + /** \internal The number of nodes in the search tree. */ + long unsigned int nof_nodes; + /** \internal The number of leaf nodes in the search tree. */ + long unsigned int nof_leaf_nodes; + /** \internal The number of bad nodes in the search tree. */ + long unsigned int nof_bad_nodes; + /** \internal The number of canonical representative updates. */ + long unsigned int nof_canupdates; + /** \internal The number of generator permutations. */ + long unsigned int nof_generators; + /** \internal The maximal depth of the search tree. */ + unsigned long int max_level; + /** \internal Reset the statistics. */ + void reset() + { + group_size.assign(1); + group_size_approx = 1.0; + nof_nodes = 0; + nof_leaf_nodes = 0; + nof_bad_nodes = 0; + nof_canupdates = 0; + nof_generators = 0; + max_level = 0; + } +public: + Stats() { reset(); } + + /** The size of the automorphism group. */ + const BigNum& get_group_size() const {return group_size;} + /** An approximation (due to possible overflows/rounding errors) of + * the size of the automorphism group. */ + long double get_group_size_approx() const {return group_size_approx;} + /** The number of nodes in the search tree. */ + long unsigned int get_nof_nodes() const {return nof_nodes;} + /** The number of leaf nodes in the search tree. */ + long unsigned int get_nof_leaf_nodes() const {return nof_leaf_nodes;} + /** The number of bad nodes in the search tree. */ + long unsigned int get_nof_bad_nodes() const {return nof_bad_nodes;} + /** The number of canonical representative updates. */ + long unsigned int get_nof_canupdates() const {return nof_canupdates;} + /** The number of generator permutations. */ + long unsigned int get_nof_generators() const {return nof_generators;} + /** The maximal depth of the search tree. */ + unsigned long int get_max_level() const {return max_level;} +}; + +} // namespace bliss + +#endif // BLISS_STATS_HH diff --git a/src/rigraph/core/isomorphism/bliss/uintseqhash.cc b/src/rigraph/core/isomorphism/bliss/uintseqhash.cc new file mode 100644 index 0000000..3e6f2b3 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/uintseqhash.cc @@ -0,0 +1,117 @@ +#include "uintseqhash.hh" + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +namespace bliss { + +/* + * Random bits generated by + * http://www.fourmilab.ch/hotbits/ + */ +static unsigned int rtab[256] = { + 0xAEAA35B8, 0x65632E16, 0x155EDBA9, 0x01349B39, + 0x8EB8BD97, 0x8E4C5367, 0x8EA78B35, 0x2B1B4072, + 0xC1163893, 0x269A8642, 0xC79D7F6D, 0x6A32DEA0, + 0xD4D2DA56, 0xD96D4F47, 0x47B5F48A, 0x2587C6BF, + 0x642B71D8, 0x5DBBAF58, 0x5C178169, 0xA16D9279, + 0x75CDA063, 0x291BC48B, 0x01AC2F47, 0x5416DF7C, + 0x45307514, 0xB3E1317B, 0xE1C7A8DE, 0x3ACDAC96, + 0x11B96831, 0x32DE22DD, 0x6A1DA93B, 0x58B62381, + 0x283810E2, 0xBC30E6A6, 0x8EE51705, 0xB06E8DFB, + 0x729AB12A, 0xA9634922, 0x1A6E8525, 0x49DD4E19, + 0xE5DB3D44, 0x8C5B3A02, 0xEBDE2864, 0xA9146D9F, + 0x736D2CB4, 0xF5229F42, 0x712BA846, 0x20631593, + 0x89C02603, 0xD5A5BF6A, 0x823F4E18, 0x5BE5DEFF, + 0x1C4EBBFA, 0x5FAB8490, 0x6E559B0C, 0x1FE528D6, + 0xB3198066, 0x4A965EB5, 0xFE8BB3D5, 0x4D2F6234, + 0x5F125AA4, 0xBCC640FA, 0x4F8BC191, 0xA447E537, + 0xAC474D3C, 0x703BFA2C, 0x617DC0E7, 0xF26299D7, + 0xC90FD835, 0x33B71C7B, 0x6D83E138, 0xCBB1BB14, + 0x029CF5FF, 0x7CBD093D, 0x4C9825EF, 0x845C4D6D, + 0x124349A5, 0x53942D21, 0x800E60DA, 0x2BA6EB7F, + 0xCEBF30D3, 0xEB18D449, 0xE281F724, 0x58B1CB09, + 0xD469A13D, 0x9C7495C3, 0xE53A7810, 0xA866C08E, + 0x832A038B, 0xDDDCA484, 0xD5FE0DDE, 0x0756002B, + 0x2FF51342, 0x60FEC9C8, 0x061A53E3, 0x47B1884E, + 0xDC17E461, 0xA17A6A37, 0x3158E7E2, 0xA40D873B, + 0x45AE2140, 0xC8F36149, 0x63A4EE2D, 0xD7107447, + 0x6F90994F, 0x5006770F, 0xC1F3CA9A, 0x91B317B2, + 0xF61B4406, 0xA8C9EE8F, 0xC6939B75, 0xB28BBC3B, + 0x36BF4AEF, 0x3B12118D, 0x4D536ECF, 0x9CF4B46B, + 0xE8AB1E03, 0x8225A360, 0x7AE4A130, 0xC4EE8B50, + 0x50651797, 0x5BB4C59F, 0xD120EE47, 0x24F3A386, + 0xBE579B45, 0x3A378EFC, 0xC5AB007B, 0x3668942B, + 0x2DBDCC3A, 0x6F37F64C, 0xC24F862A, 0xB6F97FCF, + 0x9E4FA23D, 0x551AE769, 0x46A8A5A6, 0xDC1BCFDD, + 0x8F684CF9, 0x501D811B, 0x84279F80, 0x2614E0AC, + 0x86445276, 0xAEA0CE71, 0x0812250F, 0xB586D18A, + 0xC68D721B, 0x44514E1D, 0x37CDB99A, 0x24731F89, + 0xFA72E589, 0x81E6EBA2, 0x15452965, 0x55523D9D, + 0x2DC47E14, 0x2E7FA107, 0xA7790F23, 0x40EBFDBB, + 0x77E7906B, 0x6C1DB960, 0x1A8B9898, 0x65FA0D90, + 0xED28B4D8, 0x34C3ED75, 0x768FD2EC, 0xFAB60BCB, + 0x962C75F4, 0x304F0498, 0x0A41A36B, 0xF7DE2A4A, + 0xF4770FE2, 0x73C93BBB, 0xD21C82C5, 0x6C387447, + 0x8CDB4CB9, 0x2CC243E8, 0x41859E3D, 0xB667B9CB, + 0x89681E8A, 0x61A0526C, 0x883EDDDC, 0x539DE9A4, + 0xC29E1DEC, 0x97C71EC5, 0x4A560A66, 0xBD7ECACF, + 0x576AE998, 0x31CE5616, 0x97172A6C, 0x83D047C4, + 0x274EA9A8, 0xEB31A9DA, 0x327209B5, 0x14D1F2CB, + 0x00FE1D96, 0x817DBE08, 0xD3E55AED, 0xF2D30AFC, + 0xFB072660, 0x866687D6, 0x92552EB9, 0xEA8219CD, + 0xF7927269, 0xF1948483, 0x694C1DF5, 0xB7D8B7BF, + 0xFFBC5D2F, 0x2E88B849, 0x883FD32B, 0xA0331192, + 0x8CB244DF, 0x41FAF895, 0x16902220, 0x97FB512A, + 0x2BEA3CC4, 0xAF9CAE61, 0x41ACD0D5, 0xFD2F28FF, + 0xE780ADFA, 0xB3A3A76E, 0x7112AD87, 0x7C3D6058, + 0x69E64FFF, 0xE5F8617C, 0x8580727C, 0x41F54F04, + 0xD72BE498, 0x653D1795, 0x1275A327, 0x14B499D4, + 0x4E34D553, 0x4687AA39, 0x68B64292, 0x5C18ABC3, + 0x41EABFCC, 0x92A85616, 0x82684CF8, 0x5B9F8A4E, + 0x35382FFE, 0xFB936318, 0x52C08E15, 0x80918B2E, + 0x199EDEE0, 0xA9470163, 0xEC44ACDD, 0x612D6735, + 0x8F88EA7D, 0x759F5EA4, 0xE5CC7240, 0x68CFEB8B, + 0x04725601, 0x0C22C23E, 0x5BC97174, 0x89965841, + 0x5D939479, 0x690F338A, 0x3C2D4380, 0xDAE97F2B +}; + + +void UintSeqHash::update(unsigned int i) +{ + i++; + while(i > 0) + { + h ^= rtab[i & 0xff]; +#if 1 + const unsigned int b = (h & 0x80000000) >> 31; + i = i >> 8; + h = (h << 1) | b; +#else + const unsigned int b = h & 0x80000000; + h = h << 1; + if(b != 0) + h++; + i = i >> 8; +#endif + } +} + + +} // namespace bliss diff --git a/src/rigraph/core/isomorphism/bliss/uintseqhash.hh b/src/rigraph/core/isomorphism/bliss/uintseqhash.hh new file mode 100644 index 0000000..4ac4e01 --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/uintseqhash.hh @@ -0,0 +1,63 @@ +#ifndef BLISS_UINTSEQHASH_HH +#define BLISS_UINTSEQHASH_HH + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +namespace bliss { + +/** + * \brief A updatable hash for sequences of unsigned ints. + */ +class UintSeqHash +{ +protected: + unsigned int h; +public: + UintSeqHash() {h = 0; } + UintSeqHash(const UintSeqHash &other) {h = other.h; } + UintSeqHash& operator=(const UintSeqHash &other) {h = other.h; return *this; } + + /** Reset the hash value. */ + void reset() {h = 0; } + + /** Add the unsigned int \a n to the sequence. */ + void update(unsigned int n); + + /** Get the hash value of the sequence seen so far. */ + unsigned int get_value() const {return h; } + + /** Compare the hash values of this and \a other. + * Return -1/0/1 if the value of this is smaller/equal/greater than + * that of \a other. */ + int cmp(const UintSeqHash &other) const { + return (h < other.h)?-1:((h == other.h)?0:1); + } + /** An abbreviation for cmp(other) < 0 */ + bool is_lt(const UintSeqHash &other) const {return cmp(other) < 0; } + /** An abbreviation for cmp(other) <= 0 */ + bool is_le(const UintSeqHash &other) const {return cmp(other) <= 0; } + /** An abbreviation for cmp(other) == 0 */ + bool is_equal(const UintSeqHash &other) const {return cmp(other) == 0; } +}; + + +} // namespace bliss + +#endif // BLISS_UINTSEQHASH_HH diff --git a/src/rigraph/core/isomorphism/bliss/utils.cc b/src/rigraph/core/isomorphism/bliss/utils.cc new file mode 100644 index 0000000..e2913cb --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/utils.cc @@ -0,0 +1,60 @@ +#include +#include "utils.hh" + +/* Allow using 'and' instead of '&&' with MSVC */ +#if _MSC_VER +#include +#endif + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +namespace bliss { + +bool +is_permutation(const unsigned int N, const unsigned int* perm) +{ + if(N == 0) + return true; + std::vector m(N, false); + for(unsigned int i = 0; i < N; i++) { + if(perm[i] >= N) return false; + if(m[perm[i]]) return false; + m[perm[i]] = true; + } + return true; +} + +bool +is_permutation(const std::vector& perm) +{ + const unsigned int N = perm.size(); + if(N == 0) + return true; + std::vector m(N, false); + for(unsigned int i = 0; i < N; i++) { + if(perm[i] >= N) return false; + if(m[perm[i]]) return false; + m[perm[i]] = true; + } + return true; +} + + +} // namespace bliss diff --git a/src/rigraph/core/isomorphism/bliss/utils.hh b/src/rigraph/core/isomorphism/bliss/utils.hh new file mode 100644 index 0000000..4f3514a --- /dev/null +++ b/src/rigraph/core/isomorphism/bliss/utils.hh @@ -0,0 +1,46 @@ +#ifndef BLISS_UTILS_HH +#define BLISS_UTILS_HH + +/* + Copyright (c) 2003-2021 Tommi Junttila + Released under the GNU Lesser General Public License version 3. + + This file is part of bliss. + + bliss is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, version 3 of the License. + + bliss 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bliss. If not, see . +*/ + +/** + * \file + * \brief Some small utilities. + */ + +#include + +namespace bliss { + +/** + * Check whether \a perm is a valid permutation on {0,...,N-1}. + * Slow, mainly for debugging and validation purposes. + */ +bool is_permutation(const unsigned int N, const unsigned int* perm); + +/** + * Check whether \a perm is a valid permutation on {0,...,N-1}. + * Slow, mainly for debugging and validation purposes. + */ +bool is_permutation(const std::vector& perm); + +} // namespace bliss + +#endif // BLISS_UTILS_HH diff --git a/src/rigraph/core/isomorphism/isoclasses.c b/src/rigraph/core/isomorphism/isoclasses.c new file mode 100644 index 0000000..56750d0 --- /dev/null +++ b/src/rigraph/core/isomorphism/isoclasses.c @@ -0,0 +1,2876 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "igraph_topology.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "isoclasses.h" + +/* + * Small labelled graphs are encoded into a compact representation, a "code", + * that fits into a single integer value. Each non-loop edge corresponds to + * a specific bit of the integer. The edge-to-bit mappings are stored in + * the "isoclass_idx" arrays while the bit-to-edge mappings are in the "classedges" + * arrays. + * + * The "isoclass2" array is a mapping from the code of each possible labelled + * graph to its isomorphism class. A canonical representative of each isomorphism + * class is stored in "isographs". + * + * In the names of arrays, the number refers to the vertex count, while "u" + * indicates undirected graphs (the other arrays store directed ones). + * + * Description of each array for graphs of size n: + * + * isosclass_idx represents an n-by-n matrix stored in column-major order. + * Element i,j of the matrix is an integer with a single bit set. This bit, + * if set, represents edge i-j in the graph code. + * + * isoclass2[code] gives the isomorphism class of the graph represented by code. + * Classes are labelled by integers starting at 0, after ordering them by the + * graph code of their smallest-code representative. + * + * isographs[class] is the code of a graph belonging to the given class. For each + * class, the representative with the smallest code is chosen. + * + * classedges[2*i] - classedges[2*i+1] are the endpoints of the edge represented + * by bit i in the code. Bits are numbered from most to least significant, thus + * the most significant one has index i=0. + */ + +const unsigned int igraph_i_isoclass_3_idx[] = { 0, 4, 16, 1, 0, 32, 2, 8, 0 }; + +const unsigned int igraph_i_isoclass_4_idx[] = { + 0, 8, 64, 512, 1, 0, 128, 1024, 2, 16, 0, 2048, 4, 32, 256, 0 +}; + +const unsigned int igraph_i_isoclass_3u_idx[] = { 0, 1, 2, 1, 0, 4, 2, 4, 0 }; + +const unsigned int igraph_i_isoclass_4u_idx[] = { + 0, 1, 2, 8, 1, 0, 4, 16, 2, 4, 0, 32, 8, 16, 32, 0 +}; + +const unsigned int igraph_i_isoclass_5u_idx[] = { + 0, 1, 2, 8, 64, 1, 0, 4, 16, 128, 2, 4, 0, 32, 256, 8, 16, 32, 0, 512, 64, 128, 256, 512, 0 +}; + +const unsigned int igraph_i_isoclass_6u_idx[] = { + 0, 1, 2, 8, 64, 1024, 1, 0, 4, 16, 128, 2048, 2, 4, 0, 32, 256, 4096, + 8, 16, 32, 0, 512, 8192, 64, 128, 256, 512, 0, 16384, 1024, 2048, + 4096, 8192, 16384, 0 +}; + +const unsigned int igraph_i_isoclass2_3[] = { + 0, 1, 1, 2, 1, 3, 4, 5, 1, 4, 6, 7, 2, 5, 7, 8, 1, 4, 3, 5, 6, 9, 9, 10, 4, 11, + 9, 12, 7, 12, 13, 14, 1, 6, 4, 7, 4, 9, 11, 12, 3, 9, 9, 13, 5, 10, 12, 14, 2, 7, 5, 8, + 7, 13, 12, 14, 5, 12, 10, 14, 8, 14, 14, 15 +}; + +const unsigned int igraph_i_isoclass2_3u[] = { + 0, 1, 1, 2, 1, 2, 2, 3 +}; + +const unsigned int igraph_i_isoclass2_4u[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 4, 5, 6, 6, 7, 1, 2, 5, 6, 2, 4, 6, 7, 2, 3, + 6, 7, 6, 7, 8, 9, 1, 5, 2, 6, 2, 6, 4, 7, 2, 6, 3, 7, 6, 8, 7, 9, 2, 6, 6, 8, + 3, 7, 7, 9, 4, 7, 7, 9, 7, 9, 9, 10 +}; + +const unsigned int igraph_i_isoclass2_4[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 4, 5, 6, 5, 6, 7, 8, 1, 5, 9, 10, + 11, 12, 13, 14, 2, 6, 10, 15, 12, 16, 17, 18, 1, 5, 11, 12, 9, 10, 13, 14, + 2, 6, 12, 16, 10, 15, 17, 18, 2, 7, 13, 17, 13, 17, 19, 20, 3, 8, 14, 18, + 14, 18, 20, 21, 1, 5, 4, 6, 5, 7, 6, 8, 9, 22, 22, 23, 24, 25, 25, 26, + 5, 27, 22, 28, 29, 30, 31, 32, 10, 28, 33, 34, 35, 36, 37, 38, 11, 29, 39, 40, + 41, 42, 43, 44, 13, 31, 45, 46, 47, 48, 49, 50, 12, 30, 45, 51, 52, 53, 54, 55, + 14, 32, 56, 57, 58, 59, 60, 61, 1, 9, 5, 10, 11, 13, 12, 14, 5, 22, 27, 28, + 29, 31, 30, 32, 4, 22, 22, 33, 39, 45, 45, 56, 6, 23, 28, 34, 40, 46, 51, 57, + 5, 24, 29, 35, 41, 47, 52, 58, 7, 25, 30, 36, 42, 48, 53, 59, 6, 25, 31, 37, + 43, 49, 54, 60, 8, 26, 32, 38, 44, 50, 55, 61, 2, 10, 6, 15, 12, 17, 16, 18, + 10, 33, 28, 34, 35, 37, 36, 38, 6, 28, 23, 34, 40, 51, 46, 57, 15, 34, 34, 62, + 63, 64, 64, 65, 12, 35, 40, 63, 66, 67, 68, 69, 17, 37, 51, 64, 67, 70, 71, 72, + 16, 36, 46, 64, 68, 71, 73, 74, 18, 38, 57, 65, 69, 72, 74, 75, 1, 11, 5, 12, + 9, 13, 10, 14, 11, 39, 29, 40, 41, 43, 42, 44, 5, 29, 24, 35, 41, 52, 47, 58, + 12, 40, 35, 63, 66, 68, 67, 69, 9, 41, 41, 66, 76, 77, 77, 78, 13, 43, 52, 68, + 77, 79, 80, 81, 10, 42, 47, 67, 77, 80, 82, 83, 14, 44, 58, 69, 78, 81, 83, 84, + 2, 12, 6, 16, 10, 17, 15, 18, 13, 45, 31, 46, 47, 49, 48, 50, 7, 30, 25, 36, + 42, 53, 48, 59, 17, 51, 37, 64, 67, 71, 70, 72, 13, 52, 43, 68, 77, 80, 79, 81, + 19, 54, 54, 73, 82, 85, 85, 86, 17, 53, 49, 71, 80, 87, 85, 88, 20, 55, 60, 74, + 83, 88, 89, 90, 2, 13, 7, 17, 13, 19, 17, 20, 12, 45, 30, 51, 52, 54, 53, 55, + 6, 31, 25, 37, 43, 54, 49, 60, 16, 46, 36, 64, 68, 73, 71, 74, 10, 47, 42, 67, + 77, 82, 80, 83, 17, 49, 53, 71, 80, 85, 87, 88, 15, 48, 48, 70, 79, 85, 85, 89, + 18, 50, 59, 72, 81, 86, 88, 90, 3, 14, 8, 18, 14, 20, 18, 21, 14, 56, 32, 57, + 58, 60, 59, 61, 8, 32, 26, 38, 44, 55, 50, 61, 18, 57, 38, 65, 69, 74, 72, 75, + 14, 58, 44, 69, 78, 83, 81, 84, 20, 60, 55, 74, 83, 89, 88, 90, 18, 59, 50, 72, + 81, 88, 86, 90, 21, 61, 61, 75, 84, 90, 90, 91, 1, 5, 5, 7, 4, 6, 6, 8, + 9, 22, 24, 25, 22, 23, 25, 26, 11, 29, 41, 42, 39, 40, 43, 44, 13, 31, 47, 48, + 45, 46, 49, 50, 5, 27, 29, 30, 22, 28, 31, 32, 10, 28, 35, 36, 33, 34, 37, 38, + 12, 30, 52, 53, 45, 51, 54, 55, 14, 32, 58, 59, 56, 57, 60, 61, 9, 24, 22, 25, + 22, 25, 23, 26, 76, 92, 92, 93, 92, 93, 93, 94, 41, 95, 96, 97, 98, 99, 100, 101, + 77, 102, 103, 104, 105, 106, 107, 108, 41, 95, 98, 99, 96, 97, 100, 101, 77, 102, 105, 106, + 103, 104, 107, 108, 66, 109, 110, 111, 110, 111, 112, 113, 78, 114, 115, 116, 115, 116, 117, 118, + 11, 41, 29, 42, 39, 43, 40, 44, 41, 96, 95, 97, 98, 100, 99, 101, 39, 98, 98, 119, + 120, 121, 121, 122, 43, 100, 123, 124, 121, 125, 126, 127, 29, 95, 128, 129, 98, 123, 130, 131, + 42, 97, 129, 132, 119, 124, 133, 134, 40, 99, 130, 133, 121, 126, 135, 136, 44, 101, 131, 134, + 122, 127, 136, 137, 13, 47, 31, 48, 45, 49, 46, 50, 77, 103, 102, 104, 105, 107, 106, 108, + 43, 123, 100, 124, 121, 126, 125, 127, 79, 138, 138, 139, 140, 141, 141, 142, 52, 143, 130, 144, + 110, 145, 146, 147, 80, 148, 149, 150, 151, 152, 153, 154, 68, 155, 146, 156, 157, 158, 159, 160, + 81, 161, 162, 163, 164, 165, 166, 167, 5, 29, 27, 30, 22, 31, 28, 32, 41, 98, 95, 99, + 96, 100, 97, 101, 29, 128, 95, 129, 98, 130, 123, 131, 52, 130, 143, 144, 110, 146, 145, 147, + 24, 95, 95, 109, 92, 102, 102, 114, 47, 123, 143, 155, 103, 138, 148, 161, 35, 129, 143, 168, + 105, 149, 169, 170, 58, 131, 171, 172, 115, 162, 173, 174, 10, 35, 28, 36, 33, 37, 34, 38, + 77, 105, 102, 106, 103, 107, 104, 108, 42, 129, 97, 132, 119, 133, 124, 134, 80, 149, 148, 150, + 151, 153, 152, 154, 47, 143, 123, 155, 103, 148, 138, 161, 82, 169, 169, 175, 176, 177, 177, 178, + 67, 168, 145, 179, 151, 180, 181, 182, 83, 170, 173, 183, 184, 185, 186, 187, 12, 52, 30, 53, + 45, 54, 51, 55, 66, 110, 109, 111, 110, 112, 111, 113, 40, 130, 99, 133, 121, 135, 126, 136, + 68, 146, 155, 156, 157, 159, 158, 160, 35, 143, 129, 168, 105, 169, 149, 170, 67, 145, 168, 179, + 151, 181, 180, 182, 63, 144, 144, 188, 140, 189, 189, 190, 69, 147, 172, 191, 164, 192, 193, 194, + 14, 58, 32, 59, 56, 60, 57, 61, 78, 115, 114, 116, 115, 117, 116, 118, 44, 131, 101, 134, + 122, 136, 127, 137, 81, 162, 161, 163, 164, 166, 165, 167, 58, 171, 131, 172, 115, 173, 162, 174, + 83, 173, 170, 183, 184, 186, 185, 187, 69, 172, 147, 191, 164, 193, 192, 194, 84, 174, 174, 195, + 196, 197, 197, 198, 1, 9, 11, 13, 5, 10, 12, 14, 5, 22, 29, 31, 27, 28, 30, 32, + 5, 24, 41, 47, 29, 35, 52, 58, 7, 25, 42, 48, 30, 36, 53, 59, 4, 22, 39, 45, + 22, 33, 45, 56, 6, 23, 40, 46, 28, 34, 51, 57, 6, 25, 43, 49, 31, 37, 54, 60, + 8, 26, 44, 50, 32, 38, 55, 61, 11, 41, 39, 43, 29, 42, 40, 44, 41, 96, 98, 100, + 95, 97, 99, 101, 29, 95, 98, 123, 128, 129, 130, 131, 42, 97, 119, 124, 129, 132, 133, 134, + 39, 98, 120, 121, 98, 119, 121, 122, 43, 100, 121, 125, 123, 124, 126, 127, 40, 99, 121, 126, + 130, 133, 135, 136, 44, 101, 122, 127, 131, 134, 136, 137, 9, 76, 41, 77, 41, 77, 66, 78, + 24, 92, 95, 102, 95, 102, 109, 114, 22, 92, 96, 103, 98, 105, 110, 115, 25, 93, 97, 104, + 99, 106, 111, 116, 22, 92, 98, 105, 96, 103, 110, 115, 25, 93, 99, 106, 97, 104, 111, 116, + 23, 93, 100, 107, 100, 107, 112, 117, 26, 94, 101, 108, 101, 108, 113, 118, 13, 77, 43, 79, + 52, 80, 68, 81, 47, 103, 123, 138, 143, 148, 155, 161, 31, 102, 100, 138, 130, 149, 146, 162, + 48, 104, 124, 139, 144, 150, 156, 163, 45, 105, 121, 140, 110, 151, 157, 164, 49, 107, 126, 141, + 145, 152, 158, 165, 46, 106, 125, 141, 146, 153, 159, 166, 50, 108, 127, 142, 147, 154, 160, 167, + 5, 41, 29, 52, 24, 47, 35, 58, 29, 98, 128, 130, 95, 123, 129, 131, 27, 95, 95, 143, + 95, 143, 143, 171, 30, 99, 129, 144, 109, 155, 168, 172, 22, 96, 98, 110, 92, 103, 105, 115, + 31, 100, 130, 146, 102, 138, 149, 162, 28, 97, 123, 145, 102, 148, 169, 173, 32, 101, 131, 147, + 114, 161, 170, 174, 12, 66, 40, 68, 35, 67, 63, 69, 52, 110, 130, 146, 143, 145, 144, 147, + 30, 109, 99, 155, 129, 168, 144, 172, 53, 111, 133, 156, 168, 179, 188, 191, 45, 110, 121, 157, + 105, 151, 140, 164, 54, 112, 135, 159, 169, 181, 189, 192, 51, 111, 126, 158, 149, 180, 189, 193, + 55, 113, 136, 160, 170, 182, 190, 194, 10, 77, 42, 80, 47, 82, 67, 83, 35, 105, 129, 149, + 143, 169, 168, 170, 28, 102, 97, 148, 123, 169, 145, 173, 36, 106, 132, 150, 155, 175, 179, 183, + 33, 103, 119, 151, 103, 176, 151, 184, 37, 107, 133, 153, 148, 177, 180, 185, 34, 104, 124, 152, + 138, 177, 181, 186, 38, 108, 134, 154, 161, 178, 182, 187, 14, 78, 44, 81, 58, 83, 69, 84, + 58, 115, 131, 162, 171, 173, 172, 174, 32, 114, 101, 161, 131, 170, 147, 174, 59, 116, 134, 163, + 172, 183, 191, 195, 56, 115, 122, 164, 115, 184, 164, 196, 60, 117, 136, 166, 173, 186, 193, 197, + 57, 116, 127, 165, 162, 185, 192, 197, 61, 118, 137, 167, 174, 187, 194, 198, 2, 10, 12, 17, + 6, 15, 16, 18, 10, 33, 35, 37, 28, 34, 36, 38, 12, 35, 66, 67, 40, 63, 68, 69, + 17, 37, 67, 70, 51, 64, 71, 72, 6, 28, 40, 51, 23, 34, 46, 57, 15, 34, 63, 64, + 34, 62, 64, 65, 16, 36, 68, 71, 46, 64, 73, 74, 18, 38, 69, 72, 57, 65, 74, 75, + 13, 47, 45, 49, 31, 48, 46, 50, 77, 103, 105, 107, 102, 104, 106, 108, 52, 143, 110, 145, + 130, 144, 146, 147, 80, 148, 151, 152, 149, 150, 153, 154, 43, 123, 121, 126, 100, 124, 125, 127, + 79, 138, 140, 141, 138, 139, 141, 142, 68, 155, 157, 158, 146, 156, 159, 160, 81, 161, 164, 165, + 162, 163, 166, 167, 13, 77, 52, 80, 43, 79, 68, 81, 47, 103, 143, 148, 123, 138, 155, 161, + 45, 105, 110, 151, 121, 140, 157, 164, 49, 107, 145, 152, 126, 141, 158, 165, 31, 102, 130, 149, + 100, 138, 146, 162, 48, 104, 144, 150, 124, 139, 156, 163, 46, 106, 146, 153, 125, 141, 159, 166, + 50, 108, 147, 154, 127, 142, 160, 167, 19, 82, 54, 85, 54, 85, 73, 86, 82, 176, 169, 177, + 169, 177, 175, 178, 54, 169, 112, 181, 135, 189, 159, 192, 85, 177, 181, 199, 189, 200, 201, 202, + 54, 169, 135, 189, 112, 181, 159, 192, 85, 177, 189, 200, 181, 199, 201, 202, 73, 175, 159, 201, + 159, 201, 203, 204, 86, 178, 192, 202, 192, 202, 204, 205, 7, 42, 30, 53, 25, 48, 36, 59, + 42, 119, 129, 133, 97, 124, 132, 134, 30, 129, 109, 168, 99, 144, 155, 172, 53, 133, 168, 188, + 111, 156, 179, 191, 25, 97, 99, 111, 93, 104, 106, 116, 48, 124, 144, 156, 104, 139, 150, 163, + 36, 132, 155, 179, 106, 150, 175, 183, 59, 134, 172, 191, 116, 163, 183, 195, 17, 67, 51, 71, + 37, 70, 64, 72, 80, 151, 149, 153, 148, 152, 150, 154, 53, 168, 111, 179, 133, 188, 156, 191, + 87, 180, 180, 206, 180, 206, 206, 207, 49, 145, 126, 158, 107, 152, 141, 165, 85, 181, 189, 201, + 177, 199, 200, 202, 71, 179, 158, 208, 153, 206, 201, 209, 88, 182, 193, 209, 185, 210, 211, 212, + 17, 80, 53, 87, 49, 85, 71, 88, 67, 151, 168, 180, 145, 181, 179, 182, 51, 149, 111, 180, + 126, 189, 158, 193, 71, 153, 179, 206, 158, 201, 208, 209, 37, 148, 133, 180, 107, 177, 153, 185, + 70, 152, 188, 206, 152, 199, 206, 210, 64, 150, 156, 206, 141, 200, 201, 211, 72, 154, 191, 207, + 165, 202, 209, 212, 20, 83, 55, 88, 60, 89, 74, 90, 83, 184, 170, 185, 173, 186, 183, 187, + 55, 170, 113, 182, 136, 190, 160, 194, 88, 185, 182, 210, 193, 211, 209, 212, 60, 173, 136, 193, + 117, 186, 166, 197, 89, 186, 190, 211, 186, 213, 211, 214, 74, 183, 160, 209, 166, 211, 204, 215, + 90, 187, 194, 212, 197, 214, 215, 216, 1, 11, 9, 13, 5, 12, 10, 14, 11, 39, 41, 43, + 29, 40, 42, 44, 9, 41, 76, 77, 41, 66, 77, 78, 13, 43, 77, 79, 52, 68, 80, 81, + 5, 29, 41, 52, 24, 35, 47, 58, 12, 40, 66, 68, 35, 63, 67, 69, 10, 42, 77, 80, + 47, 67, 82, 83, 14, 44, 78, 81, 58, 69, 83, 84, 5, 29, 22, 31, 27, 30, 28, 32, + 41, 98, 96, 100, 95, 99, 97, 101, 24, 95, 92, 102, 95, 109, 102, 114, 47, 123, 103, 138, + 143, 155, 148, 161, 29, 128, 98, 130, 95, 129, 123, 131, 52, 130, 110, 146, 143, 144, 145, 147, + 35, 129, 105, 149, 143, 168, 169, 170, 58, 131, 115, 162, 171, 172, 173, 174, 5, 41, 24, 47, + 29, 52, 35, 58, 29, 98, 95, 123, 128, 130, 129, 131, 22, 96, 92, 103, 98, 110, 105, 115, + 31, 100, 102, 138, 130, 146, 149, 162, 27, 95, 95, 143, 95, 143, 143, 171, 30, 99, 109, 155, + 129, 144, 168, 172, 28, 97, 102, 148, 123, 145, 169, 173, 32, 101, 114, 161, 131, 147, 170, 174, + 7, 42, 25, 48, 30, 53, 36, 59, 42, 119, 97, 124, 129, 133, 132, 134, 25, 97, 93, 104, + 99, 111, 106, 116, 48, 124, 104, 139, 144, 156, 150, 163, 30, 129, 99, 144, 109, 168, 155, 172, + 53, 133, 111, 156, 168, 188, 179, 191, 36, 132, 106, 150, 155, 179, 175, 183, 59, 134, 116, 163, + 172, 191, 183, 195, 4, 39, 22, 45, 22, 45, 33, 56, 39, 120, 98, 121, 98, 121, 119, 122, + 22, 98, 92, 105, 96, 110, 103, 115, 45, 121, 105, 140, 110, 157, 151, 164, 22, 98, 96, 110, + 92, 105, 103, 115, 45, 121, 110, 157, 105, 140, 151, 164, 33, 119, 103, 151, 103, 151, 176, 184, + 56, 122, 115, 164, 115, 164, 184, 196, 6, 40, 23, 46, 28, 51, 34, 57, 43, 121, 100, 125, + 123, 126, 124, 127, 25, 99, 93, 106, 97, 111, 104, 116, 49, 126, 107, 141, 145, 158, 152, 165, + 31, 130, 100, 146, 102, 149, 138, 162, 54, 135, 112, 159, 169, 189, 181, 192, 37, 133, 107, 153, + 148, 180, 177, 185, 60, 136, 117, 166, 173, 193, 186, 197, 6, 43, 25, 49, 31, 54, 37, 60, + 40, 121, 99, 126, 130, 135, 133, 136, 23, 100, 93, 107, 100, 112, 107, 117, 46, 125, 106, 141, + 146, 159, 153, 166, 28, 123, 97, 145, 102, 169, 148, 173, 51, 126, 111, 158, 149, 189, 180, 193, + 34, 124, 104, 152, 138, 181, 177, 186, 57, 127, 116, 165, 162, 192, 185, 197, 8, 44, 26, 50, + 32, 55, 38, 61, 44, 122, 101, 127, 131, 136, 134, 137, 26, 101, 94, 108, 101, 113, 108, 118, + 50, 127, 108, 142, 147, 160, 154, 167, 32, 131, 101, 147, 114, 170, 161, 174, 55, 136, 113, 160, + 170, 190, 182, 194, 38, 134, 108, 154, 161, 182, 178, 187, 61, 137, 118, 167, 174, 194, 187, 198, + 2, 12, 10, 17, 6, 16, 15, 18, 13, 45, 47, 49, 31, 46, 48, 50, 13, 52, 77, 80, + 43, 68, 79, 81, 19, 54, 82, 85, 54, 73, 85, 86, 7, 30, 42, 53, 25, 36, 48, 59, + 17, 51, 67, 71, 37, 64, 70, 72, 17, 53, 80, 87, 49, 71, 85, 88, 20, 55, 83, 88, + 60, 74, 89, 90, 10, 35, 33, 37, 28, 36, 34, 38, 77, 105, 103, 107, 102, 106, 104, 108, + 47, 143, 103, 148, 123, 155, 138, 161, 82, 169, 176, 177, 169, 175, 177, 178, 42, 129, 119, 133, + 97, 132, 124, 134, 80, 149, 151, 153, 148, 150, 152, 154, 67, 168, 151, 180, 145, 179, 181, 182, + 83, 170, 184, 185, 173, 183, 186, 187, 12, 66, 35, 67, 40, 68, 63, 69, 52, 110, 143, 145, + 130, 146, 144, 147, 45, 110, 105, 151, 121, 157, 140, 164, 54, 112, 169, 181, 135, 159, 189, 192, + 30, 109, 129, 168, 99, 155, 144, 172, 53, 111, 168, 179, 133, 156, 188, 191, 51, 111, 149, 180, + 126, 158, 189, 193, 55, 113, 170, 182, 136, 160, 190, 194, 17, 67, 37, 70, 51, 71, 64, 72, + 80, 151, 148, 152, 149, 153, 150, 154, 49, 145, 107, 152, 126, 158, 141, 165, 85, 181, 177, 199, + 189, 201, 200, 202, 53, 168, 133, 188, 111, 179, 156, 191, 87, 180, 180, 206, 180, 206, 206, 207, + 71, 179, 153, 206, 158, 208, 201, 209, 88, 182, 185, 210, 193, 209, 211, 212, 6, 40, 28, 51, + 23, 46, 34, 57, 43, 121, 123, 126, 100, 125, 124, 127, 31, 130, 102, 149, 100, 146, 138, 162, + 54, 135, 169, 189, 112, 159, 181, 192, 25, 99, 97, 111, 93, 106, 104, 116, 49, 126, 145, 158, + 107, 141, 152, 165, 37, 133, 148, 180, 107, 153, 177, 185, 60, 136, 173, 193, 117, 166, 186, 197, + 15, 63, 34, 64, 34, 64, 62, 65, 79, 140, 138, 141, 138, 141, 139, 142, 48, 144, 104, 150, + 124, 156, 139, 163, 85, 189, 177, 200, 181, 201, 199, 202, 48, 144, 124, 156, 104, 150, 139, 163, + 85, 189, 181, 201, 177, 200, 199, 202, 70, 188, 152, 206, 152, 206, 199, 210, 89, 190, 186, 211, + 186, 211, 213, 214, 16, 68, 36, 71, 46, 73, 64, 74, 68, 157, 155, 158, 146, 159, 156, 160, + 46, 146, 106, 153, 125, 159, 141, 166, 73, 159, 175, 201, 159, 203, 201, 204, 36, 155, 132, 179, + 106, 175, 150, 183, 71, 158, 179, 208, 153, 201, 206, 209, 64, 156, 150, 206, 141, 201, 200, 211, + 74, 160, 183, 209, 166, 204, 211, 215, 18, 69, 38, 72, 57, 74, 65, 75, 81, 164, 161, 165, + 162, 166, 163, 167, 50, 147, 108, 154, 127, 160, 142, 167, 86, 192, 178, 202, 192, 204, 202, 205, + 59, 172, 134, 191, 116, 183, 163, 195, 88, 193, 182, 209, 185, 211, 210, 212, 72, 191, 154, 207, + 165, 209, 202, 212, 90, 194, 187, 212, 197, 215, 214, 216, 2, 13, 13, 19, 7, 17, 17, 20, + 12, 45, 52, 54, 30, 51, 53, 55, 10, 47, 77, 82, 42, 67, 80, 83, 17, 49, 80, 85, + 53, 71, 87, 88, 6, 31, 43, 54, 25, 37, 49, 60, 16, 46, 68, 73, 36, 64, 71, 74, + 15, 48, 79, 85, 48, 70, 85, 89, 18, 50, 81, 86, 59, 72, 88, 90, 12, 52, 45, 54, + 30, 53, 51, 55, 66, 110, 110, 112, 109, 111, 111, 113, 35, 143, 105, 169, 129, 168, 149, 170, + 67, 145, 151, 181, 168, 179, 180, 182, 40, 130, 121, 135, 99, 133, 126, 136, 68, 146, 157, 159, + 155, 156, 158, 160, 63, 144, 140, 189, 144, 188, 189, 190, 69, 147, 164, 192, 172, 191, 193, 194, + 10, 77, 47, 82, 42, 80, 67, 83, 35, 105, 143, 169, 129, 149, 168, 170, 33, 103, 103, 176, + 119, 151, 151, 184, 37, 107, 148, 177, 133, 153, 180, 185, 28, 102, 123, 169, 97, 148, 145, 173, + 36, 106, 155, 175, 132, 150, 179, 183, 34, 104, 138, 177, 124, 152, 181, 186, 38, 108, 161, 178, + 134, 154, 182, 187, 17, 80, 49, 85, 53, 87, 71, 88, 67, 151, 145, 181, 168, 180, 179, 182, + 37, 148, 107, 177, 133, 180, 153, 185, 70, 152, 152, 199, 188, 206, 206, 210, 51, 149, 126, 189, + 111, 180, 158, 193, 71, 153, 158, 201, 179, 206, 208, 209, 64, 150, 141, 200, 156, 206, 201, 211, + 72, 154, 165, 202, 191, 207, 209, 212, 6, 43, 31, 54, 25, 49, 37, 60, 40, 121, 130, 135, + 99, 126, 133, 136, 28, 123, 102, 169, 97, 145, 148, 173, 51, 126, 149, 189, 111, 158, 180, 193, + 23, 100, 100, 112, 93, 107, 107, 117, 46, 125, 146, 159, 106, 141, 153, 166, 34, 124, 138, 181, + 104, 152, 177, 186, 57, 127, 162, 192, 116, 165, 185, 197, 16, 68, 46, 73, 36, 71, 64, 74, + 68, 157, 146, 159, 155, 158, 156, 160, 36, 155, 106, 175, 132, 179, 150, 183, 71, 158, 153, 201, + 179, 208, 206, 209, 46, 146, 125, 159, 106, 153, 141, 166, 73, 159, 159, 203, 175, 201, 201, 204, + 64, 156, 141, 201, 150, 206, 200, 211, 74, 160, 166, 204, 183, 209, 211, 215, 15, 79, 48, 85, + 48, 85, 70, 89, 63, 140, 144, 189, 144, 189, 188, 190, 34, 138, 104, 177, 124, 181, 152, 186, + 64, 141, 150, 200, 156, 201, 206, 211, 34, 138, 124, 181, 104, 177, 152, 186, 64, 141, 156, 201, + 150, 200, 206, 211, 62, 139, 139, 199, 139, 199, 199, 213, 65, 142, 163, 202, 163, 202, 210, 214, + 18, 81, 50, 86, 59, 88, 72, 90, 69, 164, 147, 192, 172, 193, 191, 194, 38, 161, 108, 178, + 134, 182, 154, 187, 72, 165, 154, 202, 191, 209, 207, 212, 57, 162, 127, 192, 116, 185, 165, 197, + 74, 166, 160, 204, 183, 211, 209, 215, 65, 163, 142, 202, 163, 210, 202, 214, 75, 167, 167, 205, + 195, 212, 212, 216, 3, 14, 14, 20, 8, 18, 18, 21, 14, 56, 58, 60, 32, 57, 59, 61, + 14, 58, 78, 83, 44, 69, 81, 84, 20, 60, 83, 89, 55, 74, 88, 90, 8, 32, 44, 55, + 26, 38, 50, 61, 18, 57, 69, 74, 38, 65, 72, 75, 18, 59, 81, 88, 50, 72, 86, 90, + 21, 61, 84, 90, 61, 75, 90, 91, 14, 58, 56, 60, 32, 59, 57, 61, 78, 115, 115, 117, + 114, 116, 116, 118, 58, 171, 115, 173, 131, 172, 162, 174, 83, 173, 184, 186, 170, 183, 185, 187, + 44, 131, 122, 136, 101, 134, 127, 137, 81, 162, 164, 166, 161, 163, 165, 167, 69, 172, 164, 193, + 147, 191, 192, 194, 84, 174, 196, 197, 174, 195, 197, 198, 14, 78, 58, 83, 44, 81, 69, 84, + 58, 115, 171, 173, 131, 162, 172, 174, 56, 115, 115, 184, 122, 164, 164, 196, 60, 117, 173, 186, + 136, 166, 193, 197, 32, 114, 131, 170, 101, 161, 147, 174, 59, 116, 172, 183, 134, 163, 191, 195, + 57, 116, 162, 185, 127, 165, 192, 197, 61, 118, 174, 187, 137, 167, 194, 198, 20, 83, 60, 89, + 55, 88, 74, 90, 83, 184, 173, 186, 170, 185, 183, 187, 60, 173, 117, 186, 136, 193, 166, 197, + 89, 186, 186, 213, 190, 211, 211, 214, 55, 170, 136, 190, 113, 182, 160, 194, 88, 185, 193, 211, + 182, 210, 209, 212, 74, 183, 166, 211, 160, 209, 204, 215, 90, 187, 197, 214, 194, 212, 215, 216, + 8, 44, 32, 55, 26, 50, 38, 61, 44, 122, 131, 136, 101, 127, 134, 137, 32, 131, 114, 170, + 101, 147, 161, 174, 55, 136, 170, 190, 113, 160, 182, 194, 26, 101, 101, 113, 94, 108, 108, 118, + 50, 127, 147, 160, 108, 142, 154, 167, 38, 134, 161, 182, 108, 154, 178, 187, 61, 137, 174, 194, + 118, 167, 187, 198, 18, 69, 57, 74, 38, 72, 65, 75, 81, 164, 162, 166, 161, 165, 163, 167, + 59, 172, 116, 183, 134, 191, 163, 195, 88, 193, 185, 211, 182, 209, 210, 212, 50, 147, 127, 160, + 108, 154, 142, 167, 86, 192, 192, 204, 178, 202, 202, 205, 72, 191, 165, 209, 154, 207, 202, 212, + 90, 194, 197, 215, 187, 212, 214, 216, 18, 81, 59, 88, 50, 86, 72, 90, 69, 164, 172, 193, + 147, 192, 191, 194, 57, 162, 116, 185, 127, 192, 165, 197, 74, 166, 183, 211, 160, 204, 209, 215, + 38, 161, 134, 182, 108, 178, 154, 187, 72, 165, 191, 209, 154, 202, 207, 212, 65, 163, 163, 210, + 142, 202, 202, 214, 75, 167, 195, 212, 167, 205, 212, 216, 21, 84, 61, 90, 61, 90, 75, 91, + 84, 196, 174, 197, 174, 197, 195, 198, 61, 174, 118, 187, 137, 194, 167, 198, 90, 197, 187, 214, + 194, 215, 212, 216, 61, 174, 137, 194, 118, 187, 167, 198, 90, 197, 194, 215, 187, 214, 212, 216, + 75, 195, 167, 212, 167, 212, 205, 216, 91, 198, 198, 216, 198, 216, 216, 217 +}; + +const unsigned int igraph_i_isoclass2_5u[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 4, 5, 6, 6, 7, 1, 2, 5, 6, 2, 4, 6, + 7, 2, 3, 6, 7, 6, 7, 8, 9, 1, 5, 2, 6, 2, 6, 4, 7, 2, 6, 3, 7, 6, 8, + 7, 9, 2, 6, 6, 8, 3, 7, 7, 9, 4, 7, 7, 9, 7, 9, 9, 10, 1, 2, 2, 4, 5, + 6, 6, 7, 2, 4, 4, 11, 12, 13, 13, 14, 5, 6, 12, 13, 12, 13, 15, 16, + 6, 7, 13, 14, 15, 16, 17, 18, 5, 12, 6, 13, 12, 15, 13, 16, 6, 13, 7, + 14, 15, 17, 16, 18, 12, 15, 15, 17, 19, 20, 20, 21, 13, 16, 16, 18, + 20, 21, 21, 22, 1, 2, 5, 6, 2, 4, 6, 7, 5, 6, 12, 13, 12, 13, 15, 16, + 2, 4, 12, 13, 4, 11, 13, 14, 6, 7, 15, 16, 13, 14, 17, 18, 5, 12, 12, + 15, 6, 13, 13, 16, 12, 15, 19, 20, 15, 17, 20, 21, 6, 13, 15, 17, 7, + 14, 16, 18, 13, 16, 20, 21, 16, 18, 21, 22, 2, 3, 6, 7, 6, 7, 8, 9, + 6, 7, 13, 14, 15, 16, 17, 18, 6, 7, 15, 16, 13, 14, 17, 18, 8, 9, 17, + 18, 17, 18, 23, 24, 12, 19, 15, 20, 15, 20, 17, 21, 15, 20, 20, 25, + 26, 27, 27, 28, 15, 20, 26, 27, 20, 25, 27, 28, 17, 21, 27, 28, 27, + 28, 29, 30, 1, 5, 2, 6, 2, 6, 4, 7, 5, 12, 6, 13, 12, 15, 13, 16, 5, + 12, 12, 15, 6, 13, 13, 16, 12, 19, 15, 20, 15, 20, 17, 21, 2, 12, 4, + 13, 4, 13, 11, 14, 6, 15, 7, 16, 13, 17, 14, 18, 6, 15, 13, 17, 7, + 16, 14, 18, 13, 20, 16, 21, 16, 21, 18, 22, 2, 6, 3, 7, 6, 8, 7, 9, + 6, 13, 7, 14, 15, 17, 16, 18, 12, 15, 19, 20, 15, 17, 20, 21, 15, 20, + 20, 25, 26, 27, 27, 28, 6, 15, 7, 16, 13, 17, 14, 18, 8, 17, 9, 18, + 17, 23, 18, 24, 15, 26, 20, 27, 20, 27, 25, 28, 17, 27, 21, 28, 27, + 29, 28, 30, 2, 6, 6, 8, 3, 7, 7, 9, 12, 15, 15, 17, 19, 20, 20, 21, + 6, 13, 15, 17, 7, 14, 16, 18, 15, 20, 26, 27, 20, 25, 27, 28, 6, 15, + 13, 17, 7, 16, 14, 18, 15, 26, 20, 27, 20, 27, 25, 28, 8, 17, 17, 23, + 9, 18, 18, 24, 17, 27, 27, 29, 21, 28, 28, 30, 4, 7, 7, 9, 7, 9, 9, + 10, 13, 16, 16, 18, 20, 21, 21, 22, 13, 16, 20, 21, 16, 18, 21, 22, + 17, 21, 27, 28, 27, 28, 29, 30, 13, 20, 16, 21, 16, 21, 18, 22, 17, + 27, 21, 28, 27, 29, 28, 30, 17, 27, 27, 29, 21, 28, 28, 30, 23, 29, + 29, 31, 29, 31, 31, 32, 1, 5, 5, 12, 5, 12, 12, 19, 2, 6, 6, 13, 12, + 15, 15, 20, 2, 6, 12, 15, 6, 13, 15, 20, 4, 7, 13, 16, 13, 16, 17, + 21, 2, 12, 6, 15, 6, 15, 13, 20, 4, 13, 7, 16, 13, 17, 16, 21, 4, 13, + 13, 17, 7, 16, 16, 21, 11, 14, 14, 18, 14, 18, 18, 22, 2, 6, 6, 13, + 12, 15, 15, 20, 3, 7, 7, 14, 19, 20, 20, 25, 6, 8, 15, 17, 15, 17, + 26, 27, 7, 9, 16, 18, 20, 21, 27, 28, 6, 15, 8, 17, 15, 26, 17, 27, + 7, 16, 9, 18, 20, 27, 21, 28, 13, 17, 17, 23, 20, 27, 27, 29, 14, 18, + 18, 24, 25, 28, 28, 30, 2, 6, 12, 15, 6, 13, 15, 20, 6, 8, 15, 17, + 15, 17, 26, 27, 3, 7, 19, 20, 7, 14, 20, 25, 7, 9, 20, 21, 16, 18, + 27, 28, 6, 15, 15, 26, 8, 17, 17, 27, 13, 17, 20, 27, 17, 23, 27, 29, + 7, 16, 20, 27, 9, 18, 21, 28, 14, 18, 25, 28, 18, 24, 28, 30, 4, 7, + 13, 16, 13, 16, 17, 21, 7, 9, 16, 18, 20, 21, 27, 28, 7, 9, 20, 21, + 16, 18, 27, 28, 9, 10, 21, 22, 21, 22, 29, 30, 13, 20, 17, 27, 17, + 27, 23, 29, 16, 21, 21, 28, 27, 29, 29, 31, 16, 21, 27, 29, 21, 28, + 29, 31, 18, 22, 28, 30, 28, 30, 31, 32, 2, 12, 6, 15, 6, 15, 13, 20, + 6, 15, 8, 17, 15, 26, 17, 27, 6, 15, 15, 26, 8, 17, 17, 27, 13, 20, + 17, 27, 17, 27, 23, 29, 3, 19, 7, 20, 7, 20, 14, 25, 7, 20, 9, 21, + 16, 27, 18, 28, 7, 20, 16, 27, 9, 21, 18, 28, 14, 25, 18, 28, 18, 28, + 24, 30, 4, 13, 7, 16, 13, 17, 16, 21, 7, 16, 9, 18, 20, 27, 21, 28, + 13, 17, 20, 27, 17, 23, 27, 29, 16, 21, 21, 28, 27, 29, 29, 31, 7, + 20, 9, 21, 16, 27, 18, 28, 9, 21, 10, 22, 21, 29, 22, 30, 16, 27, 21, + 29, 21, 29, 28, 31, 18, 28, 22, 30, 28, 31, 30, 32, 4, 13, 13, 17, 7, + 16, 16, 21, 13, 17, 17, 23, 20, 27, 27, 29, 7, 16, 20, 27, 9, 18, 21, + 28, 16, 21, 27, 29, 21, 28, 29, 31, 7, 20, 16, 27, 9, 21, 18, 28, 16, + 27, 21, 29, 21, 29, 28, 31, 9, 21, 21, 29, 10, 22, 22, 30, 18, 28, + 28, 31, 22, 30, 30, 32, 11, 14, 14, 18, 14, 18, 18, 22, 14, 18, 18, + 24, 25, 28, 28, 30, 14, 18, 25, 28, 18, 24, 28, 30, 18, 22, 28, 30, + 28, 30, 31, 32, 14, 25, 18, 28, 18, 28, 24, 30, 18, 28, 22, 30, 28, + 31, 30, 32, 18, 28, 28, 31, 22, 30, 30, 32, 24, 30, 30, 32, 30, 32, + 32, 33 +}; + +const unsigned int igraph_i_isoclass2_6u[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 4, 5, 6, 6, 7, 1, 2, 5, 6, 2, 4, 6, + 7, 2, 3, 6, 7, 6, 7, 8, 9, 1, 5, 2, 6, 2, 6, 4, 7, 2, 6, 3, 7, 6, 8, + 7, 9, 2, 6, 6, 8, 3, 7, 7, 9, 4, 7, 7, 9, 7, 9, 9, 10, 1, 2, 2, 4, 5, + 6, 6, 7, 2, 4, 4, 11, 12, 13, 13, 14, 5, 6, 12, 13, 12, 13, 15, 16, + 6, 7, 13, 14, 15, 16, 17, 18, 5, 12, 6, 13, 12, 15, 13, 16, 6, 13, 7, + 14, 15, 17, 16, 18, 12, 15, 15, 17, 19, 20, 20, 21, 13, 16, 16, 18, + 20, 21, 21, 22, 1, 2, 5, 6, 2, 4, 6, 7, 5, 6, 12, 13, 12, 13, 15, 16, + 2, 4, 12, 13, 4, 11, 13, 14, 6, 7, 15, 16, 13, 14, 17, 18, 5, 12, 12, + 15, 6, 13, 13, 16, 12, 15, 19, 20, 15, 17, 20, 21, 6, 13, 15, 17, 7, + 14, 16, 18, 13, 16, 20, 21, 16, 18, 21, 22, 2, 3, 6, 7, 6, 7, 8, 9, + 6, 7, 13, 14, 15, 16, 17, 18, 6, 7, 15, 16, 13, 14, 17, 18, 8, 9, 17, + 18, 17, 18, 23, 24, 12, 19, 15, 20, 15, 20, 17, 21, 15, 20, 20, 25, + 26, 27, 27, 28, 15, 20, 26, 27, 20, 25, 27, 28, 17, 21, 27, 28, 27, + 28, 29, 30, 1, 5, 2, 6, 2, 6, 4, 7, 5, 12, 6, 13, 12, 15, 13, 16, 5, + 12, 12, 15, 6, 13, 13, 16, 12, 19, 15, 20, 15, 20, 17, 21, 2, 12, 4, + 13, 4, 13, 11, 14, 6, 15, 7, 16, 13, 17, 14, 18, 6, 15, 13, 17, 7, + 16, 14, 18, 13, 20, 16, 21, 16, 21, 18, 22, 2, 6, 3, 7, 6, 8, 7, 9, + 6, 13, 7, 14, 15, 17, 16, 18, 12, 15, 19, 20, 15, 17, 20, 21, 15, 20, + 20, 25, 26, 27, 27, 28, 6, 15, 7, 16, 13, 17, 14, 18, 8, 17, 9, 18, + 17, 23, 18, 24, 15, 26, 20, 27, 20, 27, 25, 28, 17, 27, 21, 28, 27, + 29, 28, 30, 2, 6, 6, 8, 3, 7, 7, 9, 12, 15, 15, 17, 19, 20, 20, 21, + 6, 13, 15, 17, 7, 14, 16, 18, 15, 20, 26, 27, 20, 25, 27, 28, 6, 15, + 13, 17, 7, 16, 14, 18, 15, 26, 20, 27, 20, 27, 25, 28, 8, 17, 17, 23, + 9, 18, 18, 24, 17, 27, 27, 29, 21, 28, 28, 30, 4, 7, 7, 9, 7, 9, 9, + 10, 13, 16, 16, 18, 20, 21, 21, 22, 13, 16, 20, 21, 16, 18, 21, 22, + 17, 21, 27, 28, 27, 28, 29, 30, 13, 20, 16, 21, 16, 21, 18, 22, 17, + 27, 21, 28, 27, 29, 28, 30, 17, 27, 27, 29, 21, 28, 28, 30, 23, 29, + 29, 31, 29, 31, 31, 32, 1, 5, 5, 12, 5, 12, 12, 19, 2, 6, 6, 13, 12, + 15, 15, 20, 2, 6, 12, 15, 6, 13, 15, 20, 4, 7, 13, 16, 13, 16, 17, + 21, 2, 12, 6, 15, 6, 15, 13, 20, 4, 13, 7, 16, 13, 17, 16, 21, 4, 13, + 13, 17, 7, 16, 16, 21, 11, 14, 14, 18, 14, 18, 18, 22, 2, 6, 6, 13, + 12, 15, 15, 20, 3, 7, 7, 14, 19, 20, 20, 25, 6, 8, 15, 17, 15, 17, + 26, 27, 7, 9, 16, 18, 20, 21, 27, 28, 6, 15, 8, 17, 15, 26, 17, 27, + 7, 16, 9, 18, 20, 27, 21, 28, 13, 17, 17, 23, 20, 27, 27, 29, 14, 18, + 18, 24, 25, 28, 28, 30, 2, 6, 12, 15, 6, 13, 15, 20, 6, 8, 15, 17, + 15, 17, 26, 27, 3, 7, 19, 20, 7, 14, 20, 25, 7, 9, 20, 21, 16, 18, + 27, 28, 6, 15, 15, 26, 8, 17, 17, 27, 13, 17, 20, 27, 17, 23, 27, 29, + 7, 16, 20, 27, 9, 18, 21, 28, 14, 18, 25, 28, 18, 24, 28, 30, 4, 7, + 13, 16, 13, 16, 17, 21, 7, 9, 16, 18, 20, 21, 27, 28, 7, 9, 20, 21, + 16, 18, 27, 28, 9, 10, 21, 22, 21, 22, 29, 30, 13, 20, 17, 27, 17, + 27, 23, 29, 16, 21, 21, 28, 27, 29, 29, 31, 16, 21, 27, 29, 21, 28, + 29, 31, 18, 22, 28, 30, 28, 30, 31, 32, 2, 12, 6, 15, 6, 15, 13, 20, + 6, 15, 8, 17, 15, 26, 17, 27, 6, 15, 15, 26, 8, 17, 17, 27, 13, 20, + 17, 27, 17, 27, 23, 29, 3, 19, 7, 20, 7, 20, 14, 25, 7, 20, 9, 21, + 16, 27, 18, 28, 7, 20, 16, 27, 9, 21, 18, 28, 14, 25, 18, 28, 18, 28, + 24, 30, 4, 13, 7, 16, 13, 17, 16, 21, 7, 16, 9, 18, 20, 27, 21, 28, + 13, 17, 20, 27, 17, 23, 27, 29, 16, 21, 21, 28, 27, 29, 29, 31, 7, + 20, 9, 21, 16, 27, 18, 28, 9, 21, 10, 22, 21, 29, 22, 30, 16, 27, 21, + 29, 21, 29, 28, 31, 18, 28, 22, 30, 28, 31, 30, 32, 4, 13, 13, 17, 7, + 16, 16, 21, 13, 17, 17, 23, 20, 27, 27, 29, 7, 16, 20, 27, 9, 18, 21, + 28, 16, 21, 27, 29, 21, 28, 29, 31, 7, 20, 16, 27, 9, 21, 18, 28, 16, + 27, 21, 29, 21, 29, 28, 31, 9, 21, 21, 29, 10, 22, 22, 30, 18, 28, + 28, 31, 22, 30, 30, 32, 11, 14, 14, 18, 14, 18, 18, 22, 14, 18, 18, + 24, 25, 28, 28, 30, 14, 18, 25, 28, 18, 24, 28, 30, 18, 22, 28, 30, + 28, 30, 31, 32, 14, 25, 18, 28, 18, 28, 24, 30, 18, 28, 22, 30, 28, + 31, 30, 32, 18, 28, 28, 31, 22, 30, 30, 32, 24, 30, 30, 32, 30, 32, + 32, 33, 1, 2, 2, 4, 5, 6, 6, 7, 2, 4, 4, 11, 12, 13, 13, 14, 5, 6, + 12, 13, 12, 13, 15, 16, 6, 7, 13, 14, 15, 16, 17, 18, 5, 12, 6, 13, + 12, 15, 13, 16, 6, 13, 7, 14, 15, 17, 16, 18, 12, 15, 15, 17, 19, 20, + 20, 21, 13, 16, 16, 18, 20, 21, 21, 22, 2, 4, 4, 11, 12, 13, 13, 14, + 4, 11, 11, 34, 35, 36, 36, 37, 12, 13, 35, 36, 38, 39, 40, 41, 13, + 14, 36, 37, 40, 41, 42, 43, 12, 35, 13, 36, 38, 40, 39, 41, 13, 36, + 14, 37, 40, 42, 41, 43, 38, 40, 40, 42, 44, 45, 45, 46, 39, 41, 41, + 43, 45, 46, 46, 47, 5, 6, 12, 13, 12, 13, 15, 16, 12, 13, 35, 36, 38, + 39, 40, 41, 12, 13, 38, 39, 35, 36, 40, 41, 15, 16, 40, 41, 40, 41, + 48, 49, 50, 51, 51, 52, 51, 52, 52, 53, 51, 52, 54, 55, 56, 57, 58, + 59, 51, 52, 56, 57, 54, 55, 58, 59, 52, 53, 58, 59, 58, 59, 60, 61, + 6, 7, 13, 14, 15, 16, 17, 18, 13, 14, 36, 37, 40, 41, 42, 43, 15, 16, + 40, 41, 40, 41, 48, 49, 17, 18, 42, 43, 48, 49, 62, 63, 51, 54, 52, + 55, 56, 58, 57, 59, 52, 55, 55, 64, 65, 66, 66, 67, 56, 58, 65, 66, + 68, 69, 70, 71, 57, 59, 66, 67, 70, 71, 72, 73, 5, 12, 6, 13, 12, 15, + 13, 16, 12, 35, 13, 36, 38, 40, 39, 41, 50, 51, 51, 52, 51, 52, 52, + 53, 51, 54, 52, 55, 56, 58, 57, 59, 12, 38, 13, 39, 35, 40, 36, 41, + 15, 40, 16, 41, 40, 48, 41, 49, 51, 56, 52, 57, 54, 58, 55, 59, 52, + 58, 53, 59, 58, 60, 59, 61, 6, 13, 7, 14, 15, 17, 16, 18, 13, 36, 14, + 37, 40, 42, 41, 43, 51, 52, 54, 55, 56, 57, 58, 59, 52, 55, 55, 64, + 65, 66, 66, 67, 15, 40, 16, 41, 40, 48, 41, 49, 17, 42, 18, 43, 48, + 62, 49, 63, 56, 65, 58, 66, 68, 70, 69, 71, 57, 66, 59, 67, 70, 72, + 71, 73, 12, 15, 15, 17, 19, 20, 20, 21, 38, 40, 40, 42, 44, 45, 45, + 46, 51, 52, 56, 57, 54, 55, 58, 59, 56, 58, 65, 66, 68, 69, 70, 71, + 51, 56, 52, 57, 54, 58, 55, 59, 56, 65, 58, 66, 68, 70, 69, 71, 74, + 75, 75, 76, 77, 78, 78, 79, 75, 80, 80, 81, 82, 83, 83, 84, 13, 16, + 16, 18, 20, 21, 21, 22, 39, 41, 41, 43, 45, 46, 46, 47, 52, 53, 58, + 59, 58, 59, 60, 61, 57, 59, 66, 67, 70, 71, 72, 73, 52, 58, 53, 59, + 58, 60, 59, 61, 57, 66, 59, 67, 70, 72, 71, 73, 75, 80, 80, 81, 82, + 83, 83, 84, 76, 81, 81, 85, 86, 87, 87, 88, 5, 12, 12, 35, 50, 51, + 51, 54, 6, 13, 13, 36, 51, 52, 52, 55, 12, 15, 38, 40, 51, 52, 56, + 58, 13, 16, 39, 41, 52, 53, 57, 59, 12, 38, 15, 40, 51, 56, 52, 58, + 13, 39, 16, 41, 52, 57, 53, 59, 35, 40, 40, 48, 54, 58, 58, 60, 36, + 41, 41, 49, 55, 59, 59, 61, 6, 13, 13, 36, 51, 52, 52, 55, 7, 14, 14, + 37, 54, 55, 55, 64, 15, 17, 40, 42, 56, 57, 65, 66, 16, 18, 41, 43, + 58, 59, 66, 67, 15, 40, 17, 42, 56, 65, 57, 66, 16, 41, 18, 43, 58, + 66, 59, 67, 40, 48, 48, 62, 68, 70, 70, 72, 41, 49, 49, 63, 69, 71, + 71, 73, 12, 15, 38, 40, 51, 52, 56, 58, 15, 17, 40, 42, 56, 57, 65, + 66, 19, 20, 44, 45, 54, 55, 68, 69, 20, 21, 45, 46, 58, 59, 70, 71, + 51, 56, 56, 65, 74, 75, 75, 80, 52, 57, 58, 66, 75, 76, 80, 81, 54, + 58, 68, 70, 77, 78, 82, 83, 55, 59, 69, 71, 78, 79, 83, 84, 13, 16, + 39, 41, 52, 53, 57, 59, 16, 18, 41, 43, 58, 59, 66, 67, 20, 21, 45, + 46, 58, 59, 70, 71, 21, 22, 46, 47, 60, 61, 72, 73, 52, 58, 57, 66, + 75, 80, 76, 81, 53, 59, 59, 67, 80, 81, 81, 85, 58, 60, 70, 72, 82, + 83, 86, 87, 59, 61, 71, 73, 83, 84, 87, 88, 12, 38, 15, 40, 51, 56, + 52, 58, 15, 40, 17, 42, 56, 65, 57, 66, 51, 56, 56, 65, 74, 75, 75, + 80, 52, 58, 57, 66, 75, 80, 76, 81, 19, 44, 20, 45, 54, 68, 55, 69, + 20, 45, 21, 46, 58, 70, 59, 71, 54, 68, 58, 70, 77, 82, 78, 83, 55, + 69, 59, 71, 78, 83, 79, 84, 13, 39, 16, 41, 52, 57, 53, 59, 16, 41, + 18, 43, 58, 66, 59, 67, 52, 57, 58, 66, 75, 76, 80, 81, 53, 59, 59, + 67, 80, 81, 81, 85, 20, 45, 21, 46, 58, 70, 59, 71, 21, 46, 22, 47, + 60, 72, 61, 73, 58, 70, 60, 72, 82, 86, 83, 87, 59, 71, 61, 73, 83, + 87, 84, 88, 35, 40, 40, 48, 54, 58, 58, 60, 40, 48, 48, 62, 68, 70, + 70, 72, 54, 58, 68, 70, 77, 78, 82, 83, 58, 60, 70, 72, 82, 83, 86, + 87, 54, 68, 58, 70, 77, 82, 78, 83, 58, 70, 60, 72, 82, 86, 83, 87, + 77, 82, 82, 86, 89, 90, 90, 91, 78, 83, 83, 87, 90, 91, 91, 92, 36, + 41, 41, 49, 55, 59, 59, 61, 41, 49, 49, 63, 69, 71, 71, 73, 55, 59, + 69, 71, 78, 79, 83, 84, 59, 61, 71, 73, 83, 84, 87, 88, 55, 69, 59, + 71, 78, 83, 79, 84, 59, 71, 61, 73, 83, 87, 84, 88, 78, 83, 83, 87, + 90, 91, 91, 92, 79, 84, 84, 88, 91, 92, 92, 93, 1, 2, 5, 6, 2, 4, 6, + 7, 5, 6, 12, 13, 12, 13, 15, 16, 2, 4, 12, 13, 4, 11, 13, 14, 6, 7, + 15, 16, 13, 14, 17, 18, 5, 12, 12, 15, 6, 13, 13, 16, 12, 15, 19, 20, + 15, 17, 20, 21, 6, 13, 15, 17, 7, 14, 16, 18, 13, 16, 20, 21, 16, 18, + 21, 22, 5, 6, 12, 13, 12, 13, 15, 16, 12, 13, 35, 36, 38, 39, 40, 41, + 12, 13, 38, 39, 35, 36, 40, 41, 15, 16, 40, 41, 40, 41, 48, 49, 50, + 51, 51, 52, 51, 52, 52, 53, 51, 52, 54, 55, 56, 57, 58, 59, 51, 52, + 56, 57, 54, 55, 58, 59, 52, 53, 58, 59, 58, 59, 60, 61, 2, 4, 12, 13, + 4, 11, 13, 14, 12, 13, 38, 39, 35, 36, 40, 41, 4, 11, 35, 36, 11, 34, + 36, 37, 13, 14, 40, 41, 36, 37, 42, 43, 12, 35, 38, 40, 13, 36, 39, + 41, 38, 40, 44, 45, 40, 42, 45, 46, 13, 36, 40, 42, 14, 37, 41, 43, + 39, 41, 45, 46, 41, 43, 46, 47, 6, 7, 15, 16, 13, 14, 17, 18, 15, 16, + 40, 41, 40, 41, 48, 49, 13, 14, 40, 41, 36, 37, 42, 43, 17, 18, 48, + 49, 42, 43, 62, 63, 51, 54, 56, 58, 52, 55, 57, 59, 56, 58, 68, 69, + 65, 66, 70, 71, 52, 55, 65, 66, 55, 64, 66, 67, 57, 59, 70, 71, 66, + 67, 72, 73, 5, 12, 12, 15, 6, 13, 13, 16, 50, 51, 51, 52, 51, 52, 52, + 53, 12, 35, 38, 40, 13, 36, 39, 41, 51, 54, 56, 58, 52, 55, 57, 59, + 12, 38, 35, 40, 13, 39, 36, 41, 51, 56, 54, 58, 52, 57, 55, 59, 15, + 40, 40, 48, 16, 41, 41, 49, 52, 58, 58, 60, 53, 59, 59, 61, 12, 15, + 19, 20, 15, 17, 20, 21, 51, 52, 54, 55, 56, 57, 58, 59, 38, 40, 44, + 45, 40, 42, 45, 46, 56, 58, 68, 69, 65, 66, 70, 71, 51, 56, 54, 58, + 52, 57, 55, 59, 74, 75, 77, 78, 75, 76, 78, 79, 56, 65, 68, 70, 58, + 66, 69, 71, 75, 80, 82, 83, 80, 81, 83, 84, 6, 13, 15, 17, 7, 14, 16, + 18, 51, 52, 56, 57, 54, 55, 58, 59, 13, 36, 40, 42, 14, 37, 41, 43, + 52, 55, 65, 66, 55, 64, 66, 67, 15, 40, 40, 48, 16, 41, 41, 49, 56, + 65, 68, 70, 58, 66, 69, 71, 17, 42, 48, 62, 18, 43, 49, 63, 57, 66, + 70, 72, 59, 67, 71, 73, 13, 16, 20, 21, 16, 18, 21, 22, 52, 53, 58, + 59, 58, 59, 60, 61, 39, 41, 45, 46, 41, 43, 46, 47, 57, 59, 70, 71, + 66, 67, 72, 73, 52, 58, 58, 60, 53, 59, 59, 61, 75, 80, 82, 83, 80, + 81, 83, 84, 57, 66, 70, 72, 59, 67, 71, 73, 76, 81, 86, 87, 81, 85, + 87, 88, 5, 12, 50, 51, 12, 35, 51, 54, 12, 15, 51, 52, 38, 40, 56, + 58, 6, 13, 51, 52, 13, 36, 52, 55, 13, 16, 52, 53, 39, 41, 57, 59, + 12, 38, 51, 56, 15, 40, 52, 58, 35, 40, 54, 58, 40, 48, 58, 60, 13, + 39, 52, 57, 16, 41, 53, 59, 36, 41, 55, 59, 41, 49, 59, 61, 12, 15, + 51, 52, 38, 40, 56, 58, 19, 20, 54, 55, 44, 45, 68, 69, 15, 17, 56, + 57, 40, 42, 65, 66, 20, 21, 58, 59, 45, 46, 70, 71, 51, 56, 74, 75, + 56, 65, 75, 80, 54, 58, 77, 78, 68, 70, 82, 83, 52, 57, 75, 76, 58, + 66, 80, 81, 55, 59, 78, 79, 69, 71, 83, 84, 6, 13, 51, 52, 13, 36, + 52, 55, 15, 17, 56, 57, 40, 42, 65, 66, 7, 14, 54, 55, 14, 37, 55, + 64, 16, 18, 58, 59, 41, 43, 66, 67, 15, 40, 56, 65, 17, 42, 57, 66, + 40, 48, 68, 70, 48, 62, 70, 72, 16, 41, 58, 66, 18, 43, 59, 67, 41, + 49, 69, 71, 49, 63, 71, 73, 13, 16, 52, 53, 39, 41, 57, 59, 20, 21, + 58, 59, 45, 46, 70, 71, 16, 18, 58, 59, 41, 43, 66, 67, 21, 22, 60, + 61, 46, 47, 72, 73, 52, 58, 75, 80, 57, 66, 76, 81, 58, 60, 82, 83, + 70, 72, 86, 87, 53, 59, 80, 81, 59, 67, 81, 85, 59, 61, 83, 84, 71, + 73, 87, 88, 12, 38, 51, 56, 15, 40, 52, 58, 51, 56, 74, 75, 56, 65, + 75, 80, 15, 40, 56, 65, 17, 42, 57, 66, 52, 58, 75, 80, 57, 66, 76, + 81, 19, 44, 54, 68, 20, 45, 55, 69, 54, 68, 77, 82, 58, 70, 78, 83, + 20, 45, 58, 70, 21, 46, 59, 71, 55, 69, 78, 83, 59, 71, 79, 84, 35, + 40, 54, 58, 40, 48, 58, 60, 54, 58, 77, 78, 68, 70, 82, 83, 40, 48, + 68, 70, 48, 62, 70, 72, 58, 60, 82, 83, 70, 72, 86, 87, 54, 68, 77, + 82, 58, 70, 78, 83, 77, 82, 89, 90, 82, 86, 90, 91, 58, 70, 82, 86, + 60, 72, 83, 87, 78, 83, 90, 91, 83, 87, 91, 92, 13, 39, 52, 57, 16, + 41, 53, 59, 52, 57, 75, 76, 58, 66, 80, 81, 16, 41, 58, 66, 18, 43, + 59, 67, 53, 59, 80, 81, 59, 67, 81, 85, 20, 45, 58, 70, 21, 46, 59, + 71, 58, 70, 82, 86, 60, 72, 83, 87, 21, 46, 60, 72, 22, 47, 61, 73, + 59, 71, 83, 87, 61, 73, 84, 88, 36, 41, 55, 59, 41, 49, 59, 61, 55, + 59, 78, 79, 69, 71, 83, 84, 41, 49, 69, 71, 49, 63, 71, 73, 59, 61, + 83, 84, 71, 73, 87, 88, 55, 69, 78, 83, 59, 71, 79, 84, 78, 83, 90, + 91, 83, 87, 91, 92, 59, 71, 83, 87, 61, 73, 84, 88, 79, 84, 91, 92, + 84, 88, 92, 93, 2, 3, 6, 7, 6, 7, 8, 9, 6, 7, 13, 14, 15, 16, 17, 18, + 6, 7, 15, 16, 13, 14, 17, 18, 8, 9, 17, 18, 17, 18, 23, 24, 12, 19, + 15, 20, 15, 20, 17, 21, 15, 20, 20, 25, 26, 27, 27, 28, 15, 20, 26, + 27, 20, 25, 27, 28, 17, 21, 27, 28, 27, 28, 29, 30, 6, 7, 13, 14, 15, + 16, 17, 18, 13, 14, 36, 37, 40, 41, 42, 43, 15, 16, 40, 41, 40, 41, + 48, 49, 17, 18, 42, 43, 48, 49, 62, 63, 51, 54, 52, 55, 56, 58, 57, + 59, 52, 55, 55, 64, 65, 66, 66, 67, 56, 58, 65, 66, 68, 69, 70, 71, + 57, 59, 66, 67, 70, 71, 72, 73, 6, 7, 15, 16, 13, 14, 17, 18, 15, 16, + 40, 41, 40, 41, 48, 49, 13, 14, 40, 41, 36, 37, 42, 43, 17, 18, 48, + 49, 42, 43, 62, 63, 51, 54, 56, 58, 52, 55, 57, 59, 56, 58, 68, 69, + 65, 66, 70, 71, 52, 55, 65, 66, 55, 64, 66, 67, 57, 59, 70, 71, 66, + 67, 72, 73, 8, 9, 17, 18, 17, 18, 23, 24, 17, 18, 42, 43, 48, 49, 62, + 63, 17, 18, 48, 49, 42, 43, 62, 63, 23, 24, 62, 63, 62, 63, 94, 95, + 74, 77, 75, 78, 75, 78, 76, 79, 75, 78, 96, 97, 98, 99, 100, 101, 75, + 78, 98, 99, 96, 97, 100, 101, 76, 79, 100, 101, 100, 101, 102, 103, + 12, 19, 15, 20, 15, 20, 17, 21, 51, 54, 52, 55, 56, 58, 57, 59, 51, + 54, 56, 58, 52, 55, 57, 59, 74, 77, 75, 78, 75, 78, 76, 79, 38, 44, + 40, 45, 40, 45, 42, 46, 56, 68, 58, 69, 65, 70, 66, 71, 56, 68, 65, + 70, 58, 69, 66, 71, 75, 82, 80, 83, 80, 83, 81, 84, 15, 20, 20, 25, + 26, 27, 27, 28, 52, 55, 55, 64, 65, 66, 66, 67, 56, 58, 68, 69, 65, + 66, 70, 71, 75, 78, 96, 97, 98, 99, 100, 101, 56, 68, 58, 69, 65, 70, + 66, 71, 75, 96, 78, 97, 98, 100, 99, 101, 104, 105, 105, 106, 105, + 106, 106, 107, 108, 109, 109, 110, 111, 112, 112, 113, 15, 20, 26, + 27, 20, 25, 27, 28, 56, 58, 65, 66, 68, 69, 70, 71, 52, 55, 65, 66, + 55, 64, 66, 67, 75, 78, 98, 99, 96, 97, 100, 101, 56, 68, 65, 70, 58, + 69, 66, 71, 104, 105, 105, 106, 105, 106, 106, 107, 75, 96, 98, 100, + 78, 97, 99, 101, 108, 109, 111, 112, 109, 110, 112, 113, 17, 21, 27, + 28, 27, 28, 29, 30, 57, 59, 66, 67, 70, 71, 72, 73, 57, 59, 70, 71, + 66, 67, 72, 73, 76, 79, 100, 101, 100, 101, 102, 103, 75, 82, 80, 83, + 80, 83, 81, 84, 108, 109, 109, 110, 111, 112, 112, 113, 108, 109, + 111, 112, 109, 110, 112, 113, 114, 115, 116, 117, 116, 117, 118, 119, + 12, 19, 51, 54, 51, 54, 74, 77, 15, 20, 52, 55, 56, 58, 75, 78, 15, + 20, 56, 58, 52, 55, 75, 78, 17, 21, 57, 59, 57, 59, 76, 79, 38, 44, + 56, 68, 56, 68, 75, 82, 40, 45, 58, 69, 65, 70, 80, 83, 40, 45, 65, + 70, 58, 69, 80, 83, 42, 46, 66, 71, 66, 71, 81, 84, 15, 20, 52, 55, + 56, 58, 75, 78, 20, 25, 55, 64, 68, 69, 96, 97, 26, 27, 65, 66, 65, + 66, 98, 99, 27, 28, 66, 67, 70, 71, 100, 101, 56, 68, 75, 96, 104, + 105, 108, 109, 58, 69, 78, 97, 105, 106, 109, 110, 65, 70, 98, 100, + 105, 106, 111, 112, 66, 71, 99, 101, 106, 107, 112, 113, 15, 20, 56, + 58, 52, 55, 75, 78, 26, 27, 65, 66, 65, 66, 98, 99, 20, 25, 68, 69, + 55, 64, 96, 97, 27, 28, 70, 71, 66, 67, 100, 101, 56, 68, 104, 105, + 75, 96, 108, 109, 65, 70, 105, 106, 98, 100, 111, 112, 58, 69, 105, + 106, 78, 97, 109, 110, 66, 71, 106, 107, 99, 101, 112, 113, 17, 21, + 57, 59, 57, 59, 76, 79, 27, 28, 66, 67, 70, 71, 100, 101, 27, 28, 70, + 71, 66, 67, 100, 101, 29, 30, 72, 73, 72, 73, 102, 103, 75, 82, 108, + 109, 108, 109, 114, 115, 80, 83, 109, 110, 111, 112, 116, 117, 80, + 83, 111, 112, 109, 110, 116, 117, 81, 84, 112, 113, 112, 113, 118, + 119, 38, 44, 56, 68, 56, 68, 75, 82, 56, 68, 75, 96, 104, 105, 108, + 109, 56, 68, 104, 105, 75, 96, 108, 109, 75, 82, 108, 109, 108, 109, + 114, 115, 44, 120, 68, 121, 68, 121, 96, 122, 68, 121, 82, 122, 105, + 123, 109, 124, 68, 121, 105, 123, 82, 122, 109, 124, 96, 122, 109, + 124, 109, 124, 115, 125, 40, 45, 58, 69, 65, 70, 80, 83, 58, 69, 78, + 97, 105, 106, 109, 110, 65, 70, 105, 106, 98, 100, 111, 112, 80, 83, + 109, 110, 111, 112, 116, 117, 68, 121, 82, 122, 105, 123, 109, 124, + 82, 122, 90, 126, 127, 128, 129, 130, 105, 123, 127, 128, 127, 128, + 131, 132, 109, 124, 129, 130, 131, 132, 133, 134, 40, 45, 65, 70, 58, + 69, 80, 83, 65, 70, 98, 100, 105, 106, 111, 112, 58, 69, 105, 106, + 78, 97, 109, 110, 80, 83, 111, 112, 109, 110, 116, 117, 68, 121, 105, + 123, 82, 122, 109, 124, 105, 123, 127, 128, 127, 128, 131, 132, 82, + 122, 127, 128, 90, 126, 129, 130, 109, 124, 131, 132, 129, 130, 133, + 134, 42, 46, 66, 71, 66, 71, 81, 84, 66, 71, 99, 101, 106, 107, 112, + 113, 66, 71, 106, 107, 99, 101, 112, 113, 81, 84, 112, 113, 112, 113, + 118, 119, 96, 122, 109, 124, 109, 124, 115, 125, 109, 124, 129, 130, + 131, 132, 133, 134, 109, 124, 131, 132, 129, 130, 133, 134, 115, 125, + 133, 134, 133, 134, 135, 136, 1, 5, 2, 6, 2, 6, 4, 7, 5, 12, 6, 13, + 12, 15, 13, 16, 5, 12, 12, 15, 6, 13, 13, 16, 12, 19, 15, 20, 15, 20, + 17, 21, 2, 12, 4, 13, 4, 13, 11, 14, 6, 15, 7, 16, 13, 17, 14, 18, 6, + 15, 13, 17, 7, 16, 14, 18, 13, 20, 16, 21, 16, 21, 18, 22, 5, 12, 6, + 13, 12, 15, 13, 16, 12, 35, 13, 36, 38, 40, 39, 41, 50, 51, 51, 52, + 51, 52, 52, 53, 51, 54, 52, 55, 56, 58, 57, 59, 12, 38, 13, 39, 35, + 40, 36, 41, 15, 40, 16, 41, 40, 48, 41, 49, 51, 56, 52, 57, 54, 58, + 55, 59, 52, 58, 53, 59, 58, 60, 59, 61, 5, 12, 12, 15, 6, 13, 13, 16, + 50, 51, 51, 52, 51, 52, 52, 53, 12, 35, 38, 40, 13, 36, 39, 41, 51, + 54, 56, 58, 52, 55, 57, 59, 12, 38, 35, 40, 13, 39, 36, 41, 51, 56, + 54, 58, 52, 57, 55, 59, 15, 40, 40, 48, 16, 41, 41, 49, 52, 58, 58, + 60, 53, 59, 59, 61, 12, 19, 15, 20, 15, 20, 17, 21, 51, 54, 52, 55, + 56, 58, 57, 59, 51, 54, 56, 58, 52, 55, 57, 59, 74, 77, 75, 78, 75, + 78, 76, 79, 38, 44, 40, 45, 40, 45, 42, 46, 56, 68, 58, 69, 65, 70, + 66, 71, 56, 68, 65, 70, 58, 69, 66, 71, 75, 82, 80, 83, 80, 83, 81, + 84, 2, 12, 4, 13, 4, 13, 11, 14, 12, 38, 13, 39, 35, 40, 36, 41, 12, + 38, 35, 40, 13, 39, 36, 41, 38, 44, 40, 45, 40, 45, 42, 46, 4, 35, + 11, 36, 11, 36, 34, 37, 13, 40, 14, 41, 36, 42, 37, 43, 13, 40, 36, + 42, 14, 41, 37, 43, 39, 45, 41, 46, 41, 46, 43, 47, 6, 15, 7, 16, 13, + 17, 14, 18, 15, 40, 16, 41, 40, 48, 41, 49, 51, 56, 54, 58, 52, 57, + 55, 59, 56, 68, 58, 69, 65, 70, 66, 71, 13, 40, 14, 41, 36, 42, 37, + 43, 17, 48, 18, 49, 42, 62, 43, 63, 52, 65, 55, 66, 55, 66, 64, 67, + 57, 70, 59, 71, 66, 72, 67, 73, 6, 15, 13, 17, 7, 16, 14, 18, 51, 56, + 52, 57, 54, 58, 55, 59, 15, 40, 40, 48, 16, 41, 41, 49, 56, 68, 65, + 70, 58, 69, 66, 71, 13, 40, 36, 42, 14, 41, 37, 43, 52, 65, 55, 66, + 55, 66, 64, 67, 17, 48, 42, 62, 18, 49, 43, 63, 57, 70, 66, 72, 59, + 71, 67, 73, 13, 20, 16, 21, 16, 21, 18, 22, 52, 58, 53, 59, 58, 60, + 59, 61, 52, 58, 58, 60, 53, 59, 59, 61, 75, 82, 80, 83, 80, 83, 81, + 84, 39, 45, 41, 46, 41, 46, 43, 47, 57, 70, 59, 71, 66, 72, 67, 73, + 57, 70, 66, 72, 59, 71, 67, 73, 76, 86, 81, 87, 81, 87, 85, 88, 5, + 50, 12, 51, 12, 51, 35, 54, 12, 51, 15, 52, 38, 56, 40, 58, 12, 51, + 38, 56, 15, 52, 40, 58, 35, 54, 40, 58, 40, 58, 48, 60, 6, 51, 13, + 52, 13, 52, 36, 55, 13, 52, 16, 53, 39, 57, 41, 59, 13, 52, 39, 57, + 16, 53, 41, 59, 36, 55, 41, 59, 41, 59, 49, 61, 12, 51, 15, 52, 38, + 56, 40, 58, 19, 54, 20, 55, 44, 68, 45, 69, 51, 74, 56, 75, 56, 75, + 65, 80, 54, 77, 58, 78, 68, 82, 70, 83, 15, 56, 17, 57, 40, 65, 42, + 66, 20, 58, 21, 59, 45, 70, 46, 71, 52, 75, 57, 76, 58, 80, 66, 81, + 55, 78, 59, 79, 69, 83, 71, 84, 12, 51, 38, 56, 15, 52, 40, 58, 51, + 74, 56, 75, 56, 75, 65, 80, 19, 54, 44, 68, 20, 55, 45, 69, 54, 77, + 68, 82, 58, 78, 70, 83, 15, 56, 40, 65, 17, 57, 42, 66, 52, 75, 58, + 80, 57, 76, 66, 81, 20, 58, 45, 70, 21, 59, 46, 71, 55, 78, 69, 83, + 59, 79, 71, 84, 35, 54, 40, 58, 40, 58, 48, 60, 54, 77, 58, 78, 68, + 82, 70, 83, 54, 77, 68, 82, 58, 78, 70, 83, 77, 89, 82, 90, 82, 90, + 86, 91, 40, 68, 48, 70, 48, 70, 62, 72, 58, 82, 60, 83, 70, 86, 72, + 87, 58, 82, 70, 86, 60, 83, 72, 87, 78, 90, 83, 91, 83, 91, 87, 92, + 6, 51, 13, 52, 13, 52, 36, 55, 15, 56, 17, 57, 40, 65, 42, 66, 15, + 56, 40, 65, 17, 57, 42, 66, 40, 68, 48, 70, 48, 70, 62, 72, 7, 54, + 14, 55, 14, 55, 37, 64, 16, 58, 18, 59, 41, 66, 43, 67, 16, 58, 41, + 66, 18, 59, 43, 67, 41, 69, 49, 71, 49, 71, 63, 73, 13, 52, 16, 53, + 39, 57, 41, 59, 20, 58, 21, 59, 45, 70, 46, 71, 52, 75, 58, 80, 57, + 76, 66, 81, 58, 82, 60, 83, 70, 86, 72, 87, 16, 58, 18, 59, 41, 66, + 43, 67, 21, 60, 22, 61, 46, 72, 47, 73, 53, 80, 59, 81, 59, 81, 67, + 85, 59, 83, 61, 84, 71, 87, 73, 88, 13, 52, 39, 57, 16, 53, 41, 59, + 52, 75, 57, 76, 58, 80, 66, 81, 20, 58, 45, 70, 21, 59, 46, 71, 58, + 82, 70, 86, 60, 83, 72, 87, 16, 58, 41, 66, 18, 59, 43, 67, 53, 80, + 59, 81, 59, 81, 67, 85, 21, 60, 46, 72, 22, 61, 47, 73, 59, 83, 71, + 87, 61, 84, 73, 88, 36, 55, 41, 59, 41, 59, 49, 61, 55, 78, 59, 79, + 69, 83, 71, 84, 55, 78, 69, 83, 59, 79, 71, 84, 78, 90, 83, 91, 83, + 91, 87, 92, 41, 69, 49, 71, 49, 71, 63, 73, 59, 83, 61, 84, 71, 87, + 73, 88, 59, 83, 71, 87, 61, 84, 73, 88, 79, 91, 84, 92, 84, 92, 88, + 93, 2, 6, 3, 7, 6, 8, 7, 9, 6, 13, 7, 14, 15, 17, 16, 18, 12, 15, 19, + 20, 15, 17, 20, 21, 15, 20, 20, 25, 26, 27, 27, 28, 6, 15, 7, 16, 13, + 17, 14, 18, 8, 17, 9, 18, 17, 23, 18, 24, 15, 26, 20, 27, 20, 27, 25, + 28, 17, 27, 21, 28, 27, 29, 28, 30, 6, 13, 7, 14, 15, 17, 16, 18, 13, + 36, 14, 37, 40, 42, 41, 43, 51, 52, 54, 55, 56, 57, 58, 59, 52, 55, + 55, 64, 65, 66, 66, 67, 15, 40, 16, 41, 40, 48, 41, 49, 17, 42, 18, + 43, 48, 62, 49, 63, 56, 65, 58, 66, 68, 70, 69, 71, 57, 66, 59, 67, + 70, 72, 71, 73, 12, 15, 19, 20, 15, 17, 20, 21, 51, 52, 54, 55, 56, + 57, 58, 59, 38, 40, 44, 45, 40, 42, 45, 46, 56, 58, 68, 69, 65, 66, + 70, 71, 51, 56, 54, 58, 52, 57, 55, 59, 74, 75, 77, 78, 75, 76, 78, + 79, 56, 65, 68, 70, 58, 66, 69, 71, 75, 80, 82, 83, 80, 81, 83, 84, + 15, 20, 20, 25, 26, 27, 27, 28, 52, 55, 55, 64, 65, 66, 66, 67, 56, + 58, 68, 69, 65, 66, 70, 71, 75, 78, 96, 97, 98, 99, 100, 101, 56, 68, + 58, 69, 65, 70, 66, 71, 75, 96, 78, 97, 98, 100, 99, 101, 104, 105, + 105, 106, 105, 106, 106, 107, 108, 109, 109, 110, 111, 112, 112, 113, + 6, 15, 7, 16, 13, 17, 14, 18, 15, 40, 16, 41, 40, 48, 41, 49, 51, 56, + 54, 58, 52, 57, 55, 59, 56, 68, 58, 69, 65, 70, 66, 71, 13, 40, 14, + 41, 36, 42, 37, 43, 17, 48, 18, 49, 42, 62, 43, 63, 52, 65, 55, 66, + 55, 66, 64, 67, 57, 70, 59, 71, 66, 72, 67, 73, 8, 17, 9, 18, 17, 23, + 18, 24, 17, 42, 18, 43, 48, 62, 49, 63, 74, 75, 77, 78, 75, 76, 78, + 79, 75, 96, 78, 97, 98, 100, 99, 101, 17, 48, 18, 49, 42, 62, 43, 63, + 23, 62, 24, 63, 62, 94, 63, 95, 75, 98, 78, 99, 96, 100, 97, 101, 76, + 100, 79, 101, 100, 102, 101, 103, 15, 26, 20, 27, 20, 27, 25, 28, 56, + 65, 58, 66, 68, 70, 69, 71, 56, 65, 68, 70, 58, 66, 69, 71, 104, 105, + 105, 106, 105, 106, 106, 107, 52, 65, 55, 66, 55, 66, 64, 67, 75, 98, + 78, 99, 96, 100, 97, 101, 75, 98, 96, 100, 78, 99, 97, 101, 108, 111, + 109, 112, 109, 112, 110, 113, 17, 27, 21, 28, 27, 29, 28, 30, 57, 66, + 59, 67, 70, 72, 71, 73, 75, 80, 82, 83, 80, 81, 83, 84, 108, 109, + 109, 110, 111, 112, 112, 113, 57, 70, 59, 71, 66, 72, 67, 73, 76, + 100, 79, 101, 100, 102, 101, 103, 108, 111, 109, 112, 109, 112, 110, + 113, 114, 116, 115, 117, 116, 118, 117, 119, 12, 51, 19, 54, 51, 74, + 54, 77, 15, 52, 20, 55, 56, 75, 58, 78, 38, 56, 44, 68, 56, 75, 68, + 82, 40, 58, 45, 69, 65, 80, 70, 83, 15, 56, 20, 58, 52, 75, 55, 78, + 17, 57, 21, 59, 57, 76, 59, 79, 40, 65, 45, 70, 58, 80, 69, 83, 42, + 66, 46, 71, 66, 81, 71, 84, 15, 52, 20, 55, 56, 75, 58, 78, 20, 55, + 25, 64, 68, 96, 69, 97, 56, 75, 68, 96, 104, 108, 105, 109, 58, 78, + 69, 97, 105, 109, 106, 110, 26, 65, 27, 66, 65, 98, 66, 99, 27, 66, + 28, 67, 70, 100, 71, 101, 65, 98, 70, 100, 105, 111, 106, 112, 66, + 99, 71, 101, 106, 112, 107, 113, 38, 56, 44, 68, 56, 75, 68, 82, 56, + 75, 68, 96, 104, 108, 105, 109, 44, 68, 120, 121, 68, 96, 121, 122, + 68, 82, 121, 122, 105, 109, 123, 124, 56, 104, 68, 105, 75, 108, 96, + 109, 75, 108, 82, 109, 108, 114, 109, 115, 68, 105, 121, 123, 82, + 109, 122, 124, 96, 109, 122, 124, 109, 115, 124, 125, 40, 58, 45, 69, + 65, 80, 70, 83, 58, 78, 69, 97, 105, 109, 106, 110, 68, 82, 121, 122, + 105, 109, 123, 124, 82, 90, 122, 126, 127, 129, 128, 130, 65, 105, + 70, 106, 98, 111, 100, 112, 80, 109, 83, 110, 111, 116, 112, 117, + 105, 127, 123, 128, 127, 131, 128, 132, 109, 129, 124, 130, 131, 133, + 132, 134, 15, 56, 20, 58, 52, 75, 55, 78, 26, 65, 27, 66, 65, 98, 66, + 99, 56, 104, 68, 105, 75, 108, 96, 109, 65, 105, 70, 106, 98, 111, + 100, 112, 20, 68, 25, 69, 55, 96, 64, 97, 27, 70, 28, 71, 66, 100, + 67, 101, 58, 105, 69, 106, 78, 109, 97, 110, 66, 106, 71, 107, 99, + 112, 101, 113, 17, 57, 21, 59, 57, 76, 59, 79, 27, 66, 28, 67, 70, + 100, 71, 101, 75, 108, 82, 109, 108, 114, 109, 115, 80, 109, 83, 110, + 111, 116, 112, 117, 27, 70, 28, 71, 66, 100, 67, 101, 29, 72, 30, 73, + 72, 102, 73, 103, 80, 111, 83, 112, 109, 116, 110, 117, 81, 112, 84, + 113, 112, 118, 113, 119, 40, 65, 45, 70, 58, 80, 69, 83, 65, 98, 70, + 100, 105, 111, 106, 112, 68, 105, 121, 123, 82, 109, 122, 124, 105, + 127, 123, 128, 127, 131, 128, 132, 58, 105, 69, 106, 78, 109, 97, + 110, 80, 111, 83, 112, 109, 116, 110, 117, 82, 127, 122, 128, 90, + 129, 126, 130, 109, 131, 124, 132, 129, 133, 130, 134, 42, 66, 46, + 71, 66, 81, 71, 84, 66, 99, 71, 101, 106, 112, 107, 113, 96, 109, + 122, 124, 109, 115, 124, 125, 109, 129, 124, 130, 131, 133, 132, 134, + 66, 106, 71, 107, 99, 112, 101, 113, 81, 112, 84, 113, 112, 118, 113, + 119, 109, 131, 124, 132, 129, 133, 130, 134, 115, 133, 125, 134, 133, + 135, 134, 136, 2, 6, 6, 8, 3, 7, 7, 9, 12, 15, 15, 17, 19, 20, 20, + 21, 6, 13, 15, 17, 7, 14, 16, 18, 15, 20, 26, 27, 20, 25, 27, 28, 6, + 15, 13, 17, 7, 16, 14, 18, 15, 26, 20, 27, 20, 27, 25, 28, 8, 17, 17, + 23, 9, 18, 18, 24, 17, 27, 27, 29, 21, 28, 28, 30, 12, 15, 15, 17, + 19, 20, 20, 21, 38, 40, 40, 42, 44, 45, 45, 46, 51, 52, 56, 57, 54, + 55, 58, 59, 56, 58, 65, 66, 68, 69, 70, 71, 51, 56, 52, 57, 54, 58, + 55, 59, 56, 65, 58, 66, 68, 70, 69, 71, 74, 75, 75, 76, 77, 78, 78, + 79, 75, 80, 80, 81, 82, 83, 83, 84, 6, 13, 15, 17, 7, 14, 16, 18, 51, + 52, 56, 57, 54, 55, 58, 59, 13, 36, 40, 42, 14, 37, 41, 43, 52, 55, + 65, 66, 55, 64, 66, 67, 15, 40, 40, 48, 16, 41, 41, 49, 56, 65, 68, + 70, 58, 66, 69, 71, 17, 42, 48, 62, 18, 43, 49, 63, 57, 66, 70, 72, + 59, 67, 71, 73, 15, 20, 26, 27, 20, 25, 27, 28, 56, 58, 65, 66, 68, + 69, 70, 71, 52, 55, 65, 66, 55, 64, 66, 67, 75, 78, 98, 99, 96, 97, + 100, 101, 56, 68, 65, 70, 58, 69, 66, 71, 104, 105, 105, 106, 105, + 106, 106, 107, 75, 96, 98, 100, 78, 97, 99, 101, 108, 109, 111, 112, + 109, 110, 112, 113, 6, 15, 13, 17, 7, 16, 14, 18, 51, 56, 52, 57, 54, + 58, 55, 59, 15, 40, 40, 48, 16, 41, 41, 49, 56, 68, 65, 70, 58, 69, + 66, 71, 13, 40, 36, 42, 14, 41, 37, 43, 52, 65, 55, 66, 55, 66, 64, + 67, 17, 48, 42, 62, 18, 49, 43, 63, 57, 70, 66, 72, 59, 71, 67, 73, + 15, 26, 20, 27, 20, 27, 25, 28, 56, 65, 58, 66, 68, 70, 69, 71, 56, + 65, 68, 70, 58, 66, 69, 71, 104, 105, 105, 106, 105, 106, 106, 107, + 52, 65, 55, 66, 55, 66, 64, 67, 75, 98, 78, 99, 96, 100, 97, 101, 75, + 98, 96, 100, 78, 99, 97, 101, 108, 111, 109, 112, 109, 112, 110, 113, + 8, 17, 17, 23, 9, 18, 18, 24, 74, 75, 75, 76, 77, 78, 78, 79, 17, 42, + 48, 62, 18, 43, 49, 63, 75, 96, 98, 100, 78, 97, 99, 101, 17, 48, 42, + 62, 18, 49, 43, 63, 75, 98, 96, 100, 78, 99, 97, 101, 23, 62, 62, 94, + 24, 63, 63, 95, 76, 100, 100, 102, 79, 101, 101, 103, 17, 27, 27, 29, + 21, 28, 28, 30, 75, 80, 80, 81, 82, 83, 83, 84, 57, 66, 70, 72, 59, + 67, 71, 73, 108, 109, 111, 112, 109, 110, 112, 113, 57, 70, 66, 72, + 59, 71, 67, 73, 108, 111, 109, 112, 109, 112, 110, 113, 76, 100, 100, + 102, 79, 101, 101, 103, 114, 116, 116, 118, 115, 117, 117, 119, 12, + 51, 51, 74, 19, 54, 54, 77, 38, 56, 56, 75, 44, 68, 68, 82, 15, 52, + 56, 75, 20, 55, 58, 78, 40, 58, 65, 80, 45, 69, 70, 83, 15, 56, 52, + 75, 20, 58, 55, 78, 40, 65, 58, 80, 45, 70, 69, 83, 17, 57, 57, 76, + 21, 59, 59, 79, 42, 66, 66, 81, 46, 71, 71, 84, 38, 56, 56, 75, 44, + 68, 68, 82, 44, 68, 68, 96, 120, 121, 121, 122, 56, 75, 104, 108, 68, + 96, 105, 109, 68, 82, 105, 109, 121, 122, 123, 124, 56, 104, 75, 108, + 68, 105, 96, 109, 68, 105, 82, 109, 121, 123, 122, 124, 75, 108, 108, + 114, 82, 109, 109, 115, 96, 109, 109, 115, 122, 124, 124, 125, 15, + 52, 56, 75, 20, 55, 58, 78, 56, 75, 104, 108, 68, 96, 105, 109, 20, + 55, 68, 96, 25, 64, 69, 97, 58, 78, 105, 109, 69, 97, 106, 110, 26, + 65, 65, 98, 27, 66, 66, 99, 65, 98, 105, 111, 70, 100, 106, 112, 27, + 66, 70, 100, 28, 67, 71, 101, 66, 99, 106, 112, 71, 101, 107, 113, + 40, 58, 65, 80, 45, 69, 70, 83, 68, 82, 105, 109, 121, 122, 123, 124, + 58, 78, 105, 109, 69, 97, 106, 110, 82, 90, 127, 129, 122, 126, 128, + 130, 65, 105, 98, 111, 70, 106, 100, 112, 105, 127, 127, 131, 123, + 128, 128, 132, 80, 109, 111, 116, 83, 110, 112, 117, 109, 129, 131, + 133, 124, 130, 132, 134, 15, 56, 52, 75, 20, 58, 55, 78, 56, 104, 75, + 108, 68, 105, 96, 109, 26, 65, 65, 98, 27, 66, 66, 99, 65, 105, 98, + 111, 70, 106, 100, 112, 20, 68, 55, 96, 25, 69, 64, 97, 58, 105, 78, + 109, 69, 106, 97, 110, 27, 70, 66, 100, 28, 71, 67, 101, 66, 106, 99, + 112, 71, 107, 101, 113, 40, 65, 58, 80, 45, 70, 69, 83, 68, 105, 82, + 109, 121, 123, 122, 124, 65, 98, 105, 111, 70, 100, 106, 112, 105, + 127, 127, 131, 123, 128, 128, 132, 58, 105, 78, 109, 69, 106, 97, + 110, 82, 127, 90, 129, 122, 128, 126, 130, 80, 111, 109, 116, 83, + 112, 110, 117, 109, 131, 129, 133, 124, 132, 130, 134, 17, 57, 57, + 76, 21, 59, 59, 79, 75, 108, 108, 114, 82, 109, 109, 115, 27, 66, 70, + 100, 28, 67, 71, 101, 80, 109, 111, 116, 83, 110, 112, 117, 27, 70, + 66, 100, 28, 71, 67, 101, 80, 111, 109, 116, 83, 112, 110, 117, 29, + 72, 72, 102, 30, 73, 73, 103, 81, 112, 112, 118, 84, 113, 113, 119, + 42, 66, 66, 81, 46, 71, 71, 84, 96, 109, 109, 115, 122, 124, 124, + 125, 66, 99, 106, 112, 71, 101, 107, 113, 109, 129, 131, 133, 124, + 130, 132, 134, 66, 106, 99, 112, 71, 107, 101, 113, 109, 131, 129, + 133, 124, 132, 130, 134, 81, 112, 112, 118, 84, 113, 113, 119, 115, + 133, 133, 135, 125, 134, 134, 136, 4, 7, 7, 9, 7, 9, 9, 10, 13, 16, + 16, 18, 20, 21, 21, 22, 13, 16, 20, 21, 16, 18, 21, 22, 17, 21, 27, + 28, 27, 28, 29, 30, 13, 20, 16, 21, 16, 21, 18, 22, 17, 27, 21, 28, + 27, 29, 28, 30, 17, 27, 27, 29, 21, 28, 28, 30, 23, 29, 29, 31, 29, + 31, 31, 32, 13, 16, 16, 18, 20, 21, 21, 22, 39, 41, 41, 43, 45, 46, + 46, 47, 52, 53, 58, 59, 58, 59, 60, 61, 57, 59, 66, 67, 70, 71, 72, + 73, 52, 58, 53, 59, 58, 60, 59, 61, 57, 66, 59, 67, 70, 72, 71, 73, + 75, 80, 80, 81, 82, 83, 83, 84, 76, 81, 81, 85, 86, 87, 87, 88, 13, + 16, 20, 21, 16, 18, 21, 22, 52, 53, 58, 59, 58, 59, 60, 61, 39, 41, + 45, 46, 41, 43, 46, 47, 57, 59, 70, 71, 66, 67, 72, 73, 52, 58, 58, + 60, 53, 59, 59, 61, 75, 80, 82, 83, 80, 81, 83, 84, 57, 66, 70, 72, + 59, 67, 71, 73, 76, 81, 86, 87, 81, 85, 87, 88, 17, 21, 27, 28, 27, + 28, 29, 30, 57, 59, 66, 67, 70, 71, 72, 73, 57, 59, 70, 71, 66, 67, + 72, 73, 76, 79, 100, 101, 100, 101, 102, 103, 75, 82, 80, 83, 80, 83, + 81, 84, 108, 109, 109, 110, 111, 112, 112, 113, 108, 109, 111, 112, + 109, 110, 112, 113, 114, 115, 116, 117, 116, 117, 118, 119, 13, 20, + 16, 21, 16, 21, 18, 22, 52, 58, 53, 59, 58, 60, 59, 61, 52, 58, 58, + 60, 53, 59, 59, 61, 75, 82, 80, 83, 80, 83, 81, 84, 39, 45, 41, 46, + 41, 46, 43, 47, 57, 70, 59, 71, 66, 72, 67, 73, 57, 70, 66, 72, 59, + 71, 67, 73, 76, 86, 81, 87, 81, 87, 85, 88, 17, 27, 21, 28, 27, 29, + 28, 30, 57, 66, 59, 67, 70, 72, 71, 73, 75, 80, 82, 83, 80, 81, 83, + 84, 108, 109, 109, 110, 111, 112, 112, 113, 57, 70, 59, 71, 66, 72, + 67, 73, 76, 100, 79, 101, 100, 102, 101, 103, 108, 111, 109, 112, + 109, 112, 110, 113, 114, 116, 115, 117, 116, 118, 117, 119, 17, 27, + 27, 29, 21, 28, 28, 30, 75, 80, 80, 81, 82, 83, 83, 84, 57, 66, 70, + 72, 59, 67, 71, 73, 108, 109, 111, 112, 109, 110, 112, 113, 57, 70, + 66, 72, 59, 71, 67, 73, 108, 111, 109, 112, 109, 112, 110, 113, 76, + 100, 100, 102, 79, 101, 101, 103, 114, 116, 116, 118, 115, 117, 117, + 119, 23, 29, 29, 31, 29, 31, 31, 32, 76, 81, 81, 85, 86, 87, 87, 88, + 76, 81, 86, 87, 81, 85, 87, 88, 114, 115, 116, 117, 116, 117, 118, + 119, 76, 86, 81, 87, 81, 87, 85, 88, 114, 116, 115, 117, 116, 118, + 117, 119, 114, 116, 116, 118, 115, 117, 117, 119, 137, 138, 138, 139, + 138, 139, 139, 140, 35, 54, 54, 77, 54, 77, 77, 89, 40, 58, 58, 78, + 68, 82, 82, 90, 40, 58, 68, 82, 58, 78, 82, 90, 48, 60, 70, 83, 70, + 83, 86, 91, 40, 68, 58, 82, 58, 82, 78, 90, 48, 70, 60, 83, 70, 86, + 83, 91, 48, 70, 70, 86, 60, 83, 83, 91, 62, 72, 72, 87, 72, 87, 87, + 92, 40, 58, 58, 78, 68, 82, 82, 90, 45, 69, 69, 97, 121, 122, 122, + 126, 65, 80, 105, 109, 105, 109, 127, 129, 70, 83, 106, 110, 123, + 124, 128, 130, 65, 105, 80, 109, 105, 127, 109, 129, 70, 106, 83, + 110, 123, 128, 124, 130, 98, 111, 111, 116, 127, 131, 131, 133, 100, + 112, 112, 117, 128, 132, 132, 134, 40, 58, 68, 82, 58, 78, 82, 90, + 65, 80, 105, 109, 105, 109, 127, 129, 45, 69, 121, 122, 69, 97, 122, + 126, 70, 83, 123, 124, 106, 110, 128, 130, 65, 105, 105, 127, 80, + 109, 109, 129, 98, 111, 127, 131, 111, 116, 131, 133, 70, 106, 123, + 128, 83, 110, 124, 130, 100, 112, 128, 132, 112, 117, 132, 134, 48, + 60, 70, 83, 70, 83, 86, 91, 70, 83, 106, 110, 123, 124, 128, 130, 70, + 83, 123, 124, 106, 110, 128, 130, 86, 91, 128, 130, 128, 130, 141, + 142, 98, 127, 111, 131, 111, 131, 116, 133, 111, 131, 131, 143, 144, + 145, 145, 146, 111, 131, 144, 145, 131, 143, 145, 146, 116, 133, 145, + 146, 145, 146, 147, 148, 40, 68, 58, 82, 58, 82, 78, 90, 65, 105, 80, + 109, 105, 127, 109, 129, 65, 105, 105, 127, 80, 109, 109, 129, 98, + 127, 111, 131, 111, 131, 116, 133, 45, 121, 69, 122, 69, 122, 97, + 126, 70, 123, 83, 124, 106, 128, 110, 130, 70, 123, 106, 128, 83, + 124, 110, 130, 100, 128, 112, 132, 112, 132, 117, 134, 48, 70, 60, + 83, 70, 86, 83, 91, 70, 106, 83, 110, 123, 128, 124, 130, 98, 111, + 127, 131, 111, 116, 131, 133, 111, 131, 131, 143, 144, 145, 145, 146, + 70, 123, 83, 124, 106, 128, 110, 130, 86, 128, 91, 130, 128, 141, + 130, 142, 111, 144, 131, 145, 131, 145, 143, 146, 116, 145, 133, 146, + 145, 147, 146, 148, 48, 70, 70, 86, 60, 83, 83, 91, 98, 111, 111, + 116, 127, 131, 131, 133, 70, 106, 123, 128, 83, 110, 124, 130, 111, + 131, 144, 145, 131, 143, 145, 146, 70, 123, 106, 128, 83, 124, 110, + 130, 111, 144, 131, 145, 131, 145, 143, 146, 86, 128, 128, 141, 91, + 130, 130, 142, 116, 145, 145, 147, 133, 146, 146, 148, 62, 72, 72, + 87, 72, 87, 87, 92, 100, 112, 112, 117, 128, 132, 132, 134, 100, 112, + 128, 132, 112, 117, 132, 134, 116, 133, 145, 146, 145, 146, 147, 148, + 100, 128, 112, 132, 112, 132, 117, 134, 116, 145, 133, 146, 145, 147, + 146, 148, 116, 145, 145, 147, 133, 146, 146, 148, 138, 149, 149, 150, + 149, 150, 150, 151, 1, 5, 5, 12, 5, 12, 12, 19, 2, 6, 6, 13, 12, 15, + 15, 20, 2, 6, 12, 15, 6, 13, 15, 20, 4, 7, 13, 16, 13, 16, 17, 21, 2, + 12, 6, 15, 6, 15, 13, 20, 4, 13, 7, 16, 13, 17, 16, 21, 4, 13, 13, + 17, 7, 16, 16, 21, 11, 14, 14, 18, 14, 18, 18, 22, 5, 12, 12, 35, 50, + 51, 51, 54, 6, 13, 13, 36, 51, 52, 52, 55, 12, 15, 38, 40, 51, 52, + 56, 58, 13, 16, 39, 41, 52, 53, 57, 59, 12, 38, 15, 40, 51, 56, 52, + 58, 13, 39, 16, 41, 52, 57, 53, 59, 35, 40, 40, 48, 54, 58, 58, 60, + 36, 41, 41, 49, 55, 59, 59, 61, 5, 12, 50, 51, 12, 35, 51, 54, 12, + 15, 51, 52, 38, 40, 56, 58, 6, 13, 51, 52, 13, 36, 52, 55, 13, 16, + 52, 53, 39, 41, 57, 59, 12, 38, 51, 56, 15, 40, 52, 58, 35, 40, 54, + 58, 40, 48, 58, 60, 13, 39, 52, 57, 16, 41, 53, 59, 36, 41, 55, 59, + 41, 49, 59, 61, 12, 19, 51, 54, 51, 54, 74, 77, 15, 20, 52, 55, 56, + 58, 75, 78, 15, 20, 56, 58, 52, 55, 75, 78, 17, 21, 57, 59, 57, 59, + 76, 79, 38, 44, 56, 68, 56, 68, 75, 82, 40, 45, 58, 69, 65, 70, 80, + 83, 40, 45, 65, 70, 58, 69, 80, 83, 42, 46, 66, 71, 66, 71, 81, 84, + 5, 50, 12, 51, 12, 51, 35, 54, 12, 51, 15, 52, 38, 56, 40, 58, 12, + 51, 38, 56, 15, 52, 40, 58, 35, 54, 40, 58, 40, 58, 48, 60, 6, 51, + 13, 52, 13, 52, 36, 55, 13, 52, 16, 53, 39, 57, 41, 59, 13, 52, 39, + 57, 16, 53, 41, 59, 36, 55, 41, 59, 41, 59, 49, 61, 12, 51, 19, 54, + 51, 74, 54, 77, 15, 52, 20, 55, 56, 75, 58, 78, 38, 56, 44, 68, 56, + 75, 68, 82, 40, 58, 45, 69, 65, 80, 70, 83, 15, 56, 20, 58, 52, 75, + 55, 78, 17, 57, 21, 59, 57, 76, 59, 79, 40, 65, 45, 70, 58, 80, 69, + 83, 42, 66, 46, 71, 66, 81, 71, 84, 12, 51, 51, 74, 19, 54, 54, 77, + 38, 56, 56, 75, 44, 68, 68, 82, 15, 52, 56, 75, 20, 55, 58, 78, 40, + 58, 65, 80, 45, 69, 70, 83, 15, 56, 52, 75, 20, 58, 55, 78, 40, 65, + 58, 80, 45, 70, 69, 83, 17, 57, 57, 76, 21, 59, 59, 79, 42, 66, 66, + 81, 46, 71, 71, 84, 35, 54, 54, 77, 54, 77, 77, 89, 40, 58, 58, 78, + 68, 82, 82, 90, 40, 58, 68, 82, 58, 78, 82, 90, 48, 60, 70, 83, 70, + 83, 86, 91, 40, 68, 58, 82, 58, 82, 78, 90, 48, 70, 60, 83, 70, 86, + 83, 91, 48, 70, 70, 86, 60, 83, 83, 91, 62, 72, 72, 87, 72, 87, 87, + 92, 2, 12, 12, 38, 12, 38, 38, 44, 4, 13, 13, 39, 35, 40, 40, 45, 4, + 13, 35, 40, 13, 39, 40, 45, 11, 14, 36, 41, 36, 41, 42, 46, 4, 35, + 13, 40, 13, 40, 39, 45, 11, 36, 14, 41, 36, 42, 41, 46, 11, 36, 36, + 42, 14, 41, 41, 46, 34, 37, 37, 43, 37, 43, 43, 47, 6, 15, 15, 40, + 51, 56, 56, 68, 7, 16, 16, 41, 54, 58, 58, 69, 13, 17, 40, 48, 52, + 57, 65, 70, 14, 18, 41, 49, 55, 59, 66, 71, 13, 40, 17, 48, 52, 65, + 57, 70, 14, 41, 18, 49, 55, 66, 59, 71, 36, 42, 42, 62, 55, 66, 66, + 72, 37, 43, 43, 63, 64, 67, 67, 73, 6, 15, 51, 56, 15, 40, 56, 68, + 13, 17, 52, 57, 40, 48, 65, 70, 7, 16, 54, 58, 16, 41, 58, 69, 14, + 18, 55, 59, 41, 49, 66, 71, 13, 40, 52, 65, 17, 48, 57, 70, 36, 42, + 55, 66, 42, 62, 66, 72, 14, 41, 55, 66, 18, 49, 59, 71, 37, 43, 64, + 67, 43, 63, 67, 73, 13, 20, 52, 58, 52, 58, 75, 82, 16, 21, 53, 59, + 58, 60, 80, 83, 16, 21, 58, 60, 53, 59, 80, 83, 18, 22, 59, 61, 59, + 61, 81, 84, 39, 45, 57, 70, 57, 70, 76, 86, 41, 46, 59, 71, 66, 72, + 81, 87, 41, 46, 66, 72, 59, 71, 81, 87, 43, 47, 67, 73, 67, 73, 85, + 88, 6, 51, 15, 56, 15, 56, 40, 68, 13, 52, 17, 57, 40, 65, 48, 70, + 13, 52, 40, 65, 17, 57, 48, 70, 36, 55, 42, 66, 42, 66, 62, 72, 7, + 54, 16, 58, 16, 58, 41, 69, 14, 55, 18, 59, 41, 66, 49, 71, 14, 55, + 41, 66, 18, 59, 49, 71, 37, 64, 43, 67, 43, 67, 63, 73, 13, 52, 20, + 58, 52, 75, 58, 82, 16, 53, 21, 59, 58, 80, 60, 83, 39, 57, 45, 70, + 57, 76, 70, 86, 41, 59, 46, 71, 66, 81, 72, 87, 16, 58, 21, 60, 53, + 80, 59, 83, 18, 59, 22, 61, 59, 81, 61, 84, 41, 66, 46, 72, 59, 81, + 71, 87, 43, 67, 47, 73, 67, 85, 73, 88, 13, 52, 52, 75, 20, 58, 58, + 82, 39, 57, 57, 76, 45, 70, 70, 86, 16, 53, 58, 80, 21, 59, 60, 83, + 41, 59, 66, 81, 46, 71, 72, 87, 16, 58, 53, 80, 21, 60, 59, 83, 41, + 66, 59, 81, 46, 72, 71, 87, 18, 59, 59, 81, 22, 61, 61, 84, 43, 67, + 67, 85, 47, 73, 73, 88, 36, 55, 55, 78, 55, 78, 78, 90, 41, 59, 59, + 79, 69, 83, 83, 91, 41, 59, 69, 83, 59, 79, 83, 91, 49, 61, 71, 84, + 71, 84, 87, 92, 41, 69, 59, 83, 59, 83, 79, 91, 49, 71, 61, 84, 71, + 87, 84, 92, 49, 71, 71, 87, 61, 84, 84, 92, 63, 73, 73, 88, 73, 88, + 88, 93, 2, 6, 6, 13, 12, 15, 15, 20, 3, 7, 7, 14, 19, 20, 20, 25, 6, + 8, 15, 17, 15, 17, 26, 27, 7, 9, 16, 18, 20, 21, 27, 28, 6, 15, 8, + 17, 15, 26, 17, 27, 7, 16, 9, 18, 20, 27, 21, 28, 13, 17, 17, 23, 20, + 27, 27, 29, 14, 18, 18, 24, 25, 28, 28, 30, 6, 13, 13, 36, 51, 52, + 52, 55, 7, 14, 14, 37, 54, 55, 55, 64, 15, 17, 40, 42, 56, 57, 65, + 66, 16, 18, 41, 43, 58, 59, 66, 67, 15, 40, 17, 42, 56, 65, 57, 66, + 16, 41, 18, 43, 58, 66, 59, 67, 40, 48, 48, 62, 68, 70, 70, 72, 41, + 49, 49, 63, 69, 71, 71, 73, 12, 15, 51, 52, 38, 40, 56, 58, 19, 20, + 54, 55, 44, 45, 68, 69, 15, 17, 56, 57, 40, 42, 65, 66, 20, 21, 58, + 59, 45, 46, 70, 71, 51, 56, 74, 75, 56, 65, 75, 80, 54, 58, 77, 78, + 68, 70, 82, 83, 52, 57, 75, 76, 58, 66, 80, 81, 55, 59, 78, 79, 69, + 71, 83, 84, 15, 20, 52, 55, 56, 58, 75, 78, 20, 25, 55, 64, 68, 69, + 96, 97, 26, 27, 65, 66, 65, 66, 98, 99, 27, 28, 66, 67, 70, 71, 100, + 101, 56, 68, 75, 96, 104, 105, 108, 109, 58, 69, 78, 97, 105, 106, + 109, 110, 65, 70, 98, 100, 105, 106, 111, 112, 66, 71, 99, 101, 106, + 107, 112, 113, 12, 51, 15, 52, 38, 56, 40, 58, 19, 54, 20, 55, 44, + 68, 45, 69, 51, 74, 56, 75, 56, 75, 65, 80, 54, 77, 58, 78, 68, 82, + 70, 83, 15, 56, 17, 57, 40, 65, 42, 66, 20, 58, 21, 59, 45, 70, 46, + 71, 52, 75, 57, 76, 58, 80, 66, 81, 55, 78, 59, 79, 69, 83, 71, 84, + 15, 52, 20, 55, 56, 75, 58, 78, 20, 55, 25, 64, 68, 96, 69, 97, 56, + 75, 68, 96, 104, 108, 105, 109, 58, 78, 69, 97, 105, 109, 106, 110, + 26, 65, 27, 66, 65, 98, 66, 99, 27, 66, 28, 67, 70, 100, 71, 101, 65, + 98, 70, 100, 105, 111, 106, 112, 66, 99, 71, 101, 106, 112, 107, 113, + 38, 56, 56, 75, 44, 68, 68, 82, 44, 68, 68, 96, 120, 121, 121, 122, + 56, 75, 104, 108, 68, 96, 105, 109, 68, 82, 105, 109, 121, 122, 123, + 124, 56, 104, 75, 108, 68, 105, 96, 109, 68, 105, 82, 109, 121, 123, + 122, 124, 75, 108, 108, 114, 82, 109, 109, 115, 96, 109, 109, 115, + 122, 124, 124, 125, 40, 58, 58, 78, 68, 82, 82, 90, 45, 69, 69, 97, + 121, 122, 122, 126, 65, 80, 105, 109, 105, 109, 127, 129, 70, 83, + 106, 110, 123, 124, 128, 130, 65, 105, 80, 109, 105, 127, 109, 129, + 70, 106, 83, 110, 123, 128, 124, 130, 98, 111, 111, 116, 127, 131, + 131, 133, 100, 112, 112, 117, 128, 132, 132, 134, 6, 15, 15, 40, 51, + 56, 56, 68, 7, 16, 16, 41, 54, 58, 58, 69, 13, 17, 40, 48, 52, 57, + 65, 70, 14, 18, 41, 49, 55, 59, 66, 71, 13, 40, 17, 48, 52, 65, 57, + 70, 14, 41, 18, 49, 55, 66, 59, 71, 36, 42, 42, 62, 55, 66, 66, 72, + 37, 43, 43, 63, 64, 67, 67, 73, 8, 17, 17, 42, 74, 75, 75, 96, 9, 18, + 18, 43, 77, 78, 78, 97, 17, 23, 48, 62, 75, 76, 98, 100, 18, 24, 49, + 63, 78, 79, 99, 101, 17, 48, 23, 62, 75, 98, 76, 100, 18, 49, 24, 63, + 78, 99, 79, 101, 42, 62, 62, 94, 96, 100, 100, 102, 43, 63, 63, 95, + 97, 101, 101, 103, 15, 26, 56, 65, 56, 65, 104, 105, 20, 27, 58, 66, + 68, 70, 105, 106, 20, 27, 68, 70, 58, 66, 105, 106, 25, 28, 69, 71, + 69, 71, 106, 107, 52, 65, 75, 98, 75, 98, 108, 111, 55, 66, 78, 99, + 96, 100, 109, 112, 55, 66, 96, 100, 78, 99, 109, 112, 64, 67, 97, + 101, 97, 101, 110, 113, 17, 27, 57, 66, 75, 80, 108, 109, 21, 28, 59, + 67, 82, 83, 109, 110, 27, 29, 70, 72, 80, 81, 111, 112, 28, 30, 71, + 73, 83, 84, 112, 113, 57, 70, 76, 100, 108, 111, 114, 116, 59, 71, + 79, 101, 109, 112, 115, 117, 66, 72, 100, 102, 109, 112, 116, 118, + 67, 73, 101, 103, 110, 113, 117, 119, 15, 56, 26, 65, 56, 104, 65, + 105, 20, 58, 27, 66, 68, 105, 70, 106, 52, 75, 65, 98, 75, 108, 98, + 111, 55, 78, 66, 99, 96, 109, 100, 112, 20, 68, 27, 70, 58, 105, 66, + 106, 25, 69, 28, 71, 69, 106, 71, 107, 55, 96, 66, 100, 78, 109, 99, + 112, 64, 97, 67, 101, 97, 110, 101, 113, 17, 57, 27, 66, 75, 108, 80, + 109, 21, 59, 28, 67, 82, 109, 83, 110, 57, 76, 70, 100, 108, 114, + 111, 116, 59, 79, 71, 101, 109, 115, 112, 117, 27, 70, 29, 72, 80, + 111, 81, 112, 28, 71, 30, 73, 83, 112, 84, 113, 66, 100, 72, 102, + 109, 116, 112, 118, 67, 101, 73, 103, 110, 117, 113, 119, 40, 65, 65, + 98, 68, 105, 105, 127, 45, 70, 70, 100, 121, 123, 123, 128, 58, 80, + 105, 111, 82, 109, 127, 131, 69, 83, 106, 112, 122, 124, 128, 132, + 58, 105, 80, 111, 82, 127, 109, 131, 69, 106, 83, 112, 122, 128, 124, + 132, 78, 109, 109, 116, 90, 129, 129, 133, 97, 110, 110, 117, 126, + 130, 130, 134, 42, 66, 66, 99, 96, 109, 109, 129, 46, 71, 71, 101, + 122, 124, 124, 130, 66, 81, 106, 112, 109, 115, 131, 133, 71, 84, + 107, 113, 124, 125, 132, 134, 66, 106, 81, 112, 109, 131, 115, 133, + 71, 107, 84, 113, 124, 132, 125, 134, 99, 112, 112, 118, 129, 133, + 133, 135, 101, 113, 113, 119, 130, 134, 134, 136, 2, 6, 12, 15, 6, + 13, 15, 20, 6, 8, 15, 17, 15, 17, 26, 27, 3, 7, 19, 20, 7, 14, 20, + 25, 7, 9, 20, 21, 16, 18, 27, 28, 6, 15, 15, 26, 8, 17, 17, 27, 13, + 17, 20, 27, 17, 23, 27, 29, 7, 16, 20, 27, 9, 18, 21, 28, 14, 18, 25, + 28, 18, 24, 28, 30, 12, 15, 38, 40, 51, 52, 56, 58, 15, 17, 40, 42, + 56, 57, 65, 66, 19, 20, 44, 45, 54, 55, 68, 69, 20, 21, 45, 46, 58, + 59, 70, 71, 51, 56, 56, 65, 74, 75, 75, 80, 52, 57, 58, 66, 75, 76, + 80, 81, 54, 58, 68, 70, 77, 78, 82, 83, 55, 59, 69, 71, 78, 79, 83, + 84, 6, 13, 51, 52, 13, 36, 52, 55, 15, 17, 56, 57, 40, 42, 65, 66, 7, + 14, 54, 55, 14, 37, 55, 64, 16, 18, 58, 59, 41, 43, 66, 67, 15, 40, + 56, 65, 17, 42, 57, 66, 40, 48, 68, 70, 48, 62, 70, 72, 16, 41, 58, + 66, 18, 43, 59, 67, 41, 49, 69, 71, 49, 63, 71, 73, 15, 20, 56, 58, + 52, 55, 75, 78, 26, 27, 65, 66, 65, 66, 98, 99, 20, 25, 68, 69, 55, + 64, 96, 97, 27, 28, 70, 71, 66, 67, 100, 101, 56, 68, 104, 105, 75, + 96, 108, 109, 65, 70, 105, 106, 98, 100, 111, 112, 58, 69, 105, 106, + 78, 97, 109, 110, 66, 71, 106, 107, 99, 101, 112, 113, 12, 51, 38, + 56, 15, 52, 40, 58, 51, 74, 56, 75, 56, 75, 65, 80, 19, 54, 44, 68, + 20, 55, 45, 69, 54, 77, 68, 82, 58, 78, 70, 83, 15, 56, 40, 65, 17, + 57, 42, 66, 52, 75, 58, 80, 57, 76, 66, 81, 20, 58, 45, 70, 21, 59, + 46, 71, 55, 78, 69, 83, 59, 79, 71, 84, 38, 56, 44, 68, 56, 75, 68, + 82, 56, 75, 68, 96, 104, 108, 105, 109, 44, 68, 120, 121, 68, 96, + 121, 122, 68, 82, 121, 122, 105, 109, 123, 124, 56, 104, 68, 105, 75, + 108, 96, 109, 75, 108, 82, 109, 108, 114, 109, 115, 68, 105, 121, + 123, 82, 109, 122, 124, 96, 109, 122, 124, 109, 115, 124, 125, 15, + 52, 56, 75, 20, 55, 58, 78, 56, 75, 104, 108, 68, 96, 105, 109, 20, + 55, 68, 96, 25, 64, 69, 97, 58, 78, 105, 109, 69, 97, 106, 110, 26, + 65, 65, 98, 27, 66, 66, 99, 65, 98, 105, 111, 70, 100, 106, 112, 27, + 66, 70, 100, 28, 67, 71, 101, 66, 99, 106, 112, 71, 101, 107, 113, + 40, 58, 68, 82, 58, 78, 82, 90, 65, 80, 105, 109, 105, 109, 127, 129, + 45, 69, 121, 122, 69, 97, 122, 126, 70, 83, 123, 124, 106, 110, 128, + 130, 65, 105, 105, 127, 80, 109, 109, 129, 98, 111, 127, 131, 111, + 116, 131, 133, 70, 106, 123, 128, 83, 110, 124, 130, 100, 112, 128, + 132, 112, 117, 132, 134, 6, 15, 51, 56, 15, 40, 56, 68, 13, 17, 52, + 57, 40, 48, 65, 70, 7, 16, 54, 58, 16, 41, 58, 69, 14, 18, 55, 59, + 41, 49, 66, 71, 13, 40, 52, 65, 17, 48, 57, 70, 36, 42, 55, 66, 42, + 62, 66, 72, 14, 41, 55, 66, 18, 49, 59, 71, 37, 43, 64, 67, 43, 63, + 67, 73, 15, 26, 56, 65, 56, 65, 104, 105, 20, 27, 58, 66, 68, 70, + 105, 106, 20, 27, 68, 70, 58, 66, 105, 106, 25, 28, 69, 71, 69, 71, + 106, 107, 52, 65, 75, 98, 75, 98, 108, 111, 55, 66, 78, 99, 96, 100, + 109, 112, 55, 66, 96, 100, 78, 99, 109, 112, 64, 67, 97, 101, 97, + 101, 110, 113, 8, 17, 74, 75, 17, 42, 75, 96, 17, 23, 75, 76, 48, 62, + 98, 100, 9, 18, 77, 78, 18, 43, 78, 97, 18, 24, 78, 79, 49, 63, 99, + 101, 17, 48, 75, 98, 23, 62, 76, 100, 42, 62, 96, 100, 62, 94, 100, + 102, 18, 49, 78, 99, 24, 63, 79, 101, 43, 63, 97, 101, 63, 95, 101, + 103, 17, 27, 75, 80, 57, 66, 108, 109, 27, 29, 80, 81, 70, 72, 111, + 112, 21, 28, 82, 83, 59, 67, 109, 110, 28, 30, 83, 84, 71, 73, 112, + 113, 57, 70, 108, 111, 76, 100, 114, 116, 66, 72, 109, 112, 100, 102, + 116, 118, 59, 71, 109, 112, 79, 101, 115, 117, 67, 73, 110, 113, 101, + 103, 117, 119, 15, 56, 56, 104, 26, 65, 65, 105, 52, 75, 75, 108, 65, + 98, 98, 111, 20, 58, 68, 105, 27, 66, 70, 106, 55, 78, 96, 109, 66, + 99, 100, 112, 20, 68, 58, 105, 27, 70, 66, 106, 55, 96, 78, 109, 66, + 100, 99, 112, 25, 69, 69, 106, 28, 71, 71, 107, 64, 97, 97, 110, 67, + 101, 101, 113, 40, 65, 68, 105, 65, 98, 105, 127, 58, 80, 82, 109, + 105, 111, 127, 131, 45, 70, 121, 123, 70, 100, 123, 128, 69, 83, 122, + 124, 106, 112, 128, 132, 58, 105, 82, 127, 80, 111, 109, 131, 78, + 109, 90, 129, 109, 116, 129, 133, 69, 106, 122, 128, 83, 112, 124, + 132, 97, 110, 126, 130, 110, 117, 130, 134, 17, 57, 75, 108, 27, 66, + 80, 109, 57, 76, 108, 114, 70, 100, 111, 116, 21, 59, 82, 109, 28, + 67, 83, 110, 59, 79, 109, 115, 71, 101, 112, 117, 27, 70, 80, 111, + 29, 72, 81, 112, 66, 100, 109, 116, 72, 102, 112, 118, 28, 71, 83, + 112, 30, 73, 84, 113, 67, 101, 110, 117, 73, 103, 113, 119, 42, 66, + 96, 109, 66, 99, 109, 129, 66, 81, 109, 115, 106, 112, 131, 133, 46, + 71, 122, 124, 71, 101, 124, 130, 71, 84, 124, 125, 107, 113, 132, + 134, 66, 106, 109, 131, 81, 112, 115, 133, 99, 112, 129, 133, 112, + 118, 133, 135, 71, 107, 124, 132, 84, 113, 125, 134, 101, 113, 130, + 134, 113, 119, 134, 136, 4, 7, 13, 16, 13, 16, 17, 21, 7, 9, 16, 18, + 20, 21, 27, 28, 7, 9, 20, 21, 16, 18, 27, 28, 9, 10, 21, 22, 21, 22, + 29, 30, 13, 20, 17, 27, 17, 27, 23, 29, 16, 21, 21, 28, 27, 29, 29, + 31, 16, 21, 27, 29, 21, 28, 29, 31, 18, 22, 28, 30, 28, 30, 31, 32, + 13, 16, 39, 41, 52, 53, 57, 59, 16, 18, 41, 43, 58, 59, 66, 67, 20, + 21, 45, 46, 58, 59, 70, 71, 21, 22, 46, 47, 60, 61, 72, 73, 52, 58, + 57, 66, 75, 80, 76, 81, 53, 59, 59, 67, 80, 81, 81, 85, 58, 60, 70, + 72, 82, 83, 86, 87, 59, 61, 71, 73, 83, 84, 87, 88, 13, 16, 52, 53, + 39, 41, 57, 59, 20, 21, 58, 59, 45, 46, 70, 71, 16, 18, 58, 59, 41, + 43, 66, 67, 21, 22, 60, 61, 46, 47, 72, 73, 52, 58, 75, 80, 57, 66, + 76, 81, 58, 60, 82, 83, 70, 72, 86, 87, 53, 59, 80, 81, 59, 67, 81, + 85, 59, 61, 83, 84, 71, 73, 87, 88, 17, 21, 57, 59, 57, 59, 76, 79, + 27, 28, 66, 67, 70, 71, 100, 101, 27, 28, 70, 71, 66, 67, 100, 101, + 29, 30, 72, 73, 72, 73, 102, 103, 75, 82, 108, 109, 108, 109, 114, + 115, 80, 83, 109, 110, 111, 112, 116, 117, 80, 83, 111, 112, 109, + 110, 116, 117, 81, 84, 112, 113, 112, 113, 118, 119, 35, 54, 40, 58, + 40, 58, 48, 60, 54, 77, 58, 78, 68, 82, 70, 83, 54, 77, 68, 82, 58, + 78, 70, 83, 77, 89, 82, 90, 82, 90, 86, 91, 40, 68, 48, 70, 48, 70, + 62, 72, 58, 82, 60, 83, 70, 86, 72, 87, 58, 82, 70, 86, 60, 83, 72, + 87, 78, 90, 83, 91, 83, 91, 87, 92, 40, 58, 45, 69, 65, 80, 70, 83, + 58, 78, 69, 97, 105, 109, 106, 110, 68, 82, 121, 122, 105, 109, 123, + 124, 82, 90, 122, 126, 127, 129, 128, 130, 65, 105, 70, 106, 98, 111, + 100, 112, 80, 109, 83, 110, 111, 116, 112, 117, 105, 127, 123, 128, + 127, 131, 128, 132, 109, 129, 124, 130, 131, 133, 132, 134, 40, 58, + 65, 80, 45, 69, 70, 83, 68, 82, 105, 109, 121, 122, 123, 124, 58, 78, + 105, 109, 69, 97, 106, 110, 82, 90, 127, 129, 122, 126, 128, 130, 65, + 105, 98, 111, 70, 106, 100, 112, 105, 127, 127, 131, 123, 128, 128, + 132, 80, 109, 111, 116, 83, 110, 112, 117, 109, 129, 131, 133, 124, + 130, 132, 134, 48, 60, 70, 83, 70, 83, 86, 91, 70, 83, 106, 110, 123, + 124, 128, 130, 70, 83, 123, 124, 106, 110, 128, 130, 86, 91, 128, + 130, 128, 130, 141, 142, 98, 127, 111, 131, 111, 131, 116, 133, 111, + 131, 131, 143, 144, 145, 145, 146, 111, 131, 144, 145, 131, 143, 145, + 146, 116, 133, 145, 146, 145, 146, 147, 148, 13, 20, 52, 58, 52, 58, + 75, 82, 16, 21, 53, 59, 58, 60, 80, 83, 16, 21, 58, 60, 53, 59, 80, + 83, 18, 22, 59, 61, 59, 61, 81, 84, 39, 45, 57, 70, 57, 70, 76, 86, + 41, 46, 59, 71, 66, 72, 81, 87, 41, 46, 66, 72, 59, 71, 81, 87, 43, + 47, 67, 73, 67, 73, 85, 88, 17, 27, 57, 66, 75, 80, 108, 109, 21, 28, + 59, 67, 82, 83, 109, 110, 27, 29, 70, 72, 80, 81, 111, 112, 28, 30, + 71, 73, 83, 84, 112, 113, 57, 70, 76, 100, 108, 111, 114, 116, 59, + 71, 79, 101, 109, 112, 115, 117, 66, 72, 100, 102, 109, 112, 116, + 118, 67, 73, 101, 103, 110, 113, 117, 119, 17, 27, 75, 80, 57, 66, + 108, 109, 27, 29, 80, 81, 70, 72, 111, 112, 21, 28, 82, 83, 59, 67, + 109, 110, 28, 30, 83, 84, 71, 73, 112, 113, 57, 70, 108, 111, 76, + 100, 114, 116, 66, 72, 109, 112, 100, 102, 116, 118, 59, 71, 109, + 112, 79, 101, 115, 117, 67, 73, 110, 113, 101, 103, 117, 119, 23, 29, + 76, 81, 76, 81, 114, 115, 29, 31, 81, 85, 86, 87, 116, 117, 29, 31, + 86, 87, 81, 85, 116, 117, 31, 32, 87, 88, 87, 88, 118, 119, 76, 86, + 114, 116, 114, 116, 137, 138, 81, 87, 115, 117, 116, 118, 138, 139, + 81, 87, 116, 118, 115, 117, 138, 139, 85, 88, 117, 119, 117, 119, + 139, 140, 40, 68, 65, 105, 65, 105, 98, 127, 58, 82, 80, 109, 105, + 127, 111, 131, 58, 82, 105, 127, 80, 109, 111, 131, 78, 90, 109, 129, + 109, 129, 116, 133, 45, 121, 70, 123, 70, 123, 100, 128, 69, 122, 83, + 124, 106, 128, 112, 132, 69, 122, 106, 128, 83, 124, 112, 132, 97, + 126, 110, 130, 110, 130, 117, 134, 48, 70, 70, 106, 98, 111, 111, + 131, 60, 83, 83, 110, 127, 131, 131, 143, 70, 86, 123, 128, 111, 116, + 144, 145, 83, 91, 124, 130, 131, 133, 145, 146, 70, 123, 86, 128, + 111, 144, 116, 145, 83, 124, 91, 130, 131, 145, 133, 146, 106, 128, + 128, 141, 131, 145, 145, 147, 110, 130, 130, 142, 143, 146, 146, 148, + 48, 70, 98, 111, 70, 106, 111, 131, 70, 86, 111, 116, 123, 128, 144, + 145, 60, 83, 127, 131, 83, 110, 131, 143, 83, 91, 131, 133, 124, 130, + 145, 146, 70, 123, 111, 144, 86, 128, 116, 145, 106, 128, 131, 145, + 128, 141, 145, 147, 83, 124, 131, 145, 91, 130, 133, 146, 110, 130, + 143, 146, 130, 142, 146, 148, 62, 72, 100, 112, 100, 112, 116, 133, + 72, 87, 112, 117, 128, 132, 145, 146, 72, 87, 128, 132, 112, 117, + 145, 146, 87, 92, 132, 134, 132, 134, 147, 148, 100, 128, 116, 145, + 116, 145, 138, 149, 112, 132, 133, 146, 145, 147, 149, 150, 112, 132, + 145, 147, 133, 146, 149, 150, 117, 134, 146, 148, 146, 148, 150, 151, + 2, 12, 6, 15, 6, 15, 13, 20, 6, 15, 8, 17, 15, 26, 17, 27, 6, 15, 15, + 26, 8, 17, 17, 27, 13, 20, 17, 27, 17, 27, 23, 29, 3, 19, 7, 20, 7, + 20, 14, 25, 7, 20, 9, 21, 16, 27, 18, 28, 7, 20, 16, 27, 9, 21, 18, + 28, 14, 25, 18, 28, 18, 28, 24, 30, 12, 38, 15, 40, 51, 56, 52, 58, + 15, 40, 17, 42, 56, 65, 57, 66, 51, 56, 56, 65, 74, 75, 75, 80, 52, + 58, 57, 66, 75, 80, 76, 81, 19, 44, 20, 45, 54, 68, 55, 69, 20, 45, + 21, 46, 58, 70, 59, 71, 54, 68, 58, 70, 77, 82, 78, 83, 55, 69, 59, + 71, 78, 83, 79, 84, 12, 38, 51, 56, 15, 40, 52, 58, 51, 56, 74, 75, + 56, 65, 75, 80, 15, 40, 56, 65, 17, 42, 57, 66, 52, 58, 75, 80, 57, + 66, 76, 81, 19, 44, 54, 68, 20, 45, 55, 69, 54, 68, 77, 82, 58, 70, + 78, 83, 20, 45, 58, 70, 21, 46, 59, 71, 55, 69, 78, 83, 59, 71, 79, + 84, 38, 44, 56, 68, 56, 68, 75, 82, 56, 68, 75, 96, 104, 105, 108, + 109, 56, 68, 104, 105, 75, 96, 108, 109, 75, 82, 108, 109, 108, 109, + 114, 115, 44, 120, 68, 121, 68, 121, 96, 122, 68, 121, 82, 122, 105, + 123, 109, 124, 68, 121, 105, 123, 82, 122, 109, 124, 96, 122, 109, + 124, 109, 124, 115, 125, 6, 51, 13, 52, 13, 52, 36, 55, 15, 56, 17, + 57, 40, 65, 42, 66, 15, 56, 40, 65, 17, 57, 42, 66, 40, 68, 48, 70, + 48, 70, 62, 72, 7, 54, 14, 55, 14, 55, 37, 64, 16, 58, 18, 59, 41, + 66, 43, 67, 16, 58, 41, 66, 18, 59, 43, 67, 41, 69, 49, 71, 49, 71, + 63, 73, 15, 56, 20, 58, 52, 75, 55, 78, 26, 65, 27, 66, 65, 98, 66, + 99, 56, 104, 68, 105, 75, 108, 96, 109, 65, 105, 70, 106, 98, 111, + 100, 112, 20, 68, 25, 69, 55, 96, 64, 97, 27, 70, 28, 71, 66, 100, + 67, 101, 58, 105, 69, 106, 78, 109, 97, 110, 66, 106, 71, 107, 99, + 112, 101, 113, 15, 56, 52, 75, 20, 58, 55, 78, 56, 104, 75, 108, 68, + 105, 96, 109, 26, 65, 65, 98, 27, 66, 66, 99, 65, 105, 98, 111, 70, + 106, 100, 112, 20, 68, 55, 96, 25, 69, 64, 97, 58, 105, 78, 109, 69, + 106, 97, 110, 27, 70, 66, 100, 28, 71, 67, 101, 66, 106, 99, 112, 71, + 107, 101, 113, 40, 68, 58, 82, 58, 82, 78, 90, 65, 105, 80, 109, 105, + 127, 109, 129, 65, 105, 105, 127, 80, 109, 109, 129, 98, 127, 111, + 131, 111, 131, 116, 133, 45, 121, 69, 122, 69, 122, 97, 126, 70, 123, + 83, 124, 106, 128, 110, 130, 70, 123, 106, 128, 83, 124, 110, 130, + 100, 128, 112, 132, 112, 132, 117, 134, 6, 51, 15, 56, 15, 56, 40, + 68, 13, 52, 17, 57, 40, 65, 48, 70, 13, 52, 40, 65, 17, 57, 48, 70, + 36, 55, 42, 66, 42, 66, 62, 72, 7, 54, 16, 58, 16, 58, 41, 69, 14, + 55, 18, 59, 41, 66, 49, 71, 14, 55, 41, 66, 18, 59, 49, 71, 37, 64, + 43, 67, 43, 67, 63, 73, 15, 56, 26, 65, 56, 104, 65, 105, 20, 58, 27, + 66, 68, 105, 70, 106, 52, 75, 65, 98, 75, 108, 98, 111, 55, 78, 66, + 99, 96, 109, 100, 112, 20, 68, 27, 70, 58, 105, 66, 106, 25, 69, 28, + 71, 69, 106, 71, 107, 55, 96, 66, 100, 78, 109, 99, 112, 64, 97, 67, + 101, 97, 110, 101, 113, 15, 56, 56, 104, 26, 65, 65, 105, 52, 75, 75, + 108, 65, 98, 98, 111, 20, 58, 68, 105, 27, 66, 70, 106, 55, 78, 96, + 109, 66, 99, 100, 112, 20, 68, 58, 105, 27, 70, 66, 106, 55, 96, 78, + 109, 66, 100, 99, 112, 25, 69, 69, 106, 28, 71, 71, 107, 64, 97, 97, + 110, 67, 101, 101, 113, 40, 68, 65, 105, 65, 105, 98, 127, 58, 82, + 80, 109, 105, 127, 111, 131, 58, 82, 105, 127, 80, 109, 111, 131, 78, + 90, 109, 129, 109, 129, 116, 133, 45, 121, 70, 123, 70, 123, 100, + 128, 69, 122, 83, 124, 106, 128, 112, 132, 69, 122, 106, 128, 83, + 124, 112, 132, 97, 126, 110, 130, 110, 130, 117, 134, 8, 74, 17, 75, + 17, 75, 42, 96, 17, 75, 23, 76, 48, 98, 62, 100, 17, 75, 48, 98, 23, + 76, 62, 100, 42, 96, 62, 100, 62, 100, 94, 102, 9, 77, 18, 78, 18, + 78, 43, 97, 18, 78, 24, 79, 49, 99, 63, 101, 18, 78, 49, 99, 24, 79, + 63, 101, 43, 97, 63, 101, 63, 101, 95, 103, 17, 75, 27, 80, 57, 108, + 66, 109, 27, 80, 29, 81, 70, 111, 72, 112, 57, 108, 70, 111, 76, 114, + 100, 116, 66, 109, 72, 112, 100, 116, 102, 118, 21, 82, 28, 83, 59, + 109, 67, 110, 28, 83, 30, 84, 71, 112, 73, 113, 59, 109, 71, 112, 79, + 115, 101, 117, 67, 110, 73, 113, 101, 117, 103, 119, 17, 75, 57, 108, + 27, 80, 66, 109, 57, 108, 76, 114, 70, 111, 100, 116, 27, 80, 70, + 111, 29, 81, 72, 112, 66, 109, 100, 116, 72, 112, 102, 118, 21, 82, + 59, 109, 28, 83, 67, 110, 59, 109, 79, 115, 71, 112, 101, 117, 28, + 83, 71, 112, 30, 84, 73, 113, 67, 110, 101, 117, 73, 113, 103, 119, + 42, 96, 66, 109, 66, 109, 99, 129, 66, 109, 81, 115, 106, 131, 112, + 133, 66, 109, 106, 131, 81, 115, 112, 133, 99, 129, 112, 133, 112, + 133, 118, 135, 46, 122, 71, 124, 71, 124, 101, 130, 71, 124, 84, 125, + 107, 132, 113, 134, 71, 124, 107, 132, 84, 125, 113, 134, 101, 130, + 113, 134, 113, 134, 119, 136, 4, 13, 7, 16, 13, 17, 16, 21, 7, 16, 9, + 18, 20, 27, 21, 28, 13, 17, 20, 27, 17, 23, 27, 29, 16, 21, 21, 28, + 27, 29, 29, 31, 7, 20, 9, 21, 16, 27, 18, 28, 9, 21, 10, 22, 21, 29, + 22, 30, 16, 27, 21, 29, 21, 29, 28, 31, 18, 28, 22, 30, 28, 31, 30, + 32, 13, 39, 16, 41, 52, 57, 53, 59, 16, 41, 18, 43, 58, 66, 59, 67, + 52, 57, 58, 66, 75, 76, 80, 81, 53, 59, 59, 67, 80, 81, 81, 85, 20, + 45, 21, 46, 58, 70, 59, 71, 21, 46, 22, 47, 60, 72, 61, 73, 58, 70, + 60, 72, 82, 86, 83, 87, 59, 71, 61, 73, 83, 87, 84, 88, 35, 40, 54, + 58, 40, 48, 58, 60, 54, 58, 77, 78, 68, 70, 82, 83, 40, 48, 68, 70, + 48, 62, 70, 72, 58, 60, 82, 83, 70, 72, 86, 87, 54, 68, 77, 82, 58, + 70, 78, 83, 77, 82, 89, 90, 82, 86, 90, 91, 58, 70, 82, 86, 60, 72, + 83, 87, 78, 83, 90, 91, 83, 87, 91, 92, 40, 45, 58, 69, 65, 70, 80, + 83, 58, 69, 78, 97, 105, 106, 109, 110, 65, 70, 105, 106, 98, 100, + 111, 112, 80, 83, 109, 110, 111, 112, 116, 117, 68, 121, 82, 122, + 105, 123, 109, 124, 82, 122, 90, 126, 127, 128, 129, 130, 105, 123, + 127, 128, 127, 128, 131, 132, 109, 124, 129, 130, 131, 132, 133, 134, + 13, 52, 16, 53, 39, 57, 41, 59, 20, 58, 21, 59, 45, 70, 46, 71, 52, + 75, 58, 80, 57, 76, 66, 81, 58, 82, 60, 83, 70, 86, 72, 87, 16, 58, + 18, 59, 41, 66, 43, 67, 21, 60, 22, 61, 46, 72, 47, 73, 53, 80, 59, + 81, 59, 81, 67, 85, 59, 83, 61, 84, 71, 87, 73, 88, 17, 57, 21, 59, + 57, 76, 59, 79, 27, 66, 28, 67, 70, 100, 71, 101, 75, 108, 82, 109, + 108, 114, 109, 115, 80, 109, 83, 110, 111, 116, 112, 117, 27, 70, 28, + 71, 66, 100, 67, 101, 29, 72, 30, 73, 72, 102, 73, 103, 80, 111, 83, + 112, 109, 116, 110, 117, 81, 112, 84, 113, 112, 118, 113, 119, 40, + 65, 58, 80, 45, 70, 69, 83, 68, 105, 82, 109, 121, 123, 122, 124, 65, + 98, 105, 111, 70, 100, 106, 112, 105, 127, 127, 131, 123, 128, 128, + 132, 58, 105, 78, 109, 69, 106, 97, 110, 82, 127, 90, 129, 122, 128, + 126, 130, 80, 111, 109, 116, 83, 112, 110, 117, 109, 131, 129, 133, + 124, 132, 130, 134, 48, 70, 60, 83, 70, 86, 83, 91, 70, 106, 83, 110, + 123, 128, 124, 130, 98, 111, 127, 131, 111, 116, 131, 133, 111, 131, + 131, 143, 144, 145, 145, 146, 70, 123, 83, 124, 106, 128, 110, 130, + 86, 128, 91, 130, 128, 141, 130, 142, 111, 144, 131, 145, 131, 145, + 143, 146, 116, 145, 133, 146, 145, 147, 146, 148, 13, 52, 20, 58, 52, + 75, 58, 82, 16, 53, 21, 59, 58, 80, 60, 83, 39, 57, 45, 70, 57, 76, + 70, 86, 41, 59, 46, 71, 66, 81, 72, 87, 16, 58, 21, 60, 53, 80, 59, + 83, 18, 59, 22, 61, 59, 81, 61, 84, 41, 66, 46, 72, 59, 81, 71, 87, + 43, 67, 47, 73, 67, 85, 73, 88, 17, 57, 27, 66, 75, 108, 80, 109, 21, + 59, 28, 67, 82, 109, 83, 110, 57, 76, 70, 100, 108, 114, 111, 116, + 59, 79, 71, 101, 109, 115, 112, 117, 27, 70, 29, 72, 80, 111, 81, + 112, 28, 71, 30, 73, 83, 112, 84, 113, 66, 100, 72, 102, 109, 116, + 112, 118, 67, 101, 73, 103, 110, 117, 113, 119, 40, 65, 68, 105, 65, + 98, 105, 127, 58, 80, 82, 109, 105, 111, 127, 131, 45, 70, 121, 123, + 70, 100, 123, 128, 69, 83, 122, 124, 106, 112, 128, 132, 58, 105, 82, + 127, 80, 111, 109, 131, 78, 109, 90, 129, 109, 116, 129, 133, 69, + 106, 122, 128, 83, 112, 124, 132, 97, 110, 126, 130, 110, 117, 130, + 134, 48, 70, 70, 106, 98, 111, 111, 131, 60, 83, 83, 110, 127, 131, + 131, 143, 70, 86, 123, 128, 111, 116, 144, 145, 83, 91, 124, 130, + 131, 133, 145, 146, 70, 123, 86, 128, 111, 144, 116, 145, 83, 124, + 91, 130, 131, 145, 133, 146, 106, 128, 128, 141, 131, 145, 145, 147, + 110, 130, 130, 142, 143, 146, 146, 148, 17, 75, 27, 80, 57, 108, 66, + 109, 27, 80, 29, 81, 70, 111, 72, 112, 57, 108, 70, 111, 76, 114, + 100, 116, 66, 109, 72, 112, 100, 116, 102, 118, 21, 82, 28, 83, 59, + 109, 67, 110, 28, 83, 30, 84, 71, 112, 73, 113, 59, 109, 71, 112, 79, + 115, 101, 117, 67, 110, 73, 113, 101, 117, 103, 119, 23, 76, 29, 81, + 76, 114, 81, 115, 29, 81, 31, 85, 86, 116, 87, 117, 76, 114, 86, 116, + 114, 137, 116, 138, 81, 115, 87, 117, 116, 138, 118, 139, 29, 86, 31, + 87, 81, 116, 85, 117, 31, 87, 32, 88, 87, 118, 88, 119, 81, 116, 87, + 118, 115, 138, 117, 139, 85, 117, 88, 119, 117, 139, 119, 140, 48, + 98, 70, 111, 70, 111, 106, 131, 70, 111, 86, 116, 123, 144, 128, 145, + 70, 111, 123, 144, 86, 116, 128, 145, 106, 131, 128, 145, 128, 145, + 141, 147, 60, 127, 83, 131, 83, 131, 110, 143, 83, 131, 91, 133, 124, + 145, 130, 146, 83, 131, 124, 145, 91, 133, 130, 146, 110, 143, 130, + 146, 130, 146, 142, 148, 62, 100, 72, 112, 100, 116, 112, 133, 72, + 112, 87, 117, 128, 145, 132, 146, 100, 116, 128, 145, 116, 138, 145, + 149, 112, 133, 132, 146, 145, 149, 147, 150, 72, 128, 87, 132, 112, + 145, 117, 146, 87, 132, 92, 134, 132, 147, 134, 148, 112, 145, 132, + 147, 133, 149, 146, 150, 117, 146, 134, 148, 146, 150, 148, 151, 4, + 13, 13, 17, 7, 16, 16, 21, 13, 17, 17, 23, 20, 27, 27, 29, 7, 16, 20, + 27, 9, 18, 21, 28, 16, 21, 27, 29, 21, 28, 29, 31, 7, 20, 16, 27, 9, + 21, 18, 28, 16, 27, 21, 29, 21, 29, 28, 31, 9, 21, 21, 29, 10, 22, + 22, 30, 18, 28, 28, 31, 22, 30, 30, 32, 35, 40, 40, 48, 54, 58, 58, + 60, 40, 48, 48, 62, 68, 70, 70, 72, 54, 58, 68, 70, 77, 78, 82, 83, + 58, 60, 70, 72, 82, 83, 86, 87, 54, 68, 58, 70, 77, 82, 78, 83, 58, + 70, 60, 72, 82, 86, 83, 87, 77, 82, 82, 86, 89, 90, 90, 91, 78, 83, + 83, 87, 90, 91, 91, 92, 13, 39, 52, 57, 16, 41, 53, 59, 52, 57, 75, + 76, 58, 66, 80, 81, 16, 41, 58, 66, 18, 43, 59, 67, 53, 59, 80, 81, + 59, 67, 81, 85, 20, 45, 58, 70, 21, 46, 59, 71, 58, 70, 82, 86, 60, + 72, 83, 87, 21, 46, 60, 72, 22, 47, 61, 73, 59, 71, 83, 87, 61, 73, + 84, 88, 40, 45, 65, 70, 58, 69, 80, 83, 65, 70, 98, 100, 105, 106, + 111, 112, 58, 69, 105, 106, 78, 97, 109, 110, 80, 83, 111, 112, 109, + 110, 116, 117, 68, 121, 105, 123, 82, 122, 109, 124, 105, 123, 127, + 128, 127, 128, 131, 132, 82, 122, 127, 128, 90, 126, 129, 130, 109, + 124, 131, 132, 129, 130, 133, 134, 13, 52, 39, 57, 16, 53, 41, 59, + 52, 75, 57, 76, 58, 80, 66, 81, 20, 58, 45, 70, 21, 59, 46, 71, 58, + 82, 70, 86, 60, 83, 72, 87, 16, 58, 41, 66, 18, 59, 43, 67, 53, 80, + 59, 81, 59, 81, 67, 85, 21, 60, 46, 72, 22, 61, 47, 73, 59, 83, 71, + 87, 61, 84, 73, 88, 40, 65, 45, 70, 58, 80, 69, 83, 65, 98, 70, 100, + 105, 111, 106, 112, 68, 105, 121, 123, 82, 109, 122, 124, 105, 127, + 123, 128, 127, 131, 128, 132, 58, 105, 69, 106, 78, 109, 97, 110, 80, + 111, 83, 112, 109, 116, 110, 117, 82, 127, 122, 128, 90, 129, 126, + 130, 109, 131, 124, 132, 129, 133, 130, 134, 17, 57, 57, 76, 21, 59, + 59, 79, 75, 108, 108, 114, 82, 109, 109, 115, 27, 66, 70, 100, 28, + 67, 71, 101, 80, 109, 111, 116, 83, 110, 112, 117, 27, 70, 66, 100, + 28, 71, 67, 101, 80, 111, 109, 116, 83, 112, 110, 117, 29, 72, 72, + 102, 30, 73, 73, 103, 81, 112, 112, 118, 84, 113, 113, 119, 48, 70, + 70, 86, 60, 83, 83, 91, 98, 111, 111, 116, 127, 131, 131, 133, 70, + 106, 123, 128, 83, 110, 124, 130, 111, 131, 144, 145, 131, 143, 145, + 146, 70, 123, 106, 128, 83, 124, 110, 130, 111, 144, 131, 145, 131, + 145, 143, 146, 86, 128, 128, 141, 91, 130, 130, 142, 116, 145, 145, + 147, 133, 146, 146, 148, 13, 52, 52, 75, 20, 58, 58, 82, 39, 57, 57, + 76, 45, 70, 70, 86, 16, 53, 58, 80, 21, 59, 60, 83, 41, 59, 66, 81, + 46, 71, 72, 87, 16, 58, 53, 80, 21, 60, 59, 83, 41, 66, 59, 81, 46, + 72, 71, 87, 18, 59, 59, 81, 22, 61, 61, 84, 43, 67, 67, 85, 47, 73, + 73, 88, 40, 65, 65, 98, 68, 105, 105, 127, 45, 70, 70, 100, 121, 123, + 123, 128, 58, 80, 105, 111, 82, 109, 127, 131, 69, 83, 106, 112, 122, + 124, 128, 132, 58, 105, 80, 111, 82, 127, 109, 131, 69, 106, 83, 112, + 122, 128, 124, 132, 78, 109, 109, 116, 90, 129, 129, 133, 97, 110, + 110, 117, 126, 130, 130, 134, 17, 57, 75, 108, 27, 66, 80, 109, 57, + 76, 108, 114, 70, 100, 111, 116, 21, 59, 82, 109, 28, 67, 83, 110, + 59, 79, 109, 115, 71, 101, 112, 117, 27, 70, 80, 111, 29, 72, 81, + 112, 66, 100, 109, 116, 72, 102, 112, 118, 28, 71, 83, 112, 30, 73, + 84, 113, 67, 101, 110, 117, 73, 103, 113, 119, 48, 70, 98, 111, 70, + 106, 111, 131, 70, 86, 111, 116, 123, 128, 144, 145, 60, 83, 127, + 131, 83, 110, 131, 143, 83, 91, 131, 133, 124, 130, 145, 146, 70, + 123, 111, 144, 86, 128, 116, 145, 106, 128, 131, 145, 128, 141, 145, + 147, 83, 124, 131, 145, 91, 130, 133, 146, 110, 130, 143, 146, 130, + 142, 146, 148, 17, 75, 57, 108, 27, 80, 66, 109, 57, 108, 76, 114, + 70, 111, 100, 116, 27, 80, 70, 111, 29, 81, 72, 112, 66, 109, 100, + 116, 72, 112, 102, 118, 21, 82, 59, 109, 28, 83, 67, 110, 59, 109, + 79, 115, 71, 112, 101, 117, 28, 83, 71, 112, 30, 84, 73, 113, 67, + 110, 101, 117, 73, 113, 103, 119, 48, 98, 70, 111, 70, 111, 106, 131, + 70, 111, 86, 116, 123, 144, 128, 145, 70, 111, 123, 144, 86, 116, + 128, 145, 106, 131, 128, 145, 128, 145, 141, 147, 60, 127, 83, 131, + 83, 131, 110, 143, 83, 131, 91, 133, 124, 145, 130, 146, 83, 131, + 124, 145, 91, 133, 130, 146, 110, 143, 130, 146, 130, 146, 142, 148, + 23, 76, 76, 114, 29, 81, 81, 115, 76, 114, 114, 137, 86, 116, 116, + 138, 29, 81, 86, 116, 31, 85, 87, 117, 81, 115, 116, 138, 87, 117, + 118, 139, 29, 86, 81, 116, 31, 87, 85, 117, 81, 116, 115, 138, 87, + 118, 117, 139, 31, 87, 87, 118, 32, 88, 88, 119, 85, 117, 117, 139, + 88, 119, 119, 140, 62, 100, 100, 116, 72, 112, 112, 133, 100, 116, + 116, 138, 128, 145, 145, 149, 72, 112, 128, 145, 87, 117, 132, 146, + 112, 133, 145, 149, 132, 146, 147, 150, 72, 128, 112, 145, 87, 132, + 117, 146, 112, 145, 133, 149, 132, 147, 146, 150, 87, 132, 132, 147, + 92, 134, 134, 148, 117, 146, 146, 150, 134, 148, 148, 151, 11, 14, + 14, 18, 14, 18, 18, 22, 14, 18, 18, 24, 25, 28, 28, 30, 14, 18, 25, + 28, 18, 24, 28, 30, 18, 22, 28, 30, 28, 30, 31, 32, 14, 25, 18, 28, + 18, 28, 24, 30, 18, 28, 22, 30, 28, 31, 30, 32, 18, 28, 28, 31, 22, + 30, 30, 32, 24, 30, 30, 32, 30, 32, 32, 33, 36, 41, 41, 49, 55, 59, + 59, 61, 41, 49, 49, 63, 69, 71, 71, 73, 55, 59, 69, 71, 78, 79, 83, + 84, 59, 61, 71, 73, 83, 84, 87, 88, 55, 69, 59, 71, 78, 83, 79, 84, + 59, 71, 61, 73, 83, 87, 84, 88, 78, 83, 83, 87, 90, 91, 91, 92, 79, + 84, 84, 88, 91, 92, 92, 93, 36, 41, 55, 59, 41, 49, 59, 61, 55, 59, + 78, 79, 69, 71, 83, 84, 41, 49, 69, 71, 49, 63, 71, 73, 59, 61, 83, + 84, 71, 73, 87, 88, 55, 69, 78, 83, 59, 71, 79, 84, 78, 83, 90, 91, + 83, 87, 91, 92, 59, 71, 83, 87, 61, 73, 84, 88, 79, 84, 91, 92, 84, + 88, 92, 93, 42, 46, 66, 71, 66, 71, 81, 84, 66, 71, 99, 101, 106, + 107, 112, 113, 66, 71, 106, 107, 99, 101, 112, 113, 81, 84, 112, 113, + 112, 113, 118, 119, 96, 122, 109, 124, 109, 124, 115, 125, 109, 124, + 129, 130, 131, 132, 133, 134, 109, 124, 131, 132, 129, 130, 133, 134, + 115, 125, 133, 134, 133, 134, 135, 136, 36, 55, 41, 59, 41, 59, 49, + 61, 55, 78, 59, 79, 69, 83, 71, 84, 55, 78, 69, 83, 59, 79, 71, 84, + 78, 90, 83, 91, 83, 91, 87, 92, 41, 69, 49, 71, 49, 71, 63, 73, 59, + 83, 61, 84, 71, 87, 73, 88, 59, 83, 71, 87, 61, 84, 73, 88, 79, 91, + 84, 92, 84, 92, 88, 93, 42, 66, 46, 71, 66, 81, 71, 84, 66, 99, 71, + 101, 106, 112, 107, 113, 96, 109, 122, 124, 109, 115, 124, 125, 109, + 129, 124, 130, 131, 133, 132, 134, 66, 106, 71, 107, 99, 112, 101, + 113, 81, 112, 84, 113, 112, 118, 113, 119, 109, 131, 124, 132, 129, + 133, 130, 134, 115, 133, 125, 134, 133, 135, 134, 136, 42, 66, 66, + 81, 46, 71, 71, 84, 96, 109, 109, 115, 122, 124, 124, 125, 66, 99, + 106, 112, 71, 101, 107, 113, 109, 129, 131, 133, 124, 130, 132, 134, + 66, 106, 99, 112, 71, 107, 101, 113, 109, 131, 129, 133, 124, 132, + 130, 134, 81, 112, 112, 118, 84, 113, 113, 119, 115, 133, 133, 135, + 125, 134, 134, 136, 62, 72, 72, 87, 72, 87, 87, 92, 100, 112, 112, + 117, 128, 132, 132, 134, 100, 112, 128, 132, 112, 117, 132, 134, 116, + 133, 145, 146, 145, 146, 147, 148, 100, 128, 112, 132, 112, 132, 117, + 134, 116, 145, 133, 146, 145, 147, 146, 148, 116, 145, 145, 147, 133, + 146, 146, 148, 138, 149, 149, 150, 149, 150, 150, 151, 36, 55, 55, + 78, 55, 78, 78, 90, 41, 59, 59, 79, 69, 83, 83, 91, 41, 59, 69, 83, + 59, 79, 83, 91, 49, 61, 71, 84, 71, 84, 87, 92, 41, 69, 59, 83, 59, + 83, 79, 91, 49, 71, 61, 84, 71, 87, 84, 92, 49, 71, 71, 87, 61, 84, + 84, 92, 63, 73, 73, 88, 73, 88, 88, 93, 42, 66, 66, 99, 96, 109, 109, + 129, 46, 71, 71, 101, 122, 124, 124, 130, 66, 81, 106, 112, 109, 115, + 131, 133, 71, 84, 107, 113, 124, 125, 132, 134, 66, 106, 81, 112, + 109, 131, 115, 133, 71, 107, 84, 113, 124, 132, 125, 134, 99, 112, + 112, 118, 129, 133, 133, 135, 101, 113, 113, 119, 130, 134, 134, 136, + 42, 66, 96, 109, 66, 99, 109, 129, 66, 81, 109, 115, 106, 112, 131, + 133, 46, 71, 122, 124, 71, 101, 124, 130, 71, 84, 124, 125, 107, 113, + 132, 134, 66, 106, 109, 131, 81, 112, 115, 133, 99, 112, 129, 133, + 112, 118, 133, 135, 71, 107, 124, 132, 84, 113, 125, 134, 101, 113, + 130, 134, 113, 119, 134, 136, 62, 72, 100, 112, 100, 112, 116, 133, + 72, 87, 112, 117, 128, 132, 145, 146, 72, 87, 128, 132, 112, 117, + 145, 146, 87, 92, 132, 134, 132, 134, 147, 148, 100, 128, 116, 145, + 116, 145, 138, 149, 112, 132, 133, 146, 145, 147, 149, 150, 112, 132, + 145, 147, 133, 146, 149, 150, 117, 134, 146, 148, 146, 148, 150, 151, + 42, 96, 66, 109, 66, 109, 99, 129, 66, 109, 81, 115, 106, 131, 112, + 133, 66, 109, 106, 131, 81, 115, 112, 133, 99, 129, 112, 133, 112, + 133, 118, 135, 46, 122, 71, 124, 71, 124, 101, 130, 71, 124, 84, 125, + 107, 132, 113, 134, 71, 124, 107, 132, 84, 125, 113, 134, 101, 130, + 113, 134, 113, 134, 119, 136, 62, 100, 72, 112, 100, 116, 112, 133, + 72, 112, 87, 117, 128, 145, 132, 146, 100, 116, 128, 145, 116, 138, + 145, 149, 112, 133, 132, 146, 145, 149, 147, 150, 72, 128, 87, 132, + 112, 145, 117, 146, 87, 132, 92, 134, 132, 147, 134, 148, 112, 145, + 132, 147, 133, 149, 146, 150, 117, 146, 134, 148, 146, 150, 148, 151, + 62, 100, 100, 116, 72, 112, 112, 133, 100, 116, 116, 138, 128, 145, + 145, 149, 72, 112, 128, 145, 87, 117, 132, 146, 112, 133, 145, 149, + 132, 146, 147, 150, 72, 128, 112, 145, 87, 132, 117, 146, 112, 145, + 133, 149, 132, 147, 146, 150, 87, 132, 132, 147, 92, 134, 134, 148, + 117, 146, 146, 150, 134, 148, 148, 151, 94, 102, 102, 118, 102, 118, + 118, 135, 102, 118, 118, 139, 141, 147, 147, 150, 102, 118, 141, 147, + 118, 139, 147, 150, 118, 135, 147, 150, 147, 150, 152, 153, 102, 141, + 118, 147, 118, 147, 139, 150, 118, 147, 135, 150, 147, 152, 150, 153, + 118, 147, 147, 152, 135, 150, 150, 153, 139, 150, 150, 153, 150, 153, + 153, 154, 1, 5, 5, 12, 5, 12, 12, 19, 5, 12, 12, 35, 50, 51, 51, 54, + 5, 12, 50, 51, 12, 35, 51, 54, 12, 19, 51, 54, 51, 54, 74, 77, 5, 50, + 12, 51, 12, 51, 35, 54, 12, 51, 19, 54, 51, 74, 54, 77, 12, 51, 51, + 74, 19, 54, 54, 77, 35, 54, 54, 77, 54, 77, 77, 89, 2, 6, 6, 13, 12, + 15, 15, 20, 6, 13, 13, 36, 51, 52, 52, 55, 12, 15, 51, 52, 38, 40, + 56, 58, 15, 20, 52, 55, 56, 58, 75, 78, 12, 51, 15, 52, 38, 56, 40, + 58, 15, 52, 20, 55, 56, 75, 58, 78, 38, 56, 56, 75, 44, 68, 68, 82, + 40, 58, 58, 78, 68, 82, 82, 90, 2, 6, 12, 15, 6, 13, 15, 20, 12, 15, + 38, 40, 51, 52, 56, 58, 6, 13, 51, 52, 13, 36, 52, 55, 15, 20, 56, + 58, 52, 55, 75, 78, 12, 51, 38, 56, 15, 52, 40, 58, 38, 56, 44, 68, + 56, 75, 68, 82, 15, 52, 56, 75, 20, 55, 58, 78, 40, 58, 68, 82, 58, + 78, 82, 90, 4, 7, 13, 16, 13, 16, 17, 21, 13, 16, 39, 41, 52, 53, 57, + 59, 13, 16, 52, 53, 39, 41, 57, 59, 17, 21, 57, 59, 57, 59, 76, 79, + 35, 54, 40, 58, 40, 58, 48, 60, 40, 58, 45, 69, 65, 80, 70, 83, 40, + 58, 65, 80, 45, 69, 70, 83, 48, 60, 70, 83, 70, 83, 86, 91, 2, 12, 6, + 15, 6, 15, 13, 20, 12, 38, 15, 40, 51, 56, 52, 58, 12, 38, 51, 56, + 15, 40, 52, 58, 38, 44, 56, 68, 56, 68, 75, 82, 6, 51, 13, 52, 13, 52, + 36, 55, 15, 56, 20, 58, 52, 75, 55, 78, 15, 56, 52, 75, 20, 58, 55, + 78, 40, 68, 58, 82, 58, 82, 78, 90, 4, 13, 7, 16, 13, 17, 16, 21, 13, + 39, 16, 41, 52, 57, 53, 59, 35, 40, 54, 58, 40, 48, 58, 60, 40, 45, + 58, 69, 65, 70, 80, 83, 13, 52, 16, 53, 39, 57, 41, 59, 17, 57, 21, + 59, 57, 76, 59, 79, 40, 65, 58, 80, 45, 70, 69, 83, 48, 70, 60, 83, + 70, 86, 83, 91, 4, 13, 13, 17, 7, 16, 16, 21, 35, 40, 40, 48, 54, 58, + 58, 60, 13, 39, 52, 57, 16, 41, 53, 59, 40, 45, 65, 70, 58, 69, 80, + 83, 13, 52, 39, 57, 16, 53, 41, 59, 40, 65, 45, 70, 58, 80, 69, 83, + 17, 57, 57, 76, 21, 59, 59, 79, 48, 70, 70, 86, 60, 83, 83, 91, 11, + 14, 14, 18, 14, 18, 18, 22, 36, 41, 41, 49, 55, 59, 59, 61, 36, 41, + 55, 59, 41, 49, 59, 61, 42, 46, 66, 71, 66, 71, 81, 84, 36, 55, 41, + 59, 41, 59, 49, 61, 42, 66, 46, 71, 66, 81, 71, 84, 42, 66, 66, 81, + 46, 71, 71, 84, 62, 72, 72, 87, 72, 87, 87, 92, 2, 12, 12, 38, 12, + 38, 38, 44, 6, 15, 15, 40, 51, 56, 56, 68, 6, 15, 51, 56, 15, 40, 56, + 68, 13, 20, 52, 58, 52, 58, 75, 82, 6, 51, 15, 56, 15, 56, 40, 68, + 13, 52, 20, 58, 52, 75, 58, 82, 13, 52, 52, 75, 20, 58, 58, 82, 36, + 55, 55, 78, 55, 78, 78, 90, 4, 13, 13, 39, 35, 40, 40, 45, 7, 16, 16, + 41, 54, 58, 58, 69, 13, 17, 52, 57, 40, 48, 65, 70, 16, 21, 53, 59, + 58, 60, 80, 83, 13, 52, 17, 57, 40, 65, 48, 70, 16, 53, 21, 59, 58, + 80, 60, 83, 39, 57, 57, 76, 45, 70, 70, 86, 41, 59, 59, 79, 69, 83, + 83, 91, 4, 13, 35, 40, 13, 39, 40, 45, 13, 17, 40, 48, 52, 57, 65, + 70, 7, 16, 54, 58, 16, 41, 58, 69, 16, 21, 58, 60, 53, 59, 80, 83, + 13, 52, 40, 65, 17, 57, 48, 70, 39, 57, 45, 70, 57, 76, 70, 86, 16, + 53, 58, 80, 21, 59, 60, 83, 41, 59, 69, 83, 59, 79, 83, 91, 11, 14, + 36, 41, 36, 41, 42, 46, 14, 18, 41, 49, 55, 59, 66, 71, 14, 18, 55, + 59, 41, 49, 66, 71, 18, 22, 59, 61, 59, 61, 81, 84, 36, 55, 42, 66, + 42, 66, 62, 72, 41, 59, 46, 71, 66, 81, 72, 87, 41, 59, 66, 81, 46, + 71, 72, 87, 49, 61, 71, 84, 71, 84, 87, 92, 4, 35, 13, 40, 13, 40, + 39, 45, 13, 40, 17, 48, 52, 65, 57, 70, 13, 40, 52, 65, 17, 48, 57, + 70, 39, 45, 57, 70, 57, 70, 76, 86, 7, 54, 16, 58, 16, 58, 41, 69, + 16, 58, 21, 60, 53, 80, 59, 83, 16, 58, 53, 80, 21, 60, 59, 83, 41, + 69, 59, 83, 59, 83, 79, 91, 11, 36, 14, 41, 36, 42, 41, 46, 14, 41, + 18, 49, 55, 66, 59, 71, 36, 42, 55, 66, 42, 62, 66, 72, 41, 46, 59, + 71, 66, 72, 81, 87, 14, 55, 18, 59, 41, 66, 49, 71, 18, 59, 22, 61, + 59, 81, 61, 84, 41, 66, 59, 81, 46, 72, 71, 87, 49, 71, 61, 84, 71, + 87, 84, 92, 11, 36, 36, 42, 14, 41, 41, 46, 36, 42, 42, 62, 55, 66, + 66, 72, 14, 41, 55, 66, 18, 49, 59, 71, 41, 46, 66, 72, 59, 71, 81, + 87, 14, 55, 41, 66, 18, 59, 49, 71, 41, 66, 46, 72, 59, 81, 71, 87, + 18, 59, 59, 81, 22, 61, 61, 84, 49, 71, 71, 87, 61, 84, 84, 92, 34, + 37, 37, 43, 37, 43, 43, 47, 37, 43, 43, 63, 64, 67, 67, 73, 37, 43, + 64, 67, 43, 63, 67, 73, 43, 47, 67, 73, 67, 73, 85, 88, 37, 64, 43, + 67, 43, 67, 63, 73, 43, 67, 47, 73, 67, 85, 73, 88, 43, 67, 67, 85, + 47, 73, 73, 88, 63, 73, 73, 88, 73, 88, 88, 93, 2, 6, 6, 13, 12, 15, + 15, 20, 6, 13, 13, 36, 51, 52, 52, 55, 12, 15, 51, 52, 38, 40, 56, + 58, 15, 20, 52, 55, 56, 58, 75, 78, 12, 51, 15, 52, 38, 56, 40, 58, + 15, 52, 20, 55, 56, 75, 58, 78, 38, 56, 56, 75, 44, 68, 68, 82, 40, + 58, 58, 78, 68, 82, 82, 90, 3, 7, 7, 14, 19, 20, 20, 25, 7, 14, 14, + 37, 54, 55, 55, 64, 19, 20, 54, 55, 44, 45, 68, 69, 20, 25, 55, 64, + 68, 69, 96, 97, 19, 54, 20, 55, 44, 68, 45, 69, 20, 55, 25, 64, 68, + 96, 69, 97, 44, 68, 68, 96, 120, 121, 121, 122, 45, 69, 69, 97, 121, + 122, 122, 126, 6, 8, 15, 17, 15, 17, 26, 27, 15, 17, 40, 42, 56, 57, + 65, 66, 15, 17, 56, 57, 40, 42, 65, 66, 26, 27, 65, 66, 65, 66, 98, + 99, 51, 74, 56, 75, 56, 75, 65, 80, 56, 75, 68, 96, 104, 108, 105, + 109, 56, 75, 104, 108, 68, 96, 105, 109, 65, 80, 105, 109, 105, 109, + 127, 129, 7, 9, 16, 18, 20, 21, 27, 28, 16, 18, 41, 43, 58, 59, 66, + 67, 20, 21, 58, 59, 45, 46, 70, 71, 27, 28, 66, 67, 70, 71, 100, 101, + 54, 77, 58, 78, 68, 82, 70, 83, 58, 78, 69, 97, 105, 109, 106, 110, + 68, 82, 105, 109, 121, 122, 123, 124, 70, 83, 106, 110, 123, 124, + 128, 130, 6, 15, 8, 17, 15, 26, 17, 27, 15, 40, 17, 42, 56, 65, 57, + 66, 51, 56, 74, 75, 56, 65, 75, 80, 56, 68, 75, 96, 104, 105, 108, + 109, 15, 56, 17, 57, 40, 65, 42, 66, 26, 65, 27, 66, 65, 98, 66, 99, + 56, 104, 75, 108, 68, 105, 96, 109, 65, 105, 80, 109, 105, 127, 109, + 129, 7, 16, 9, 18, 20, 27, 21, 28, 16, 41, 18, 43, 58, 66, 59, 67, + 54, 58, 77, 78, 68, 70, 82, 83, 58, 69, 78, 97, 105, 106, 109, 110, + 20, 58, 21, 59, 45, 70, 46, 71, 27, 66, 28, 67, 70, 100, 71, 101, 68, + 105, 82, 109, 121, 123, 122, 124, 70, 106, 83, 110, 123, 128, 124, + 130, 13, 17, 17, 23, 20, 27, 27, 29, 40, 48, 48, 62, 68, 70, 70, 72, + 52, 57, 75, 76, 58, 66, 80, 81, 65, 70, 98, 100, 105, 106, 111, 112, + 52, 75, 57, 76, 58, 80, 66, 81, 65, 98, 70, 100, 105, 111, 106, 112, + 75, 108, 108, 114, 82, 109, 109, 115, 98, 111, 111, 116, 127, 131, + 131, 133, 14, 18, 18, 24, 25, 28, 28, 30, 41, 49, 49, 63, 69, 71, 71, + 73, 55, 59, 78, 79, 69, 71, 83, 84, 66, 71, 99, 101, 106, 107, 112, + 113, 55, 78, 59, 79, 69, 83, 71, 84, 66, 99, 71, 101, 106, 112, 107, + 113, 96, 109, 109, 115, 122, 124, 124, 125, 100, 112, 112, 117, 128, + 132, 132, 134, 6, 15, 15, 40, 51, 56, 56, 68, 8, 17, 17, 42, 74, 75, + 75, 96, 15, 26, 56, 65, 56, 65, 104, 105, 17, 27, 57, 66, 75, 80, + 108, 109, 15, 56, 26, 65, 56, 104, 65, 105, 17, 57, 27, 66, 75, 108, + 80, 109, 40, 65, 65, 98, 68, 105, 105, 127, 42, 66, 66, 99, 96, 109, + 109, 129, 7, 16, 16, 41, 54, 58, 58, 69, 9, 18, 18, 43, 77, 78, 78, + 97, 20, 27, 58, 66, 68, 70, 105, 106, 21, 28, 59, 67, 82, 83, 109, + 110, 20, 58, 27, 66, 68, 105, 70, 106, 21, 59, 28, 67, 82, 109, 83, + 110, 45, 70, 70, 100, 121, 123, 123, 128, 46, 71, 71, 101, 122, 124, + 124, 130, 13, 17, 40, 48, 52, 57, 65, 70, 17, 23, 48, 62, 75, 76, 98, + 100, 20, 27, 68, 70, 58, 66, 105, 106, 27, 29, 70, 72, 80, 81, 111, + 112, 52, 75, 65, 98, 75, 108, 98, 111, 57, 76, 70, 100, 108, 114, + 111, 116, 58, 80, 105, 111, 82, 109, 127, 131, 66, 81, 106, 112, 109, + 115, 131, 133, 14, 18, 41, 49, 55, 59, 66, 71, 18, 24, 49, 63, 78, + 79, 99, 101, 25, 28, 69, 71, 69, 71, 106, 107, 28, 30, 71, 73, 83, + 84, 112, 113, 55, 78, 66, 99, 96, 109, 100, 112, 59, 79, 71, 101, + 109, 115, 112, 117, 69, 83, 106, 112, 122, 124, 128, 132, 71, 84, + 107, 113, 124, 125, 132, 134, 13, 40, 17, 48, 52, 65, 57, 70, 17, 48, + 23, 62, 75, 98, 76, 100, 52, 65, 75, 98, 75, 98, 108, 111, 57, 70, + 76, 100, 108, 111, 114, 116, 20, 68, 27, 70, 58, 105, 66, 106, 27, + 70, 29, 72, 80, 111, 81, 112, 58, 105, 80, 111, 82, 127, 109, 131, + 66, 106, 81, 112, 109, 131, 115, 133, 14, 41, 18, 49, 55, 66, 59, 71, + 18, 49, 24, 63, 78, 99, 79, 101, 55, 66, 78, 99, 96, 100, 109, 112, + 59, 71, 79, 101, 109, 112, 115, 117, 25, 69, 28, 71, 69, 106, 71, + 107, 28, 71, 30, 73, 83, 112, 84, 113, 69, 106, 83, 112, 122, 128, + 124, 132, 71, 107, 84, 113, 124, 132, 125, 134, 36, 42, 42, 62, 55, + 66, 66, 72, 42, 62, 62, 94, 96, 100, 100, 102, 55, 66, 96, 100, 78, + 99, 109, 112, 66, 72, 100, 102, 109, 112, 116, 118, 55, 96, 66, 100, + 78, 109, 99, 112, 66, 100, 72, 102, 109, 116, 112, 118, 78, 109, 109, + 116, 90, 129, 129, 133, 99, 112, 112, 118, 129, 133, 133, 135, 37, + 43, 43, 63, 64, 67, 67, 73, 43, 63, 63, 95, 97, 101, 101, 103, 64, + 67, 97, 101, 97, 101, 110, 113, 67, 73, 101, 103, 110, 113, 117, 119, + 64, 97, 67, 101, 97, 110, 101, 113, 67, 101, 73, 103, 110, 117, 113, + 119, 97, 110, 110, 117, 126, 130, 130, 134, 101, 113, 113, 119, 130, + 134, 134, 136, 2, 6, 12, 15, 6, 13, 15, 20, 12, 15, 38, 40, 51, 52, + 56, 58, 6, 13, 51, 52, 13, 36, 52, 55, 15, 20, 56, 58, 52, 55, 75, + 78, 12, 51, 38, 56, 15, 52, 40, 58, 38, 56, 44, 68, 56, 75, 68, 82, + 15, 52, 56, 75, 20, 55, 58, 78, 40, 58, 68, 82, 58, 78, 82, 90, 6, 8, + 15, 17, 15, 17, 26, 27, 15, 17, 40, 42, 56, 57, 65, 66, 15, 17, 56, + 57, 40, 42, 65, 66, 26, 27, 65, 66, 65, 66, 98, 99, 51, 74, 56, 75, + 56, 75, 65, 80, 56, 75, 68, 96, 104, 108, 105, 109, 56, 75, 104, 108, + 68, 96, 105, 109, 65, 80, 105, 109, 105, 109, 127, 129, 3, 7, 19, 20, + 7, 14, 20, 25, 19, 20, 44, 45, 54, 55, 68, 69, 7, 14, 54, 55, 14, 37, + 55, 64, 20, 25, 68, 69, 55, 64, 96, 97, 19, 54, 44, 68, 20, 55, 45, + 69, 44, 68, 120, 121, 68, 96, 121, 122, 20, 55, 68, 96, 25, 64, 69, + 97, 45, 69, 121, 122, 69, 97, 122, 126, 7, 9, 20, 21, 16, 18, 27, 28, + 20, 21, 45, 46, 58, 59, 70, 71, 16, 18, 58, 59, 41, 43, 66, 67, 27, + 28, 70, 71, 66, 67, 100, 101, 54, 77, 68, 82, 58, 78, 70, 83, 68, 82, + 121, 122, 105, 109, 123, 124, 58, 78, 105, 109, 69, 97, 106, 110, 70, + 83, 123, 124, 106, 110, 128, 130, 6, 15, 15, 26, 8, 17, 17, 27, 51, + 56, 56, 65, 74, 75, 75, 80, 15, 40, 56, 65, 17, 42, 57, 66, 56, 68, + 104, 105, 75, 96, 108, 109, 15, 56, 40, 65, 17, 57, 42, 66, 56, 104, + 68, 105, 75, 108, 96, 109, 26, 65, 65, 98, 27, 66, 66, 99, 65, 105, + 105, 127, 80, 109, 109, 129, 13, 17, 20, 27, 17, 23, 27, 29, 52, 57, + 58, 66, 75, 76, 80, 81, 40, 48, 68, 70, 48, 62, 70, 72, 65, 70, 105, + 106, 98, 100, 111, 112, 52, 75, 58, 80, 57, 76, 66, 81, 75, 108, 82, + 109, 108, 114, 109, 115, 65, 98, 105, 111, 70, 100, 106, 112, 98, + 111, 127, 131, 111, 116, 131, 133, 7, 16, 20, 27, 9, 18, 21, 28, 54, + 58, 68, 70, 77, 78, 82, 83, 16, 41, 58, 66, 18, 43, 59, 67, 58, 69, + 105, 106, 78, 97, 109, 110, 20, 58, 45, 70, 21, 59, 46, 71, 68, 105, + 121, 123, 82, 109, 122, 124, 27, 66, 70, 100, 28, 67, 71, 101, 70, + 106, 123, 128, 83, 110, 124, 130, 14, 18, 25, 28, 18, 24, 28, 30, 55, + 59, 69, 71, 78, 79, 83, 84, 41, 49, 69, 71, 49, 63, 71, 73, 66, 71, + 106, 107, 99, 101, 112, 113, 55, 78, 69, 83, 59, 79, 71, 84, 96, 109, + 122, 124, 109, 115, 124, 125, 66, 99, 106, 112, 71, 101, 107, 113, + 100, 112, 128, 132, 112, 117, 132, 134, 6, 15, 51, 56, 15, 40, 56, + 68, 15, 26, 56, 65, 56, 65, 104, 105, 8, 17, 74, 75, 17, 42, 75, 96, + 17, 27, 75, 80, 57, 66, 108, 109, 15, 56, 56, 104, 26, 65, 65, 105, + 40, 65, 68, 105, 65, 98, 105, 127, 17, 57, 75, 108, 27, 66, 80, 109, + 42, 66, 96, 109, 66, 99, 109, 129, 13, 17, 52, 57, 40, 48, 65, 70, + 20, 27, 58, 66, 68, 70, 105, 106, 17, 23, 75, 76, 48, 62, 98, 100, + 27, 29, 80, 81, 70, 72, 111, 112, 52, 75, 75, 108, 65, 98, 98, 111, + 58, 80, 82, 109, 105, 111, 127, 131, 57, 76, 108, 114, 70, 100, 111, + 116, 66, 81, 109, 115, 106, 112, 131, 133, 7, 16, 54, 58, 16, 41, 58, + 69, 20, 27, 68, 70, 58, 66, 105, 106, 9, 18, 77, 78, 18, 43, 78, 97, + 21, 28, 82, 83, 59, 67, 109, 110, 20, 58, 68, 105, 27, 66, 70, 106, + 45, 70, 121, 123, 70, 100, 123, 128, 21, 59, 82, 109, 28, 67, 83, + 110, 46, 71, 122, 124, 71, 101, 124, 130, 14, 18, 55, 59, 41, 49, 66, + 71, 25, 28, 69, 71, 69, 71, 106, 107, 18, 24, 78, 79, 49, 63, 99, + 101, 28, 30, 83, 84, 71, 73, 112, 113, 55, 78, 96, 109, 66, 99, 100, + 112, 69, 83, 122, 124, 106, 112, 128, 132, 59, 79, 109, 115, 71, 101, + 112, 117, 71, 84, 124, 125, 107, 113, 132, 134, 13, 40, 52, 65, 17, + 48, 57, 70, 52, 65, 75, 98, 75, 98, 108, 111, 17, 48, 75, 98, 23, 62, + 76, 100, 57, 70, 108, 111, 76, 100, 114, 116, 20, 68, 58, 105, 27, + 70, 66, 106, 58, 105, 82, 127, 80, 111, 109, 131, 27, 70, 80, 111, + 29, 72, 81, 112, 66, 106, 109, 131, 81, 112, 115, 133, 36, 42, 55, + 66, 42, 62, 66, 72, 55, 66, 78, 99, 96, 100, 109, 112, 42, 62, 96, + 100, 62, 94, 100, 102, 66, 72, 109, 112, 100, 102, 116, 118, 55, 96, + 78, 109, 66, 100, 99, 112, 78, 109, 90, 129, 109, 116, 129, 133, 66, + 100, 109, 116, 72, 102, 112, 118, 99, 112, 129, 133, 112, 118, 133, + 135, 14, 41, 55, 66, 18, 49, 59, 71, 55, 66, 96, 100, 78, 99, 109, + 112, 18, 49, 78, 99, 24, 63, 79, 101, 59, 71, 109, 112, 79, 101, 115, + 117, 25, 69, 69, 106, 28, 71, 71, 107, 69, 106, 122, 128, 83, 112, + 124, 132, 28, 71, 83, 112, 30, 73, 84, 113, 71, 107, 124, 132, 84, + 113, 125, 134, 37, 43, 64, 67, 43, 63, 67, 73, 64, 67, 97, 101, 97, + 101, 110, 113, 43, 63, 97, 101, 63, 95, 101, 103, 67, 73, 110, 113, + 101, 103, 117, 119, 64, 97, 97, 110, 67, 101, 101, 113, 97, 110, 126, + 130, 110, 117, 130, 134, 67, 101, 110, 117, 73, 103, 113, 119, 101, + 113, 130, 134, 113, 119, 134, 136, 4, 7, 13, 16, 13, 16, 17, 21, 13, + 16, 39, 41, 52, 53, 57, 59, 13, 16, 52, 53, 39, 41, 57, 59, 17, 21, + 57, 59, 57, 59, 76, 79, 35, 54, 40, 58, 40, 58, 48, 60, 40, 58, 45, + 69, 65, 80, 70, 83, 40, 58, 65, 80, 45, 69, 70, 83, 48, 60, 70, 83, + 70, 83, 86, 91, 7, 9, 16, 18, 20, 21, 27, 28, 16, 18, 41, 43, 58, 59, + 66, 67, 20, 21, 58, 59, 45, 46, 70, 71, 27, 28, 66, 67, 70, 71, 100, + 101, 54, 77, 58, 78, 68, 82, 70, 83, 58, 78, 69, 97, 105, 109, 106, + 110, 68, 82, 105, 109, 121, 122, 123, 124, 70, 83, 106, 110, 123, + 124, 128, 130, 7, 9, 20, 21, 16, 18, 27, 28, 20, 21, 45, 46, 58, 59, + 70, 71, 16, 18, 58, 59, 41, 43, 66, 67, 27, 28, 70, 71, 66, 67, 100, + 101, 54, 77, 68, 82, 58, 78, 70, 83, 68, 82, 121, 122, 105, 109, 123, + 124, 58, 78, 105, 109, 69, 97, 106, 110, 70, 83, 123, 124, 106, 110, + 128, 130, 9, 10, 21, 22, 21, 22, 29, 30, 21, 22, 46, 47, 60, 61, 72, + 73, 21, 22, 60, 61, 46, 47, 72, 73, 29, 30, 72, 73, 72, 73, 102, 103, + 77, 89, 82, 90, 82, 90, 86, 91, 82, 90, 122, 126, 127, 129, 128, 130, + 82, 90, 127, 129, 122, 126, 128, 130, 86, 91, 128, 130, 128, 130, + 141, 142, 13, 20, 17, 27, 17, 27, 23, 29, 52, 58, 57, 66, 75, 80, 76, + 81, 52, 58, 75, 80, 57, 66, 76, 81, 75, 82, 108, 109, 108, 109, 114, + 115, 40, 68, 48, 70, 48, 70, 62, 72, 65, 105, 70, 106, 98, 111, 100, + 112, 65, 105, 98, 111, 70, 106, 100, 112, 98, 127, 111, 131, 111, + 131, 116, 133, 16, 21, 21, 28, 27, 29, 29, 31, 53, 59, 59, 67, 80, + 81, 81, 85, 58, 60, 82, 83, 70, 72, 86, 87, 80, 83, 109, 110, 111, + 112, 116, 117, 58, 82, 60, 83, 70, 86, 72, 87, 80, 109, 83, 110, 111, + 116, 112, 117, 105, 127, 127, 131, 123, 128, 128, 132, 111, 131, 131, + 143, 144, 145, 145, 146, 16, 21, 27, 29, 21, 28, 29, 31, 58, 60, 70, + 72, 82, 83, 86, 87, 53, 59, 80, 81, 59, 67, 81, 85, 80, 83, 111, 112, + 109, 110, 116, 117, 58, 82, 70, 86, 60, 83, 72, 87, 105, 127, 123, + 128, 127, 131, 128, 132, 80, 109, 111, 116, 83, 110, 112, 117, 111, + 131, 144, 145, 131, 143, 145, 146, 18, 22, 28, 30, 28, 30, 31, 32, + 59, 61, 71, 73, 83, 84, 87, 88, 59, 61, 83, 84, 71, 73, 87, 88, 81, + 84, 112, 113, 112, 113, 118, 119, 78, 90, 83, 91, 83, 91, 87, 92, + 109, 129, 124, 130, 131, 133, 132, 134, 109, 129, 131, 133, 124, 130, + 132, 134, 116, 133, 145, 146, 145, 146, 147, 148, 13, 20, 52, 58, 52, + 58, 75, 82, 17, 27, 57, 66, 75, 80, 108, 109, 17, 27, 75, 80, 57, 66, + 108, 109, 23, 29, 76, 81, 76, 81, 114, 115, 40, 68, 65, 105, 65, 105, + 98, 127, 48, 70, 70, 106, 98, 111, 111, 131, 48, 70, 98, 111, 70, + 106, 111, 131, 62, 72, 100, 112, 100, 112, 116, 133, 16, 21, 53, 59, + 58, 60, 80, 83, 21, 28, 59, 67, 82, 83, 109, 110, 27, 29, 80, 81, 70, + 72, 111, 112, 29, 31, 81, 85, 86, 87, 116, 117, 58, 82, 80, 109, 105, + 127, 111, 131, 60, 83, 83, 110, 127, 131, 131, 143, 70, 86, 111, 116, + 123, 128, 144, 145, 72, 87, 112, 117, 128, 132, 145, 146, 16, 21, 58, + 60, 53, 59, 80, 83, 27, 29, 70, 72, 80, 81, 111, 112, 21, 28, 82, 83, + 59, 67, 109, 110, 29, 31, 86, 87, 81, 85, 116, 117, 58, 82, 105, 127, + 80, 109, 111, 131, 70, 86, 123, 128, 111, 116, 144, 145, 60, 83, 127, + 131, 83, 110, 131, 143, 72, 87, 128, 132, 112, 117, 145, 146, 18, 22, + 59, 61, 59, 61, 81, 84, 28, 30, 71, 73, 83, 84, 112, 113, 28, 30, 83, + 84, 71, 73, 112, 113, 31, 32, 87, 88, 87, 88, 118, 119, 78, 90, 109, + 129, 109, 129, 116, 133, 83, 91, 124, 130, 131, 133, 145, 146, 83, + 91, 131, 133, 124, 130, 145, 146, 87, 92, 132, 134, 132, 134, 147, + 148, 39, 45, 57, 70, 57, 70, 76, 86, 57, 70, 76, 100, 108, 111, 114, + 116, 57, 70, 108, 111, 76, 100, 114, 116, 76, 86, 114, 116, 114, 116, + 137, 138, 45, 121, 70, 123, 70, 123, 100, 128, 70, 123, 86, 128, 111, + 144, 116, 145, 70, 123, 111, 144, 86, 128, 116, 145, 100, 128, 116, + 145, 116, 145, 138, 149, 41, 46, 59, 71, 66, 72, 81, 87, 59, 71, 79, + 101, 109, 112, 115, 117, 66, 72, 109, 112, 100, 102, 116, 118, 81, + 87, 115, 117, 116, 118, 138, 139, 69, 122, 83, 124, 106, 128, 112, + 132, 83, 124, 91, 130, 131, 145, 133, 146, 106, 128, 131, 145, 128, + 141, 145, 147, 112, 132, 133, 146, 145, 147, 149, 150, 41, 46, 66, + 72, 59, 71, 81, 87, 66, 72, 100, 102, 109, 112, 116, 118, 59, 71, + 109, 112, 79, 101, 115, 117, 81, 87, 116, 118, 115, 117, 138, 139, + 69, 122, 106, 128, 83, 124, 112, 132, 106, 128, 128, 141, 131, 145, + 145, 147, 83, 124, 131, 145, 91, 130, 133, 146, 112, 132, 145, 147, + 133, 146, 149, 150, 43, 47, 67, 73, 67, 73, 85, 88, 67, 73, 101, 103, + 110, 113, 117, 119, 67, 73, 110, 113, 101, 103, 117, 119, 85, 88, + 117, 119, 117, 119, 139, 140, 97, 126, 110, 130, 110, 130, 117, 134, + 110, 130, 130, 142, 143, 146, 146, 148, 110, 130, 143, 146, 130, 142, + 146, 148, 117, 134, 146, 148, 146, 148, 150, 151, 2, 12, 6, 15, 6, + 15, 13, 20, 12, 38, 15, 40, 51, 56, 52, 58, 12, 38, 51, 56, 15, 40, + 52, 58, 38, 44, 56, 68, 56, 68, 75, 82, 6, 51, 13, 52, 13, 52, 36, + 55, 15, 56, 20, 58, 52, 75, 55, 78, 15, 56, 52, 75, 20, 58, 55, 78, + 40, 68, 58, 82, 58, 82, 78, 90, 6, 15, 8, 17, 15, 26, 17, 27, 15, 40, + 17, 42, 56, 65, 57, 66, 51, 56, 74, 75, 56, 65, 75, 80, 56, 68, 75, + 96, 104, 105, 108, 109, 15, 56, 17, 57, 40, 65, 42, 66, 26, 65, 27, + 66, 65, 98, 66, 99, 56, 104, 75, 108, 68, 105, 96, 109, 65, 105, 80, + 109, 105, 127, 109, 129, 6, 15, 15, 26, 8, 17, 17, 27, 51, 56, 56, + 65, 74, 75, 75, 80, 15, 40, 56, 65, 17, 42, 57, 66, 56, 68, 104, 105, + 75, 96, 108, 109, 15, 56, 40, 65, 17, 57, 42, 66, 56, 104, 68, 105, + 75, 108, 96, 109, 26, 65, 65, 98, 27, 66, 66, 99, 65, 105, 105, 127, + 80, 109, 109, 129, 13, 20, 17, 27, 17, 27, 23, 29, 52, 58, 57, 66, + 75, 80, 76, 81, 52, 58, 75, 80, 57, 66, 76, 81, 75, 82, 108, 109, + 108, 109, 114, 115, 40, 68, 48, 70, 48, 70, 62, 72, 65, 105, 70, 106, + 98, 111, 100, 112, 65, 105, 98, 111, 70, 106, 100, 112, 98, 127, 111, + 131, 111, 131, 116, 133, 3, 19, 7, 20, 7, 20, 14, 25, 19, 44, 20, 45, + 54, 68, 55, 69, 19, 44, 54, 68, 20, 45, 55, 69, 44, 120, 68, 121, 68, + 121, 96, 122, 7, 54, 14, 55, 14, 55, 37, 64, 20, 68, 25, 69, 55, 96, + 64, 97, 20, 68, 55, 96, 25, 69, 64, 97, 45, 121, 69, 122, 69, 122, + 97, 126, 7, 20, 9, 21, 16, 27, 18, 28, 20, 45, 21, 46, 58, 70, 59, + 71, 54, 68, 77, 82, 58, 70, 78, 83, 68, 121, 82, 122, 105, 123, 109, + 124, 16, 58, 18, 59, 41, 66, 43, 67, 27, 70, 28, 71, 66, 100, 67, + 101, 58, 105, 78, 109, 69, 106, 97, 110, 70, 123, 83, 124, 106, 128, + 110, 130, 7, 20, 16, 27, 9, 21, 18, 28, 54, 68, 58, 70, 77, 82, 78, + 83, 20, 45, 58, 70, 21, 46, 59, 71, 68, 121, 105, 123, 82, 122, 109, + 124, 16, 58, 41, 66, 18, 59, 43, 67, 58, 105, 69, 106, 78, 109, 97, + 110, 27, 70, 66, 100, 28, 71, 67, 101, 70, 123, 106, 128, 83, 124, + 110, 130, 14, 25, 18, 28, 18, 28, 24, 30, 55, 69, 59, 71, 78, 83, 79, + 84, 55, 69, 78, 83, 59, 71, 79, 84, 96, 122, 109, 124, 109, 124, 115, + 125, 41, 69, 49, 71, 49, 71, 63, 73, 66, 106, 71, 107, 99, 112, 101, + 113, 66, 106, 99, 112, 71, 107, 101, 113, 100, 128, 112, 132, 112, + 132, 117, 134, 6, 51, 15, 56, 15, 56, 40, 68, 15, 56, 26, 65, 56, + 104, 65, 105, 15, 56, 56, 104, 26, 65, 65, 105, 40, 68, 65, 105, 65, + 105, 98, 127, 8, 74, 17, 75, 17, 75, 42, 96, 17, 75, 27, 80, 57, 108, + 66, 109, 17, 75, 57, 108, 27, 80, 66, 109, 42, 96, 66, 109, 66, 109, + 99, 129, 13, 52, 17, 57, 40, 65, 48, 70, 20, 58, 27, 66, 68, 105, 70, + 106, 52, 75, 75, 108, 65, 98, 98, 111, 58, 82, 80, 109, 105, 127, + 111, 131, 17, 75, 23, 76, 48, 98, 62, 100, 27, 80, 29, 81, 70, 111, + 72, 112, 57, 108, 76, 114, 70, 111, 100, 116, 66, 109, 81, 115, 106, + 131, 112, 133, 13, 52, 40, 65, 17, 57, 48, 70, 52, 75, 65, 98, 75, + 108, 98, 111, 20, 58, 68, 105, 27, 66, 70, 106, 58, 82, 105, 127, 80, + 109, 111, 131, 17, 75, 48, 98, 23, 76, 62, 100, 57, 108, 70, 111, 76, + 114, 100, 116, 27, 80, 70, 111, 29, 81, 72, 112, 66, 109, 106, 131, + 81, 115, 112, 133, 36, 55, 42, 66, 42, 66, 62, 72, 55, 78, 66, 99, + 96, 109, 100, 112, 55, 78, 96, 109, 66, 99, 100, 112, 78, 90, 109, + 129, 109, 129, 116, 133, 42, 96, 62, 100, 62, 100, 94, 102, 66, 109, + 72, 112, 100, 116, 102, 118, 66, 109, 100, 116, 72, 112, 102, 118, + 99, 129, 112, 133, 112, 133, 118, 135, 7, 54, 16, 58, 16, 58, 41, 69, + 20, 68, 27, 70, 58, 105, 66, 106, 20, 68, 58, 105, 27, 70, 66, 106, + 45, 121, 70, 123, 70, 123, 100, 128, 9, 77, 18, 78, 18, 78, 43, 97, + 21, 82, 28, 83, 59, 109, 67, 110, 21, 82, 59, 109, 28, 83, 67, 110, + 46, 122, 71, 124, 71, 124, 101, 130, 14, 55, 18, 59, 41, 66, 49, 71, + 25, 69, 28, 71, 69, 106, 71, 107, 55, 96, 78, 109, 66, 100, 99, 112, + 69, 122, 83, 124, 106, 128, 112, 132, 18, 78, 24, 79, 49, 99, 63, + 101, 28, 83, 30, 84, 71, 112, 73, 113, 59, 109, 79, 115, 71, 112, + 101, 117, 71, 124, 84, 125, 107, 132, 113, 134, 14, 55, 41, 66, 18, + 59, 49, 71, 55, 96, 66, 100, 78, 109, 99, 112, 25, 69, 69, 106, 28, + 71, 71, 107, 69, 122, 106, 128, 83, 124, 112, 132, 18, 78, 49, 99, + 24, 79, 63, 101, 59, 109, 71, 112, 79, 115, 101, 117, 28, 83, 71, + 112, 30, 84, 73, 113, 71, 124, 107, 132, 84, 125, 113, 134, 37, 64, + 43, 67, 43, 67, 63, 73, 64, 97, 67, 101, 97, 110, 101, 113, 64, 97, + 97, 110, 67, 101, 101, 113, 97, 126, 110, 130, 110, 130, 117, 134, + 43, 97, 63, 101, 63, 101, 95, 103, 67, 110, 73, 113, 101, 117, 103, + 119, 67, 110, 101, 117, 73, 113, 103, 119, 101, 130, 113, 134, 113, + 134, 119, 136, 4, 13, 7, 16, 13, 17, 16, 21, 13, 39, 16, 41, 52, 57, + 53, 59, 35, 40, 54, 58, 40, 48, 58, 60, 40, 45, 58, 69, 65, 70, 80, + 83, 13, 52, 16, 53, 39, 57, 41, 59, 17, 57, 21, 59, 57, 76, 59, 79, + 40, 65, 58, 80, 45, 70, 69, 83, 48, 70, 60, 83, 70, 86, 83, 91, 7, + 16, 9, 18, 20, 27, 21, 28, 16, 41, 18, 43, 58, 66, 59, 67, 54, 58, + 77, 78, 68, 70, 82, 83, 58, 69, 78, 97, 105, 106, 109, 110, 20, 58, + 21, 59, 45, 70, 46, 71, 27, 66, 28, 67, 70, 100, 71, 101, 68, 105, + 82, 109, 121, 123, 122, 124, 70, 106, 83, 110, 123, 128, 124, 130, + 13, 17, 20, 27, 17, 23, 27, 29, 52, 57, 58, 66, 75, 76, 80, 81, 40, + 48, 68, 70, 48, 62, 70, 72, 65, 70, 105, 106, 98, 100, 111, 112, 52, + 75, 58, 80, 57, 76, 66, 81, 75, 108, 82, 109, 108, 114, 109, 115, 65, + 98, 105, 111, 70, 100, 106, 112, 98, 111, 127, 131, 111, 116, 131, + 133, 16, 21, 21, 28, 27, 29, 29, 31, 53, 59, 59, 67, 80, 81, 81, 85, + 58, 60, 82, 83, 70, 72, 86, 87, 80, 83, 109, 110, 111, 112, 116, 117, + 58, 82, 60, 83, 70, 86, 72, 87, 80, 109, 83, 110, 111, 116, 112, 117, + 105, 127, 127, 131, 123, 128, 128, 132, 111, 131, 131, 143, 144, 145, + 145, 146, 7, 20, 9, 21, 16, 27, 18, 28, 20, 45, 21, 46, 58, 70, 59, + 71, 54, 68, 77, 82, 58, 70, 78, 83, 68, 121, 82, 122, 105, 123, 109, + 124, 16, 58, 18, 59, 41, 66, 43, 67, 27, 70, 28, 71, 66, 100, 67, + 101, 58, 105, 78, 109, 69, 106, 97, 110, 70, 123, 83, 124, 106, 128, + 110, 130, 9, 21, 10, 22, 21, 29, 22, 30, 21, 46, 22, 47, 60, 72, 61, + 73, 77, 82, 89, 90, 82, 86, 90, 91, 82, 122, 90, 126, 127, 128, 129, + 130, 21, 60, 22, 61, 46, 72, 47, 73, 29, 72, 30, 73, 72, 102, 73, + 103, 82, 127, 90, 129, 122, 128, 126, 130, 86, 128, 91, 130, 128, + 141, 130, 142, 16, 27, 21, 29, 21, 29, 28, 31, 58, 70, 60, 72, 82, + 86, 83, 87, 58, 70, 82, 86, 60, 72, 83, 87, 105, 123, 127, 128, 127, + 128, 131, 132, 53, 80, 59, 81, 59, 81, 67, 85, 80, 111, 83, 112, 109, + 116, 110, 117, 80, 111, 109, 116, 83, 112, 110, 117, 111, 144, 131, + 145, 131, 145, 143, 146, 18, 28, 22, 30, 28, 31, 30, 32, 59, 71, 61, + 73, 83, 87, 84, 88, 78, 83, 90, 91, 83, 87, 91, 92, 109, 124, 129, + 130, 131, 132, 133, 134, 59, 83, 61, 84, 71, 87, 73, 88, 81, 112, 84, + 113, 112, 118, 113, 119, 109, 131, 129, 133, 124, 132, 130, 134, 116, + 145, 133, 146, 145, 147, 146, 148, 13, 52, 20, 58, 52, 75, 58, 82, + 17, 57, 27, 66, 75, 108, 80, 109, 40, 65, 68, 105, 65, 98, 105, 127, + 48, 70, 70, 106, 98, 111, 111, 131, 17, 75, 27, 80, 57, 108, 66, 109, + 23, 76, 29, 81, 76, 114, 81, 115, 48, 98, 70, 111, 70, 111, 106, 131, + 62, 100, 72, 112, 100, 116, 112, 133, 16, 53, 21, 59, 58, 80, 60, 83, + 21, 59, 28, 67, 82, 109, 83, 110, 58, 80, 82, 109, 105, 111, 127, + 131, 60, 83, 83, 110, 127, 131, 131, 143, 27, 80, 29, 81, 70, 111, + 72, 112, 29, 81, 31, 85, 86, 116, 87, 117, 70, 111, 86, 116, 123, + 144, 128, 145, 72, 112, 87, 117, 128, 145, 132, 146, 39, 57, 45, 70, + 57, 76, 70, 86, 57, 76, 70, 100, 108, 114, 111, 116, 45, 70, 121, + 123, 70, 100, 123, 128, 70, 86, 123, 128, 111, 116, 144, 145, 57, + 108, 70, 111, 76, 114, 100, 116, 76, 114, 86, 116, 114, 137, 116, + 138, 70, 111, 123, 144, 86, 116, 128, 145, 100, 116, 128, 145, 116, + 138, 145, 149, 41, 59, 46, 71, 66, 81, 72, 87, 59, 79, 71, 101, 109, + 115, 112, 117, 69, 83, 122, 124, 106, 112, 128, 132, 83, 91, 124, + 130, 131, 133, 145, 146, 66, 109, 72, 112, 100, 116, 102, 118, 81, + 115, 87, 117, 116, 138, 118, 139, 106, 131, 128, 145, 128, 145, 141, + 147, 112, 133, 132, 146, 145, 149, 147, 150, 16, 58, 21, 60, 53, 80, + 59, 83, 27, 70, 29, 72, 80, 111, 81, 112, 58, 105, 82, 127, 80, 111, + 109, 131, 70, 123, 86, 128, 111, 144, 116, 145, 21, 82, 28, 83, 59, + 109, 67, 110, 29, 86, 31, 87, 81, 116, 85, 117, 60, 127, 83, 131, 83, + 131, 110, 143, 72, 128, 87, 132, 112, 145, 117, 146, 18, 59, 22, 61, + 59, 81, 61, 84, 28, 71, 30, 73, 83, 112, 84, 113, 78, 109, 90, 129, + 109, 116, 129, 133, 83, 124, 91, 130, 131, 145, 133, 146, 28, 83, 30, + 84, 71, 112, 73, 113, 31, 87, 32, 88, 87, 118, 88, 119, 83, 131, 91, + 133, 124, 145, 130, 146, 87, 132, 92, 134, 132, 147, 134, 148, 41, + 66, 46, 72, 59, 81, 71, 87, 66, 100, 72, 102, 109, 116, 112, 118, 69, + 106, 122, 128, 83, 112, 124, 132, 106, 128, 128, 141, 131, 145, 145, + 147, 59, 109, 71, 112, 79, 115, 101, 117, 81, 116, 87, 118, 115, 138, + 117, 139, 83, 131, 124, 145, 91, 133, 130, 146, 112, 145, 132, 147, + 133, 149, 146, 150, 43, 67, 47, 73, 67, 85, 73, 88, 67, 101, 73, 103, + 110, 117, 113, 119, 97, 110, 126, 130, 110, 117, 130, 134, 110, 130, + 130, 142, 143, 146, 146, 148, 67, 110, 73, 113, 101, 117, 103, 119, + 85, 117, 88, 119, 117, 139, 119, 140, 110, 143, 130, 146, 130, 146, + 142, 148, 117, 146, 134, 148, 146, 150, 148, 151, 4, 13, 13, 17, 7, + 16, 16, 21, 35, 40, 40, 48, 54, 58, 58, 60, 13, 39, 52, 57, 16, 41, + 53, 59, 40, 45, 65, 70, 58, 69, 80, 83, 13, 52, 39, 57, 16, 53, 41, + 59, 40, 65, 45, 70, 58, 80, 69, 83, 17, 57, 57, 76, 21, 59, 59, 79, + 48, 70, 70, 86, 60, 83, 83, 91, 13, 17, 17, 23, 20, 27, 27, 29, 40, + 48, 48, 62, 68, 70, 70, 72, 52, 57, 75, 76, 58, 66, 80, 81, 65, 70, + 98, 100, 105, 106, 111, 112, 52, 75, 57, 76, 58, 80, 66, 81, 65, 98, + 70, 100, 105, 111, 106, 112, 75, 108, 108, 114, 82, 109, 109, 115, + 98, 111, 111, 116, 127, 131, 131, 133, 7, 16, 20, 27, 9, 18, 21, 28, + 54, 58, 68, 70, 77, 78, 82, 83, 16, 41, 58, 66, 18, 43, 59, 67, 58, + 69, 105, 106, 78, 97, 109, 110, 20, 58, 45, 70, 21, 59, 46, 71, 68, + 105, 121, 123, 82, 109, 122, 124, 27, 66, 70, 100, 28, 67, 71, 101, + 70, 106, 123, 128, 83, 110, 124, 130, 16, 21, 27, 29, 21, 28, 29, 31, + 58, 60, 70, 72, 82, 83, 86, 87, 53, 59, 80, 81, 59, 67, 81, 85, 80, + 83, 111, 112, 109, 110, 116, 117, 58, 82, 70, 86, 60, 83, 72, 87, + 105, 127, 123, 128, 127, 131, 128, 132, 80, 109, 111, 116, 83, 110, + 112, 117, 111, 131, 144, 145, 131, 143, 145, 146, 7, 20, 16, 27, 9, + 21, 18, 28, 54, 68, 58, 70, 77, 82, 78, 83, 20, 45, 58, 70, 21, 46, + 59, 71, 68, 121, 105, 123, 82, 122, 109, 124, 16, 58, 41, 66, 18, 59, + 43, 67, 58, 105, 69, 106, 78, 109, 97, 110, 27, 70, 66, 100, 28, 71, + 67, 101, 70, 123, 106, 128, 83, 124, 110, 130, 16, 27, 21, 29, 21, + 29, 28, 31, 58, 70, 60, 72, 82, 86, 83, 87, 58, 70, 82, 86, 60, 72, + 83, 87, 105, 123, 127, 128, 127, 128, 131, 132, 53, 80, 59, 81, 59, + 81, 67, 85, 80, 111, 83, 112, 109, 116, 110, 117, 80, 111, 109, 116, + 83, 112, 110, 117, 111, 144, 131, 145, 131, 145, 143, 146, 9, 21, 21, + 29, 10, 22, 22, 30, 77, 82, 82, 86, 89, 90, 90, 91, 21, 46, 60, 72, + 22, 47, 61, 73, 82, 122, 127, 128, 90, 126, 129, 130, 21, 60, 46, 72, + 22, 61, 47, 73, 82, 127, 122, 128, 90, 129, 126, 130, 29, 72, 72, + 102, 30, 73, 73, 103, 86, 128, 128, 141, 91, 130, 130, 142, 18, 28, + 28, 31, 22, 30, 30, 32, 78, 83, 83, 87, 90, 91, 91, 92, 59, 71, 83, + 87, 61, 73, 84, 88, 109, 124, 131, 132, 129, 130, 133, 134, 59, 83, + 71, 87, 61, 84, 73, 88, 109, 131, 124, 132, 129, 133, 130, 134, 81, + 112, 112, 118, 84, 113, 113, 119, 116, 145, 145, 147, 133, 146, 146, + 148, 13, 52, 52, 75, 20, 58, 58, 82, 40, 65, 65, 98, 68, 105, 105, + 127, 17, 57, 75, 108, 27, 66, 80, 109, 48, 70, 98, 111, 70, 106, 111, + 131, 17, 75, 57, 108, 27, 80, 66, 109, 48, 98, 70, 111, 70, 111, 106, + 131, 23, 76, 76, 114, 29, 81, 81, 115, 62, 100, 100, 116, 72, 112, + 112, 133, 39, 57, 57, 76, 45, 70, 70, 86, 45, 70, 70, 100, 121, 123, + 123, 128, 57, 76, 108, 114, 70, 100, 111, 116, 70, 86, 111, 116, 123, + 128, 144, 145, 57, 108, 76, 114, 70, 111, 100, 116, 70, 111, 86, 116, + 123, 144, 128, 145, 76, 114, 114, 137, 86, 116, 116, 138, 100, 116, + 116, 138, 128, 145, 145, 149, 16, 53, 58, 80, 21, 59, 60, 83, 58, 80, + 105, 111, 82, 109, 127, 131, 21, 59, 82, 109, 28, 67, 83, 110, 60, + 83, 127, 131, 83, 110, 131, 143, 27, 80, 70, 111, 29, 81, 72, 112, + 70, 111, 123, 144, 86, 116, 128, 145, 29, 81, 86, 116, 31, 85, 87, + 117, 72, 112, 128, 145, 87, 117, 132, 146, 41, 59, 66, 81, 46, 71, + 72, 87, 69, 83, 106, 112, 122, 124, 128, 132, 59, 79, 109, 115, 71, + 101, 112, 117, 83, 91, 131, 133, 124, 130, 145, 146, 66, 109, 100, + 116, 72, 112, 102, 118, 106, 131, 128, 145, 128, 145, 141, 147, 81, + 115, 116, 138, 87, 117, 118, 139, 112, 133, 145, 149, 132, 146, 147, + 150, 16, 58, 53, 80, 21, 60, 59, 83, 58, 105, 80, 111, 82, 127, 109, + 131, 27, 70, 80, 111, 29, 72, 81, 112, 70, 123, 111, 144, 86, 128, + 116, 145, 21, 82, 59, 109, 28, 83, 67, 110, 60, 127, 83, 131, 83, + 131, 110, 143, 29, 86, 81, 116, 31, 87, 85, 117, 72, 128, 112, 145, + 87, 132, 117, 146, 41, 66, 59, 81, 46, 72, 71, 87, 69, 106, 83, 112, + 122, 128, 124, 132, 66, 100, 109, 116, 72, 102, 112, 118, 106, 128, + 131, 145, 128, 141, 145, 147, 59, 109, 79, 115, 71, 112, 101, 117, + 83, 131, 91, 133, 124, 145, 130, 146, 81, 116, 115, 138, 87, 118, + 117, 139, 112, 145, 133, 149, 132, 147, 146, 150, 18, 59, 59, 81, 22, + 61, 61, 84, 78, 109, 109, 116, 90, 129, 129, 133, 28, 71, 83, 112, + 30, 73, 84, 113, 83, 124, 131, 145, 91, 130, 133, 146, 28, 83, 71, + 112, 30, 84, 73, 113, 83, 131, 124, 145, 91, 133, 130, 146, 31, 87, + 87, 118, 32, 88, 88, 119, 87, 132, 132, 147, 92, 134, 134, 148, 43, + 67, 67, 85, 47, 73, 73, 88, 97, 110, 110, 117, 126, 130, 130, 134, + 67, 101, 110, 117, 73, 103, 113, 119, 110, 130, 143, 146, 130, 142, + 146, 148, 67, 110, 101, 117, 73, 113, 103, 119, 110, 143, 130, 146, + 130, 146, 142, 148, 85, 117, 117, 139, 88, 119, 119, 140, 117, 146, + 146, 150, 134, 148, 148, 151, 11, 14, 14, 18, 14, 18, 18, 22, 36, 41, + 41, 49, 55, 59, 59, 61, 36, 41, 55, 59, 41, 49, 59, 61, 42, 46, 66, + 71, 66, 71, 81, 84, 36, 55, 41, 59, 41, 59, 49, 61, 42, 66, 46, 71, + 66, 81, 71, 84, 42, 66, 66, 81, 46, 71, 71, 84, 62, 72, 72, 87, 72, + 87, 87, 92, 14, 18, 18, 24, 25, 28, 28, 30, 41, 49, 49, 63, 69, 71, + 71, 73, 55, 59, 78, 79, 69, 71, 83, 84, 66, 71, 99, 101, 106, 107, + 112, 113, 55, 78, 59, 79, 69, 83, 71, 84, 66, 99, 71, 101, 106, 112, + 107, 113, 96, 109, 109, 115, 122, 124, 124, 125, 100, 112, 112, 117, + 128, 132, 132, 134, 14, 18, 25, 28, 18, 24, 28, 30, 55, 59, 69, 71, + 78, 79, 83, 84, 41, 49, 69, 71, 49, 63, 71, 73, 66, 71, 106, 107, 99, + 101, 112, 113, 55, 78, 69, 83, 59, 79, 71, 84, 96, 109, 122, 124, + 109, 115, 124, 125, 66, 99, 106, 112, 71, 101, 107, 113, 100, 112, + 128, 132, 112, 117, 132, 134, 18, 22, 28, 30, 28, 30, 31, 32, 59, 61, + 71, 73, 83, 84, 87, 88, 59, 61, 83, 84, 71, 73, 87, 88, 81, 84, 112, + 113, 112, 113, 118, 119, 78, 90, 83, 91, 83, 91, 87, 92, 109, 129, + 124, 130, 131, 133, 132, 134, 109, 129, 131, 133, 124, 130, 132, 134, + 116, 133, 145, 146, 145, 146, 147, 148, 14, 25, 18, 28, 18, 28, 24, + 30, 55, 69, 59, 71, 78, 83, 79, 84, 55, 69, 78, 83, 59, 71, 79, 84, + 96, 122, 109, 124, 109, 124, 115, 125, 41, 69, 49, 71, 49, 71, 63, + 73, 66, 106, 71, 107, 99, 112, 101, 113, 66, 106, 99, 112, 71, 107, + 101, 113, 100, 128, 112, 132, 112, 132, 117, 134, 18, 28, 22, 30, 28, + 31, 30, 32, 59, 71, 61, 73, 83, 87, 84, 88, 78, 83, 90, 91, 83, 87, + 91, 92, 109, 124, 129, 130, 131, 132, 133, 134, 59, 83, 61, 84, 71, + 87, 73, 88, 81, 112, 84, 113, 112, 118, 113, 119, 109, 131, 129, 133, + 124, 132, 130, 134, 116, 145, 133, 146, 145, 147, 146, 148, 18, 28, + 28, 31, 22, 30, 30, 32, 78, 83, 83, 87, 90, 91, 91, 92, 59, 71, 83, + 87, 61, 73, 84, 88, 109, 124, 131, 132, 129, 130, 133, 134, 59, 83, + 71, 87, 61, 84, 73, 88, 109, 131, 124, 132, 129, 133, 130, 134, 81, + 112, 112, 118, 84, 113, 113, 119, 116, 145, 145, 147, 133, 146, 146, + 148, 24, 30, 30, 32, 30, 32, 32, 33, 79, 84, 84, 88, 91, 92, 92, 93, + 79, 84, 91, 92, 84, 88, 92, 93, 115, 125, 133, 134, 133, 134, 135, + 136, 79, 91, 84, 92, 84, 92, 88, 93, 115, 133, 125, 134, 133, 135, + 134, 136, 115, 133, 133, 135, 125, 134, 134, 136, 138, 149, 149, 150, + 149, 150, 150, 151, 36, 55, 55, 78, 55, 78, 78, 90, 42, 66, 66, 99, + 96, 109, 109, 129, 42, 66, 96, 109, 66, 99, 109, 129, 62, 72, 100, + 112, 100, 112, 116, 133, 42, 96, 66, 109, 66, 109, 99, 129, 62, 100, + 72, 112, 100, 116, 112, 133, 62, 100, 100, 116, 72, 112, 112, 133, + 94, 102, 102, 118, 102, 118, 118, 135, 41, 59, 59, 79, 69, 83, 83, + 91, 46, 71, 71, 101, 122, 124, 124, 130, 66, 81, 109, 115, 106, 112, + 131, 133, 72, 87, 112, 117, 128, 132, 145, 146, 66, 109, 81, 115, + 106, 131, 112, 133, 72, 112, 87, 117, 128, 145, 132, 146, 100, 116, + 116, 138, 128, 145, 145, 149, 102, 118, 118, 139, 141, 147, 147, 150, + 41, 59, 69, 83, 59, 79, 83, 91, 66, 81, 106, 112, 109, 115, 131, 133, + 46, 71, 122, 124, 71, 101, 124, 130, 72, 87, 128, 132, 112, 117, 145, + 146, 66, 109, 106, 131, 81, 115, 112, 133, 100, 116, 128, 145, 116, + 138, 145, 149, 72, 112, 128, 145, 87, 117, 132, 146, 102, 118, 141, + 147, 118, 139, 147, 150, 49, 61, 71, 84, 71, 84, 87, 92, 71, 84, 107, + 113, 124, 125, 132, 134, 71, 84, 124, 125, 107, 113, 132, 134, 87, + 92, 132, 134, 132, 134, 147, 148, 99, 129, 112, 133, 112, 133, 118, + 135, 112, 133, 132, 146, 145, 149, 147, 150, 112, 133, 145, 149, 132, + 146, 147, 150, 118, 135, 147, 150, 147, 150, 152, 153, 41, 69, 59, + 83, 59, 83, 79, 91, 66, 106, 81, 112, 109, 131, 115, 133, 66, 106, + 109, 131, 81, 112, 115, 133, 100, 128, 116, 145, 116, 145, 138, 149, + 46, 122, 71, 124, 71, 124, 101, 130, 72, 128, 87, 132, 112, 145, 117, + 146, 72, 128, 112, 145, 87, 132, 117, 146, 102, 141, 118, 147, 118, + 147, 139, 150, 49, 71, 61, 84, 71, 87, 84, 92, 71, 107, 84, 113, 124, + 132, 125, 134, 99, 112, 129, 133, 112, 118, 133, 135, 112, 132, 133, + 146, 145, 147, 149, 150, 71, 124, 84, 125, 107, 132, 113, 134, 87, + 132, 92, 134, 132, 147, 134, 148, 112, 145, 133, 149, 132, 147, 146, + 150, 118, 147, 135, 150, 147, 152, 150, 153, 49, 71, 71, 87, 61, 84, + 84, 92, 99, 112, 112, 118, 129, 133, 133, 135, 71, 107, 124, 132, 84, + 113, 125, 134, 112, 132, 145, 147, 133, 146, 149, 150, 71, 124, 107, + 132, 84, 125, 113, 134, 112, 145, 132, 147, 133, 149, 146, 150, 87, + 132, 132, 147, 92, 134, 134, 148, 118, 147, 147, 152, 135, 150, 150, + 153, 63, 73, 73, 88, 73, 88, 88, 93, 101, 113, 113, 119, 130, 134, + 134, 136, 101, 113, 130, 134, 113, 119, 134, 136, 117, 134, 146, 148, + 146, 148, 150, 151, 101, 130, 113, 134, 113, 134, 119, 136, 117, 146, + 134, 148, 146, 150, 148, 151, 117, 146, 146, 150, 134, 148, 148, 151, + 139, 150, 150, 153, 150, 153, 153, 154, 2, 12, 12, 38, 12, 38, 38, + 44, 6, 15, 15, 40, 51, 56, 56, 68, 6, 15, 51, 56, 15, 40, 56, 68, 13, + 20, 52, 58, 52, 58, 75, 82, 6, 51, 15, 56, 15, 56, 40, 68, 13, 52, + 20, 58, 52, 75, 58, 82, 13, 52, 52, 75, 20, 58, 58, 82, 36, 55, 55, + 78, 55, 78, 78, 90, 6, 15, 15, 40, 51, 56, 56, 68, 8, 17, 17, 42, 74, + 75, 75, 96, 15, 26, 56, 65, 56, 65, 104, 105, 17, 27, 57, 66, 75, 80, + 108, 109, 15, 56, 26, 65, 56, 104, 65, 105, 17, 57, 27, 66, 75, 108, + 80, 109, 40, 65, 65, 98, 68, 105, 105, 127, 42, 66, 66, 99, 96, 109, + 109, 129, 6, 15, 51, 56, 15, 40, 56, 68, 15, 26, 56, 65, 56, 65, 104, + 105, 8, 17, 74, 75, 17, 42, 75, 96, 17, 27, 75, 80, 57, 66, 108, 109, + 15, 56, 56, 104, 26, 65, 65, 105, 40, 65, 68, 105, 65, 98, 105, 127, + 17, 57, 75, 108, 27, 66, 80, 109, 42, 66, 96, 109, 66, 99, 109, 129, + 13, 20, 52, 58, 52, 58, 75, 82, 17, 27, 57, 66, 75, 80, 108, 109, 17, + 27, 75, 80, 57, 66, 108, 109, 23, 29, 76, 81, 76, 81, 114, 115, 40, + 68, 65, 105, 65, 105, 98, 127, 48, 70, 70, 106, 98, 111, 111, 131, + 48, 70, 98, 111, 70, 106, 111, 131, 62, 72, 100, 112, 100, 112, 116, + 133, 6, 51, 15, 56, 15, 56, 40, 68, 15, 56, 26, 65, 56, 104, 65, 105, + 15, 56, 56, 104, 26, 65, 65, 105, 40, 68, 65, 105, 65, 105, 98, 127, + 8, 74, 17, 75, 17, 75, 42, 96, 17, 75, 27, 80, 57, 108, 66, 109, 17, + 75, 57, 108, 27, 80, 66, 109, 42, 96, 66, 109, 66, 109, 99, 129, 13, + 52, 20, 58, 52, 75, 58, 82, 17, 57, 27, 66, 75, 108, 80, 109, 40, 65, + 68, 105, 65, 98, 105, 127, 48, 70, 70, 106, 98, 111, 111, 131, 17, + 75, 27, 80, 57, 108, 66, 109, 23, 76, 29, 81, 76, 114, 81, 115, 48, + 98, 70, 111, 70, 111, 106, 131, 62, 100, 72, 112, 100, 116, 112, 133, + 13, 52, 52, 75, 20, 58, 58, 82, 40, 65, 65, 98, 68, 105, 105, 127, + 17, 57, 75, 108, 27, 66, 80, 109, 48, 70, 98, 111, 70, 106, 111, 131, + 17, 75, 57, 108, 27, 80, 66, 109, 48, 98, 70, 111, 70, 111, 106, 131, + 23, 76, 76, 114, 29, 81, 81, 115, 62, 100, 100, 116, 72, 112, 112, + 133, 36, 55, 55, 78, 55, 78, 78, 90, 42, 66, 66, 99, 96, 109, 109, + 129, 42, 66, 96, 109, 66, 99, 109, 129, 62, 72, 100, 112, 100, 112, + 116, 133, 42, 96, 66, 109, 66, 109, 99, 129, 62, 100, 72, 112, 100, + 116, 112, 133, 62, 100, 100, 116, 72, 112, 112, 133, 94, 102, 102, + 118, 102, 118, 118, 135, 3, 19, 19, 44, 19, 44, 44, 120, 7, 20, 20, + 45, 54, 68, 68, 121, 7, 20, 54, 68, 20, 45, 68, 121, 14, 25, 55, 69, + 55, 69, 96, 122, 7, 54, 20, 68, 20, 68, 45, 121, 14, 55, 25, 69, 55, + 96, 69, 122, 14, 55, 55, 96, 25, 69, 69, 122, 37, 64, 64, 97, 64, 97, + 97, 126, 7, 20, 20, 45, 54, 68, 68, 121, 9, 21, 21, 46, 77, 82, 82, + 122, 16, 27, 58, 70, 58, 70, 105, 123, 18, 28, 59, 71, 78, 83, 109, + 124, 16, 58, 27, 70, 58, 105, 70, 123, 18, 59, 28, 71, 78, 109, 83, + 124, 41, 66, 66, 100, 69, 106, 106, 128, 43, 67, 67, 101, 97, 110, + 110, 130, 7, 20, 54, 68, 20, 45, 68, 121, 16, 27, 58, 70, 58, 70, + 105, 123, 9, 21, 77, 82, 21, 46, 82, 122, 18, 28, 78, 83, 59, 71, + 109, 124, 16, 58, 58, 105, 27, 70, 70, 123, 41, 66, 69, 106, 66, 100, + 106, 128, 18, 59, 78, 109, 28, 71, 83, 124, 43, 67, 97, 110, 67, 101, + 110, 130, 14, 25, 55, 69, 55, 69, 96, 122, 18, 28, 59, 71, 78, 83, + 109, 124, 18, 28, 78, 83, 59, 71, 109, 124, 24, 30, 79, 84, 79, 84, + 115, 125, 41, 69, 66, 106, 66, 106, 100, 128, 49, 71, 71, 107, 99, + 112, 112, 132, 49, 71, 99, 112, 71, 107, 112, 132, 63, 73, 101, 113, + 101, 113, 117, 134, 7, 54, 20, 68, 20, 68, 45, 121, 16, 58, 27, 70, + 58, 105, 70, 123, 16, 58, 58, 105, 27, 70, 70, 123, 41, 69, 66, 106, + 66, 106, 100, 128, 9, 77, 21, 82, 21, 82, 46, 122, 18, 78, 28, 83, + 59, 109, 71, 124, 18, 78, 59, 109, 28, 83, 71, 124, 43, 97, 67, 110, + 67, 110, 101, 130, 14, 55, 25, 69, 55, 96, 69, 122, 18, 59, 28, 71, + 78, 109, 83, 124, 41, 66, 69, 106, 66, 100, 106, 128, 49, 71, 71, + 107, 99, 112, 112, 132, 18, 78, 28, 83, 59, 109, 71, 124, 24, 79, 30, + 84, 79, 115, 84, 125, 49, 99, 71, 112, 71, 112, 107, 132, 63, 101, + 73, 113, 101, 117, 113, 134, 14, 55, 55, 96, 25, 69, 69, 122, 41, 66, + 66, 100, 69, 106, 106, 128, 18, 59, 78, 109, 28, 71, 83, 124, 49, 71, + 99, 112, 71, 107, 112, 132, 18, 78, 59, 109, 28, 83, 71, 124, 49, 99, + 71, 112, 71, 112, 107, 132, 24, 79, 79, 115, 30, 84, 84, 125, 63, + 101, 101, 117, 73, 113, 113, 134, 37, 64, 64, 97, 64, 97, 97, 126, + 43, 67, 67, 101, 97, 110, 110, 130, 43, 67, 97, 110, 67, 101, 110, + 130, 63, 73, 101, 113, 101, 113, 117, 134, 43, 97, 67, 110, 67, 110, + 101, 130, 63, 101, 73, 113, 101, 117, 113, 134, 63, 101, 101, 117, + 73, 113, 113, 134, 95, 103, 103, 119, 103, 119, 119, 136, 4, 13, 13, + 39, 35, 40, 40, 45, 7, 16, 16, 41, 54, 58, 58, 69, 13, 17, 52, 57, + 40, 48, 65, 70, 16, 21, 53, 59, 58, 60, 80, 83, 13, 52, 17, 57, 40, + 65, 48, 70, 16, 53, 21, 59, 58, 80, 60, 83, 39, 57, 57, 76, 45, 70, + 70, 86, 41, 59, 59, 79, 69, 83, 83, 91, 7, 16, 16, 41, 54, 58, 58, + 69, 9, 18, 18, 43, 77, 78, 78, 97, 20, 27, 58, 66, 68, 70, 105, 106, + 21, 28, 59, 67, 82, 83, 109, 110, 20, 58, 27, 66, 68, 105, 70, 106, + 21, 59, 28, 67, 82, 109, 83, 110, 45, 70, 70, 100, 121, 123, 123, + 128, 46, 71, 71, 101, 122, 124, 124, 130, 13, 17, 52, 57, 40, 48, 65, + 70, 20, 27, 58, 66, 68, 70, 105, 106, 17, 23, 75, 76, 48, 62, 98, + 100, 27, 29, 80, 81, 70, 72, 111, 112, 52, 75, 75, 108, 65, 98, 98, + 111, 58, 80, 82, 109, 105, 111, 127, 131, 57, 76, 108, 114, 70, 100, + 111, 116, 66, 81, 109, 115, 106, 112, 131, 133, 16, 21, 53, 59, 58, + 60, 80, 83, 21, 28, 59, 67, 82, 83, 109, 110, 27, 29, 80, 81, 70, 72, + 111, 112, 29, 31, 81, 85, 86, 87, 116, 117, 58, 82, 80, 109, 105, + 127, 111, 131, 60, 83, 83, 110, 127, 131, 131, 143, 70, 86, 111, 116, + 123, 128, 144, 145, 72, 87, 112, 117, 128, 132, 145, 146, 13, 52, 17, + 57, 40, 65, 48, 70, 20, 58, 27, 66, 68, 105, 70, 106, 52, 75, 75, + 108, 65, 98, 98, 111, 58, 82, 80, 109, 105, 127, 111, 131, 17, 75, + 23, 76, 48, 98, 62, 100, 27, 80, 29, 81, 70, 111, 72, 112, 57, 108, + 76, 114, 70, 111, 100, 116, 66, 109, 81, 115, 106, 131, 112, 133, 16, + 53, 21, 59, 58, 80, 60, 83, 21, 59, 28, 67, 82, 109, 83, 110, 58, 80, + 82, 109, 105, 111, 127, 131, 60, 83, 83, 110, 127, 131, 131, 143, 27, + 80, 29, 81, 70, 111, 72, 112, 29, 81, 31, 85, 86, 116, 87, 117, 70, + 111, 86, 116, 123, 144, 128, 145, 72, 112, 87, 117, 128, 145, 132, + 146, 39, 57, 57, 76, 45, 70, 70, 86, 45, 70, 70, 100, 121, 123, 123, + 128, 57, 76, 108, 114, 70, 100, 111, 116, 70, 86, 111, 116, 123, 128, + 144, 145, 57, 108, 76, 114, 70, 111, 100, 116, 70, 111, 86, 116, 123, + 144, 128, 145, 76, 114, 114, 137, 86, 116, 116, 138, 100, 116, 116, + 138, 128, 145, 145, 149, 41, 59, 59, 79, 69, 83, 83, 91, 46, 71, 71, + 101, 122, 124, 124, 130, 66, 81, 109, 115, 106, 112, 131, 133, 72, + 87, 112, 117, 128, 132, 145, 146, 66, 109, 81, 115, 106, 131, 112, + 133, 72, 112, 87, 117, 128, 145, 132, 146, 100, 116, 116, 138, 128, + 145, 145, 149, 102, 118, 118, 139, 141, 147, 147, 150, 7, 20, 20, 45, + 54, 68, 68, 121, 9, 21, 21, 46, 77, 82, 82, 122, 16, 27, 58, 70, 58, + 70, 105, 123, 18, 28, 59, 71, 78, 83, 109, 124, 16, 58, 27, 70, 58, + 105, 70, 123, 18, 59, 28, 71, 78, 109, 83, 124, 41, 66, 66, 100, 69, + 106, 106, 128, 43, 67, 67, 101, 97, 110, 110, 130, 9, 21, 21, 46, 77, + 82, 82, 122, 10, 22, 22, 47, 89, 90, 90, 126, 21, 29, 60, 72, 82, 86, + 127, 128, 22, 30, 61, 73, 90, 91, 129, 130, 21, 60, 29, 72, 82, 127, + 86, 128, 22, 61, 30, 73, 90, 129, 91, 130, 46, 72, 72, 102, 122, 128, + 128, 141, 47, 73, 73, 103, 126, 130, 130, 142, 16, 27, 58, 70, 58, + 70, 105, 123, 21, 29, 60, 72, 82, 86, 127, 128, 21, 29, 82, 86, 60, + 72, 127, 128, 28, 31, 83, 87, 83, 87, 131, 132, 53, 80, 80, 111, 80, + 111, 111, 144, 59, 81, 83, 112, 109, 116, 131, 145, 59, 81, 109, 116, + 83, 112, 131, 145, 67, 85, 110, 117, 110, 117, 143, 146, 18, 28, 59, + 71, 78, 83, 109, 124, 22, 30, 61, 73, 90, 91, 129, 130, 28, 31, 83, + 87, 83, 87, 131, 132, 30, 32, 84, 88, 91, 92, 133, 134, 59, 83, 81, + 112, 109, 131, 116, 145, 61, 84, 84, 113, 129, 133, 133, 146, 71, 87, + 112, 118, 124, 132, 145, 147, 73, 88, 113, 119, 130, 134, 146, 148, + 16, 58, 27, 70, 58, 105, 70, 123, 21, 60, 29, 72, 82, 127, 86, 128, + 53, 80, 80, 111, 80, 111, 111, 144, 59, 83, 81, 112, 109, 131, 116, + 145, 21, 82, 29, 86, 60, 127, 72, 128, 28, 83, 31, 87, 83, 131, 87, + 132, 59, 109, 81, 116, 83, 131, 112, 145, 67, 110, 85, 117, 110, 143, + 117, 146, 18, 59, 28, 71, 78, 109, 83, 124, 22, 61, 30, 73, 90, 129, + 91, 130, 59, 81, 83, 112, 109, 116, 131, 145, 61, 84, 84, 113, 129, + 133, 133, 146, 28, 83, 31, 87, 83, 131, 87, 132, 30, 84, 32, 88, 91, + 133, 92, 134, 71, 112, 87, 118, 124, 145, 132, 147, 73, 113, 88, 119, + 130, 146, 134, 148, 41, 66, 66, 100, 69, 106, 106, 128, 46, 72, 72, + 102, 122, 128, 128, 141, 59, 81, 109, 116, 83, 112, 131, 145, 71, 87, + 112, 118, 124, 132, 145, 147, 59, 109, 81, 116, 83, 131, 112, 145, + 71, 112, 87, 118, 124, 145, 132, 147, 79, 115, 115, 138, 91, 133, + 133, 149, 101, 117, 117, 139, 130, 146, 146, 150, 43, 67, 67, 101, + 97, 110, 110, 130, 47, 73, 73, 103, 126, 130, 130, 142, 67, 85, 110, + 117, 110, 117, 143, 146, 73, 88, 113, 119, 130, 134, 146, 148, 67, + 110, 85, 117, 110, 143, 117, 146, 73, 113, 88, 119, 130, 146, 134, + 148, 101, 117, 117, 139, 130, 146, 146, 150, 103, 119, 119, 140, 142, + 148, 148, 151, 4, 13, 35, 40, 13, 39, 40, 45, 13, 17, 40, 48, 52, 57, + 65, 70, 7, 16, 54, 58, 16, 41, 58, 69, 16, 21, 58, 60, 53, 59, 80, + 83, 13, 52, 40, 65, 17, 57, 48, 70, 39, 57, 45, 70, 57, 76, 70, 86, + 16, 53, 58, 80, 21, 59, 60, 83, 41, 59, 69, 83, 59, 79, 83, 91, 13, + 17, 40, 48, 52, 57, 65, 70, 17, 23, 48, 62, 75, 76, 98, 100, 20, 27, + 68, 70, 58, 66, 105, 106, 27, 29, 70, 72, 80, 81, 111, 112, 52, 75, + 65, 98, 75, 108, 98, 111, 57, 76, 70, 100, 108, 114, 111, 116, 58, + 80, 105, 111, 82, 109, 127, 131, 66, 81, 106, 112, 109, 115, 131, + 133, 7, 16, 54, 58, 16, 41, 58, 69, 20, 27, 68, 70, 58, 66, 105, 106, + 9, 18, 77, 78, 18, 43, 78, 97, 21, 28, 82, 83, 59, 67, 109, 110, 20, + 58, 68, 105, 27, 66, 70, 106, 45, 70, 121, 123, 70, 100, 123, 128, + 21, 59, 82, 109, 28, 67, 83, 110, 46, 71, 122, 124, 71, 101, 124, + 130, 16, 21, 58, 60, 53, 59, 80, 83, 27, 29, 70, 72, 80, 81, 111, + 112, 21, 28, 82, 83, 59, 67, 109, 110, 29, 31, 86, 87, 81, 85, 116, + 117, 58, 82, 105, 127, 80, 109, 111, 131, 70, 86, 123, 128, 111, 116, + 144, 145, 60, 83, 127, 131, 83, 110, 131, 143, 72, 87, 128, 132, 112, + 117, 145, 146, 13, 52, 40, 65, 17, 57, 48, 70, 52, 75, 65, 98, 75, + 108, 98, 111, 20, 58, 68, 105, 27, 66, 70, 106, 58, 82, 105, 127, 80, + 109, 111, 131, 17, 75, 48, 98, 23, 76, 62, 100, 57, 108, 70, 111, 76, + 114, 100, 116, 27, 80, 70, 111, 29, 81, 72, 112, 66, 109, 106, 131, + 81, 115, 112, 133, 39, 57, 45, 70, 57, 76, 70, 86, 57, 76, 70, 100, + 108, 114, 111, 116, 45, 70, 121, 123, 70, 100, 123, 128, 70, 86, 123, + 128, 111, 116, 144, 145, 57, 108, 70, 111, 76, 114, 100, 116, 76, + 114, 86, 116, 114, 137, 116, 138, 70, 111, 123, 144, 86, 116, 128, + 145, 100, 116, 128, 145, 116, 138, 145, 149, 16, 53, 58, 80, 21, 59, + 60, 83, 58, 80, 105, 111, 82, 109, 127, 131, 21, 59, 82, 109, 28, 67, + 83, 110, 60, 83, 127, 131, 83, 110, 131, 143, 27, 80, 70, 111, 29, + 81, 72, 112, 70, 111, 123, 144, 86, 116, 128, 145, 29, 81, 86, 116, + 31, 85, 87, 117, 72, 112, 128, 145, 87, 117, 132, 146, 41, 59, 69, + 83, 59, 79, 83, 91, 66, 81, 106, 112, 109, 115, 131, 133, 46, 71, + 122, 124, 71, 101, 124, 130, 72, 87, 128, 132, 112, 117, 145, 146, + 66, 109, 106, 131, 81, 115, 112, 133, 100, 116, 128, 145, 116, 138, + 145, 149, 72, 112, 128, 145, 87, 117, 132, 146, 102, 118, 141, 147, + 118, 139, 147, 150, 7, 20, 54, 68, 20, 45, 68, 121, 16, 27, 58, 70, + 58, 70, 105, 123, 9, 21, 77, 82, 21, 46, 82, 122, 18, 28, 78, 83, 59, + 71, 109, 124, 16, 58, 58, 105, 27, 70, 70, 123, 41, 66, 69, 106, 66, + 100, 106, 128, 18, 59, 78, 109, 28, 71, 83, 124, 43, 67, 97, 110, 67, + 101, 110, 130, 16, 27, 58, 70, 58, 70, 105, 123, 21, 29, 60, 72, 82, + 86, 127, 128, 21, 29, 82, 86, 60, 72, 127, 128, 28, 31, 83, 87, 83, + 87, 131, 132, 53, 80, 80, 111, 80, 111, 111, 144, 59, 81, 83, 112, + 109, 116, 131, 145, 59, 81, 109, 116, 83, 112, 131, 145, 67, 85, 110, + 117, 110, 117, 143, 146, 9, 21, 77, 82, 21, 46, 82, 122, 21, 29, 82, + 86, 60, 72, 127, 128, 10, 22, 89, 90, 22, 47, 90, 126, 22, 30, 90, + 91, 61, 73, 129, 130, 21, 60, 82, 127, 29, 72, 86, 128, 46, 72, 122, + 128, 72, 102, 128, 141, 22, 61, 90, 129, 30, 73, 91, 130, 47, 73, + 126, 130, 73, 103, 130, 142, 18, 28, 78, 83, 59, 71, 109, 124, 28, + 31, 83, 87, 83, 87, 131, 132, 22, 30, 90, 91, 61, 73, 129, 130, 30, + 32, 91, 92, 84, 88, 133, 134, 59, 83, 109, 131, 81, 112, 116, 145, + 71, 87, 124, 132, 112, 118, 145, 147, 61, 84, 129, 133, 84, 113, 133, + 146, 73, 88, 130, 134, 113, 119, 146, 148, 16, 58, 58, 105, 27, 70, + 70, 123, 53, 80, 80, 111, 80, 111, 111, 144, 21, 60, 82, 127, 29, 72, + 86, 128, 59, 83, 109, 131, 81, 112, 116, 145, 21, 82, 60, 127, 29, + 86, 72, 128, 59, 109, 83, 131, 81, 116, 112, 145, 28, 83, 83, 131, + 31, 87, 87, 132, 67, 110, 110, 143, 85, 117, 117, 146, 41, 66, 69, + 106, 66, 100, 106, 128, 59, 81, 83, 112, 109, 116, 131, 145, 46, 72, + 122, 128, 72, 102, 128, 141, 71, 87, 124, 132, 112, 118, 145, 147, + 59, 109, 83, 131, 81, 116, 112, 145, 79, 115, 91, 133, 115, 138, 133, + 149, 71, 112, 124, 145, 87, 118, 132, 147, 101, 117, 130, 146, 117, + 139, 146, 150, 18, 59, 78, 109, 28, 71, 83, 124, 59, 81, 109, 116, + 83, 112, 131, 145, 22, 61, 90, 129, 30, 73, 91, 130, 61, 84, 129, + 133, 84, 113, 133, 146, 28, 83, 83, 131, 31, 87, 87, 132, 71, 112, + 124, 145, 87, 118, 132, 147, 30, 84, 91, 133, 32, 88, 92, 134, 73, + 113, 130, 146, 88, 119, 134, 148, 43, 67, 97, 110, 67, 101, 110, 130, + 67, 85, 110, 117, 110, 117, 143, 146, 47, 73, 126, 130, 73, 103, 130, + 142, 73, 88, 130, 134, 113, 119, 146, 148, 67, 110, 110, 143, 85, + 117, 117, 146, 101, 117, 130, 146, 117, 139, 146, 150, 73, 113, 130, + 146, 88, 119, 134, 148, 103, 119, 142, 148, 119, 140, 148, 151, 11, + 14, 36, 41, 36, 41, 42, 46, 14, 18, 41, 49, 55, 59, 66, 71, 14, 18, + 55, 59, 41, 49, 66, 71, 18, 22, 59, 61, 59, 61, 81, 84, 36, 55, 42, + 66, 42, 66, 62, 72, 41, 59, 46, 71, 66, 81, 72, 87, 41, 59, 66, 81, + 46, 71, 72, 87, 49, 61, 71, 84, 71, 84, 87, 92, 14, 18, 41, 49, 55, + 59, 66, 71, 18, 24, 49, 63, 78, 79, 99, 101, 25, 28, 69, 71, 69, 71, + 106, 107, 28, 30, 71, 73, 83, 84, 112, 113, 55, 78, 66, 99, 96, 109, + 100, 112, 59, 79, 71, 101, 109, 115, 112, 117, 69, 83, 106, 112, 122, + 124, 128, 132, 71, 84, 107, 113, 124, 125, 132, 134, 14, 18, 55, 59, + 41, 49, 66, 71, 25, 28, 69, 71, 69, 71, 106, 107, 18, 24, 78, 79, 49, + 63, 99, 101, 28, 30, 83, 84, 71, 73, 112, 113, 55, 78, 96, 109, 66, + 99, 100, 112, 69, 83, 122, 124, 106, 112, 128, 132, 59, 79, 109, 115, + 71, 101, 112, 117, 71, 84, 124, 125, 107, 113, 132, 134, 18, 22, 59, + 61, 59, 61, 81, 84, 28, 30, 71, 73, 83, 84, 112, 113, 28, 30, 83, 84, + 71, 73, 112, 113, 31, 32, 87, 88, 87, 88, 118, 119, 78, 90, 109, 129, + 109, 129, 116, 133, 83, 91, 124, 130, 131, 133, 145, 146, 83, 91, + 131, 133, 124, 130, 145, 146, 87, 92, 132, 134, 132, 134, 147, 148, + 36, 55, 42, 66, 42, 66, 62, 72, 55, 78, 66, 99, 96, 109, 100, 112, + 55, 78, 96, 109, 66, 99, 100, 112, 78, 90, 109, 129, 109, 129, 116, + 133, 42, 96, 62, 100, 62, 100, 94, 102, 66, 109, 72, 112, 100, 116, + 102, 118, 66, 109, 100, 116, 72, 112, 102, 118, 99, 129, 112, 133, + 112, 133, 118, 135, 41, 59, 46, 71, 66, 81, 72, 87, 59, 79, 71, 101, + 109, 115, 112, 117, 69, 83, 122, 124, 106, 112, 128, 132, 83, 91, + 124, 130, 131, 133, 145, 146, 66, 109, 72, 112, 100, 116, 102, 118, + 81, 115, 87, 117, 116, 138, 118, 139, 106, 131, 128, 145, 128, 145, + 141, 147, 112, 133, 132, 146, 145, 149, 147, 150, 41, 59, 66, 81, 46, + 71, 72, 87, 69, 83, 106, 112, 122, 124, 128, 132, 59, 79, 109, 115, + 71, 101, 112, 117, 83, 91, 131, 133, 124, 130, 145, 146, 66, 109, + 100, 116, 72, 112, 102, 118, 106, 131, 128, 145, 128, 145, 141, 147, + 81, 115, 116, 138, 87, 117, 118, 139, 112, 133, 145, 149, 132, 146, + 147, 150, 49, 61, 71, 84, 71, 84, 87, 92, 71, 84, 107, 113, 124, 125, + 132, 134, 71, 84, 124, 125, 107, 113, 132, 134, 87, 92, 132, 134, + 132, 134, 147, 148, 99, 129, 112, 133, 112, 133, 118, 135, 112, 133, + 132, 146, 145, 149, 147, 150, 112, 133, 145, 149, 132, 146, 147, 150, + 118, 135, 147, 150, 147, 150, 152, 153, 14, 25, 55, 69, 55, 69, 96, + 122, 18, 28, 59, 71, 78, 83, 109, 124, 18, 28, 78, 83, 59, 71, 109, + 124, 24, 30, 79, 84, 79, 84, 115, 125, 41, 69, 66, 106, 66, 106, 100, + 128, 49, 71, 71, 107, 99, 112, 112, 132, 49, 71, 99, 112, 71, 107, + 112, 132, 63, 73, 101, 113, 101, 113, 117, 134, 18, 28, 59, 71, 78, + 83, 109, 124, 22, 30, 61, 73, 90, 91, 129, 130, 28, 31, 83, 87, 83, + 87, 131, 132, 30, 32, 84, 88, 91, 92, 133, 134, 59, 83, 81, 112, 109, + 131, 116, 145, 61, 84, 84, 113, 129, 133, 133, 146, 71, 87, 112, 118, + 124, 132, 145, 147, 73, 88, 113, 119, 130, 134, 146, 148, 18, 28, 78, + 83, 59, 71, 109, 124, 28, 31, 83, 87, 83, 87, 131, 132, 22, 30, 90, + 91, 61, 73, 129, 130, 30, 32, 91, 92, 84, 88, 133, 134, 59, 83, 109, + 131, 81, 112, 116, 145, 71, 87, 124, 132, 112, 118, 145, 147, 61, 84, + 129, 133, 84, 113, 133, 146, 73, 88, 130, 134, 113, 119, 146, 148, + 24, 30, 79, 84, 79, 84, 115, 125, 30, 32, 84, 88, 91, 92, 133, 134, + 30, 32, 91, 92, 84, 88, 133, 134, 32, 33, 92, 93, 92, 93, 135, 136, + 79, 91, 115, 133, 115, 133, 138, 149, 84, 92, 125, 134, 133, 135, + 149, 150, 84, 92, 133, 135, 125, 134, 149, 150, 88, 93, 134, 136, + 134, 136, 150, 151, 41, 69, 66, 106, 66, 106, 100, 128, 59, 83, 81, + 112, 109, 131, 116, 145, 59, 83, 109, 131, 81, 112, 116, 145, 79, 91, + 115, 133, 115, 133, 138, 149, 46, 122, 72, 128, 72, 128, 102, 141, + 71, 124, 87, 132, 112, 145, 118, 147, 71, 124, 112, 145, 87, 132, + 118, 147, 101, 130, 117, 146, 117, 146, 139, 150, 49, 71, 71, 107, + 99, 112, 112, 132, 61, 84, 84, 113, 129, 133, 133, 146, 71, 87, 124, + 132, 112, 118, 145, 147, 84, 92, 125, 134, 133, 135, 149, 150, 71, + 124, 87, 132, 112, 145, 118, 147, 84, 125, 92, 134, 133, 149, 135, + 150, 107, 132, 132, 147, 132, 147, 147, 152, 113, 134, 134, 148, 146, + 150, 150, 153, 49, 71, 99, 112, 71, 107, 112, 132, 71, 87, 112, 118, + 124, 132, 145, 147, 61, 84, 129, 133, 84, 113, 133, 146, 84, 92, 133, + 135, 125, 134, 149, 150, 71, 124, 112, 145, 87, 132, 118, 147, 107, + 132, 132, 147, 132, 147, 147, 152, 84, 125, 133, 149, 92, 134, 135, + 150, 113, 134, 146, 150, 134, 148, 150, 153, 63, 73, 101, 113, 101, + 113, 117, 134, 73, 88, 113, 119, 130, 134, 146, 148, 73, 88, 130, + 134, 113, 119, 146, 148, 88, 93, 134, 136, 134, 136, 150, 151, 101, + 130, 117, 146, 117, 146, 139, 150, 113, 134, 134, 148, 146, 150, 150, + 153, 113, 134, 146, 150, 134, 148, 150, 153, 119, 136, 148, 151, 148, + 151, 153, 154, 4, 35, 13, 40, 13, 40, 39, 45, 13, 40, 17, 48, 52, 65, + 57, 70, 13, 40, 52, 65, 17, 48, 57, 70, 39, 45, 57, 70, 57, 70, 76, + 86, 7, 54, 16, 58, 16, 58, 41, 69, 16, 58, 21, 60, 53, 80, 59, 83, + 16, 58, 53, 80, 21, 60, 59, 83, 41, 69, 59, 83, 59, 83, 79, 91, 13, + 40, 17, 48, 52, 65, 57, 70, 17, 48, 23, 62, 75, 98, 76, 100, 52, 65, + 75, 98, 75, 98, 108, 111, 57, 70, 76, 100, 108, 111, 114, 116, 20, + 68, 27, 70, 58, 105, 66, 106, 27, 70, 29, 72, 80, 111, 81, 112, 58, + 105, 80, 111, 82, 127, 109, 131, 66, 106, 81, 112, 109, 131, 115, + 133, 13, 40, 52, 65, 17, 48, 57, 70, 52, 65, 75, 98, 75, 98, 108, + 111, 17, 48, 75, 98, 23, 62, 76, 100, 57, 70, 108, 111, 76, 100, 114, + 116, 20, 68, 58, 105, 27, 70, 66, 106, 58, 105, 82, 127, 80, 111, + 109, 131, 27, 70, 80, 111, 29, 72, 81, 112, 66, 106, 109, 131, 81, + 112, 115, 133, 39, 45, 57, 70, 57, 70, 76, 86, 57, 70, 76, 100, 108, + 111, 114, 116, 57, 70, 108, 111, 76, 100, 114, 116, 76, 86, 114, 116, + 114, 116, 137, 138, 45, 121, 70, 123, 70, 123, 100, 128, 70, 123, 86, + 128, 111, 144, 116, 145, 70, 123, 111, 144, 86, 128, 116, 145, 100, + 128, 116, 145, 116, 145, 138, 149, 7, 54, 16, 58, 16, 58, 41, 69, 20, + 68, 27, 70, 58, 105, 66, 106, 20, 68, 58, 105, 27, 70, 66, 106, 45, + 121, 70, 123, 70, 123, 100, 128, 9, 77, 18, 78, 18, 78, 43, 97, 21, + 82, 28, 83, 59, 109, 67, 110, 21, 82, 59, 109, 28, 83, 67, 110, 46, + 122, 71, 124, 71, 124, 101, 130, 16, 58, 21, 60, 53, 80, 59, 83, 27, + 70, 29, 72, 80, 111, 81, 112, 58, 105, 82, 127, 80, 111, 109, 131, + 70, 123, 86, 128, 111, 144, 116, 145, 21, 82, 28, 83, 59, 109, 67, + 110, 29, 86, 31, 87, 81, 116, 85, 117, 60, 127, 83, 131, 83, 131, + 110, 143, 72, 128, 87, 132, 112, 145, 117, 146, 16, 58, 53, 80, 21, + 60, 59, 83, 58, 105, 80, 111, 82, 127, 109, 131, 27, 70, 80, 111, 29, + 72, 81, 112, 70, 123, 111, 144, 86, 128, 116, 145, 21, 82, 59, 109, + 28, 83, 67, 110, 60, 127, 83, 131, 83, 131, 110, 143, 29, 86, 81, + 116, 31, 87, 85, 117, 72, 128, 112, 145, 87, 132, 117, 146, 41, 69, + 59, 83, 59, 83, 79, 91, 66, 106, 81, 112, 109, 131, 115, 133, 66, + 106, 109, 131, 81, 112, 115, 133, 100, 128, 116, 145, 116, 145, 138, + 149, 46, 122, 71, 124, 71, 124, 101, 130, 72, 128, 87, 132, 112, 145, + 117, 146, 72, 128, 112, 145, 87, 132, 117, 146, 102, 141, 118, 147, + 118, 147, 139, 150, 7, 54, 20, 68, 20, 68, 45, 121, 16, 58, 27, 70, + 58, 105, 70, 123, 16, 58, 58, 105, 27, 70, 70, 123, 41, 69, 66, 106, + 66, 106, 100, 128, 9, 77, 21, 82, 21, 82, 46, 122, 18, 78, 28, 83, + 59, 109, 71, 124, 18, 78, 59, 109, 28, 83, 71, 124, 43, 97, 67, 110, + 67, 110, 101, 130, 16, 58, 27, 70, 58, 105, 70, 123, 21, 60, 29, 72, + 82, 127, 86, 128, 53, 80, 80, 111, 80, 111, 111, 144, 59, 83, 81, + 112, 109, 131, 116, 145, 21, 82, 29, 86, 60, 127, 72, 128, 28, 83, + 31, 87, 83, 131, 87, 132, 59, 109, 81, 116, 83, 131, 112, 145, 67, + 110, 85, 117, 110, 143, 117, 146, 16, 58, 58, 105, 27, 70, 70, 123, + 53, 80, 80, 111, 80, 111, 111, 144, 21, 60, 82, 127, 29, 72, 86, 128, + 59, 83, 109, 131, 81, 112, 116, 145, 21, 82, 60, 127, 29, 86, 72, + 128, 59, 109, 83, 131, 81, 116, 112, 145, 28, 83, 83, 131, 31, 87, + 87, 132, 67, 110, 110, 143, 85, 117, 117, 146, 41, 69, 66, 106, 66, + 106, 100, 128, 59, 83, 81, 112, 109, 131, 116, 145, 59, 83, 109, 131, + 81, 112, 116, 145, 79, 91, 115, 133, 115, 133, 138, 149, 46, 122, 72, + 128, 72, 128, 102, 141, 71, 124, 87, 132, 112, 145, 118, 147, 71, + 124, 112, 145, 87, 132, 118, 147, 101, 130, 117, 146, 117, 146, 139, + 150, 9, 77, 21, 82, 21, 82, 46, 122, 21, 82, 29, 86, 60, 127, 72, + 128, 21, 82, 60, 127, 29, 86, 72, 128, 46, 122, 72, 128, 72, 128, + 102, 141, 10, 89, 22, 90, 22, 90, 47, 126, 22, 90, 30, 91, 61, 129, + 73, 130, 22, 90, 61, 129, 30, 91, 73, 130, 47, 126, 73, 130, 73, 130, + 103, 142, 18, 78, 28, 83, 59, 109, 71, 124, 28, 83, 31, 87, 83, 131, + 87, 132, 59, 109, 83, 131, 81, 116, 112, 145, 71, 124, 87, 132, 112, + 145, 118, 147, 22, 90, 30, 91, 61, 129, 73, 130, 30, 91, 32, 92, 84, + 133, 88, 134, 61, 129, 84, 133, 84, 133, 113, 146, 73, 130, 88, 134, + 113, 146, 119, 148, 18, 78, 59, 109, 28, 83, 71, 124, 59, 109, 81, + 116, 83, 131, 112, 145, 28, 83, 83, 131, 31, 87, 87, 132, 71, 124, + 112, 145, 87, 132, 118, 147, 22, 90, 61, 129, 30, 91, 73, 130, 61, + 129, 84, 133, 84, 133, 113, 146, 30, 91, 84, 133, 32, 92, 88, 134, + 73, 130, 113, 146, 88, 134, 119, 148, 43, 97, 67, 110, 67, 110, 101, + 130, 67, 110, 85, 117, 110, 143, 117, 146, 67, 110, 110, 143, 85, + 117, 117, 146, 101, 130, 117, 146, 117, 146, 139, 150, 47, 126, 73, + 130, 73, 130, 103, 142, 73, 130, 88, 134, 113, 146, 119, 148, 73, + 130, 113, 146, 88, 134, 119, 148, 103, 142, 119, 148, 119, 148, 140, + 151, 11, 36, 14, 41, 36, 42, 41, 46, 14, 41, 18, 49, 55, 66, 59, 71, + 36, 42, 55, 66, 42, 62, 66, 72, 41, 46, 59, 71, 66, 72, 81, 87, 14, + 55, 18, 59, 41, 66, 49, 71, 18, 59, 22, 61, 59, 81, 61, 84, 41, 66, + 59, 81, 46, 72, 71, 87, 49, 71, 61, 84, 71, 87, 84, 92, 14, 41, 18, + 49, 55, 66, 59, 71, 18, 49, 24, 63, 78, 99, 79, 101, 55, 66, 78, 99, + 96, 100, 109, 112, 59, 71, 79, 101, 109, 112, 115, 117, 25, 69, 28, + 71, 69, 106, 71, 107, 28, 71, 30, 73, 83, 112, 84, 113, 69, 106, 83, + 112, 122, 128, 124, 132, 71, 107, 84, 113, 124, 132, 125, 134, 36, + 42, 55, 66, 42, 62, 66, 72, 55, 66, 78, 99, 96, 100, 109, 112, 42, + 62, 96, 100, 62, 94, 100, 102, 66, 72, 109, 112, 100, 102, 116, 118, + 55, 96, 78, 109, 66, 100, 99, 112, 78, 109, 90, 129, 109, 116, 129, + 133, 66, 100, 109, 116, 72, 102, 112, 118, 99, 112, 129, 133, 112, + 118, 133, 135, 41, 46, 59, 71, 66, 72, 81, 87, 59, 71, 79, 101, 109, + 112, 115, 117, 66, 72, 109, 112, 100, 102, 116, 118, 81, 87, 115, + 117, 116, 118, 138, 139, 69, 122, 83, 124, 106, 128, 112, 132, 83, + 124, 91, 130, 131, 145, 133, 146, 106, 128, 131, 145, 128, 141, 145, + 147, 112, 132, 133, 146, 145, 147, 149, 150, 14, 55, 18, 59, 41, 66, + 49, 71, 25, 69, 28, 71, 69, 106, 71, 107, 55, 96, 78, 109, 66, 100, + 99, 112, 69, 122, 83, 124, 106, 128, 112, 132, 18, 78, 24, 79, 49, + 99, 63, 101, 28, 83, 30, 84, 71, 112, 73, 113, 59, 109, 79, 115, 71, + 112, 101, 117, 71, 124, 84, 125, 107, 132, 113, 134, 18, 59, 22, 61, + 59, 81, 61, 84, 28, 71, 30, 73, 83, 112, 84, 113, 78, 109, 90, 129, + 109, 116, 129, 133, 83, 124, 91, 130, 131, 145, 133, 146, 28, 83, 30, + 84, 71, 112, 73, 113, 31, 87, 32, 88, 87, 118, 88, 119, 83, 131, 91, + 133, 124, 145, 130, 146, 87, 132, 92, 134, 132, 147, 134, 148, 41, + 66, 59, 81, 46, 72, 71, 87, 69, 106, 83, 112, 122, 128, 124, 132, 66, + 100, 109, 116, 72, 102, 112, 118, 106, 128, 131, 145, 128, 141, 145, + 147, 59, 109, 79, 115, 71, 112, 101, 117, 83, 131, 91, 133, 124, 145, + 130, 146, 81, 116, 115, 138, 87, 118, 117, 139, 112, 145, 133, 149, + 132, 147, 146, 150, 49, 71, 61, 84, 71, 87, 84, 92, 71, 107, 84, 113, + 124, 132, 125, 134, 99, 112, 129, 133, 112, 118, 133, 135, 112, 132, + 133, 146, 145, 147, 149, 150, 71, 124, 84, 125, 107, 132, 113, 134, + 87, 132, 92, 134, 132, 147, 134, 148, 112, 145, 133, 149, 132, 147, + 146, 150, 118, 147, 135, 150, 147, 152, 150, 153, 14, 55, 25, 69, 55, + 96, 69, 122, 18, 59, 28, 71, 78, 109, 83, 124, 41, 66, 69, 106, 66, + 100, 106, 128, 49, 71, 71, 107, 99, 112, 112, 132, 18, 78, 28, 83, + 59, 109, 71, 124, 24, 79, 30, 84, 79, 115, 84, 125, 49, 99, 71, 112, + 71, 112, 107, 132, 63, 101, 73, 113, 101, 117, 113, 134, 18, 59, 28, + 71, 78, 109, 83, 124, 22, 61, 30, 73, 90, 129, 91, 130, 59, 81, 83, + 112, 109, 116, 131, 145, 61, 84, 84, 113, 129, 133, 133, 146, 28, 83, + 31, 87, 83, 131, 87, 132, 30, 84, 32, 88, 91, 133, 92, 134, 71, 112, + 87, 118, 124, 145, 132, 147, 73, 113, 88, 119, 130, 146, 134, 148, + 41, 66, 69, 106, 66, 100, 106, 128, 59, 81, 83, 112, 109, 116, 131, + 145, 46, 72, 122, 128, 72, 102, 128, 141, 71, 87, 124, 132, 112, 118, + 145, 147, 59, 109, 83, 131, 81, 116, 112, 145, 79, 115, 91, 133, 115, + 138, 133, 149, 71, 112, 124, 145, 87, 118, 132, 147, 101, 117, 130, + 146, 117, 139, 146, 150, 49, 71, 71, 107, 99, 112, 112, 132, 61, 84, + 84, 113, 129, 133, 133, 146, 71, 87, 124, 132, 112, 118, 145, 147, + 84, 92, 125, 134, 133, 135, 149, 150, 71, 124, 87, 132, 112, 145, + 118, 147, 84, 125, 92, 134, 133, 149, 135, 150, 107, 132, 132, 147, + 132, 147, 147, 152, 113, 134, 134, 148, 146, 150, 150, 153, 18, 78, + 28, 83, 59, 109, 71, 124, 28, 83, 31, 87, 83, 131, 87, 132, 59, 109, + 83, 131, 81, 116, 112, 145, 71, 124, 87, 132, 112, 145, 118, 147, 22, + 90, 30, 91, 61, 129, 73, 130, 30, 91, 32, 92, 84, 133, 88, 134, 61, + 129, 84, 133, 84, 133, 113, 146, 73, 130, 88, 134, 113, 146, 119, + 148, 24, 79, 30, 84, 79, 115, 84, 125, 30, 84, 32, 88, 91, 133, 92, + 134, 79, 115, 91, 133, 115, 138, 133, 149, 84, 125, 92, 134, 133, + 149, 135, 150, 30, 91, 32, 92, 84, 133, 88, 134, 32, 92, 33, 93, 92, + 135, 93, 136, 84, 133, 92, 135, 125, 149, 134, 150, 88, 134, 93, 136, + 134, 150, 136, 151, 49, 99, 71, 112, 71, 112, 107, 132, 71, 112, 87, + 118, 124, 145, 132, 147, 71, 112, 124, 145, 87, 118, 132, 147, 107, + 132, 132, 147, 132, 147, 147, 152, 61, 129, 84, 133, 84, 133, 113, + 146, 84, 133, 92, 135, 125, 149, 134, 150, 84, 133, 125, 149, 92, + 135, 134, 150, 113, 146, 134, 150, 134, 150, 148, 153, 63, 101, 73, + 113, 101, 117, 113, 134, 73, 113, 88, 119, 130, 146, 134, 148, 101, + 117, 130, 146, 117, 139, 146, 150, 113, 134, 134, 148, 146, 150, 150, + 153, 73, 130, 88, 134, 113, 146, 119, 148, 88, 134, 93, 136, 134, + 150, 136, 151, 113, 146, 134, 150, 134, 150, 148, 153, 119, 148, 136, + 151, 148, 153, 151, 154, 11, 36, 36, 42, 14, 41, 41, 46, 36, 42, 42, + 62, 55, 66, 66, 72, 14, 41, 55, 66, 18, 49, 59, 71, 41, 46, 66, 72, + 59, 71, 81, 87, 14, 55, 41, 66, 18, 59, 49, 71, 41, 66, 46, 72, 59, + 81, 71, 87, 18, 59, 59, 81, 22, 61, 61, 84, 49, 71, 71, 87, 61, 84, + 84, 92, 36, 42, 42, 62, 55, 66, 66, 72, 42, 62, 62, 94, 96, 100, 100, + 102, 55, 66, 96, 100, 78, 99, 109, 112, 66, 72, 100, 102, 109, 112, + 116, 118, 55, 96, 66, 100, 78, 109, 99, 112, 66, 100, 72, 102, 109, + 116, 112, 118, 78, 109, 109, 116, 90, 129, 129, 133, 99, 112, 112, + 118, 129, 133, 133, 135, 14, 41, 55, 66, 18, 49, 59, 71, 55, 66, 96, + 100, 78, 99, 109, 112, 18, 49, 78, 99, 24, 63, 79, 101, 59, 71, 109, + 112, 79, 101, 115, 117, 25, 69, 69, 106, 28, 71, 71, 107, 69, 106, + 122, 128, 83, 112, 124, 132, 28, 71, 83, 112, 30, 73, 84, 113, 71, + 107, 124, 132, 84, 113, 125, 134, 41, 46, 66, 72, 59, 71, 81, 87, 66, + 72, 100, 102, 109, 112, 116, 118, 59, 71, 109, 112, 79, 101, 115, + 117, 81, 87, 116, 118, 115, 117, 138, 139, 69, 122, 106, 128, 83, + 124, 112, 132, 106, 128, 128, 141, 131, 145, 145, 147, 83, 124, 131, + 145, 91, 130, 133, 146, 112, 132, 145, 147, 133, 146, 149, 150, 14, + 55, 41, 66, 18, 59, 49, 71, 55, 96, 66, 100, 78, 109, 99, 112, 25, + 69, 69, 106, 28, 71, 71, 107, 69, 122, 106, 128, 83, 124, 112, 132, + 18, 78, 49, 99, 24, 79, 63, 101, 59, 109, 71, 112, 79, 115, 101, 117, + 28, 83, 71, 112, 30, 84, 73, 113, 71, 124, 107, 132, 84, 125, 113, + 134, 41, 66, 46, 72, 59, 81, 71, 87, 66, 100, 72, 102, 109, 116, 112, + 118, 69, 106, 122, 128, 83, 112, 124, 132, 106, 128, 128, 141, 131, + 145, 145, 147, 59, 109, 71, 112, 79, 115, 101, 117, 81, 116, 87, 118, + 115, 138, 117, 139, 83, 131, 124, 145, 91, 133, 130, 146, 112, 145, + 132, 147, 133, 149, 146, 150, 18, 59, 59, 81, 22, 61, 61, 84, 78, + 109, 109, 116, 90, 129, 129, 133, 28, 71, 83, 112, 30, 73, 84, 113, + 83, 124, 131, 145, 91, 130, 133, 146, 28, 83, 71, 112, 30, 84, 73, + 113, 83, 131, 124, 145, 91, 133, 130, 146, 31, 87, 87, 118, 32, 88, + 88, 119, 87, 132, 132, 147, 92, 134, 134, 148, 49, 71, 71, 87, 61, + 84, 84, 92, 99, 112, 112, 118, 129, 133, 133, 135, 71, 107, 124, 132, + 84, 113, 125, 134, 112, 132, 145, 147, 133, 146, 149, 150, 71, 124, + 107, 132, 84, 125, 113, 134, 112, 145, 132, 147, 133, 149, 146, 150, + 87, 132, 132, 147, 92, 134, 134, 148, 118, 147, 147, 152, 135, 150, + 150, 153, 14, 55, 55, 96, 25, 69, 69, 122, 41, 66, 66, 100, 69, 106, + 106, 128, 18, 59, 78, 109, 28, 71, 83, 124, 49, 71, 99, 112, 71, 107, + 112, 132, 18, 78, 59, 109, 28, 83, 71, 124, 49, 99, 71, 112, 71, 112, + 107, 132, 24, 79, 79, 115, 30, 84, 84, 125, 63, 101, 101, 117, 73, + 113, 113, 134, 41, 66, 66, 100, 69, 106, 106, 128, 46, 72, 72, 102, + 122, 128, 128, 141, 59, 81, 109, 116, 83, 112, 131, 145, 71, 87, 112, + 118, 124, 132, 145, 147, 59, 109, 81, 116, 83, 131, 112, 145, 71, + 112, 87, 118, 124, 145, 132, 147, 79, 115, 115, 138, 91, 133, 133, + 149, 101, 117, 117, 139, 130, 146, 146, 150, 18, 59, 78, 109, 28, 71, + 83, 124, 59, 81, 109, 116, 83, 112, 131, 145, 22, 61, 90, 129, 30, + 73, 91, 130, 61, 84, 129, 133, 84, 113, 133, 146, 28, 83, 83, 131, + 31, 87, 87, 132, 71, 112, 124, 145, 87, 118, 132, 147, 30, 84, 91, + 133, 32, 88, 92, 134, 73, 113, 130, 146, 88, 119, 134, 148, 49, 71, + 99, 112, 71, 107, 112, 132, 71, 87, 112, 118, 124, 132, 145, 147, 61, + 84, 129, 133, 84, 113, 133, 146, 84, 92, 133, 135, 125, 134, 149, + 150, 71, 124, 112, 145, 87, 132, 118, 147, 107, 132, 132, 147, 132, + 147, 147, 152, 84, 125, 133, 149, 92, 134, 135, 150, 113, 134, 146, + 150, 134, 148, 150, 153, 18, 78, 59, 109, 28, 83, 71, 124, 59, 109, + 81, 116, 83, 131, 112, 145, 28, 83, 83, 131, 31, 87, 87, 132, 71, + 124, 112, 145, 87, 132, 118, 147, 22, 90, 61, 129, 30, 91, 73, 130, + 61, 129, 84, 133, 84, 133, 113, 146, 30, 91, 84, 133, 32, 92, 88, + 134, 73, 130, 113, 146, 88, 134, 119, 148, 49, 99, 71, 112, 71, 112, + 107, 132, 71, 112, 87, 118, 124, 145, 132, 147, 71, 112, 124, 145, + 87, 118, 132, 147, 107, 132, 132, 147, 132, 147, 147, 152, 61, 129, + 84, 133, 84, 133, 113, 146, 84, 133, 92, 135, 125, 149, 134, 150, 84, + 133, 125, 149, 92, 135, 134, 150, 113, 146, 134, 150, 134, 150, 148, + 153, 24, 79, 79, 115, 30, 84, 84, 125, 79, 115, 115, 138, 91, 133, + 133, 149, 30, 84, 91, 133, 32, 88, 92, 134, 84, 125, 133, 149, 92, + 134, 135, 150, 30, 91, 84, 133, 32, 92, 88, 134, 84, 133, 125, 149, + 92, 135, 134, 150, 32, 92, 92, 135, 33, 93, 93, 136, 88, 134, 134, + 150, 93, 136, 136, 151, 63, 101, 101, 117, 73, 113, 113, 134, 101, + 117, 117, 139, 130, 146, 146, 150, 73, 113, 130, 146, 88, 119, 134, + 148, 113, 134, 146, 150, 134, 148, 150, 153, 73, 130, 113, 146, 88, + 134, 119, 148, 113, 146, 134, 150, 134, 150, 148, 153, 88, 134, 134, + 150, 93, 136, 136, 151, 119, 148, 148, 153, 136, 151, 151, 154, 34, + 37, 37, 43, 37, 43, 43, 47, 37, 43, 43, 63, 64, 67, 67, 73, 37, 43, + 64, 67, 43, 63, 67, 73, 43, 47, 67, 73, 67, 73, 85, 88, 37, 64, 43, + 67, 43, 67, 63, 73, 43, 67, 47, 73, 67, 85, 73, 88, 43, 67, 67, 85, + 47, 73, 73, 88, 63, 73, 73, 88, 73, 88, 88, 93, 37, 43, 43, 63, 64, + 67, 67, 73, 43, 63, 63, 95, 97, 101, 101, 103, 64, 67, 97, 101, 97, + 101, 110, 113, 67, 73, 101, 103, 110, 113, 117, 119, 64, 97, 67, 101, + 97, 110, 101, 113, 67, 101, 73, 103, 110, 117, 113, 119, 97, 110, + 110, 117, 126, 130, 130, 134, 101, 113, 113, 119, 130, 134, 134, 136, + 37, 43, 64, 67, 43, 63, 67, 73, 64, 67, 97, 101, 97, 101, 110, 113, + 43, 63, 97, 101, 63, 95, 101, 103, 67, 73, 110, 113, 101, 103, 117, + 119, 64, 97, 97, 110, 67, 101, 101, 113, 97, 110, 126, 130, 110, 117, + 130, 134, 67, 101, 110, 117, 73, 103, 113, 119, 101, 113, 130, 134, + 113, 119, 134, 136, 43, 47, 67, 73, 67, 73, 85, 88, 67, 73, 101, 103, + 110, 113, 117, 119, 67, 73, 110, 113, 101, 103, 117, 119, 85, 88, + 117, 119, 117, 119, 139, 140, 97, 126, 110, 130, 110, 130, 117, 134, + 110, 130, 130, 142, 143, 146, 146, 148, 110, 130, 143, 146, 130, 142, + 146, 148, 117, 134, 146, 148, 146, 148, 150, 151, 37, 64, 43, 67, 43, + 67, 63, 73, 64, 97, 67, 101, 97, 110, 101, 113, 64, 97, 97, 110, 67, + 101, 101, 113, 97, 126, 110, 130, 110, 130, 117, 134, 43, 97, 63, + 101, 63, 101, 95, 103, 67, 110, 73, 113, 101, 117, 103, 119, 67, 110, + 101, 117, 73, 113, 103, 119, 101, 130, 113, 134, 113, 134, 119, 136, + 43, 67, 47, 73, 67, 85, 73, 88, 67, 101, 73, 103, 110, 117, 113, 119, + 97, 110, 126, 130, 110, 117, 130, 134, 110, 130, 130, 142, 143, 146, + 146, 148, 67, 110, 73, 113, 101, 117, 103, 119, 85, 117, 88, 119, + 117, 139, 119, 140, 110, 143, 130, 146, 130, 146, 142, 148, 117, 146, + 134, 148, 146, 150, 148, 151, 43, 67, 67, 85, 47, 73, 73, 88, 97, + 110, 110, 117, 126, 130, 130, 134, 67, 101, 110, 117, 73, 103, 113, + 119, 110, 130, 143, 146, 130, 142, 146, 148, 67, 110, 101, 117, 73, + 113, 103, 119, 110, 143, 130, 146, 130, 146, 142, 148, 85, 117, 117, + 139, 88, 119, 119, 140, 117, 146, 146, 150, 134, 148, 148, 151, 63, + 73, 73, 88, 73, 88, 88, 93, 101, 113, 113, 119, 130, 134, 134, 136, + 101, 113, 130, 134, 113, 119, 134, 136, 117, 134, 146, 148, 146, 148, + 150, 151, 101, 130, 113, 134, 113, 134, 119, 136, 117, 146, 134, 148, + 146, 150, 148, 151, 117, 146, 146, 150, 134, 148, 148, 151, 139, 150, + 150, 153, 150, 153, 153, 154, 37, 64, 64, 97, 64, 97, 97, 126, 43, + 67, 67, 101, 97, 110, 110, 130, 43, 67, 97, 110, 67, 101, 110, 130, + 63, 73, 101, 113, 101, 113, 117, 134, 43, 97, 67, 110, 67, 110, 101, + 130, 63, 101, 73, 113, 101, 117, 113, 134, 63, 101, 101, 117, 73, + 113, 113, 134, 95, 103, 103, 119, 103, 119, 119, 136, 43, 67, 67, + 101, 97, 110, 110, 130, 47, 73, 73, 103, 126, 130, 130, 142, 67, 85, + 110, 117, 110, 117, 143, 146, 73, 88, 113, 119, 130, 134, 146, 148, + 67, 110, 85, 117, 110, 143, 117, 146, 73, 113, 88, 119, 130, 146, + 134, 148, 101, 117, 117, 139, 130, 146, 146, 150, 103, 119, 119, 140, + 142, 148, 148, 151, 43, 67, 97, 110, 67, 101, 110, 130, 67, 85, 110, + 117, 110, 117, 143, 146, 47, 73, 126, 130, 73, 103, 130, 142, 73, 88, + 130, 134, 113, 119, 146, 148, 67, 110, 110, 143, 85, 117, 117, 146, + 101, 117, 130, 146, 117, 139, 146, 150, 73, 113, 130, 146, 88, 119, + 134, 148, 103, 119, 142, 148, 119, 140, 148, 151, 63, 73, 101, 113, + 101, 113, 117, 134, 73, 88, 113, 119, 130, 134, 146, 148, 73, 88, + 130, 134, 113, 119, 146, 148, 88, 93, 134, 136, 134, 136, 150, 151, + 101, 130, 117, 146, 117, 146, 139, 150, 113, 134, 134, 148, 146, 150, + 150, 153, 113, 134, 146, 150, 134, 148, 150, 153, 119, 136, 148, 151, + 148, 151, 153, 154, 43, 97, 67, 110, 67, 110, 101, 130, 67, 110, 85, + 117, 110, 143, 117, 146, 67, 110, 110, 143, 85, 117, 117, 146, 101, + 130, 117, 146, 117, 146, 139, 150, 47, 126, 73, 130, 73, 130, 103, + 142, 73, 130, 88, 134, 113, 146, 119, 148, 73, 130, 113, 146, 88, + 134, 119, 148, 103, 142, 119, 148, 119, 148, 140, 151, 63, 101, 73, + 113, 101, 117, 113, 134, 73, 113, 88, 119, 130, 146, 134, 148, 101, + 117, 130, 146, 117, 139, 146, 150, 113, 134, 134, 148, 146, 150, 150, + 153, 73, 130, 88, 134, 113, 146, 119, 148, 88, 134, 93, 136, 134, + 150, 136, 151, 113, 146, 134, 150, 134, 150, 148, 153, 119, 148, 136, + 151, 148, 153, 151, 154, 63, 101, 101, 117, 73, 113, 113, 134, 101, + 117, 117, 139, 130, 146, 146, 150, 73, 113, 130, 146, 88, 119, 134, + 148, 113, 134, 146, 150, 134, 148, 150, 153, 73, 130, 113, 146, 88, + 134, 119, 148, 113, 146, 134, 150, 134, 150, 148, 153, 88, 134, 134, + 150, 93, 136, 136, 151, 119, 148, 148, 153, 136, 151, 151, 154, 95, + 103, 103, 119, 103, 119, 119, 136, 103, 119, 119, 140, 142, 148, 148, + 151, 103, 119, 142, 148, 119, 140, 148, 151, 119, 136, 148, 151, 148, + 151, 153, 154, 103, 142, 119, 148, 119, 148, 140, 151, 119, 148, 136, + 151, 148, 153, 151, 154, 119, 148, 148, 153, 136, 151, 151, 154, 140, + 151, 151, 154, 151, 154, 154, 155 +}; + +const unsigned int igraph_i_isographs_3[] = { 0, 1, 3, 5, 6, 7, 10, 11, 15, 21, + 23, 25, 27, 30, 31, 63 + }; +const unsigned int igraph_i_isographs_3u[] = { 0, 1, 3, 7 }; +const unsigned int igraph_i_isographs_4[] = { + 0, 1, 3, 7, 9, 10, 11, 14, 15, 18, 19, 20, 21, + 22, 23, 27, 29, 30, 31, 54, 55, 63, 73, 75, 76, 77, + 79, 81, 83, 84, 85, 86, 87, 90, 91, 92, 93, 94, 95, + 98, 99, 100, 101, 102, 103, 106, 107, 108, 109, 110, 111, 115, + 116, 117, 118, 119, 122, 123, 124, 125, 126, 127, 219, 220, 221, + 223, 228, 229, 230, 231, 237, 238, 239, 246, 247, 255, 292, 293, + 295, 301, 302, 303, 310, 311, 319, 365, 367, 373, 375, 382, 383, + 511, 585, 587, 591, 593, 594, 595, 596, 597, 598, 599, 601, 602, + 603, 604, 605, 606, 607, 625, 626, 627, 630, 631, 633, 634, 635, + 638, 639, 659, 660, 661, 663, 666, 667, 669, 670, 671, 674, 675, + 678, 679, 683, 686, 687, 694, 695, 703, 729, 731, 732, 733, 735, + 737, 739, 741, 742, 743, 745, 746, 747, 748, 749, 750, 751, 753, + 755, 756, 757, 758, 759, 761, 762, 763, 764, 765, 766, 767, 819, + 822, 823, 826, 827, 830, 831, 875, 876, 877, 879, 883, 885, 886, + 887, 891, 892, 893, 894, 895, 947, 949, 951, 955, 957, 958, 959, + 1019, 1020, 1021, 1023, 1755, 1757, 1758, 1759, 1782, 1783, 1791, 1883, 1887, + 1907, 1911, 1917, 1918, 1919, 2029, 2031, 2039, 2047, 4095 +}; +const unsigned int igraph_i_isographs_4u[] = { 0, 1, 3, 7, 11, 12, 13, + 15, 30, 31, 63 + }; +const unsigned int igraph_i_isographs_5u[] = { + 0, 1, 3, 7, 11, 12, 13, 15, 30, 31, 63, 75, 76, 77, 79, 86, 87, 94, + 95, 116, 117, 119, 127, 222, 223, 235, 236, 237, 239, 254, 255, 507, + 511, 1023 +}; +const unsigned int igraph_i_isographs_6u[] = { + 0, 1, 3, 7, 11, 12, 13, 15, 30, 31, 63, 75, 76, 77, 79, 86, 87, 94, + 95, 116, 117, 119, 127, 222, 223, 235, 236, 237, 239, 254, 255, 507, + 511, 1023, 1099, 1100, 1101, 1103, 1108, 1109, 1110, 1111, 1118, + 1119, 1140, 1141, 1143, 1151, 1182, 1183, 1184, 1185, 1187, 1191, + 1194, 1195, 1196, 1197, 1198, 1199, 1214, 1215, 1246, 1247, 1259, + 1260, 1261, 1263, 1268, 1269, 1270, 1271, 1278, 1279, 1456, 1457, + 1459, 1460, 1461, 1463, 1465, 1467, 1468, 1469, 1471, 1531, 1532, + 1533, 1535, 1972, 1973, 1975, 1983, 2047, 3294, 3295, 3306, 3307, + 3308, 3309, 3310, 3311, 3326, 3327, 3440, 3441, 3443, 3447, 3448, + 3449, 3451, 3452, 3453, 3455, 3576, 3577, 3578, 3579, 3582, 3583, + 3873, 3875, 3879, 3885, 3887, 3903, 3947, 3948, 3949, 3950, 3951, + 3958, 3959, 3966, 3967, 4094, 4095, 7672, 7673, 7675, 7679, 7902, + 7903, 7915, 7916, 7917, 7919, 7934, 7935, 8185, 8187, 8191, 16350, + 16351, 16383, 32767 +}; + +const unsigned int igraph_i_classedges_3[] = { 1, 2, 0, 2, 2, 1, 0, 1, 2, 0, 1, 0 }; +const unsigned int igraph_i_classedges_3u[] = { 1, 2, 0, 2, 0, 1 }; +const unsigned int igraph_i_classedges_4[] = { 2, 3, 1, 3, 0, 3, 3, 2, 1, 2, 0, 2, + 3, 1, 2, 1, 0, 1, 3, 0, 2, 0, 1, 0 + }; +const unsigned int igraph_i_classedges_4u[] = { 2, 3, 1, 3, 0, 3, 1, 2, 0, 2, 0, 1 }; +const unsigned int igraph_i_classedges_5u[] = { 3, 4, 2, 4, 1, 4, 0, 4, 2, 3, 1, 3, + 0, 3, 1, 2, 0, 2, 0, 1 }; +const unsigned int igraph_i_classedges_6u[] = { 4, 5, 3, 5, 2, 5, 1, 5, 0, 5, 3, 4, + 2, 4, 1, 4, 0, 4, 2, 3, 1, 3, 0, 3, + 1, 2, 0, 2, 0, 1 }; + +/** + * \function igraph_isoclass + * \brief Determine the isomorphism class of small graphs. + * + * + * All graphs with a given number of vertices belong to a number of + * isomorphism classes, with every graph in a given class being + * isomorphic to each other. + * + * + * This function gives the isomorphism class (a number) of a + * graph. Two graphs have the same isomorphism class if and only if + * they are isomorphic. + * + * + * The first isomorphism class is numbered zero and it contains the edgeless + * graph. The last isomorphism class contains the full graph. The number of + * isomorphism classes for directed graphs with three vertices is 16 + * (between 0 and 15), for undirected graph it is only 4. For graphs + * with four vertices it is 218 (directed) and 11 (undirected). + * For 5 and 6 vertex undirected graphs, it is 34 and 156, respectively. + * For more information, see https://oeis.org/A000273 and https://oeis.org/A000088. + * + * + * At the moment, 3- and 4-vertex directed graphs and 3 to 6 vertex + * undirected graphs are supported. + * + * + * Multi-edges and self-loops are ignored by this function. + * + * \param graph The graph object. + * \param isoclass Pointer to an integer, the isomorphism class will + * be stored here. + * \return Error code. + * \sa \ref igraph_isomorphic(), \ref igraph_isoclass_subgraph(), + * \ref igraph_isoclass_create(), \ref igraph_motifs_randesu(). + * + * Because of some limitations this function works only for graphs + * with three of four vertices. + * + * + * Time complexity: O(|E|), the number of edges in the graph. + */ +int igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass) { + long int e; + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + unsigned int idx, mul; + const unsigned int *arr_idx, *arr_code; + unsigned int code; + + if (igraph_is_directed(graph)) { + switch (no_of_nodes) { + case 3: + arr_idx = igraph_i_isoclass_3_idx; + arr_code = igraph_i_isoclass2_3; + mul = 3; + break; + case 4: + arr_idx = igraph_i_isoclass_4_idx; + arr_code = igraph_i_isoclass2_4; + mul = 4; + break; + default: + IGRAPH_ERROR("Directed isoclass is only implemented for graphs with 3 or 4 vertices.", + IGRAPH_UNIMPLEMENTED); + } + } else { + switch (no_of_nodes) { + case 3: + arr_idx = igraph_i_isoclass_3u_idx; + arr_code = igraph_i_isoclass2_3u; + mul = 3; + break; + case 4: + arr_idx = igraph_i_isoclass_4u_idx; + arr_code = igraph_i_isoclass2_4u; + mul = 4; + break; + case 5: + arr_idx = igraph_i_isoclass_5u_idx; + arr_code = igraph_i_isoclass2_5u; + mul = 5; + break; + case 6: + arr_idx = igraph_i_isoclass_6u_idx; + arr_code = igraph_i_isoclass2_6u; + mul = 6; + break; + default: + IGRAPH_ERROR("Undirected isoclass is only implemented for graphs with 3 to 6 vertices.", + IGRAPH_UNIMPLEMENTED); + } + } + + code = 0; + for (e = 0; e < no_of_edges; e++) { + idx = mul * IGRAPH_FROM(graph, e) + IGRAPH_TO(graph, e); + code |= arr_idx[idx]; + } + + *isoclass = (igraph_integer_t) arr_code[code]; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_isoclass_subgraph + * \brief The isomorphism class of a subgraph of a graph. + * + * This function identifies the isomorphism class of the subgraph + * induced the vertices specified in \p vids. + * + * + * At the moment, 3- and 4-vertex directed graphs and 3 to 6 vertex + * undirected graphs are supported. + * + * + * Multi-edges and self-loops are ignored by this function. + * + * \param graph The graph object. + * \param vids A vector containing the vertex ids to be considered as + * a subgraph. Each vertex id should be included at most once. + * \param isoclass Pointer to an integer, this will be set to the + * isomorphism class. + * \return Error code. + * \sa \ref igraph_isoclass(), \ref igraph_isomorphic(), + * \ref igraph_isoclass_create(). + * + * Time complexity: O((d+n)*n), d is the average degree in the network, + * and n is the number of vertices in \c vids. + */ +int igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vector_t *vids, + igraph_integer_t *isoclass) { + int subgraph_size = (int) igraph_vector_size(vids); + igraph_vector_t neis; + + unsigned int mul, idx; + const unsigned int *arr_idx, *arr_code; + unsigned int code = 0; + + long int i, j, s; + + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + if (igraph_is_directed(graph)) { + switch (subgraph_size) { + case 3: + arr_idx = igraph_i_isoclass_3_idx; + arr_code = igraph_i_isoclass2_3; + mul = 3; + break; + case 4: + arr_idx = igraph_i_isoclass_4_idx; + arr_code = igraph_i_isoclass2_4; + mul = 4; + break; + default: + IGRAPH_ERROR("Directed isoclass is only implemented for graphs with 3 or 4 vertices.", + IGRAPH_UNIMPLEMENTED); + } + } else { + switch (subgraph_size) { + case 3: + arr_idx = igraph_i_isoclass_3u_idx; + arr_code = igraph_i_isoclass2_3u; + mul = 3; + break; + case 4: + arr_idx = igraph_i_isoclass_4u_idx; + arr_code = igraph_i_isoclass2_4u; + mul = 4; + break; + case 5: + arr_idx = igraph_i_isoclass_5u_idx; + arr_code = igraph_i_isoclass2_5u; + mul = 5; + break; + case 6: + arr_idx = igraph_i_isoclass_6u_idx; + arr_code = igraph_i_isoclass2_6u; + mul = 6; + break; + default: + IGRAPH_ERROR("Undirected isoclass is only implemented for graphs with 3 to 6 vertices.", + IGRAPH_UNIMPLEMENTED); + } + } + + for (i = 0; i < subgraph_size; i++) { + long int from = (long int) VECTOR(*vids)[i]; + igraph_neighbors(graph, &neis, (igraph_integer_t) from, IGRAPH_OUT); + s = igraph_vector_size(&neis); + for (j = 0; j < s; j++) { + long int nei = (long int) VECTOR(neis)[j], to; + if (igraph_vector_search(vids, 0, nei, &to)) { + idx = (mul * i + to); + code |= arr_idx[idx]; + } + } + } + + *isoclass = (igraph_integer_t) arr_code[code]; + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_isoclass_create + * \brief Creates a graph from the given isomorphism class. + * + * + * This function creates the canonical representative graph of the + * given isomorphism class. + * + * + * The isomorphism class is an integer between 0 and the number of + * unique unlabeled (i.e. non-isomorphic) graphs on the given number + * of vertices and give directedness. See https://oeis.org/A000273 + * and https://oeis.org/A000088 for the number of directed and + * undirected graphs on \p size nodes. + * + * + * At the moment, 3- and 4-vertex directed graphs and 3 to 6 vertex + * undirected graphs are supported. + * + * \param graph Pointer to an uninitialized graph object. + * \param size The number of vertices to add to the graph. + * \param number The isomorphism class. + * \param directed Logical constant, whether to create a directed + * graph. + * \return Error code. + * \sa \ref igraph_isoclass(), + * \ref igraph_isoclass_subgraph(), + * \ref igraph_isomorphic(). + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number + * of edges in the graph to create. + */ +int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, + igraph_integer_t number, igraph_bool_t directed) { + igraph_vector_t edges; + const unsigned int *classedges; + long int graphcount; + long int power; + long int pos; + unsigned int code; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + +#define CHECK_ISOCLASS(number, directed, size, graphcount) \ + IGRAPH_ERRORF( \ + "Isoclass %" IGRAPH_PRId " requested, but there are only %ld" \ + " %s graphs of size %" IGRAPH_PRId ".", IGRAPH_EINVAL, \ + (igraph_integer_t) number, graphcount, directed ? "directed" : "undirected", size) + + if (directed) { + switch (size) { + case 3: { + classedges = igraph_i_classedges_3; + graphcount = sizeof(igraph_i_isographs_3) / sizeof(igraph_i_isographs_3[0]); + + if (number < 0 || number >= graphcount) { + CHECK_ISOCLASS(number, directed, size, graphcount); + } + + code = igraph_i_isographs_3[ (long int) number]; + power = 32; + + break; + } + case 4: { + classedges = igraph_i_classedges_4; + graphcount = sizeof(igraph_i_isographs_4) / sizeof(igraph_i_isographs_4[0]); + + if (number < 0 || number >= graphcount) { + CHECK_ISOCLASS(number, directed, size, graphcount); + } + + code = igraph_i_isographs_4[ (long int) number]; + power = 2048; + + break; + } + default: + IGRAPH_ERROR("Directed isoclasses are supported only for graphs with 3 or 4 vertices.", + IGRAPH_UNIMPLEMENTED); + } + + } else { + switch (size) { + case 3: { + classedges = igraph_i_classedges_3u; + graphcount = sizeof(igraph_i_isographs_3u) / sizeof(igraph_i_isographs_3u[0]); + + if (number < 0 || number >= graphcount) { + CHECK_ISOCLASS(number, directed, size, graphcount); + } + + code = igraph_i_isographs_3u[ (long int) number]; + power = 4; + + break; + } + case 4: { + classedges = igraph_i_classedges_4u; + graphcount = sizeof(igraph_i_isographs_4u) / sizeof(igraph_i_isographs_4u[0]); + + if (number < 0 || number >= graphcount) { + CHECK_ISOCLASS(number, directed, size, graphcount); + } + + code = igraph_i_isographs_4u[ (long int) number]; + power = 32; + + break; + } + case 5: { + classedges = igraph_i_classedges_5u; + graphcount = sizeof(igraph_i_isographs_5u) / sizeof(igraph_i_isographs_5u[0]); + + if (number < 0 || number >= graphcount) { + CHECK_ISOCLASS(number, directed, size, graphcount); + } + + code = igraph_i_isographs_5u[ (long int) number]; + power = 512; + + break; + } + case 6: { + classedges = igraph_i_classedges_6u; + graphcount = sizeof(igraph_i_isographs_6u) / sizeof(igraph_i_isographs_6u[0]); + + if (number < 0 || number >= graphcount) { + CHECK_ISOCLASS(number, directed, size, graphcount); + } + + code = igraph_i_isographs_6u[ (long int) number]; + power = 16384; + + break; + } + default: + IGRAPH_ERROR("Undirected isoclasses are supported only for graphs with 3 to 6 vertices.", + IGRAPH_UNIMPLEMENTED); + } + } + +#undef CHECK_ISOCLASS + + pos = 0; + while (code > 0) { + if (code >= power) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, classedges[2 * pos])); + IGRAPH_CHECK(igraph_vector_push_back(&edges, classedges[2 * pos + 1])); + code -= power; + } + power /= 2; + pos++; + } + + IGRAPH_CHECK(igraph_create(graph, &edges, size, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/isomorphism/isoclasses.h b/src/rigraph/core/isomorphism/isoclasses.h new file mode 100644 index 0000000..67fa9a2 --- /dev/null +++ b/src/rigraph/core/isomorphism/isoclasses.h @@ -0,0 +1,46 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2008-2020 The igraph development team + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_ISOCLASSES_H +#define IGRAPH_ISOCLASSES_H + +#include "igraph_decls.h" + +__BEGIN_DECLS + +extern const unsigned int igraph_i_isoclass2_3[]; +extern const unsigned int igraph_i_isoclass2_4[]; +extern const unsigned int igraph_i_isoclass2_3u[]; +extern const unsigned int igraph_i_isoclass2_4u[]; +extern const unsigned int igraph_i_isoclass2_5u[]; +extern const unsigned int igraph_i_isoclass2_6u[]; +extern const unsigned int igraph_i_isoclass_3_idx[]; +extern const unsigned int igraph_i_isoclass_4_idx[]; +extern const unsigned int igraph_i_isoclass_3u_idx[]; +extern const unsigned int igraph_i_isoclass_4u_idx[]; +extern const unsigned int igraph_i_isoclass_5u_idx[]; +extern const unsigned int igraph_i_isoclass_6u_idx[]; + +__END_DECLS + +#endif diff --git a/src/rigraph/core/isomorphism/isomorphism_misc.c b/src/rigraph/core/isomorphism/isomorphism_misc.c new file mode 100644 index 0000000..235e5e7 --- /dev/null +++ b/src/rigraph/core/isomorphism/isomorphism_misc.c @@ -0,0 +1,115 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_topology.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" + +/** + * \function igraph_simplify_and_colorize + * \brief Simplify the graph and compute self-loop and edge multiplicities. + * + * + * This function creates a vertex and edge colored simple graph from the input + * graph. The vertex colors are computed as the number of incident self-loops + * to each vertex in the input graph. The edge colors are computed as the number of + * parallel edges in the input graph that were merged to create each edge + * in the simple graph. + * + * + * The resulting colored simple graph is suitable for use by isomorphism checking + * algorithms such as VF2, which only support simple graphs, but can consider + * vertex and edge colors. + * + * \param graph The graph object, typically having self-loops or multi-edges. + * \param res An uninitialized graph object. The result will be stored here + * \param vertex_color Computed vertex colors corresponding to self-loop multiplicities. + * \param edge_color Computed edge colors corresponding to edge multiplicities + * \return Error code. + * + * \sa \ref igraph_simplify(), \ref igraph_isomorphic_vf2(), \ref igraph_subisomorphic_vf2() + * + */ +int igraph_simplify_and_colorize( + const igraph_t *graph, igraph_t *res, + igraph_vector_int_t *vertex_color, igraph_vector_int_t *edge_color) { + igraph_es_t es; + igraph_eit_t eit; + igraph_vector_t edges; + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int pto = -1, pfrom = -1; + long int i; + + IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_FROM)); + IGRAPH_FINALLY(igraph_es_destroy, &es); + IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + + IGRAPH_CHECK(igraph_vector_int_resize(vertex_color, no_of_nodes)); + igraph_vector_int_null(vertex_color); + + IGRAPH_CHECK(igraph_vector_int_resize(edge_color, no_of_edges)); + igraph_vector_int_null(edge_color); + + i = -1; + for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { + long int edge = IGRAPH_EIT_GET(eit); + long int from = IGRAPH_FROM(graph, edge); + long int to = IGRAPH_TO(graph, edge); + + if (to == from) { + VECTOR(*vertex_color)[to]++; + continue; + } + + if (to == pto && from == pfrom) { + VECTOR(*edge_color)[i]++; + } else { + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + i++; + VECTOR(*edge_color)[i] = 1; + } + + pfrom = from; pto = to; + } + + igraph_vector_int_resize(edge_color, i + 1); + + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, igraph_is_directed(graph))); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/isomorphism/lad.c b/src/rigraph/core/isomorphism/lad.c new file mode 100644 index 0000000..82f9a2f --- /dev/null +++ b/src/rigraph/core/isomorphism/lad.c @@ -0,0 +1,1674 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* + The contents of this file was originally taken from the LAD + homepage: http://liris.cnrs.fr/csolnon/LAD.html and then + modified to fit better into igraph. + + Unfortunately LAD seems to have no version numbers. The files + were apparently last changed on the 29th of June, 2010. + + The original copyright message follows here. The CeCILL-B V1 license + is GPL compatible, because instead of V1, one can freely choose to + use V2, and V2 is explicitly GPL compatible. +*/ + +/* This software has been written by Christine Solnon. + It is distributed under the CeCILL-B FREE SOFTWARE LICENSE + see http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html + for more details +*/ + +/* Several modifications had to be made to the original LAD implementation + to make it compile with non-C99-compliant compilers such as MSVC. In + particular, I had to remove all the variable-sized arrays. + -- Tamas Nepusz, 11 July 2013 +*/ + +#include "igraph_topology.h" +#include "igraph_interface.h" +#include "igraph_adjlist.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" +#include "igraph_memory.h" +#include "igraph_matrix.h" +#include "igraph_qsort.h" + +#include "../core/interruption.h" + +#include +#include +#include +#include + + +/* define boolean type as char */ +#define true 1 +#define false 0 +#define bool char + +/* helper to allocate an array of given size and free it using IGRAPH_FINALLY + * when needed */ +#define ALLOC_ARRAY(VAR, SIZE, TYPE) { \ + VAR = IGRAPH_CALLOC(SIZE, TYPE); \ + if (VAR == 0) { \ + IGRAPH_ERROR("cannot allocate '" #VAR "' array in LAD isomorphism search", IGRAPH_ENOMEM); \ + } \ + IGRAPH_FINALLY(igraph_free, VAR); \ + } + +/* helper to allocate an array of given size and store its address in a + * pointer array */ +#define ALLOC_ARRAY_IN_HISTORY(VAR, SIZE, TYPE, HISTORY) { \ + VAR = IGRAPH_CALLOC(SIZE, TYPE); \ + if (VAR == 0) { \ + IGRAPH_ERROR("cannot allocate '" #VAR "' array in LAD isomorphism search", IGRAPH_ENOMEM); \ + } \ + IGRAPH_FINALLY(igraph_free, VAR); \ + IGRAPH_CHECK(igraph_vector_ptr_push_back(HISTORY, VAR)); \ + IGRAPH_FINALLY_CLEAN(1); \ + } + +/* ---------------------------------------------------------*/ +/* Coming from graph.c */ +/* ---------------------------------------------------------*/ + +typedef struct { + long int nbVertices; /* Number of vertices */ + igraph_vector_t nbSucc; + igraph_adjlist_t succ; + igraph_matrix_char_t isEdge; +} Tgraph; + +static int igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* graph) { + long int i, j, n; + long int no_of_nodes = igraph_vcount(igraph); + igraph_vector_int_t *neis; + + graph->nbVertices = no_of_nodes; + + IGRAPH_CHECK(igraph_adjlist_init(igraph, &graph->succ, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &graph->succ); + + IGRAPH_VECTOR_INIT_FINALLY(&graph->nbSucc, no_of_nodes); + for (i=0; i < no_of_nodes; ++i) { + VECTOR(graph->nbSucc)[i] = igraph_vector_int_size(igraph_adjlist_get(&graph->succ, i)); + } + + IGRAPH_CHECK(igraph_matrix_char_init(&graph->isEdge, no_of_nodes, no_of_nodes)); + IGRAPH_FINALLY(igraph_matrix_char_destroy, &graph->isEdge); + + for (i = 0; i < no_of_nodes; i++) { + neis = igraph_adjlist_get(&graph->succ, i); + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + int v = (int)VECTOR(*neis)[j]; + if (MATRIX(graph->isEdge, i, v)) { + IGRAPH_ERROR("LAD functions do not support graphs with multi-edges.", IGRAPH_EINVAL); + } + MATRIX(graph->isEdge, i, v) = 1; + } + } + + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +static void igraph_i_lad_destroyGraph(Tgraph *graph) { + igraph_matrix_char_destroy(&graph->isEdge); + igraph_adjlist_destroy(&graph->succ); + igraph_vector_destroy(&graph->nbSucc); +} + + +/* ---------------------------------------------------------*/ +/* Coming from domains.c */ +/* ---------------------------------------------------------*/ + +typedef struct { + igraph_vector_int_t nbVal; /* nbVal[u] = number of values in D[u] */ + igraph_vector_int_t firstVal; /* firstVal[u] = pos in val of the + first value of D[u] */ + igraph_vector_int_t val; /* val[firstVal[u]..firstVal[u]+nbVal[u]-1] = + values of D[u] */ + igraph_matrix_int_t posInVal; + /* If v in D[u] then firstVal[u] <= posInVal[u][v] < firstVal[u]+nbVal[u] + and val[posInVal[u][v]] = v + otherwise posInVal[u][v] >= firstVal[u]+nbVal[u] */ + int valSize; /* size of val */ + igraph_matrix_int_t firstMatch; + /* firstMatch[u][v] = pos in match of the first vertex + of the covering matching of G_(u, v) */ + igraph_vector_int_t matching; + /* matching[firstMatch[u][v]..firstMatch[u][v]+nbSucc[u]-1] + = covering matching of G_(u, v) */ + int nextOutToFilter; /* position in toFilter of the next pattern node whose + domain should be filtered (-1 if no domain to + filter) */ + int lastInToFilter; /* position in toFilter of the last pattern node whose + domain should be filtered */ + igraph_vector_int_t toFilter; /* contain all pattern nodes whose + domain should be filtered */ + igraph_vector_char_t markedToFilter; /* markedToFilter[u]=true if u + is in toFilter; false otherwise */ + igraph_vector_int_t globalMatchingP; /* globalMatchingP[u] = node of Gt + matched to u in globalAllDiff(Np) */ + igraph_vector_int_t globalMatchingT; + /* globalMatchingT[v] = node of Gp matched to v in globalAllDiff(Np) + or -1 if v is not matched */ +} Tdomain; + +static bool igraph_i_lad_toFilterEmpty(Tdomain* D) { + /* return true if there is no more nodes in toFilter */ + return (D->nextOutToFilter < 0); +} + +static void igraph_i_lad_resetToFilter(Tdomain *D) { + /* empty to filter and unmark the vertices that are marked to be filtered */ + igraph_vector_char_null(&D->markedToFilter); + D->nextOutToFilter = -1; +} + + +static int igraph_i_lad_nextToFilter(Tdomain* D, int size) { + /* precondition: emptyToFilter = false + remove a node from toFilter (FIFO) + unmark this node and return it */ + int u = VECTOR(D->toFilter)[D->nextOutToFilter]; + VECTOR(D->markedToFilter)[u] = false; + if (D->nextOutToFilter == D->lastInToFilter) { + /* u was the last node in tofilter */ + D->nextOutToFilter = -1; + } else if (D->nextOutToFilter == size - 1) { + D->nextOutToFilter = 0; + } else { + D->nextOutToFilter++; + } + return u; +} + +static void igraph_i_lad_addToFilter(int u, Tdomain* D, int size) { + /* if u is not marked, then add it to toFilter and mark it */ + if (VECTOR(D->markedToFilter)[u]) { + return; + } + VECTOR(D->markedToFilter)[u] = true; + if (D->nextOutToFilter < 0) { + D->lastInToFilter = 0; + D->nextOutToFilter = 0; + } else if (D->lastInToFilter == size - 1) { + D->lastInToFilter = 0; + } else { + D->lastInToFilter++; + } + VECTOR(D->toFilter)[D->lastInToFilter] = u; +} + +static bool igraph_i_lad_isInD(int u, int v, Tdomain* D) { + /* returns true if v belongs to D(u); false otherwise */ + return (MATRIX(D->posInVal, u, v) < + VECTOR(D->firstVal)[u] + VECTOR(D->nbVal)[u]); +} + +static int igraph_i_lad_augmentingPath(int u, Tdomain* D, int nbV, bool* result) { + /* return true if there exists an augmenting path starting from u and + ending on a free vertex v in the bipartite directed graph G=(U, + V, E) such that U=pattern nodes, V=target nodes, and + E={(u, v), v in D(u)} U {(v, u), D->globalMatchingP[u]=v} + update D-globalMatchingP and D->globalMatchingT consequently */ + int *fifo, *pred; + bool *marked; + int nextIn = 0; + int nextOut = 0; + int i, v, v2, u2; + + *result = false; + + /* Allocate memory */ + ALLOC_ARRAY(fifo, nbV, int); + ALLOC_ARRAY(pred, nbV, int); + ALLOC_ARRAY(marked, nbV, bool); + + for (i = 0; i < VECTOR(D->nbVal)[u]; i++) { + v = VECTOR(D->val)[ VECTOR(D->firstVal)[u] + i ]; /* v in D(u) */ + if (VECTOR(D->globalMatchingT)[v] < 0) { + /* v is free => augmenting path found */ + VECTOR(D->globalMatchingP)[u] = v; + VECTOR(D->globalMatchingT)[v] = u; + *result = true; + goto cleanup; + } + /* v is not free => add it to fifo */ + pred[v] = u; + fifo[nextIn++] = v; + marked[v] = true; + } + while (nextOut < nextIn) { + u2 = VECTOR(D->globalMatchingT)[fifo[nextOut++]]; + for (i = 0; i < VECTOR(D->nbVal)[u2]; i++) { + v = VECTOR(D->val)[ VECTOR(D->firstVal)[u2] + i ]; /* v in D(u2) */ + if (VECTOR(D->globalMatchingT)[v] < 0) { + /* v is free => augmenting path found */ + while (u2 != u) { /* update global matching wrt path */ + v2 = VECTOR(D->globalMatchingP)[u2]; + VECTOR(D->globalMatchingP)[u2] = v; + VECTOR(D->globalMatchingT)[v] = u2; + v = v2; + u2 = pred[v]; + } + VECTOR(D->globalMatchingP)[u] = v; + VECTOR(D->globalMatchingT)[v] = u; + *result = true; + goto cleanup; + } + if (!marked[v]) { /* v is not free and not marked => add it to fifo */ + pred[v] = u2; + fifo[nextIn++] = v; + marked[v] = true; + } + } + } + +cleanup: + igraph_free(fifo); + igraph_free(pred); + igraph_free(marked); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +static int igraph_i_lad_removeAllValuesButOne(int u, int v, Tdomain* D, Tgraph* Gp, + Tgraph* Gt, bool* result) { + /* remove all values but v from D(u) and add all successors of u in + toFilter return false if an inconsistency is detected wrt to + global all diff */ + int j, oldPos, newPos; + igraph_vector_int_t *uneis = igraph_adjlist_get(&Gp->succ, u); + int n = (int) igraph_vector_int_size(uneis); + /* add all successors of u in toFilter */ + for (j = 0; j < n; j++) { + igraph_i_lad_addToFilter((int) VECTOR(*uneis)[j], D, + (int) (Gp->nbVertices)); + } + /* remove all values but v from D[u] */ + oldPos = MATRIX(D->posInVal, u, v); + newPos = VECTOR(D->firstVal)[u]; + VECTOR(D->val)[oldPos] = VECTOR(D->val)[newPos]; + VECTOR(D->val)[newPos] = v; + MATRIX(D->posInVal, u, VECTOR(D->val)[newPos]) = newPos; + MATRIX(D->posInVal, u, VECTOR(D->val)[oldPos]) = oldPos; + VECTOR(D->nbVal)[u] = 1; + /* update global matchings that support the global all different + constraint */ + if (VECTOR(D->globalMatchingP)[u] != v) { + VECTOR(D->globalMatchingT)[ VECTOR(D->globalMatchingP)[u] ] = -1; + VECTOR(D->globalMatchingP)[u] = -1; + IGRAPH_CHECK(igraph_i_lad_augmentingPath(u, D, (int) (Gt->nbVertices), result)); + } else { + *result = true; + } + return 0; +} + + +static int igraph_i_lad_removeValue(int u, int v, Tdomain* D, Tgraph* Gp, + Tgraph* Gt, bool* result) { + /* remove v from D(u) and add all successors of u in toFilter + return false if an inconsistency is detected wrt global all diff */ + int j; + igraph_vector_int_t *uneis = igraph_adjlist_get(&Gp->succ, u); + int n = (int) igraph_vector_int_size(uneis); + int oldPos, newPos; + + /* add all successors of u in toFilter */ + for (j = 0; j < n; j++) { + igraph_i_lad_addToFilter((int) VECTOR(*uneis)[j], D, + (int) (Gp->nbVertices)); + } + /* remove v from D[u] */ + oldPos = MATRIX(D->posInVal, u, v); + VECTOR(D->nbVal)[u]--; + newPos = VECTOR(D->firstVal)[u] + VECTOR(D->nbVal)[u]; + VECTOR(D->val)[oldPos] = VECTOR(D->val)[newPos]; + VECTOR(D->val)[newPos] = v; + MATRIX(D->posInVal, u, VECTOR(D->val)[oldPos]) = oldPos; + MATRIX(D->posInVal, u, VECTOR(D->val)[newPos]) = newPos; + /* update global matchings that support the global all different + constraint */ + if (VECTOR(D->globalMatchingP)[u] == v) { + VECTOR(D->globalMatchingP)[u] = -1; + VECTOR(D->globalMatchingT)[v] = -1; + IGRAPH_CHECK(igraph_i_lad_augmentingPath(u, D, (int) (Gt->nbVertices), result)); + } else { + *result = true; + } + return 0; +} + + +static int igraph_i_lad_matchVertices(int nb, igraph_vector_int_t* toBeMatched, + bool induced, Tdomain* D, Tgraph* Gp, + Tgraph* Gt, int *invalid) { + /* for each u in toBeMatched[0..nb-1], match u to + D->val[D->firstVal[u] and filter domains of other non matched + vertices wrt FC(Edges) and FC(diff) (this is not mandatory, as + LAD is stronger than FC(Edges) and GAC(allDiff) is stronger than + FC(diff), but this speeds up the solution process). + return false if an inconsistency is detected by FC(Edges) or + FC(diff); true otherwise; */ + int j, u, v, u2, oldNbVal; + igraph_vector_int_t *vneis; + bool result = false; + + while (nb > 0) { + u = VECTOR(*toBeMatched)[--nb]; + v = VECTOR(D->val)[ VECTOR(D->firstVal)[u] ]; + vneis = igraph_adjlist_get(&Gt->succ, v); + /* match u to v */ + for (u2 = 0; u2 < Gp->nbVertices; u2++) { + if (u != u2) { + oldNbVal = VECTOR(D->nbVal)[u2]; + if (igraph_i_lad_isInD(u2, v, D)) { + IGRAPH_CHECK(igraph_i_lad_removeValue(u2, v, D, Gp, Gt, &result)); + if (!result) { + *invalid = 1 ; return 0; + } + } + if (MATRIX(Gp->isEdge, u, u2)) { + /* remove from D[u2] vertices which are not adjacent to v */ + j = VECTOR(D->firstVal)[u2]; + while (j < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]) { + if (MATRIX(Gt->isEdge, v, VECTOR(D->val)[j])) { + j++; + } else { + IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(D->val)[j], D, Gp, Gt, &result)); + if (!result) { + *invalid = 1; return 0; + } + } + } + } else if (induced) { + /* (u, u2) is not an edge => remove neighbors of v from D[u2] */ + if (VECTOR(D->nbVal)[u2] < VECTOR(Gt->nbSucc)[v]) { + j = VECTOR(D->firstVal)[u2]; + while (j < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]) { + if (!MATRIX(Gt->isEdge, v, VECTOR(D->val)[j])) { + j++; + } else { + IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(D->val)[j], D, Gp, Gt, &result)); + if (!result) { + *invalid = 1; return 0; + } + } + } + } else { + for (j = 0; j < VECTOR(Gt->nbSucc)[v]; j++) { + if (igraph_i_lad_isInD(u2, (int) VECTOR(*vneis)[j], D)) { + IGRAPH_CHECK(igraph_i_lad_removeValue(u2, (int) VECTOR(*vneis)[j], D, Gp, Gt, &result)); + if (!result) { + *invalid = 1; return 0; + } + } + } + } + } + if (VECTOR(D->nbVal)[u2] == 0) { + *invalid = 1; /* D[u2] is empty */ + return 0; + } + if ((VECTOR(D->nbVal)[u2] == 1) && (oldNbVal > 1)) { + VECTOR(*toBeMatched)[nb++] = u2; + } + } + } + } + *invalid = 0; + return 0; +} + + +static bool igraph_i_lad_matchVertex(int u, bool induced, Tdomain* D, Tgraph* Gp, + Tgraph *Gt) { + int invalid; + /* match u to D->val[D->firstVal[u]] and filter domains of other non + matched vertices wrt FC(Edges) and FC(diff) (this is not + mandatory, as LAD is stronger than FC(Edges) and GAC(allDiff) + is stronger than FC(diff), but this speeds up the solution process). + return false if an inconsistency is detected by FC(Edges) or + FC(diff); true otherwise; */ + igraph_vector_int_t toBeMatched; + igraph_vector_int_init(&toBeMatched, Gp->nbVertices); + IGRAPH_FINALLY(igraph_vector_int_destroy, &toBeMatched); + VECTOR(toBeMatched)[0] = u; + IGRAPH_CHECK(igraph_i_lad_matchVertices(1, &toBeMatched, induced, D, Gp, Gt, + &invalid)); + igraph_vector_int_destroy(&toBeMatched); + IGRAPH_FINALLY_CLEAN(1); + + return invalid ? false : true; +} + + +static int igraph_i_lad_qcompare (void const *a, void const *b) { + /* function used by the qsort function */ + int pa = *((int*)a) - *((int*)b); + return pa; +} + +static bool igraph_i_lad_compare(int size_mu, int* mu, int size_mv, int* mv) { + /* return true if for every element u of mu there exists + a different element v of mv such that u <= v; + return false otherwise */ + int i, j; + igraph_qsort(mu, (size_t) size_mu, sizeof(int), igraph_i_lad_qcompare); + igraph_qsort(mv, (size_t) size_mv, sizeof(int), igraph_i_lad_qcompare); + i = size_mv - 1; + for (j = size_mu - 1; j >= 0; j--) { + if (mu[j] > mv[i]) { + return false; + } + i--; + } + return true; +} + +static int igraph_i_lad_initDomains(bool initialDomains, + const igraph_vector_ptr_t *domains, Tdomain *D, + const Tgraph *Gp, const Tgraph *Gt, int *empty) { + /* for every pattern node u, initialize D(u) with every vertex v + such that for every neighbor u' of u there exists a different + neighbor v' of v such that degree(u) <= degree(v) + if initialDomains, then filter initial domains wrt + compatibilities given in file + return false if a domain is empty and true otherwise */ + int *val; + bool *dom; + int *mu, *mv; + int matchingSize, u, v, i, j; + igraph_vector_t *vec; + + ALLOC_ARRAY(val, Gp->nbVertices * Gt->nbVertices, int); + ALLOC_ARRAY(dom, Gt->nbVertices, bool); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->globalMatchingP, Gp->nbVertices); + igraph_vector_int_fill(&D->globalMatchingP, -1L); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->globalMatchingT, Gt->nbVertices); + igraph_vector_int_fill(&D->globalMatchingT, -1L); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->nbVal, Gp->nbVertices); + + IGRAPH_CHECK(igraph_vector_int_init(&D->firstVal, Gp->nbVertices)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &D->firstVal); + + IGRAPH_CHECK(igraph_matrix_int_init(&D->posInVal, + Gp->nbVertices, Gt->nbVertices)); + IGRAPH_FINALLY(igraph_matrix_int_destroy, &D->posInVal); + + IGRAPH_CHECK(igraph_matrix_int_init(&D->firstMatch, + Gp->nbVertices, Gt->nbVertices)); + IGRAPH_FINALLY(igraph_matrix_int_destroy, &D->firstMatch); + + IGRAPH_CHECK(igraph_vector_char_init(&D->markedToFilter, Gp->nbVertices)); + IGRAPH_FINALLY(igraph_vector_char_destroy, &D->markedToFilter); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->toFilter, Gp->nbVertices); + + D->valSize = 0; + matchingSize = 0; + + for (u = 0; u < Gp->nbVertices; u++) { + igraph_vector_int_t *Gp_uneis = igraph_adjlist_get(&Gp->succ, u); + if (initialDomains) { + /* read the list of target vertices which are compatible with u */ + vec = VECTOR(*domains)[u]; + i = (int) igraph_vector_size(vec); + memset(dom, false, sizeof(bool) * (size_t)(Gt->nbVertices)); + for (j = 0; j < i; j++) { + v = (int) VECTOR(*vec)[j]; + dom[v] = true; + } + } + VECTOR(D->markedToFilter)[u] = true; + VECTOR(D->toFilter)[u] = u; + VECTOR(D->nbVal)[u] = 0; + VECTOR(D->firstVal)[u] = D->valSize; + for (v = 0; v < Gt->nbVertices; v++) { + igraph_vector_int_t *Gt_vneis = igraph_adjlist_get(&Gt->succ, v); + if ((initialDomains) && (!dom[v])) { /* v not in D(u) */ + MATRIX(D->posInVal, u, v) = (int) (VECTOR(D->firstVal)[u] + + Gt->nbVertices); + } else { + MATRIX(D->firstMatch, u, v) = matchingSize; + matchingSize += VECTOR(Gp->nbSucc)[u]; + if (VECTOR(Gp->nbSucc)[u] <= VECTOR(Gt->nbSucc)[v]) { + mu = IGRAPH_CALLOC((long int) VECTOR(Gp->nbSucc)[u], int); + if (mu == 0) { + igraph_free(val); igraph_free(dom); + IGRAPH_ERROR("cannot allocate 'mu' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM); + } + mv = IGRAPH_CALLOC((long int) VECTOR(Gt->nbSucc)[v], int); + if (mv == 0) { + igraph_free(mu); igraph_free(val); igraph_free(dom); + IGRAPH_ERROR("cannot allocate 'mv' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM); + } + for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) { + mu[i] = (int) VECTOR(Gp->nbSucc)[(long int) VECTOR(*Gp_uneis)[i]]; + } + for (i = 0; i < VECTOR(Gt->nbSucc)[v]; i++) { + mv[i] = (int) VECTOR(Gt->nbSucc)[(long int) VECTOR(*Gt_vneis)[i]]; + } + if (igraph_i_lad_compare((int) VECTOR(Gp->nbSucc)[u], mu, + (int) VECTOR(Gt->nbSucc)[v], mv) == 1) { + val[D->valSize] = v; + VECTOR(D->nbVal)[u]++; + MATRIX(D->posInVal, u, v) = D->valSize++; + } else { /* v not in D(u) */ + MATRIX(D->posInVal, u, v) = + (int)(VECTOR(D->firstVal)[u] + Gt->nbVertices); + } + igraph_free(mu); mu = 0; + igraph_free(mv); mv = 0; + } else { /* v not in D(u) */ + MATRIX(D->posInVal, u, v) = + (int) (VECTOR(D->firstVal)[u] + Gt->nbVertices); + } + } + } + if (VECTOR(D->nbVal)[u] == 0) { + *empty = 1; /* empty domain */ + + igraph_free(val); + igraph_free(dom); + + /* On this branch, 'val' and 'matching' are unused. + * We init them anyway so that we can have a consistent destructor. */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->val, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->matching, 0); + IGRAPH_FINALLY_CLEAN(12); + + return IGRAPH_SUCCESS; + } + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->val, D->valSize); + for (i = 0; i < D->valSize; i++) { + VECTOR(D->val)[i] = val[i]; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->matching, matchingSize); + igraph_vector_int_fill(&D->matching, -1); + + D->nextOutToFilter = 0; + D->lastInToFilter = (int) (Gp->nbVertices - 1); + + *empty = 0; + + igraph_free(val); + igraph_free(dom); + + IGRAPH_FINALLY_CLEAN(12); + + return IGRAPH_SUCCESS; +} + +static void igraph_i_lad_destroyDomains(Tdomain *D) { + igraph_vector_int_destroy(&D->globalMatchingP); + igraph_vector_int_destroy(&D->globalMatchingT); + igraph_vector_int_destroy(&D->nbVal); + igraph_vector_int_destroy(&D->firstVal); + igraph_matrix_int_destroy(&D->posInVal); + igraph_matrix_int_destroy(&D->firstMatch); + igraph_vector_char_destroy(&D->markedToFilter); + igraph_vector_int_destroy(&D->toFilter); + + igraph_vector_int_destroy(&D->val); + igraph_vector_int_destroy(&D->matching); +} + + +/* ---------------------------------------------------------*/ +/* Coming from allDiff.c */ +/* ---------------------------------------------------------*/ + +#define white 0 +#define grey 1 +#define black 2 +#define toBeDeleted 3 +#define deleted 4 + +static void igraph_i_lad_addToDelete(int u, int* list, int* nb, int* marked) { + if (marked[u] < toBeDeleted) { + list[(*nb)++] = u; + marked[u] = toBeDeleted; + } +} + +static int igraph_i_lad_updateMatching(int sizeOfU, int sizeOfV, + igraph_vector_int_t *degree, + igraph_vector_int_t *firstAdj, + igraph_vector_int_t *adj, + igraph_vector_int_t * matchedWithU, + int *invalid) { + /* input: + sizeOfU = number of vertices in U + sizeOfV = number of vertices in V + degree[u] = number of vertices of V which are adjacent to u + firstAdj[u] = pos in adj of the first vertex of V adjacent to u + adj[firstAdj[u]..firstAdj[u]+sizeOfU[u]-1] = vertices of V adjacent to u + + input/output: + matchedWithU[u] = vertex of V matched with u + + returns true if there exists a matching that covers U, i.e., if + for every u in 0..nbU-1, there exists a different v in 0..nb-1 + such that v is adjacent to u; returns false otherwise */ + + int *matchedWithV; /* matchedWithV[matchedWithU[u]]=u */ + int *nbPred; /* nbPred[i] = nb of predecessors of the ith + vertex of V in the DAG */ + int *pred; /* pred[i][j] = jth predecessor the ith + vertex of V in the DAG */ + int *nbSucc; /* nbSucc[i] = nb of successors of the ith + vertex of U in the DAG */ + int *succ; /* succ[i][j] = jth successor of the ith + vertex of U in the DAG */ + int *listV, *listU, *listDV, *listDU; + int nbV, nbU, nbDV, nbDU; + int i, j, k, stop, u, v; + int *markedV, *markedU; + /* markedX[i]=white if X[i] is not in the DAG + markedX[i]=grey if X[i] has been added to the DAG, but not its successors + markedX[i]=black if X[i] and its successors have been added to the DAG + markedX[i]=toBeDeleted if X[i] must be deleted from the DAG + markedX[i]=deleted if X[i] has been deleted from the DAG */ + int nbUnmatched = 0; /* number of vertices of U that are not matched */ + int *unmatched; /* vertices of U that are not matched */ + int *posInUnmatched; /* unmatched[posInUnmatched[u]]=u */ + igraph_vector_int_t path; + + if (sizeOfU > sizeOfV) { + *invalid = 1; /* trivial case of infeasibility */ + return 0; + } + + ALLOC_ARRAY(matchedWithV, sizeOfV, int); + ALLOC_ARRAY(nbPred, sizeOfV, int); + ALLOC_ARRAY(pred, sizeOfV * sizeOfU, int); + ALLOC_ARRAY(nbSucc, sizeOfU, int); + ALLOC_ARRAY(succ, sizeOfU * sizeOfV, int); + ALLOC_ARRAY(listV, sizeOfV, int); + ALLOC_ARRAY(listU, sizeOfU, int); + ALLOC_ARRAY(listDV, sizeOfV, int); + ALLOC_ARRAY(listDU, sizeOfU, int); + ALLOC_ARRAY(markedV, sizeOfV, int); + ALLOC_ARRAY(markedU, sizeOfU, int); + ALLOC_ARRAY(unmatched, sizeOfU, int); + ALLOC_ARRAY(posInUnmatched, sizeOfU, int); + + IGRAPH_CHECK(igraph_vector_int_init(&path, 0)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &path); + + /* initialize matchedWithV and unmatched */ + memset(matchedWithV, -1, (size_t)sizeOfV * sizeof(int)); + for (u = 0; u < sizeOfU; u++) { + if (VECTOR(*matchedWithU)[u] >= 0) { + matchedWithV[VECTOR(*matchedWithU)[u]] = u; + } else { + posInUnmatched[u] = nbUnmatched; + unmatched[nbUnmatched++] = u; + } + } + /* try to match unmatched vertices of U with free vertices of V */ + j = 0; + while (j < nbUnmatched) { + u = unmatched[j]; + for (i = VECTOR(*firstAdj)[u]; + ((i < VECTOR(*firstAdj)[u] + VECTOR(*degree)[u]) && + (matchedWithV[VECTOR(*adj)[i]] >= 0)); i++) { } + if (i == VECTOR(*firstAdj)[u] + VECTOR(*degree)[u]) { + j++; /* no free vertex for u */ + } else { + v = VECTOR(*adj)[i]; /* v is free => match u with v */ + VECTOR(*matchedWithU)[u] = v; + matchedWithV[v] = u; + unmatched[j] = unmatched[--nbUnmatched]; + posInUnmatched[unmatched[j]] = j; + } + } + + while (nbUnmatched > 0) { + /* Try to increase the number of matched vertices */ + /* step 1 : build the DAG */ + memset(markedU, white, (size_t) sizeOfU * sizeof(int)); + memset(nbSucc, 0, (size_t) sizeOfU * sizeof(int)); + memset(markedV, white, (size_t) sizeOfV * sizeof(int)); + memset(nbPred, 0, (size_t) sizeOfV * sizeof(int)); + /* first layer of the DAG from the free nodes of U */ + nbV = 0; + for (j = 0; j < nbUnmatched; j++) { + u = unmatched[j]; /* u is a free node of U */ + markedU[u] = black; + for (i = VECTOR(*firstAdj)[u]; + i < VECTOR(*firstAdj)[u] + VECTOR(*degree)[u]; i++) { + v = VECTOR(*adj)[i]; /* add edge (u, v) to the DAG */ + pred[v * sizeOfU + (nbPred[v]++)] = u; + succ[u * sizeOfV + (nbSucc[u]++)] = v; + if (markedV[v] == white) { /* first time v is added to the DAG*/ + markedV[v] = grey; + listV[nbV++] = v; + } + } + } + stop = 0; + while ((stop == 0) && (nbV > 0)) { + /* build next layer from nodes of V to nodes of U */ + nbU = 0; + for (i = 0; i < nbV; i++) { + v = listV[i]; + markedV[v] = black; + u = matchedWithV[v]; + if (markedU[u] == white) { /* edge (v, u) belongs to the DAG */ + markedU[u] = grey; + listU[nbU++] = u; + } + } + /* build next layer from nodes of U to nodes of V */ + nbV = 0; + for (j = 0; j < nbU; j++) { + u = listU[j]; + markedU[u] = black; + for (i = VECTOR(*firstAdj)[u]; + i < VECTOR(*firstAdj)[u] + VECTOR(*degree)[u]; i++) { + v = VECTOR(*adj)[i]; + if (markedV[v] != black) { /* add edge (u, v) to the DAG */ + pred[v * sizeOfU + (nbPred[v]++)] = u; + succ[u * sizeOfV + (nbSucc[u]++)] = v; + if (markedV[v] == white) { /* first time v is added to the DAG */ + markedV[v] = grey; + listV[nbV++] = v; + } + if (matchedWithV[v] == -1) { /* we have found a free node ! */ + stop = 1; + } + } + } + } + } + if (nbV == 0) { + *invalid = 1; + /* I know it's ugly. */ + goto cleanup; + } + + /* step 2: look for augmenting paths */ + for (k = 0; k < nbV; k++) { + v = listV[k]; + if ((matchedWithV[v] == -1) && (nbPred[v] > 0)) { + /* v is the final node of an augmenting path */ + IGRAPH_CHECK(igraph_vector_int_resize(&path, 1)); + VECTOR(path)[0] = v; + nbDV = 0; + nbDU = 0; + igraph_i_lad_addToDelete(v, listDV, &nbDV, markedV); + do { + u = pred[v * sizeOfU + 0]; /* (u, v) belongs to the augmenting path */ + IGRAPH_CHECK(igraph_vector_int_push_back(&path, u)); + igraph_i_lad_addToDelete(u, listDU, &nbDU, markedU); + if (VECTOR(*matchedWithU)[u] != -1) { + /* u is not the initial node of the augmenting path */ + v = VECTOR(*matchedWithU)[u]; /* (v, u) belongs to the + augmenting path */ + IGRAPH_CHECK(igraph_vector_int_push_back(&path, v)); + igraph_i_lad_addToDelete(v, listDV, &nbDV, markedV); + } + } while (VECTOR(*matchedWithU)[u] != -1); + + /* delete nodes of listDV and listDU */ + while ((nbDV > 0) || (nbDU > 0)) { + while (nbDV > 0) { /* delete v */ + v = listDV[--nbDV]; markedV[v] = deleted; + u = matchedWithV[v]; + if (u != -1) { + igraph_i_lad_addToDelete(u, listDU, &nbDU, markedU); + } + for (i = 0; i < nbPred[v]; i++) { + u = pred[v * sizeOfU + i]; /* delete edge (u, v) */ + for (j = 0; ((j < nbSucc[u]) && (v != succ[u * sizeOfV + j])); j++) { } + succ[u * sizeOfV + j] = succ[u * sizeOfV + (--nbSucc[u])]; + if (nbSucc[u] == 0) { + igraph_i_lad_addToDelete(u, listDU, &nbDU, markedU); + } + } + } + while (nbDU > 0) { /* delete u */ + u = listDU[--nbDU]; markedU[u] = deleted; + v = VECTOR(*matchedWithU)[u]; + if (v != -1) { + igraph_i_lad_addToDelete(v, listDV, &nbDV, markedV); + } + j = 0; + for (i = 0; i < nbSucc[u]; i++) { /* delete edge (u, v) */ + v = succ[u * sizeOfV + i]; + for (j = 0; ((j < nbPred[v]) && (u != pred[v * sizeOfU + j])); j++) { } + pred[v * sizeOfU + j] = pred[v * sizeOfU + (--nbPred[v])]; + if (nbPred[v] == 0) { + igraph_i_lad_addToDelete(v, listDV, &nbDV, markedV); + } + } + } + } + /* Remove the last node of the augmenting path from the set of + unmatched vertices */ + u = VECTOR(path)[igraph_vector_int_size(&path) - 1]; + i = posInUnmatched[u]; + unmatched[i] = unmatched[--nbUnmatched]; + posInUnmatched[unmatched[i]] = i; + /* Update the matching wrt the augmenting path */ + while (igraph_vector_int_size(&path) > 1) { + u = igraph_vector_int_pop_back(&path); + v = igraph_vector_int_pop_back(&path); + VECTOR(*matchedWithU)[u] = v; + matchedWithV[v] = u; + } + } + } + } + *invalid = 0; + +cleanup: + /* Free the allocated arrays */ + igraph_vector_int_destroy(&path); + igraph_free(posInUnmatched); + igraph_free(unmatched); + igraph_free(markedU); + igraph_free(markedV); + igraph_free(listDU); + igraph_free(listDV); + igraph_free(listU); + igraph_free(listV); + igraph_free(succ); + igraph_free(nbSucc); + igraph_free(pred); + igraph_free(nbPred); + igraph_free(matchedWithV); + IGRAPH_FINALLY_CLEAN(14); + return 0; +} + +static void igraph_i_lad_DFS(int nbU, int nbV, int u, bool* marked, int* nbSucc, + int* succ, igraph_vector_int_t * matchedWithU, + int* order, int* nb) { + /* perform a depth first search, starting from u, in the bipartite + graph Go=(U, V, E) such that + U = vertices of Gp + V = vertices of Gt + E = { (u, matchedWithU[u]) / u is a vertex of Gp } U + { (v, u) / v is a vertex of D[u] which is not matched to v} + + Given a vertex v of Gt, nbSucc[v]=number of successors of v and + succ[v]=list of successors of v. order[nb^out+1..nb^in] contains + the vertices discovered by the DFS */ + int i; + int v = VECTOR(*matchedWithU)[u]; /* the only one predecessor of v is u */ + marked[u] = true; + if (v >= 0) { + for (i = 0; i < nbSucc[v]; i++) { + if (!marked[succ[v * nbU + i]]) { + igraph_i_lad_DFS(nbU, nbV, succ[v * nbU + i], marked, nbSucc, succ, + matchedWithU, order, nb); + } + } + } + /* we have finished with u => number it */ + order[*nb] = u; (*nb)--; +} + +static int igraph_i_lad_SCC(int nbU, int nbV, int* numV, int* numU, + int* nbSucc, int* succ, + int* nbPred, int* pred, + igraph_vector_int_t * matchedWithU, + igraph_vector_int_t * matchedWithV) { + /* postrelation: numV[v]==numU[u] iff they belong to the same + strongly connected component in the bipartite graph Go=(U, V, E) + such that + U = vertices of Gp + V = vertices of Gt + E = { (u, matchedWithU[u]) / u is a vertex of Gp } U + { (v, u) / v is a vertex of D[u] which is not matched to v} + + Given a vertex v of Gt, nbSucc[v]=number of sucessors of v and + succ[v]=list of successors of v */ + int *order; + bool *marked; + int *fifo; + int u, v, i, j, k, nbSCC, nb; + + /* Allocate memory */ + ALLOC_ARRAY(order, nbU, int); + ALLOC_ARRAY(marked, nbU, bool); + ALLOC_ARRAY(fifo, nbV, int); + + /* Order vertices of Gp wrt DFS */ + nb = nbU - 1; + for (u = 0; u < nbU; u++) { + if (!marked[u]) { + igraph_i_lad_DFS(nbU, nbV, u, marked, nbSucc, succ, matchedWithU, + order, &nb); + } + } + + /* traversal starting from order[0], then order[1], ... */ + nbSCC = 0; + memset(numU, -1, (size_t) nbU * sizeof(int)); + memset(numV, -1, (size_t) nbV * sizeof(int)); + for (i = 0; i < nbU; i++) { + u = order[i]; + v = VECTOR(*matchedWithU)[u]; + if (v == -1) { + continue; + } + if (numV[v] == -1) { /* v belongs to a new SCC */ + nbSCC++; + k = 1; fifo[0] = v; + numV[v] = nbSCC; + while (k > 0) { + v = fifo[--k]; + u = VECTOR(*matchedWithV)[v]; + if (u != -1) { + numU[u] = nbSCC; + for (j = 0; j < nbPred[u]; j++) { + v = pred[u * nbV + j]; + if (numV[v] == -1) { + numV[v] = nbSCC; + fifo[k++] = v; + } + } + } + } + } + } + + /* Free memory */ + igraph_free(fifo); + igraph_free(marked); + igraph_free(order); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + + +static int igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tgraph* Gt, + Tdomain* D, int *invalid) { + /* precondition: D->globalMatchingP is an all different matching of + the pattern vertices + postcondition: filter domains wrt GAC(allDiff) + return false if an inconsistency is detected; true otherwise + + Build the bipartite directed graph Go=(U, V, E) such that + E = { (u, v) / u is a vertex of Gp which is matched to v (i.e., + v=D->globalMatchingP[u])} U + { (v, u) / v is a vertex of Gt which is in D(u) but is not + matched to u} */ + int *nbPred; /* nbPred[u] = nb of predecessors of u in Go */ + int *pred; /* pred[u][i] = ith + predecessor of u in Go */ + int *nbSucc; /* nbSucc[v] = nb of successors of v in Go */ + int *succ; /* succ[v][i] = ith + successor of v in Go */ + int u, v, i, w, oldNbVal, nbToMatch; + int *numV, *numU; + igraph_vector_int_t toMatch; + bool *used; + int *list; + int nb = 0; + bool result; + + /* Allocate memory */ + ALLOC_ARRAY(nbPred, Gp->nbVertices, int); + ALLOC_ARRAY(pred, Gp->nbVertices * Gt->nbVertices, int); + ALLOC_ARRAY(nbSucc, Gt->nbVertices, int); + ALLOC_ARRAY(succ, Gt->nbVertices * Gp->nbVertices, int); + ALLOC_ARRAY(numV, Gt->nbVertices, int); + ALLOC_ARRAY(numU, Gp->nbVertices, int); + ALLOC_ARRAY(used, Gp->nbVertices * Gt->nbVertices, bool); + ALLOC_ARRAY(list, Gt->nbVertices, int); + IGRAPH_CHECK(igraph_vector_int_init(&toMatch, Gp->nbVertices)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &toMatch); + + for (u = 0; u < Gp->nbVertices; u++) { + for (i = 0; i < VECTOR(D->nbVal)[u]; i++) { + v = VECTOR(D->val)[ VECTOR(D->firstVal)[u] + i ]; /* v in D(u) */ + used[u * Gt->nbVertices + v] = false; + if (v != VECTOR(D->globalMatchingP)[u]) { + pred[u * Gt->nbVertices + (nbPred[u]++)] = v; + succ[v * Gp->nbVertices + (nbSucc[v]++)] = u; + } + } + } + + /* mark as used all edges of paths starting from free vertices */ + for (v = 0; v < Gt->nbVertices; v++) { + if (VECTOR(D->globalMatchingT)[v] < 0) { /* v is free */ + list[nb++] = v; + numV[v] = true; + } + } + while (nb > 0) { + v = list[--nb]; + for (i = 0; i < nbSucc[v]; i++) { + u = succ[v * Gp->nbVertices + i]; + used[u * Gt->nbVertices + v] = true; + if (numU[u] == false) { + numU[u] = true; + w = VECTOR(D->globalMatchingP)[u]; + used[u * Gt->nbVertices + w] = true; + if (numV[w] == false) { + list[nb++] = w; + numV[w] = true; + } + } + } + } + + /* look for strongly connected components in Go */ + IGRAPH_CHECK( + igraph_i_lad_SCC((int)(Gp->nbVertices), (int)(Gt->nbVertices), numV, numU, + nbSucc, succ, nbPred, pred, &D->globalMatchingP, &D->globalMatchingT)); + + /* remove v from D[u] if (u, v) is not marked as used + and u and v are not in the same SCC + and D->globalMatchingP[u] != v */ + nbToMatch = 0; + for (u = 0; u < Gp->nbVertices; u++) { + oldNbVal = VECTOR(D->nbVal)[u]; + for (i = 0; i < VECTOR(D->nbVal)[u]; i++) { + v = VECTOR(D->val)[ VECTOR(D->firstVal)[u] + i ]; /* v in D(u) */ + if ((!used[u * Gt->nbVertices + v]) && (numV[v] != numU[u]) && + (VECTOR(D->globalMatchingP)[u] != v)) { + IGRAPH_CHECK(igraph_i_lad_removeValue(u, v, D, Gp, Gt, &result)); + if (!result) { + *invalid = 1; + /* Yes, this is ugly. */ + goto cleanup; + } + } + } + if (VECTOR(D->nbVal)[u] == 0) { + *invalid = 1; + /* Yes, this is ugly. */ + goto cleanup; + } + if ((oldNbVal > 1) && (VECTOR(D->nbVal)[u] == 1)) { + VECTOR(toMatch)[nbToMatch++] = u; + } + } + IGRAPH_CHECK(igraph_i_lad_matchVertices(nbToMatch, &toMatch, induced, + D, Gp, Gt, invalid)); + +cleanup: + igraph_vector_int_destroy(&toMatch); + igraph_free(list); + igraph_free(used); + igraph_free(numU); + igraph_free(numV); + igraph_free(succ); + igraph_free(nbSucc); + igraph_free(pred); + igraph_free(nbPred); + IGRAPH_FINALLY_CLEAN(9); + + return 0; +} + +/* ---------------------------------------------------------*/ +/* Coming from lad.c */ +/* ---------------------------------------------------------*/ + +static int igraph_i_lad_checkLAD(int u, int v, Tdomain* D, Tgraph* Gp, Tgraph* Gt, + bool *result) { + /* return true if G_(u, v) has a adj(u)-covering matching; false + otherwise */ + int u2, v2, i, j; + int nbMatched = 0; + igraph_vector_int_t *Gp_uneis = igraph_adjlist_get(&Gp->succ, u); + + int *num, *numInv; + igraph_vector_int_t nbComp; + igraph_vector_int_t firstComp; + igraph_vector_int_t comp; + int nbNum = 0; + int posInComp = 0; + igraph_vector_int_t matchedWithU; + int invalid; + + /* special case when u has only 1 adjacent node => no need to call + Hopcroft and Karp */ + if (VECTOR(Gp->nbSucc)[u] == 1) { + u2 = (int) VECTOR(*Gp_uneis)[0]; /* u2 is the only node adjacent to u */ + v2 = VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) ]; + if ((v2 != -1) && (igraph_i_lad_isInD(u2, v2, D))) { + *result = true; + return 0; + } + /* look for a support of edge (u, u2) for v */ + for (i = VECTOR(D->firstVal)[u2]; + i < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]; i++) { + if (MATRIX(Gt->isEdge, v, VECTOR(D->val)[i])) { + VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) ] = + VECTOR(D->val)[i]; + *result = true; + return 0; + } + } + *result = false; + return 0; + } + + /* general case (when u has more than 1 adjacent node) */ + for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) { + /* remove from the matching of G_(u, v) edges which no longer + belong to G_(u, v) */ + u2 = (int) VECTOR(*Gp_uneis)[i]; + v2 = VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) + i]; + if ((v2 != -1) && (igraph_i_lad_isInD(u2, v2, D))) { + nbMatched++; + } + } + if (nbMatched == VECTOR(Gp->nbSucc)[u]) { + *result = true; + return 0; + } /* The matching still covers adj(u) */ + + /* Allocate memory */ + ALLOC_ARRAY(num, Gt->nbVertices, int); + ALLOC_ARRAY(numInv, Gt->nbVertices, int); + + /* Build the bipartite graph + let U be the set of nodes adjacent to u + let V be the set of nodes that are adjacent to v, and that belong + to domains of nodes of U */ + /* nbComp[u]=number of elements of V that are compatible with u */ + IGRAPH_CHECK(igraph_vector_int_init(&nbComp, (long int) VECTOR(Gp->nbSucc)[u])); + IGRAPH_FINALLY(igraph_vector_int_destroy, &nbComp); + IGRAPH_CHECK(igraph_vector_int_init(&firstComp, (long int) VECTOR(Gp->nbSucc)[u])); + IGRAPH_FINALLY(igraph_vector_int_destroy, &firstComp); + /* comp[firstComp[u]..firstComp[u]+nbComp[u]-1] = nodes of Gt that + are compatible with u */ + IGRAPH_CHECK(igraph_vector_int_init(&comp, (long int) (VECTOR(Gp->nbSucc)[u] * + Gt->nbVertices))); + IGRAPH_FINALLY(igraph_vector_int_destroy, &comp); + IGRAPH_CHECK(igraph_vector_int_init(&matchedWithU, (long int) VECTOR(Gp->nbSucc)[u])); + IGRAPH_FINALLY(igraph_vector_int_destroy, &matchedWithU); + memset(num, -1, (size_t) (Gt->nbVertices) * sizeof(int)); + for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) { + u2 = (int) VECTOR(*Gp_uneis)[i]; /* u2 is adjacent to u */ + /* search for all nodes v2 in D[u2] which are adjacent to v */ + VECTOR(nbComp)[i] = 0; + VECTOR(firstComp)[i] = posInComp; + if (VECTOR(D->nbVal)[u2] > VECTOR(Gt->nbSucc)[v]) { + for (j = VECTOR(D->firstVal)[u2]; + j < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]; j++) { + v2 = VECTOR(D->val)[j]; /* v2 belongs to D[u2] */ + if (MATRIX(Gt->isEdge, v, v2)) { /* v2 is a successor of v */ + if (num[v2] < 0) { /* v2 has not yet been added to V */ + num[v2] = nbNum; + numInv[nbNum++] = v2; + } + VECTOR(comp)[posInComp++] = num[v2]; + VECTOR(nbComp)[i]++; + } + } + } else { + igraph_vector_int_t *Gt_vneis = igraph_adjlist_get(&Gt->succ, v); + for (j = 0; j < VECTOR(Gt->nbSucc)[v]; j++) { + v2 = (int) VECTOR(*Gt_vneis)[j]; /* v2 is a successor of v */ + if (igraph_i_lad_isInD(u2, v2, D)) { /* v2 belongs to D[u2] */ + if (num[v2] < 0) { /* v2 has not yet been added to V */ + num[v2] = nbNum; + numInv[nbNum++] = v2; + } + VECTOR(comp)[posInComp++] = num[v2]; + VECTOR(nbComp)[i]++; + } + } + } + if (VECTOR(nbComp)[i] == 0) { + *result = false; /* u2 has no compatible vertex in succ[v] */ + goto cleanup; + } + /* u2 is matched to v2 in the matching that supports (u, v) */ + v2 = VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) + i]; + if ((v2 != -1) && (igraph_i_lad_isInD(u2, v2, D))) { + VECTOR(matchedWithU)[i] = num[v2]; + } else { + VECTOR(matchedWithU)[i] = -1; + } + } + /* Call Hopcroft Karp to update the matching */ + IGRAPH_CHECK( + igraph_i_lad_updateMatching((int) VECTOR(Gp->nbSucc)[u], nbNum, &nbComp, + &firstComp, &comp, &matchedWithU, &invalid) + ); + if (invalid) { + *result = false; + goto cleanup; + } + for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) { + VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) + i] = + numInv[ VECTOR(matchedWithU)[i] ]; + } + *result = true; + +cleanup: + igraph_free(numInv); + igraph_free(num); + igraph_vector_int_destroy(&matchedWithU); + igraph_vector_int_destroy(&comp); + igraph_vector_int_destroy(&firstComp); + igraph_vector_int_destroy(&nbComp); + IGRAPH_FINALLY_CLEAN(6); + + return 0; +} + +/* ---------------------------------------------------------*/ +/* Coming from main.c */ +/* ---------------------------------------------------------*/ + +static int igraph_i_lad_filter(bool induced, Tdomain* D, Tgraph* Gp, Tgraph* Gt, + bool *result) { + /* filter domains of all vertices in D->toFilter wrt LAD and ensure + GAC(allDiff) + return false if some domain becomes empty; true otherwise */ + int u, v, i, oldNbVal; + int invalid; + bool result2; + while (!igraph_i_lad_toFilterEmpty(D)) { + while (!igraph_i_lad_toFilterEmpty(D)) { + u = igraph_i_lad_nextToFilter(D, (int) (Gp->nbVertices)); + oldNbVal = VECTOR(D->nbVal)[u]; + i = VECTOR(D->firstVal)[u]; + while (i < VECTOR(D->firstVal)[u] + VECTOR(D->nbVal)[u]) { + /* for every target node v in D(u), check if G_(u, v) has a + covering matching */ + v = VECTOR(D->val)[i]; + IGRAPH_CHECK(igraph_i_lad_checkLAD(u, v, D, Gp, Gt, &result2)); + if (result2) { + i++; + } else { + IGRAPH_CHECK(igraph_i_lad_removeValue(u, v, D, Gp, Gt, &result2)); + if (!result2) { + *result = false; + return 0; + } + } + } + if ((VECTOR(D->nbVal)[u] == 1) && (oldNbVal > 1) && + (!igraph_i_lad_matchVertex(u, induced, D, Gp, Gt))) { + *result = false; return 0; + } + if (VECTOR(D->nbVal)[u] == 0) { + *result = false; + return 0; + } + } + igraph_i_lad_ensureGACallDiff(induced, Gp, Gt, D, &invalid); + if (invalid) { + *result = false; + return 0; + } + } + *result = true; + return 0; +} + + + +static int igraph_i_lad_solve(int timeLimit, bool firstSol, bool induced, + Tdomain* D, Tgraph* Gp, Tgraph* Gt, + int *invalid, igraph_bool_t *iso, + igraph_vector_t *map, igraph_vector_ptr_t *maps, + int *nbNodes, int *nbFail, int *nbSol, + clock_t *begin, igraph_vector_ptr_t *alloc_history) { + /* if firstSol then search for the first solution; otherwise search + for all solutions if induced then search for induced subgraphs; + otherwise search for partial subgraphs + return false if CPU time limit exceeded before the search is + completed, return true otherwise */ + + int u, v, minDom, i; + int* nbVal; + int* globalMatching; + clock_t end = clock(); + igraph_vector_t *vec; + int* val; + bool result; + + (*nbNodes)++; + + if ( (double)(end - *begin) / CLOCKS_PER_SEC >= timeLimit) { + /* CPU time limit exceeded */ + IGRAPH_ERROR("LAD CPU time exceeded", IGRAPH_CPUTIME); + } + + /* Allocate memory */ + ALLOC_ARRAY_IN_HISTORY(nbVal, Gp->nbVertices, int, alloc_history); + ALLOC_ARRAY_IN_HISTORY(globalMatching, Gp->nbVertices, int, alloc_history); + + IGRAPH_CHECK(igraph_i_lad_filter(induced, D, Gp, Gt, &result)); + if (!result) { + /* filtering has detected an inconsistency */ + (*nbFail)++; + igraph_i_lad_resetToFilter(D); + *invalid = 0; + goto cleanup; + } + + /* The current node of the search tree is consistent wrt to LAD and + GAC(allDiff) Save domain sizes and global all different matching + and search for the non matched vertex minDom with smallest domain */ + minDom = -1; + for (u = 0; u < Gp->nbVertices; u++) { + nbVal[u] = VECTOR(D->nbVal)[u]; + if ((nbVal[u] > 1) && ((minDom < 0) || (nbVal[u] < nbVal[minDom]))) { + minDom = u; + } + globalMatching[u] = VECTOR(D->globalMatchingP)[u]; + } + + if (minDom == -1) { + /* All vertices are matched => Solution found */ + if (iso) { + *iso = 1; + } + (*nbSol)++; + if (map && igraph_vector_size(map) == 0) { + IGRAPH_CHECK(igraph_vector_resize(map, Gp->nbVertices)); + for (u = 0; u < Gp->nbVertices; u++) { + VECTOR(*map)[u] = VECTOR(D->val)[ VECTOR(D->firstVal)[u] ]; + } + } + if (maps) { + vec = IGRAPH_CALLOC(1, igraph_vector_t); + if (!vec) { + IGRAPH_ERROR("LAD failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_init(vec, Gp->nbVertices)); + IGRAPH_FINALLY(igraph_vector_destroy, vec); + for (u = 0; u < Gp->nbVertices; u++) { + VECTOR(*vec)[u] = VECTOR(D->val)[ VECTOR(D->firstVal)[u] ]; + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(maps, vec)); + IGRAPH_FINALLY_CLEAN(2); + } + igraph_i_lad_resetToFilter(D); + *invalid = 0; + goto cleanup; + } + + /* save the domain of minDom to iterate on its values */ + ALLOC_ARRAY_IN_HISTORY(val, VECTOR(D->nbVal)[minDom], int, alloc_history); + for (i = 0; i < VECTOR(D->nbVal)[minDom]; i++) { + val[i] = VECTOR(D->val)[ VECTOR(D->firstVal)[minDom] + i ]; + } + + /* branch on minDom=v, for every target node v in D(u) */ + for (i = 0; ((i < nbVal[minDom]) && ((firstSol == 0) || (*nbSol == 0))); i++) { + IGRAPH_ALLOW_INTERRUPTION(); + v = val[i]; + IGRAPH_CHECK(igraph_i_lad_removeAllValuesButOne(minDom, v, D, Gp, Gt, &result)); + if (!result || (!igraph_i_lad_matchVertex(minDom, induced, D, Gp, Gt))) { + (*nbFail)++; + (*nbNodes)++; + igraph_i_lad_resetToFilter(D); + } else { + IGRAPH_CHECK(igraph_i_lad_solve(timeLimit, firstSol, induced, + D, Gp, Gt, invalid, iso, map, maps, + nbNodes, nbFail, nbSol, begin, + alloc_history)); + } + /* restore domain sizes and global all different matching */ + igraph_vector_int_fill(&D->globalMatchingT, -1); + for (u = 0; u < Gp->nbVertices; u++) { + VECTOR(D->nbVal)[u] = nbVal[u]; + VECTOR(D->globalMatchingP)[u] = globalMatching[u]; + VECTOR(D->globalMatchingT)[globalMatching[u]] = u; + } + } + *invalid = 0; + + igraph_free(val); + igraph_vector_ptr_pop_back(alloc_history); + +cleanup: + igraph_free(globalMatching); + igraph_vector_ptr_pop_back(alloc_history); + igraph_free(nbVal); + igraph_vector_ptr_pop_back(alloc_history); + + return 0; +} + +/** + * \section about_lad + * + * + * The LAD algorithm can search for a subgraph in a larger graph, or check + * if two graphs are isomorphic. + * See Christine Solnon: AllDifferent-based Filtering for Subgraph + * Isomorphism. Artificial Intelligence, 174(12-13):850-864, 2010. + * https://doi.org/10.1016/j.artint.2010.05.002 + * as well as the homepage of the LAD library at http://liris.cnrs.fr/csolnon/LAD.html + * The implementation in igraph is based on LADv1, but it is + * modified to use igraph's own memory allocation and error handling. + * + * + * + * LAD uses the concept of domains to indicate vertex compatibility when matching the + * pattern graph. Domains can be used to implement matching of colored vertices. + * + * + * + * LAD works with both directed and undirected graphs. Graphs with multi-edges are not supported. + * + */ + +/** + * \function igraph_subisomorphic_lad + * Check subgraph isomorphism with the LAD algorithm + * + * Check whether \p pattern is isomorphic to a subgraph os \p target. + * The original LAD implementation by Christine Solnon was used as the + * basis of this code. + * + * + * See more about LAD at http://liris.cnrs.fr/csolnon/LAD.html and in + * Christine Solnon: AllDifferent-based Filtering for Subgraph + * Isomorphism. Artificial Intelligence, 174(12-13):850-864, 2010. + * https://doi.org/10.1016/j.artint.2010.05.002 + * + * \param pattern The smaller graph, it can be directed or undirected. + * \param target The bigger graph, it can be directed or undirected. + * \param domains A pointer vector, or a null pointer. If a pointer + * vector, then it must contain pointers to \c igraph_vector_t + * objects and the length of the vector must match the number of + * vertices in the \p pattern graph. For each vertex, the ids of + * the compatible vertices in the target graph are listed. + * \param iso Pointer to a boolean, or a null pointer. If not a null + * pointer, then the boolean is set to TRUE (1) if a subgraph + * isomorphism is found, and to FALSE (0) otherwise. + * \param map Pointer to a vector or a null pointer. If not a null + * pointer and a subgraph isomorphism is found, the matching + * vertices from the target graph are listed here, for each vertex + * (in vertex id order) from the pattern graph. + * \param maps Pointer vector or a null pointer. If not a null + * pointer, then all subgraph isomorphisms are stored in the + * pointer vector, in \c igraph_vector_t objects. + * \param induced Boolean, whether to search for induced matching + * subgraphs. + * \param time_limit Processor time limit in seconds. Supply zero + * here for no limit. If the time limit is over, then the function + * signals an error. + * \return Error code + * + * \sa \ref igraph_subisomorphic_vf2() for the VF2 algorithm. + * + * Time complexity: exponential. + * + * \example examples/simple/igraph_subisomorphic_lad.c + */ + +int igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target, + const igraph_vector_ptr_t *domains, + igraph_bool_t *iso, igraph_vector_t *map, + igraph_vector_ptr_t *maps, + igraph_bool_t induced, int time_limit) { + + bool firstSol = maps == 0; + bool initialDomains = domains != 0; + Tgraph Gp, Gt; + Tdomain D; + int invalidDomain; + int u, nbToMatch = 0; + igraph_vector_int_t toMatch; + /* Number of nodes in the search tree */ + int nbNodes = 0; + /* number of failed nodes in the search tree */ + int nbFail = 0; + /* number of solutions found */ + int nbSol = 0; + /* reusable structure to get CPU time usage */ + clock_t begin = clock(); + /* Stack to store memory blocks that are allocated during igraph_i_lad_solve */ + igraph_vector_ptr_t alloc_history; + + if (!iso && !map && !maps) { + IGRAPH_ERROR("Please give least one of `iso', `map' or `maps'", + IGRAPH_EINVAL); + } + + if (igraph_is_directed(pattern) != igraph_is_directed(target)) { + IGRAPH_ERROR("Cannot search for a directed pattern in an undirected target " + "or vice versa", IGRAPH_EINVAL); + } + if (time_limit <= 0) { + time_limit = INT_MAX; + } + + if (iso) { + *iso = (igraph_vcount(pattern) == 0); + } + if (map) { + igraph_vector_clear(map); + } + if (maps) { + igraph_vector_ptr_clear(maps); + } + + if (igraph_vcount(pattern) == 0) { + /* Special case for empty graphs */ + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_i_lad_createGraph(pattern, &Gp)); + IGRAPH_FINALLY(igraph_i_lad_destroyGraph, &Gp); + + IGRAPH_CHECK(igraph_i_lad_createGraph(target, &Gt)); + IGRAPH_FINALLY(igraph_i_lad_destroyGraph, &Gt); + + if (Gp.nbVertices > Gt.nbVertices) { + goto exit3; + } + + IGRAPH_CHECK(igraph_i_lad_initDomains(initialDomains, domains, &D, &Gp, &Gt, &invalidDomain)); + IGRAPH_FINALLY(igraph_i_lad_destroyDomains, &D); + + if (invalidDomain) { + goto exit2; + } + + IGRAPH_CHECK(igraph_i_lad_updateMatching((int) (Gp.nbVertices), + (int) (Gt.nbVertices), + &D.nbVal, &D.firstVal, &D.val, + &D.globalMatchingP, + &invalidDomain)); + if (invalidDomain) { + goto exit; + } + + IGRAPH_CHECK(igraph_i_lad_ensureGACallDiff((char) induced, &Gp, &Gt, &D, + &invalidDomain)); + if (invalidDomain) { + goto exit; + } + + for (u = 0; u < Gp.nbVertices; u++) { + VECTOR(D.globalMatchingT)[ VECTOR(D.globalMatchingP)[u] ] = u; + } + + IGRAPH_CHECK(igraph_vector_int_init(&toMatch, Gp.nbVertices)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &toMatch); + + for (u = 0; u < Gp.nbVertices; u++) { + if (VECTOR(D.nbVal)[u] == 1) { + VECTOR(toMatch)[nbToMatch++] = u; + } + } + IGRAPH_CHECK(igraph_i_lad_matchVertices(nbToMatch, &toMatch, (char) induced, + &D, &Gp, &Gt, &invalidDomain)); + igraph_vector_int_destroy(&toMatch); + IGRAPH_FINALLY_CLEAN(1); + if (invalidDomain) { + goto exit; + } + + IGRAPH_CHECK(igraph_vector_ptr_init(&alloc_history, 0)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &alloc_history); + + IGRAPH_CHECK(igraph_i_lad_solve(time_limit, firstSol, (char) induced, &D, + &Gp, &Gt, &invalidDomain, iso, map, maps, + &nbNodes, &nbFail, &nbSol, &begin, + &alloc_history)); + + igraph_vector_ptr_destroy_all(&alloc_history); + IGRAPH_FINALLY_CLEAN(1); + +exit: +exit2: + + igraph_i_lad_destroyDomains(&D); + IGRAPH_FINALLY_CLEAN(1); + +exit3: + + igraph_i_lad_destroyGraph(&Gt); + igraph_i_lad_destroyGraph(&Gp); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} diff --git a/src/rigraph/core/isomorphism/queries.c b/src/rigraph/core/isomorphism/queries.c new file mode 100644 index 0000000..2f55cab --- /dev/null +++ b/src/rigraph/core/isomorphism/queries.c @@ -0,0 +1,184 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_topology.h" + +#include "igraph_interface.h" +#include "igraph_structural.h" + +/** + * \section about_graph_isomorphism + * + * igraph provides four set of functions to deal with graph + * isomorphism problems. + * + * The \ref igraph_isomorphic() and \ref igraph_subisomorphic() + * functions make up the first set (in addition with the \ref + * igraph_permute_vertices() function). These functions choose the + * algorithm which is best for the supplied input graph. (The choice is + * not very sophisticated though, see their documentation for + * details.) + * + * The VF2 graph (and subgraph) isomorphism algorithm is implemented in + * igraph, these functions are the second set. See \ref + * igraph_isomorphic_vf2() and \ref igraph_subisomorphic_vf2() for + * starters. + * + * Functions for the Bliss algorithm constitute the third set, + * see \ref igraph_isomorphic_bliss(). + * + * Finally, the isomorphism classes of all graphs with three and + * four vertices are precomputed and stored in igraph, so for these + * small graphs there is a very simple fast way to decide isomorphism. + * See \ref igraph_isomorphic_34(). + * + */ + +/** + * \function igraph_isomorphic + * \brief Decides whether two graphs are isomorphic + * + * + * In simple terms, two graphs are isomorphic if they become indistinguishable + * from each other once their vertex labels are removed (rendering the vertices + * within each graph indistiguishable). More precisely, two graphs are isomorphic + * if there is a one-to-one mapping from the vertices of the first one + * to the vertices of the second such that it transforms the edge set of the + * first graph into the edge set of the second. This mapping is called + * an \em isomorphism. + * + * Currently, this function supports simple graphs and graphs + * with self-loops, but does not support multigraphs. + * + * This function decides which graph isomorphism algorithm to be + * used based on the input graphs. Right now it does the following: + * \olist + * \oli If one graph is directed and the other undirected then an + * error is triggered. + * \oli If one of the graphs has multi-edges then an error is triggered. + * \oli If the two graphs does not have the same number of vertices + * and edges it returns with \c FALSE. + * \oli Otherwise, if the graphs have three or four vertices then an O(1) + * algorithm is used with precomputed data. + * \oli Otherwise Bliss is used, see \ref igraph_isomorphic_bliss(). + * \endolist + * + * Please call the VF2 and Bliss functions directly if you need + * something more sophisticated, e.g. you need the isomorphic mapping. + * + * \param graph1 The first graph. + * \param graph2 The second graph. + * \param iso Pointer to a logical variable, will be set to TRUE (1) + * if the two graphs are isomorphic, and FALSE (0) otherwise. + * \return Error code. + * \sa \ref igraph_isoclass(), \ref igraph_isoclass_subgraph(), + * \ref igraph_isoclass_create(). + * + * Time complexity: exponential. + */ +int igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, + igraph_bool_t *iso) { + + long int nodes1 = igraph_vcount(graph1), nodes2 = igraph_vcount(graph2); + long int edges1 = igraph_ecount(graph1), edges2 = igraph_ecount(graph2); + igraph_bool_t dir1 = igraph_is_directed(graph1), dir2 = igraph_is_directed(graph2); + igraph_bool_t loop1, loop2, multi1, multi2; + + IGRAPH_CHECK(igraph_has_multiple(graph1, &multi1)); + IGRAPH_CHECK(igraph_has_multiple(graph2, &multi2)); + + if (multi1 || multi2) { + IGRAPH_ERROR("Isomorphism testing is not implemented for multigraphs", IGRAPH_UNIMPLEMENTED); + } + + if (dir1 != dir2) { + IGRAPH_ERROR("Cannot compare directed and undirected graphs", IGRAPH_EINVAL); + } else if (nodes1 != nodes2 || edges1 != edges2) { + *iso = 0; + } else if (nodes1 == 3 || nodes1 == 4) { + IGRAPH_CHECK(igraph_has_loop(graph1, &loop1)); + IGRAPH_CHECK(igraph_has_loop(graph2, &loop2)); + if (!loop1 && !loop2) { + IGRAPH_CHECK(igraph_isomorphic_34(graph1, graph2, iso)); + } else { + IGRAPH_CHECK(igraph_isomorphic_bliss(graph1, graph2, NULL, NULL, iso, + 0, 0, /*sh=*/ IGRAPH_BLISS_FL, 0, 0)); + } + } else { + IGRAPH_CHECK(igraph_isomorphic_bliss(graph1, graph2, NULL, NULL, iso, + 0, 0, /*sh=*/ IGRAPH_BLISS_FL, 0, 0)); + } + + return 0; +} + +/** + * \function igraph_isomorphic_34 + * Graph isomorphism for 3-4 vertices + * + * This function uses precomputed indices to decide isomorphism + * problems for graphs with only 3 or 4 vertices. Multi-edges + * and self-loops are ignored by this function. + * \param graph1 The first input graph. + * \param graph2 The second input graph. Must have the same + * directedness as \p graph1. + * \param iso Pointer to a boolean, the result is stored here. + * \return Error code. + * + * Time complexity: O(1). + */ +int igraph_isomorphic_34(const igraph_t *graph1, const igraph_t *graph2, + igraph_bool_t *iso) { + + igraph_integer_t class1, class2; + IGRAPH_CHECK(igraph_isoclass(graph1, &class1)); + IGRAPH_CHECK(igraph_isoclass(graph2, &class2)); + *iso = (class1 == class2); + return 0; +} + +/** + * \function igraph_subisomorphic + * \brief Decide subgraph isomorphism. + * + * Check whether \p graph2 is isomorphic to a subgraph of \p graph1. + * Currently this function just calls \ref igraph_subisomorphic_vf2() + * for all graphs. + * + * + * Currently this function does not support non-simple graphs. + * + * \param graph1 The first input graph, may be directed or + * undirected. This is supposed to be the bigger graph. + * \param graph2 The second input graph, it must have the same + * directedness as \p graph2, or an error is triggered. This is + * supposed to be the smaller graph. + * \param iso Pointer to a boolean, the result is stored here. + * \return Error code. + * + * Time complexity: exponential. + */ +int igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2, + igraph_bool_t *iso) { + + return igraph_subisomorphic_vf2(graph1, graph2, NULL, NULL, NULL, NULL, iso, NULL, NULL, NULL, NULL, NULL); +} diff --git a/src/rigraph/core/isomorphism/vf2.c b/src/rigraph/core/isomorphism/vf2.c new file mode 100644 index 0000000..33ac251 --- /dev/null +++ b/src/rigraph/core/isomorphism/vf2.c @@ -0,0 +1,1757 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_topology.h" + +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_stack.h" + +#include "../core/interruption.h" + +/** + * \section about_vf2 + * + * + * The VF2 algorithm can search for a subgraph in a larger graph, or check if two + * graphs are isomorphic. See P. Foggia, C. Sansone, M. Vento, An Improved algorithm for + * matching large graphs, Proc. of the 3rd IAPR-TC-15 International + * Workshop on Graph-based Representations, Italy, 2001. + * + * + * + * VF2 supports both vertex and edge-colored graphs, as well as custom vertex or edge + * compatibility functions. + * + * + * + * VF2 works with both directed and undirected graphs. Only simple graphs are supported. + * Self-loops or multi-edges must not be present in the graphs. Currently, the VF2 + * functions do not check that the input graph is simple: it is the responsibility + * of the user to pass in valid input. + * + */ + +/** + * \function igraph_isomorphic_function_vf2 + * The generic VF2 interface + * + * + * This function is an implementation of the VF2 isomorphism algorithm, + * see P. Foggia, C. Sansone, M. Vento, An Improved algorithm for + * matching large graphs, Proc. of the 3rd IAPR-TC-15 International + * Workshop on Graph-based Representations, Italy, 2001. + * + * For using it you need to define a callback function of type + * \ref igraph_isohandler_t. This function will be called whenever VF2 + * finds an isomorphism between the two graphs. The mapping between + * the two graphs will be also provided to this function. If the + * callback returns a nonzero value then the search is continued, + * otherwise it stops. The callback function must not destroy the + * mapping vectors that are passed to it. + * \param graph1 The first input graph. + * \param graph2 The second input graph. + * \param vertex_color1 An optional color vector for the first graph. If + * color vectors are given for both graphs, then the isomorphism is + * calculated on the colored graphs; i.e. two vertices can match + * only if their color also matches. Supply a null pointer here if + * your graphs are not colored. + * \param vertex_color2 An optional color vector for the second graph. See + * the previous argument for explanation. + * \param edge_color1 An optional edge color vector for the first + * graph. The matching edges in the two graphs must have matching + * colors as well. Supply a null pointer here if your graphs are not + * edge-colored. + * \param edge_color2 The edge color vector for the second graph. + * \param map12 Pointer to an initialized vector or \c NULL. If not \c + * NULL and the supplied graphs are isomorphic then the permutation + * taking \p graph1 to \p graph is stored here. If not \c NULL and the + * graphs are not isomorphic then a zero-length vector is returned. + * \param map21 This is the same as \p map12, but for the permutation + * taking \p graph2 to \p graph1. + * \param isohandler_fn The callback function to be called if an + * isomorphism is found. See also \ref igraph_isohandler_t. + * \param node_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two nodes are compatible. + * \param edge_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two edges are compatible. + * \param arg Extra argument to supply to functions \p isohandler_fn, \p + * node_compat_fn and \p edge_compat_fn. + * \return Error code. + * + * Time complexity: exponential. + */ + +int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph2, + const igraph_vector_int_t *vertex_color1, + const igraph_vector_int_t *vertex_color2, + const igraph_vector_int_t *edge_color1, + const igraph_vector_int_t *edge_color2, + igraph_vector_t *map12, + igraph_vector_t *map21, + igraph_isohandler_t *isohandler_fn, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg) { + + long int no_of_nodes = igraph_vcount(graph1); + long int no_of_edges = igraph_ecount(graph1); + igraph_vector_t mycore_1, mycore_2, *core_1 = &mycore_1, *core_2 = &mycore_2; + igraph_vector_t in_1, in_2, out_1, out_2; + long int in_1_size = 0, in_2_size = 0, out_1_size = 0, out_2_size = 0; + igraph_vector_int_t *inneis_1, *inneis_2, *outneis_1, *outneis_2; + long int matched_nodes = 0; + long int depth; + long int cand1, cand2; + long int last1, last2; + igraph_stack_t path; + igraph_lazy_adjlist_t inadj1, inadj2, outadj1, outadj2; + igraph_vector_t indeg1, indeg2, outdeg1, outdeg2; + long int vsize; + + if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) { + IGRAPH_ERROR("Cannot compare directed and undirected graphs", + IGRAPH_EINVAL); + } + + if ( (vertex_color1 && !vertex_color2) || (!vertex_color1 && vertex_color2) ) { + IGRAPH_WARNING("Only one graph is vertex-colored, vertex colors will be ignored"); + vertex_color1 = vertex_color2 = 0; + } + + if ( (edge_color1 && !edge_color2) || (!edge_color1 && edge_color2)) { + IGRAPH_WARNING("Only one graph is edge-colored, edge colors will be ignored"); + edge_color1 = edge_color2 = 0; + } + + if (no_of_nodes != igraph_vcount(graph2) || + no_of_edges != igraph_ecount(graph2)) { + return 0; + } + + if (vertex_color1) { + if (igraph_vector_int_size(vertex_color1) != no_of_nodes || + igraph_vector_int_size(vertex_color2) != no_of_nodes) { + IGRAPH_ERROR("Invalid vertex color vector length", IGRAPH_EINVAL); + } + } + + if (edge_color1) { + if (igraph_vector_int_size(edge_color1) != no_of_edges || + igraph_vector_int_size(edge_color2) != no_of_edges) { + IGRAPH_ERROR("Invalid edge color vector length", IGRAPH_EINVAL); + } + } + + /* Check color distribution */ + if (vertex_color1) { + int ret = 0; + igraph_vector_int_t tmp1, tmp2; + IGRAPH_CHECK(igraph_vector_int_copy(&tmp1, vertex_color1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp1); + IGRAPH_CHECK(igraph_vector_int_copy(&tmp2, vertex_color2)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp2); + igraph_vector_int_sort(&tmp1); + igraph_vector_int_sort(&tmp2); + ret = !igraph_vector_int_all_e(&tmp1, &tmp2); + igraph_vector_int_destroy(&tmp1); + igraph_vector_int_destroy(&tmp2); + IGRAPH_FINALLY_CLEAN(2); + if (ret) { + return 0; + } + } + + /* Check edge color distribution */ + if (edge_color1) { + int ret = 0; + igraph_vector_int_t tmp1, tmp2; + IGRAPH_CHECK(igraph_vector_int_copy(&tmp1, edge_color1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp1); + IGRAPH_CHECK(igraph_vector_int_copy(&tmp2, edge_color2)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp2); + igraph_vector_int_sort(&tmp1); + igraph_vector_int_sort(&tmp2); + ret = !igraph_vector_int_all_e(&tmp1, &tmp2); + igraph_vector_int_destroy(&tmp1); + igraph_vector_int_destroy(&tmp2); + IGRAPH_FINALLY_CLEAN(2); + if (ret) { + return 0; + } + } + + if (map12) { + core_1 = map12; + IGRAPH_CHECK(igraph_vector_resize(core_1, no_of_nodes)); + } else { + IGRAPH_VECTOR_INIT_FINALLY(core_1, no_of_nodes); + } + igraph_vector_fill(core_1, -1); + if (map21) { + core_2 = map21; + IGRAPH_CHECK(igraph_vector_resize(core_2, no_of_nodes)); + igraph_vector_null(core_2); + } else { + IGRAPH_VECTOR_INIT_FINALLY(core_2, no_of_nodes); + } + igraph_vector_fill(core_2, -1); + + IGRAPH_VECTOR_INIT_FINALLY(&in_1, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&in_2, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&out_1, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&out_2, no_of_nodes); + IGRAPH_CHECK(igraph_stack_init(&path, 0)); + IGRAPH_FINALLY(igraph_stack_destroy, &path); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &inadj1, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj1); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &outadj1, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj1); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &inadj2, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj2); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &outadj2, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj2); + IGRAPH_VECTOR_INIT_FINALLY(&indeg1, 0); + IGRAPH_VECTOR_INIT_FINALLY(&indeg2, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outdeg1, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outdeg2, 0); + + IGRAPH_CHECK(igraph_stack_reserve(&path, no_of_nodes * 2)); + IGRAPH_CHECK(igraph_degree(graph1, &indeg1, igraph_vss_all(), + IGRAPH_IN, IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree(graph2, &indeg2, igraph_vss_all(), + IGRAPH_IN, IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree(graph1, &outdeg1, igraph_vss_all(), + IGRAPH_OUT, IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree(graph2, &outdeg2, igraph_vss_all(), + IGRAPH_OUT, IGRAPH_LOOPS)); + + depth = 0; last1 = -1; last2 = -1; + while (depth >= 0) { + long int i; + + IGRAPH_ALLOW_INTERRUPTION(); + + cand1 = -1; cand2 = -1; + /* Search for the next pair to try */ + if ((in_1_size != in_2_size) || + (out_1_size != out_2_size)) { + /* step back, nothing to do */ + } else if (out_1_size > 0 && out_2_size > 0) { + /**************************************************************/ + /* cand2, search not always needed */ + if (last2 >= 0) { + cand2 = last2; + } else { + i = 0; + while (cand2 < 0 && i < no_of_nodes) { + if (VECTOR(out_2)[i] > 0 && VECTOR(*core_2)[i] < 0) { + cand2 = i; + } + i++; + } + } + /* search for cand1 now, it should be bigger than last1 */ + i = last1 + 1; + while (cand1 < 0 && i < no_of_nodes) { + if (VECTOR(out_1)[i] > 0 && VECTOR(*core_1)[i] < 0) { + cand1 = i; + } + i++; + } + } else if (in_1_size > 0 && in_2_size > 0) { + /**************************************************************/ + /* cand2, search not always needed */ + if (last2 >= 0) { + cand2 = last2; + } else { + i = 0; + while (cand2 < 0 && i < no_of_nodes) { + if (VECTOR(in_2)[i] > 0 && VECTOR(*core_2)[i] < 0) { + cand2 = i; + } + i++; + } + } + /* search for cand1 now, should be bigger than last1 */ + i = last1 + 1; + while (cand1 < 0 && i < no_of_nodes) { + if (VECTOR(in_1)[i] > 0 && VECTOR(*core_1)[i] < 0) { + cand1 = i; + } + i++; + } + } else { + /**************************************************************/ + /* cand2, search not always needed */ + if (last2 >= 0) { + cand2 = last2; + } else { + i = 0; + while (cand2 < 0 && i < no_of_nodes) { + if (VECTOR(*core_2)[i] < 0) { + cand2 = i; + } + i++; + } + } + /* search for cand1, should be bigger than last1 */ + i = last1 + 1; + while (cand1 < 0 && i < no_of_nodes) { + if (VECTOR(*core_1)[i] < 0) { + cand1 = i; + } + i++; + } + } + + /* Ok, we have cand1, cand2 as candidates. Or not? */ + if (cand1 < 0 || cand2 < 0) { + /**************************************************************/ + /* dead end, step back, if possible. Otherwise we'll terminate */ + if (depth >= 1) { + last2 = (long int) igraph_stack_pop(&path); + last1 = (long int) igraph_stack_pop(&path); + matched_nodes -= 1; + VECTOR(*core_1)[last1] = -1; + VECTOR(*core_2)[last2] = -1; + + if (VECTOR(in_1)[last1] != 0) { + in_1_size += 1; + } + if (VECTOR(out_1)[last1] != 0) { + out_1_size += 1; + } + if (VECTOR(in_2)[last2] != 0) { + in_2_size += 1; + } + if (VECTOR(out_2)[last2] != 0) { + out_2_size += 1; + } + + inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) last1); + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*inneis_1)[i]; + if (VECTOR(in_1)[node] == depth) { + VECTOR(in_1)[node] = 0; + in_1_size -= 1; + } + } + outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) last1); + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*outneis_1)[i]; + if (VECTOR(out_1)[node] == depth) { + VECTOR(out_1)[node] = 0; + out_1_size -= 1; + } + } + inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) last2); + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*inneis_2)[i]; + if (VECTOR(in_2)[node] == depth) { + VECTOR(in_2)[node] = 0; + in_2_size -= 1; + } + } + outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) last2); + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*outneis_2)[i]; + if (VECTOR(out_2)[node] == depth) { + VECTOR(out_2)[node] = 0; + out_2_size -= 1; + } + } + + } /* end of stepping back */ + + depth -= 1; + + } else { + /**************************************************************/ + /* step forward if worth, check if worth first */ + long int xin1 = 0, xin2 = 0, xout1 = 0, xout2 = 0; + igraph_bool_t end = 0; + inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1); + outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) cand1); + inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) cand2); + outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) cand2); + if (VECTOR(indeg1)[cand1] != VECTOR(indeg2)[cand2] || + VECTOR(outdeg1)[cand1] != VECTOR(outdeg2)[cand2]) { + end = 1; + } + if (vertex_color1 && VECTOR(*vertex_color1)[cand1] != VECTOR(*vertex_color2)[cand2]) { + end = 1; + } + if (node_compat_fn && !node_compat_fn(graph1, graph2, + (igraph_integer_t) cand1, + (igraph_integer_t) cand2, arg)) { + end = 1; + } + + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; !end && i < vsize; i++) { + long int node = (long int) VECTOR(*inneis_1)[i]; + if (VECTOR(*core_1)[node] >= 0) { + long int node2 = (long int) VECTOR(*core_1)[node]; + /* check if there is a node2->cand2 edge */ + if (!igraph_vector_int_binsearch2(inneis_2, node2)) { + end = 1; + } else if (edge_color1 || edge_compat_fn) { + igraph_integer_t eid1, eid2; + igraph_get_eid(graph1, &eid1, (igraph_integer_t) node, + (igraph_integer_t) cand1, /*directed=*/ 1, + /*error=*/ 1); + igraph_get_eid(graph2, &eid2, (igraph_integer_t) node2, + (igraph_integer_t) cand2, /*directed=*/ 1, + /*error=*/ 1); + if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] != + VECTOR(*edge_color2)[(long int)eid2]) { + end = 1; + } + if (edge_compat_fn && !edge_compat_fn(graph1, graph2, + eid1, eid2, arg)) { + end = 1; + } + } + } else { + if (VECTOR(in_1)[node] != 0) { + xin1++; + } + if (VECTOR(out_1)[node] != 0) { + xout1++; + } + } + } + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; !end && i < vsize; i++) { + long int node = (long int) VECTOR(*outneis_1)[i]; + if (VECTOR(*core_1)[node] >= 0) { + long int node2 = (long int) VECTOR(*core_1)[node]; + /* check if there is a cand2->node2 edge */ + if (!igraph_vector_int_binsearch2(outneis_2, node2)) { + end = 1; + } else if (edge_color1 || edge_compat_fn) { + igraph_integer_t eid1, eid2; + igraph_get_eid(graph1, &eid1, (igraph_integer_t) cand1, + (igraph_integer_t) node, /*directed=*/ 1, + /*error=*/ 1); + igraph_get_eid(graph2, &eid2, (igraph_integer_t) cand2, + (igraph_integer_t) node2, /*directed=*/ 1, + /*error=*/ 1); + if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] != + VECTOR(*edge_color2)[(long int)eid2]) { + end = 1; + } + if (edge_compat_fn && !edge_compat_fn(graph1, graph2, + eid1, eid2, arg)) { + end = 1; + } + } + } else { + if (VECTOR(in_1)[node] != 0) { + xin1++; + } + if (VECTOR(out_1)[node] != 0) { + xout1++; + } + } + } + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; !end && i < vsize; i++) { + long int node = (long int) VECTOR(*inneis_2)[i]; + if (VECTOR(*core_2)[node] >= 0) { + long int node2 = (long int) VECTOR(*core_2)[node]; + /* check if there is a node2->cand1 edge */ + if (!igraph_vector_int_binsearch2(inneis_1, node2)) { + end = 1; + } else if (edge_color1 || edge_compat_fn) { + igraph_integer_t eid1, eid2; + igraph_get_eid(graph1, &eid1, (igraph_integer_t) node2, + (igraph_integer_t) cand1, /*directed=*/ 1, + /*error=*/ 1); + igraph_get_eid(graph2, &eid2, (igraph_integer_t) node, + (igraph_integer_t) cand2, /*directed=*/ 1, + /*error=*/ 1); + if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] != + VECTOR(*edge_color2)[(long int)eid2]) { + end = 1; + } + if (edge_compat_fn && !edge_compat_fn(graph1, graph2, + eid1, eid2, arg)) { + end = 1; + } + } + } else { + if (VECTOR(in_2)[node] != 0) { + xin2++; + } + if (VECTOR(out_2)[node] != 0) { + xout2++; + } + } + } + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; !end && i < vsize; i++) { + long int node = (long int) VECTOR(*outneis_2)[i]; + if (VECTOR(*core_2)[node] >= 0) { + long int node2 = (long int) VECTOR(*core_2)[node]; + /* check if there is a cand1->node2 edge */ + if (!igraph_vector_int_binsearch2(outneis_1, node2)) { + end = 1; + } else if (edge_color1 || edge_compat_fn) { + igraph_integer_t eid1, eid2; + igraph_get_eid(graph1, &eid1, (igraph_integer_t) cand1, + (igraph_integer_t) node2, /*directed=*/ 1, + /*error=*/ 1); + igraph_get_eid(graph2, &eid2, (igraph_integer_t) cand2, + (igraph_integer_t) node, /*directed=*/ 1, + /*error=*/ 1); + if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] != + VECTOR(*edge_color2)[(long int)eid2]) { + end = 1; + } + if (edge_compat_fn && !edge_compat_fn(graph1, graph2, + eid1, eid2, arg)) { + end = 1; + } + } + } else { + if (VECTOR(in_2)[node] != 0) { + xin2++; + } + if (VECTOR(out_2)[node] != 0) { + xout2++; + } + } + } + + if (!end && (xin1 == xin2 && xout1 == xout2)) { + /* Ok, we add the (cand1, cand2) pair to the mapping */ + depth += 1; + IGRAPH_CHECK(igraph_stack_push(&path, cand1)); + IGRAPH_CHECK(igraph_stack_push(&path, cand2)); + matched_nodes += 1; + VECTOR(*core_1)[cand1] = cand2; + VECTOR(*core_2)[cand2] = cand1; + + /* update in_*, out_* */ + if (VECTOR(in_1)[cand1] != 0) { + in_1_size -= 1; + } + if (VECTOR(out_1)[cand1] != 0) { + out_1_size -= 1; + } + if (VECTOR(in_2)[cand2] != 0) { + in_2_size -= 1; + } + if (VECTOR(out_2)[cand2] != 0) { + out_2_size -= 1; + } + + inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1); + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*inneis_1)[i]; + if (VECTOR(in_1)[node] == 0 && VECTOR(*core_1)[node] < 0) { + VECTOR(in_1)[node] = depth; + in_1_size += 1; + } + } + outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) cand1); + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*outneis_1)[i]; + if (VECTOR(out_1)[node] == 0 && VECTOR(*core_1)[node] < 0) { + VECTOR(out_1)[node] = depth; + out_1_size += 1; + } + } + inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) cand2); + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*inneis_2)[i]; + if (VECTOR(in_2)[node] == 0 && VECTOR(*core_2)[node] < 0) { + VECTOR(in_2)[node] = depth; + in_2_size += 1; + } + } + outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) cand2); + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*outneis_2)[i]; + if (VECTOR(out_2)[node] == 0 && VECTOR(*core_2)[node] < 0) { + VECTOR(out_2)[node] = depth; + out_2_size += 1; + } + } + last1 = -1; last2 = -1; /* this the first time here */ + } else { + last1 = cand1; + last2 = cand2; + } + + } + + if (matched_nodes == no_of_nodes && isohandler_fn) { + if (!isohandler_fn(core_1, core_2, arg)) { + break; + } + } + } + + igraph_vector_destroy(&outdeg2); + igraph_vector_destroy(&outdeg1); + igraph_vector_destroy(&indeg2); + igraph_vector_destroy(&indeg1); + igraph_lazy_adjlist_destroy(&outadj2); + igraph_lazy_adjlist_destroy(&inadj2); + igraph_lazy_adjlist_destroy(&outadj1); + igraph_lazy_adjlist_destroy(&inadj1); + igraph_stack_destroy(&path); + igraph_vector_destroy(&out_2); + igraph_vector_destroy(&out_1); + igraph_vector_destroy(&in_2); + igraph_vector_destroy(&in_1); + IGRAPH_FINALLY_CLEAN(13); + if (!map21) { + igraph_vector_destroy(core_2); + IGRAPH_FINALLY_CLEAN(1); + } + if (!map12) { + igraph_vector_destroy(core_1); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +typedef struct { + igraph_isocompat_t *node_compat_fn, *edge_compat_fn; + void *arg, *carg; +} igraph_i_iso_cb_data_t; + +static igraph_bool_t igraph_i_isocompat_node_cb( + const igraph_t *graph1, + const igraph_t *graph2, + const igraph_integer_t g1_num, + const igraph_integer_t g2_num, + void *arg) { + igraph_i_iso_cb_data_t *data = arg; + return data->node_compat_fn(graph1, graph2, g1_num, g2_num, data->carg); +} + +static igraph_bool_t igraph_i_isocompat_edge_cb( + const igraph_t *graph1, + const igraph_t *graph2, + const igraph_integer_t g1_num, + const igraph_integer_t g2_num, + void *arg) { + igraph_i_iso_cb_data_t *data = arg; + return data->edge_compat_fn(graph1, graph2, g1_num, g2_num, data->carg); +} + +static igraph_bool_t igraph_i_isomorphic_vf2(igraph_vector_t *map12, + igraph_vector_t *map21, + void *arg) { + igraph_i_iso_cb_data_t *data = arg; + igraph_bool_t *iso = data->arg; + IGRAPH_UNUSED(map12); IGRAPH_UNUSED(map21); + *iso = 1; + return 0; /* don't need to continue */ +} + +/** + * \function igraph_isomorphic_vf2 + * \brief Isomorphism via VF2 + * + * + * This function performs the VF2 algorithm via calling \ref + * igraph_isomorphic_function_vf2(). + * + * Note that this function cannot be used for + * deciding subgraph isomorphism, use \ref igraph_subisomorphic_vf2() + * for that. + * \param graph1 The first graph, may be directed or undirected. + * \param graph2 The second graph. It must have the same directedness + * as \p graph1, otherwise an error is reported. + * \param vertex_color1 An optional color vector for the first graph. If + * color vectors are given for both graphs, then the isomorphism is + * calculated on the colored graphs; i.e. two vertices can match + * only if their color also matches. Supply a null pointer here if + * your graphs are not colored. + * \param vertex_color2 An optional color vector for the second graph. See + * the previous argument for explanation. + * \param edge_color1 An optional edge color vector for the first + * graph. The matching edges in the two graphs must have matching + * colors as well. Supply a null pointer here if your graphs are not + * edge-colored. + * \param edge_color2 The edge color vector for the second graph. + * \param iso Pointer to a logical constant, the result of the + * algorithm will be placed here. + * \param map12 Pointer to an initialized vector or a NULL pointer. If not + * a NULL pointer then the mapping from \p graph1 to \p graph2 is + * stored here. If the graphs are not isomorphic then the vector is + * cleared (i.e. has zero elements). + * \param map21 Pointer to an initialized vector or a NULL pointer. If not + * a NULL pointer then the mapping from \p graph2 to \p graph1 is + * stored here. If the graphs are not isomorphic then the vector is + * cleared (i.e. has zero elements). + * \param node_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two nodes are compatible. + * \param edge_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two edges are compatible. + * \param arg Extra argument to supply to functions \p node_compat_fn + * and \p edge_compat_fn. + * \return Error code. + * + * \sa \ref igraph_subisomorphic_vf2(), + * \ref igraph_count_isomorphisms_vf2(), + * \ref igraph_get_isomorphisms_vf2(), + * + * Time complexity: exponential, what did you expect? + * + * \example examples/simple/igraph_isomorphic_vf2.c + */ + +int igraph_isomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, + const igraph_vector_int_t *vertex_color1, + const igraph_vector_int_t *vertex_color2, + const igraph_vector_int_t *edge_color1, + const igraph_vector_int_t *edge_color2, + igraph_bool_t *iso, igraph_vector_t *map12, + igraph_vector_t *map21, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg) { + + igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, iso, arg }; + igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0; + igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0; + *iso = 0; + IGRAPH_CHECK(igraph_isomorphic_function_vf2(graph1, graph2, + vertex_color1, vertex_color2, + edge_color1, edge_color2, + map12, map21, + (igraph_isohandler_t*) + igraph_i_isomorphic_vf2, + ncb, ecb, &data)); + if (! *iso) { + if (map12) { + igraph_vector_clear(map12); + } + if (map21) { + igraph_vector_clear(map21); + } + } + return 0; +} + +static igraph_bool_t igraph_i_count_isomorphisms_vf2( + const igraph_vector_t *map12, + const igraph_vector_t *map21, + void *arg) { + igraph_i_iso_cb_data_t *data = arg; + igraph_integer_t *count = data->arg; + IGRAPH_UNUSED(map12); IGRAPH_UNUSED(map21); + *count += 1; + return 1; /* always continue */ +} + +/** + * \function igraph_count_isomorphisms_vf2 + * Number of isomorphisms via VF2 + * + * This function counts the number of isomorphic mappings between two + * graphs. It uses the generic \ref igraph_isomorphic_function_vf2() + * function. + * \param graph1 The first input graph, may be directed or undirected. + * \param graph2 The second input graph, it must have the same + * directedness as \p graph1, or an error will be reported. + * \param vertex_color1 An optional color vector for the first graph. If + * color vectors are given for both graphs, then the isomorphism is + * calculated on the colored graphs; i.e. two vertices can match + * only if their color also matches. Supply a null pointer here if + * your graphs are not colored. + * \param vertex_color2 An optional color vector for the second graph. See + * the previous argument for explanation. + * \param edge_color1 An optional edge color vector for the first + * graph. The matching edges in the two graphs must have matching + * colors as well. Supply a null pointer here if your graphs are not + * edge-colored. + * \param edge_color2 The edge color vector for the second graph. + * \param count Point to an integer, the result will be stored here. + * \param node_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two nodes are compatible. + * \param edge_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two edges are compatible. + * \param arg Extra argument to supply to functions \p node_compat_fn and + * \p edge_compat_fn. + * \return Error code. + * + * Time complexity: exponential. + */ + +int igraph_count_isomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, + const igraph_vector_int_t *vertex_color1, + const igraph_vector_int_t *vertex_color2, + const igraph_vector_int_t *edge_color1, + const igraph_vector_int_t *edge_color2, + igraph_integer_t *count, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg) { + + igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, + count, arg + }; + igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0; + igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0; + *count = 0; + IGRAPH_CHECK(igraph_isomorphic_function_vf2(graph1, graph2, + vertex_color1, vertex_color2, + edge_color1, edge_color2, + 0, 0, + (igraph_isohandler_t*) + igraph_i_count_isomorphisms_vf2, + ncb, ecb, &data)); + return 0; +} + +static void igraph_i_get_isomorphisms_free(igraph_vector_ptr_t *data) { + long int i, n = igraph_vector_ptr_size(data); + for (i = 0; i < n; i++) { + igraph_vector_t *vec = VECTOR(*data)[i]; + igraph_vector_destroy(vec); + igraph_free(vec); + } +} + +static int igraph_i_get_isomorphisms_vf2_inner( + const igraph_vector_t *map12, + const igraph_vector_t *map21, + void *arg) { + igraph_i_iso_cb_data_t *data = arg; + igraph_vector_ptr_t *ptrvector = data->arg; + igraph_vector_t *newvector = IGRAPH_CALLOC(1, igraph_vector_t); + IGRAPH_UNUSED(map12); + if (!newvector) { + IGRAPH_ERROR("", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newvector); + IGRAPH_CHECK(igraph_vector_copy(newvector, map21)); + IGRAPH_FINALLY(igraph_vector_destroy, newvector); + IGRAPH_CHECK(igraph_vector_ptr_push_back(ptrvector, newvector)); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_bool_t igraph_i_get_isomorphisms_vf2( + const igraph_vector_t *map12, + const igraph_vector_t *map21, + void *arg) { + return igraph_i_get_isomorphisms_vf2_inner(map12, map21, arg) == IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_isomorphisms_vf2 + * \brief Collect all isomorphic mappings of two graphs. + * + * This function finds all the isomorphic mappings between two simple + * graphs. It uses the \ref igraph_isomorphic_function_vf2() + * function. Call the function with the same graph as \p graph1 and \p + * graph2 to get automorphisms. + * \param graph1 The first input graph, may be directed or undirected. + * \param graph2 The second input graph, it must have the same + * directedness as \p graph1, or an error will be reported. + * \param vertex_color1 An optional color vector for the first graph. If + * color vectors are given for both graphs, then the isomorphism is + * calculated on the colored graphs; i.e. two vertices can match + * only if their color also matches. Supply a null pointer here if + * your graphs are not colored. + * \param vertex_color2 An optional color vector for the second graph. See + * the previous argument for explanation. + * \param edge_color1 An optional edge color vector for the first + * graph. The matching edges in the two graphs must have matching + * colors as well. Supply a null pointer here if your graphs are not + * edge-colored. + * \param edge_color2 The edge color vector for the second graph. + * \param maps Pointer vector. On return it is empty if the input graphs + * are not isomorphic. Otherwise it contains pointers to + * \ref igraph_vector_t objects, each vector is an + * isomorphic mapping of \p graph2 to \p graph1. Please note that + * you need to 1) Destroy the vectors via \ref + * igraph_vector_destroy(), 2) free them via + * \ref igraph_free() and then 3) call \ref + * igraph_vector_ptr_destroy() on the pointer vector to deallocate all + * memory when \p maps is no longer needed. + * \param node_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two nodes are compatible. + * \param edge_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two edges are compatible. + * \param arg Extra argument to supply to functions \p node_compat_fn + * and \p edge_compat_fn. + * \return Error code. + * + * Time complexity: exponential. + */ + +int igraph_get_isomorphisms_vf2(const igraph_t *graph1, + const igraph_t *graph2, + const igraph_vector_int_t *vertex_color1, + const igraph_vector_int_t *vertex_color2, + const igraph_vector_int_t *edge_color1, + const igraph_vector_int_t *edge_color2, + igraph_vector_ptr_t *maps, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg) { + + igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, maps, arg }; + igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : NULL; + igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : NULL; + + igraph_vector_ptr_clear(maps); + IGRAPH_FINALLY(igraph_i_get_isomorphisms_free, maps); + IGRAPH_CHECK(igraph_isomorphic_function_vf2(graph1, graph2, + vertex_color1, vertex_color2, + edge_color1, edge_color2, + NULL, NULL, + (igraph_isohandler_t*) + igraph_i_get_isomorphisms_vf2, + ncb, ecb, &data)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_subisomorphic_function_vf2 + * Generic VF2 function for subgraph isomorphism problems + * + * This function is the pair of \ref igraph_isomorphic_function_vf2(), + * for subgraph isomorphism problems. It searches for subgraphs of \p + * graph1 which are isomorphic to \p graph2. When it founds an + * isomorphic mapping it calls the supplied callback \p isohandler_fn. + * The mapping (and its inverse) and the additional \p arg argument + * are supplied to the callback. + * \param graph1 The first input graph, may be directed or + * undirected. This is supposed to be the larger graph. + * \param graph2 The second input graph, it must have the same + * directedness as \p graph1. This is supposed to be the smaller + * graph. + * \param vertex_color1 An optional color vector for the first graph. If + * color vectors are given for both graphs, then the subgraph isomorphism is + * calculated on the colored graphs; i.e. two vertices can match + * only if their color also matches. Supply a null pointer here if + * your graphs are not colored. + * \param vertex_color2 An optional color vector for the second graph. See + * the previous argument for explanation. + * \param edge_color1 An optional edge color vector for the first + * graph. The matching edges in the two graphs must have matching + * colors as well. Supply a null pointer here if your graphs are not + * edge-colored. + * \param edge_color2 The edge color vector for the second graph. + * \param map12 Pointer to a vector or \c NULL. If not \c NULL, then an + * isomorphic mapping from \p graph1 to \p graph2 is stored here. + * \param map21 Pointer to a vector ot \c NULL. If not \c NULL, then + * an isomorphic mapping from \p graph2 to \p graph1 is stored + * here. + * \param isohandler_fn A pointer to a function of type \ref + * igraph_isohandler_t. This will be called whenever a subgraph + * isomorphism is found. If the function returns with a non-zero value + * then the search is continued, otherwise it stops and the function + * returns. + * \param node_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two nodes are compatible. + * \param edge_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two edges are compatible. + * \param arg Extra argument to supply to functions \p isohandler_fn, \p + * node_compat_fn and \p edge_compat_fn. + * \return Error code. + * + * Time complexity: exponential. + */ + +int igraph_subisomorphic_function_vf2(const igraph_t *graph1, + const igraph_t *graph2, + const igraph_vector_int_t *vertex_color1, + const igraph_vector_int_t *vertex_color2, + const igraph_vector_int_t *edge_color1, + const igraph_vector_int_t *edge_color2, + igraph_vector_t *map12, + igraph_vector_t *map21, + igraph_isohandler_t *isohandler_fn, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg) { + + long int no_of_nodes1 = igraph_vcount(graph1), + no_of_nodes2 = igraph_vcount(graph2); + long int no_of_edges1 = igraph_ecount(graph1), + no_of_edges2 = igraph_ecount(graph2); + igraph_vector_t mycore_1, mycore_2, *core_1 = &mycore_1, *core_2 = &mycore_2; + igraph_vector_t in_1, in_2, out_1, out_2; + long int in_1_size = 0, in_2_size = 0, out_1_size = 0, out_2_size = 0; + igraph_vector_int_t *inneis_1, *inneis_2, *outneis_1, *outneis_2; + long int matched_nodes = 0; + long int depth; + long int cand1, cand2; + long int last1, last2; + igraph_stack_t path; + igraph_lazy_adjlist_t inadj1, inadj2, outadj1, outadj2; + igraph_vector_t indeg1, indeg2, outdeg1, outdeg2; + long int vsize; + + if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) { + IGRAPH_ERROR("Cannot compare directed and undirected graphs", + IGRAPH_EINVAL); + } + + if (no_of_nodes1 < no_of_nodes2 || + no_of_edges1 < no_of_edges2) { + return 0; + } + + if ( (vertex_color1 && !vertex_color2) || (!vertex_color1 && vertex_color2) ) { + IGRAPH_WARNING("Only one graph is vertex colored, colors will be ignored"); + vertex_color1 = vertex_color2 = 0; + } + + if ( (edge_color1 && !edge_color2) || (!edge_color1 && edge_color2) ) { + IGRAPH_WARNING("Only one graph is edge colored, colors will be ignored"); + edge_color1 = edge_color2 = 0; + } + + if (vertex_color1) { + if (igraph_vector_int_size(vertex_color1) != no_of_nodes1 || + igraph_vector_int_size(vertex_color2) != no_of_nodes2) { + IGRAPH_ERROR("Invalid vertex color vector length", IGRAPH_EINVAL); + } + } + + if (edge_color1) { + if (igraph_vector_int_size(edge_color1) != no_of_edges1 || + igraph_vector_int_size(edge_color2) != no_of_edges2) { + IGRAPH_ERROR("Invalid edge color vector length", IGRAPH_EINVAL); + } + } + + /* Check color distribution */ + if (vertex_color1) { + /* TODO */ + } + + /* Check edge color distribution */ + if (edge_color1) { + /* TODO */ + } + + if (map12) { + core_1 = map12; + IGRAPH_CHECK(igraph_vector_resize(core_1, no_of_nodes1)); + } else { + IGRAPH_VECTOR_INIT_FINALLY(core_1, no_of_nodes1); + } + igraph_vector_fill(core_1, -1); + if (map21) { + core_2 = map21; + IGRAPH_CHECK(igraph_vector_resize(core_2, no_of_nodes2)); + } else { + IGRAPH_VECTOR_INIT_FINALLY(core_2, no_of_nodes2); + } + igraph_vector_fill(core_2, -1); + IGRAPH_VECTOR_INIT_FINALLY(&in_1, no_of_nodes1); + IGRAPH_VECTOR_INIT_FINALLY(&in_2, no_of_nodes2); + IGRAPH_VECTOR_INIT_FINALLY(&out_1, no_of_nodes1); + IGRAPH_VECTOR_INIT_FINALLY(&out_2, no_of_nodes2); + IGRAPH_CHECK(igraph_stack_init(&path, 0)); + IGRAPH_FINALLY(igraph_stack_destroy, &path); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &inadj1, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj1); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &outadj1, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj1); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &inadj2, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj2); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &outadj2, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj2); + IGRAPH_VECTOR_INIT_FINALLY(&indeg1, 0); + IGRAPH_VECTOR_INIT_FINALLY(&indeg2, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outdeg1, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outdeg2, 0); + + IGRAPH_CHECK(igraph_stack_reserve(&path, no_of_nodes2 * 2)); + IGRAPH_CHECK(igraph_degree(graph1, &indeg1, igraph_vss_all(), + IGRAPH_IN, IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree(graph2, &indeg2, igraph_vss_all(), + IGRAPH_IN, IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree(graph1, &outdeg1, igraph_vss_all(), + IGRAPH_OUT, IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree(graph2, &outdeg2, igraph_vss_all(), + IGRAPH_OUT, IGRAPH_LOOPS)); + + depth = 0; last1 = -1; last2 = -1; + while (depth >= 0) { + long int i; + + IGRAPH_ALLOW_INTERRUPTION(); + + cand1 = -1; cand2 = -1; + /* Search for the next pair to try */ + if ((in_1_size < in_2_size) || + (out_1_size < out_2_size)) { + /* step back, nothing to do */ + } else if (out_1_size > 0 && out_2_size > 0) { + /**************************************************************/ + /* cand2, search not always needed */ + if (last2 >= 0) { + cand2 = last2; + } else { + i = 0; + while (cand2 < 0 && i < no_of_nodes2) { + if (VECTOR(out_2)[i] > 0 && VECTOR(*core_2)[i] < 0) { + cand2 = i; + } + i++; + } + } + /* search for cand1 now, it should be bigger than last1 */ + i = last1 + 1; + while (cand1 < 0 && i < no_of_nodes1) { + if (VECTOR(out_1)[i] > 0 && VECTOR(*core_1)[i] < 0) { + cand1 = i; + } + i++; + } + } else if (in_1_size > 0 && in_2_size > 0) { + /**************************************************************/ + /* cand2, search not always needed */ + if (last2 >= 0) { + cand2 = last2; + } else { + i = 0; + while (cand2 < 0 && i < no_of_nodes2) { + if (VECTOR(in_2)[i] > 0 && VECTOR(*core_2)[i] < 0) { + cand2 = i; + } + i++; + } + } + /* search for cand1 now, should be bigger than last1 */ + i = last1 + 1; + while (cand1 < 0 && i < no_of_nodes1) { + if (VECTOR(in_1)[i] > 0 && VECTOR(*core_1)[i] < 0) { + cand1 = i; + } + i++; + } + } else { + /**************************************************************/ + /* cand2, search not always needed */ + if (last2 >= 0) { + cand2 = last2; + } else { + i = 0; + while (cand2 < 0 && i < no_of_nodes2) { + if (VECTOR(*core_2)[i] < 0) { + cand2 = i; + } + i++; + } + } + /* search for cand1, should be bigger than last1 */ + i = last1 + 1; + while (cand1 < 0 && i < no_of_nodes1) { + if (VECTOR(*core_1)[i] < 0) { + cand1 = i; + } + i++; + } + } + + /* Ok, we have cand1, cand2 as candidates. Or not? */ + if (cand1 < 0 || cand2 < 0) { + /**************************************************************/ + /* dead end, step back, if possible. Otherwise we'll terminate */ + if (depth >= 1) { + last2 = (long int) igraph_stack_pop(&path); + last1 = (long int) igraph_stack_pop(&path); + matched_nodes -= 1; + VECTOR(*core_1)[last1] = -1; + VECTOR(*core_2)[last2] = -1; + + if (VECTOR(in_1)[last1] != 0) { + in_1_size += 1; + } + if (VECTOR(out_1)[last1] != 0) { + out_1_size += 1; + } + if (VECTOR(in_2)[last2] != 0) { + in_2_size += 1; + } + if (VECTOR(out_2)[last2] != 0) { + out_2_size += 1; + } + + inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) last1); + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*inneis_1)[i]; + if (VECTOR(in_1)[node] == depth) { + VECTOR(in_1)[node] = 0; + in_1_size -= 1; + } + } + outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) last1); + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*outneis_1)[i]; + if (VECTOR(out_1)[node] == depth) { + VECTOR(out_1)[node] = 0; + out_1_size -= 1; + } + } + inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) last2); + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*inneis_2)[i]; + if (VECTOR(in_2)[node] == depth) { + VECTOR(in_2)[node] = 0; + in_2_size -= 1; + } + } + outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) last2); + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*outneis_2)[i]; + if (VECTOR(out_2)[node] == depth) { + VECTOR(out_2)[node] = 0; + out_2_size -= 1; + } + } + + } /* end of stepping back */ + + depth -= 1; + + } else { + /**************************************************************/ + /* step forward if worth, check if worth first */ + long int xin1 = 0, xin2 = 0, xout1 = 0, xout2 = 0; + igraph_bool_t end = 0; + inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1); + outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) cand1); + inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) cand2); + outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) cand2); + if (VECTOR(indeg1)[cand1] < VECTOR(indeg2)[cand2] || + VECTOR(outdeg1)[cand1] < VECTOR(outdeg2)[cand2]) { + end = 1; + } + if (vertex_color1 && VECTOR(*vertex_color1)[cand1] != VECTOR(*vertex_color2)[cand2]) { + end = 1; + } + if (node_compat_fn && !node_compat_fn(graph1, graph2, + (igraph_integer_t) cand1, + (igraph_integer_t) cand2, arg)) { + end = 1; + } + + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; !end && i < vsize; i++) { + long int node = (long int) VECTOR(*inneis_1)[i]; + if (VECTOR(*core_1)[node] < 0) { + if (VECTOR(in_1)[node] != 0) { + xin1++; + } + if (VECTOR(out_1)[node] != 0) { + xout1++; + } + } + } + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; !end && i < vsize; i++) { + long int node = (long int) VECTOR(*outneis_1)[i]; + if (VECTOR(*core_1)[node] < 0) { + if (VECTOR(in_1)[node] != 0) { + xin1++; + } + if (VECTOR(out_1)[node] != 0) { + xout1++; + } + } + } + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; !end && i < vsize; i++) { + long int node = (long int) VECTOR(*inneis_2)[i]; + if (VECTOR(*core_2)[node] >= 0) { + long int node2 = (long int) VECTOR(*core_2)[node]; + /* check if there is a node2->cand1 edge */ + if (!igraph_vector_int_binsearch2(inneis_1, node2)) { + end = 1; + } else if (edge_color1 || edge_compat_fn) { + igraph_integer_t eid1, eid2; + igraph_get_eid(graph1, &eid1, (igraph_integer_t) node2, + (igraph_integer_t) cand1, /*directed=*/ 1, + /*error=*/ 1); + igraph_get_eid(graph2, &eid2, (igraph_integer_t) node, + (igraph_integer_t) cand2, /*directed=*/ 1, + /*error=*/ 1); + if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] != + VECTOR(*edge_color2)[(long int)eid2]) { + end = 1; + } + if (edge_compat_fn && !edge_compat_fn(graph1, graph2, + eid1, eid2, arg)) { + end = 1; + } + } + } else { + if (VECTOR(in_2)[node] != 0) { + xin2++; + } + if (VECTOR(out_2)[node] != 0) { + xout2++; + } + } + } + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; !end && i < vsize; i++) { + long int node = (long int) VECTOR(*outneis_2)[i]; + if (VECTOR(*core_2)[node] >= 0) { + long int node2 = (long int) VECTOR(*core_2)[node]; + /* check if there is a cand1->node2 edge */ + if (!igraph_vector_int_binsearch2(outneis_1, node2)) { + end = 1; + } else if (edge_color1 || edge_compat_fn) { + igraph_integer_t eid1, eid2; + igraph_get_eid(graph1, &eid1, (igraph_integer_t) cand1, + (igraph_integer_t) node2, /*directed=*/ 1, + /*error=*/ 1); + igraph_get_eid(graph2, &eid2, (igraph_integer_t) cand2, + (igraph_integer_t) node, /*directed=*/ 1, + /*error=*/ 1); + if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] != + VECTOR(*edge_color2)[(long int)eid2]) { + end = 1; + } + if (edge_compat_fn && !edge_compat_fn(graph1, graph2, + eid1, eid2, arg)) { + end = 1; + } + } + } else { + if (VECTOR(in_2)[node] != 0) { + xin2++; + } + if (VECTOR(out_2)[node] != 0) { + xout2++; + } + } + } + + if (!end && (xin1 >= xin2 && xout1 >= xout2)) { + /* Ok, we add the (cand1, cand2) pair to the mapping */ + depth += 1; + IGRAPH_CHECK(igraph_stack_push(&path, cand1)); + IGRAPH_CHECK(igraph_stack_push(&path, cand2)); + matched_nodes += 1; + VECTOR(*core_1)[cand1] = cand2; + VECTOR(*core_2)[cand2] = cand1; + + /* update in_*, out_* */ + if (VECTOR(in_1)[cand1] != 0) { + in_1_size -= 1; + } + if (VECTOR(out_1)[cand1] != 0) { + out_1_size -= 1; + } + if (VECTOR(in_2)[cand2] != 0) { + in_2_size -= 1; + } + if (VECTOR(out_2)[cand2] != 0) { + out_2_size -= 1; + } + + inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1); + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*inneis_1)[i]; + if (VECTOR(in_1)[node] == 0 && VECTOR(*core_1)[node] < 0) { + VECTOR(in_1)[node] = depth; + in_1_size += 1; + } + } + outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) cand1); + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*outneis_1)[i]; + if (VECTOR(out_1)[node] == 0 && VECTOR(*core_1)[node] < 0) { + VECTOR(out_1)[node] = depth; + out_1_size += 1; + } + } + inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) cand2); + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*inneis_2)[i]; + if (VECTOR(in_2)[node] == 0 && VECTOR(*core_2)[node] < 0) { + VECTOR(in_2)[node] = depth; + in_2_size += 1; + } + } + outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) cand2); + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; i < vsize; i++) { + long int node = (long int) VECTOR(*outneis_2)[i]; + if (VECTOR(out_2)[node] == 0 && VECTOR(*core_2)[node] < 0) { + VECTOR(out_2)[node] = depth; + out_2_size += 1; + } + } + last1 = -1; last2 = -1; /* this the first time here */ + } else { + last1 = cand1; + last2 = cand2; + } + + } + + if (matched_nodes == no_of_nodes2 && isohandler_fn) { + if (!isohandler_fn(core_1, core_2, arg)) { + break; + } + } + } + + igraph_vector_destroy(&outdeg2); + igraph_vector_destroy(&outdeg1); + igraph_vector_destroy(&indeg2); + igraph_vector_destroy(&indeg1); + igraph_lazy_adjlist_destroy(&outadj2); + igraph_lazy_adjlist_destroy(&inadj2); + igraph_lazy_adjlist_destroy(&outadj1); + igraph_lazy_adjlist_destroy(&inadj1); + igraph_stack_destroy(&path); + igraph_vector_destroy(&out_2); + igraph_vector_destroy(&out_1); + igraph_vector_destroy(&in_2); + igraph_vector_destroy(&in_1); + IGRAPH_FINALLY_CLEAN(13); + if (!map21) { + igraph_vector_destroy(core_2); + IGRAPH_FINALLY_CLEAN(1); + } + if (!map12) { + igraph_vector_destroy(core_1); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +static igraph_bool_t igraph_i_subisomorphic_vf2( + const igraph_vector_t *map12, + const igraph_vector_t *map21, + void *arg) { + igraph_i_iso_cb_data_t *data = arg; + igraph_bool_t *iso = data->arg; + IGRAPH_UNUSED(map12); IGRAPH_UNUSED(map21); + *iso = 1; + return 0; /* stop */ +} + +/** + * \function igraph_subisomorphic_vf2 + * Decide subgraph isomorphism using VF2 + * + * Decides whether a subgraph of \p graph1 is isomorphic to \p + * graph2. It uses \ref igraph_subisomorphic_function_vf2(). + * \param graph1 The first input graph, may be directed or + * undirected. This is supposed to be the larger graph. + * \param graph2 The second input graph, it must have the same + * directedness as \p graph1. This is supposed to be the smaller + * graph. + * \param vertex_color1 An optional color vector for the first graph. If + * color vectors are given for both graphs, then the subgraph isomorphism is + * calculated on the colored graphs; i.e. two vertices can match + * only if their color also matches. Supply a null pointer here if + * your graphs are not colored. + * \param vertex_color2 An optional color vector for the second graph. See + * the previous argument for explanation. + * \param edge_color1 An optional edge color vector for the first + * graph. The matching edges in the two graphs must have matching + * colors as well. Supply a null pointer here if your graphs are not + * edge-colored. + * \param edge_color2 The edge color vector for the second graph. + * \param iso Pointer to a boolean. The result of the decision problem + * is stored here. + * \param map12 Pointer to a vector or \c NULL. If not \c NULL, then an + * isomorphic mapping from \p graph1 to \p graph2 is stored here. + * \param map21 Pointer to a vector ot \c NULL. If not \c NULL, then + * an isomorphic mapping from \p graph2 to \p graph1 is stored + * here. + * \param node_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two nodes are compatible. + * \param edge_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two edges are compatible. + * \param arg Extra argument to supply to functions \p node_compat_fn + * and \p edge_compat_fn. + * \return Error code. + * + * Time complexity: exponential. + */ + +int igraph_subisomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, + const igraph_vector_int_t *vertex_color1, + const igraph_vector_int_t *vertex_color2, + const igraph_vector_int_t *edge_color1, + const igraph_vector_int_t *edge_color2, + igraph_bool_t *iso, igraph_vector_t *map12, + igraph_vector_t *map21, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg) { + + igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, iso, arg }; + igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0; + igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0; + + *iso = 0; + IGRAPH_CHECK(igraph_subisomorphic_function_vf2(graph1, graph2, + vertex_color1, vertex_color2, + edge_color1, edge_color2, + map12, map21, + (igraph_isohandler_t *) + igraph_i_subisomorphic_vf2, + ncb, ecb, &data)); + if (! *iso) { + if (map12) { + igraph_vector_clear(map12); + } + if (map21) { + igraph_vector_clear(map21); + } + } + return 0; +} + +static igraph_bool_t igraph_i_count_subisomorphisms_vf2( + const igraph_vector_t *map12, + const igraph_vector_t *map21, + void *arg) { + igraph_i_iso_cb_data_t *data = arg; + igraph_integer_t *count = data->arg; + IGRAPH_UNUSED(map12); IGRAPH_UNUSED(map21); + *count += 1; + return 1; /* always continue */ +} + +/** + * \function igraph_count_subisomorphisms_vf2 + * Number of subgraph isomorphisms using VF2 + * + * Count the number of isomorphisms between subgraphs of \p graph1 and + * \p graph2. This function uses \ref + * igraph_subisomorphic_function_vf2(). + * \param graph1 The first input graph, may be directed or + * undirected. This is supposed to be the larger graph. + * \param graph2 The second input graph, it must have the same + * directedness as \p graph1. This is supposed to be the smaller + * graph. + * \param vertex_color1 An optional color vector for the first graph. If + * color vectors are given for both graphs, then the subgraph isomorphism is + * calculated on the colored graphs; i.e. two vertices can match + * only if their color also matches. Supply a null pointer here if + * your graphs are not colored. + * \param vertex_color2 An optional color vector for the second graph. See + * the previous argument for explanation. + * \param edge_color1 An optional edge color vector for the first + * graph. The matching edges in the two graphs must have matching + * colors as well. Supply a null pointer here if your graphs are not + * edge-colored. + * \param edge_color2 The edge color vector for the second graph. + * \param count Pointer to an integer. The number of subgraph + * isomorphisms is stored here. + * \param node_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two nodes are compatible. + * \param edge_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two edges are compatible. + * \param arg Extra argument to supply to functions \p node_compat_fn and + * \p edge_compat_fn. + * \return Error code. + * + * Time complexity: exponential. + */ + +int igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, + const igraph_vector_int_t *vertex_color1, + const igraph_vector_int_t *vertex_color2, + const igraph_vector_int_t *edge_color1, + const igraph_vector_int_t *edge_color2, + igraph_integer_t *count, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg) { + + igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, + count, arg + }; + igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0; + igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0; + *count = 0; + IGRAPH_CHECK(igraph_subisomorphic_function_vf2(graph1, graph2, + vertex_color1, vertex_color2, + edge_color1, edge_color2, + 0, 0, + (igraph_isohandler_t*) + igraph_i_count_subisomorphisms_vf2, + ncb, ecb, &data)); + return 0; +} + +static void igraph_i_get_subisomorphisms_free(igraph_vector_ptr_t *data) { + long int i, n = igraph_vector_ptr_size(data); + for (i = 0; i < n; i++) { + igraph_vector_t *vec = VECTOR(*data)[i]; + igraph_vector_destroy(vec); + igraph_free(vec); + } +} + +static int igraph_i_get_subisomorphisms_vf2_inner( + const igraph_vector_t *map12, + const igraph_vector_t *map21, + void *arg) { + igraph_i_iso_cb_data_t *data = arg; + igraph_vector_ptr_t *vector = data->arg; + igraph_vector_t *newvector = IGRAPH_CALLOC(1, igraph_vector_t); + IGRAPH_UNUSED(map12); + if (!newvector) { + IGRAPH_ERROR("", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newvector); + IGRAPH_CHECK(igraph_vector_copy(newvector, map21)); + IGRAPH_FINALLY(igraph_vector_destroy, newvector); + IGRAPH_CHECK(igraph_vector_ptr_push_back(vector, newvector)); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_bool_t igraph_i_get_subisomorphisms_vf2( + const igraph_vector_t *map12, + const igraph_vector_t *map21, + void *arg) { + return igraph_i_get_subisomorphisms_vf2_inner(map12, map21, arg) == IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_subisomorphisms_vf2 + * \brief Return all subgraph isomorphic mappings. + * + * This function collects all isomorphic mappings of \p graph2 to a + * subgraph of \p graph1. It uses the \ref + * igraph_subisomorphic_function_vf2() function. The graphs should be simple. + * \param graph1 The first input graph, may be directed or + * undirected. This is supposed to be the larger graph. + * \param graph2 The second input graph, it must have the same + * directedness as \p graph1. This is supposed to be the smaller + * graph. + * \param vertex_color1 An optional color vector for the first graph. If + * color vectors are given for both graphs, then the subgraph isomorphism is + * calculated on the colored graphs; i.e. two vertices can match + * only if their color also matches. Supply a null pointer here if + * your graphs are not colored. + * \param vertex_color2 An optional color vector for the second graph. See + * the previous argument for explanation. + * \param edge_color1 An optional edge color vector for the first + * graph. The matching edges in the two graphs must have matching + * colors as well. Supply a null pointer here if your graphs are not + * edge-colored. + * \param edge_color2 The edge color vector for the second graph. + * \param maps Pointer vector. On return it contains pointers to + * \ref igraph_vector_t objects, each vector is an + * isomorphic mapping of \p graph2 to a subgraph of \p graph1. Please note that + * you need to 1) Destroy the vectors via \ref + * igraph_vector_destroy(), 2) free them via + * \ref igraph_free() and then 3) call \ref + * igraph_vector_ptr_destroy() on the pointer vector to deallocate all + * memory when \p maps is no longer needed. + * \param node_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two nodes are compatible. + * \param edge_compat_fn A pointer to a function of type \ref + * igraph_isocompat_t. This function will be called by the algorithm to + * determine whether two edges are compatible. + * \param arg Extra argument to supply to functions \p node_compat_fn + * and \p edge_compat_fn. + * \return Error code. + * + * Time complexity: exponential. + */ + +int igraph_get_subisomorphisms_vf2(const igraph_t *graph1, + const igraph_t *graph2, + const igraph_vector_int_t *vertex_color1, + const igraph_vector_int_t *vertex_color2, + const igraph_vector_int_t *edge_color1, + const igraph_vector_int_t *edge_color2, + igraph_vector_ptr_t *maps, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg) { + + igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, maps, arg }; + igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : NULL; + igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : NULL; + + igraph_vector_ptr_clear(maps); + IGRAPH_FINALLY(igraph_i_get_subisomorphisms_free, maps); + IGRAPH_CHECK(igraph_subisomorphic_function_vf2(graph1, graph2, + vertex_color1, vertex_color2, + edge_color1, edge_color2, + NULL, NULL, + (igraph_isohandler_t*) + igraph_i_get_subisomorphisms_vf2, + ncb, ecb, &data)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/layout/circular.c b/src/rigraph/core/layout/circular.c new file mode 100644 index 0000000..1a5eab0 --- /dev/null +++ b/src/rigraph/core/layout/circular.c @@ -0,0 +1,185 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" + +#include "../core/interruption.h" +#include "../core/math.h" + +/** + * \ingroup layout + * \function igraph_layout_circle + * \brief Places the vertices uniformly on a circle, in the order of vertex ids. + * + * \param graph Pointer to an initialized graph object. + * \param res Pointer to an initialized matrix object. This will + * contain the result and will be resized as needed. + * \param order The order of the vertices on the circle. The vertices + * not included here, will be placed at (0,0). Supply + * \ref igraph_vss_all() here for all vertices, in the order of + * their vertex ids. + * \return Error code. + * + * Time complexity: O(|V|), the + * number of vertices. + */ +int igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, + igraph_vs_t order) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_integer_t vs_size; + long int i; + igraph_vit_t vit; + + IGRAPH_CHECK(igraph_vs_size(graph, &order, &vs_size)); + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); + igraph_matrix_null(res); + + igraph_vit_create(graph, order, &vit); + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_real_t phi = 2 * M_PI / vs_size * i; + int idx = IGRAPH_VIT_GET(vit); + MATRIX(*res, idx, 0) = cos(phi); + MATRIX(*res, idx, 1) = sin(phi); + } + igraph_vit_destroy(&vit); + + return 0; +} + +/** + * \function igraph_layout_star + * \brief Generates a star-like layout. + * + * \param graph The input graph. Its edges are ignored by this function. + * \param res Pointer to an initialized matrix object. This will + * contain the result and will be resized as needed. + * \param center The id of the vertex to put in the center. + * \param order A numeric vector giving the order of the vertices + * (including the center vertex!). If a null pointer, then the + * vertices are placed in increasing vertex id order. + * \return Error code. + * + * Time complexity: O(|V|), linear in the number of vertices. + * + * \sa \ref igraph_layout_circle() and other layout generators. + */ +int igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t center, const igraph_vector_t *order) { + + long int no_of_nodes = igraph_vcount(graph); + long int c = center; + long int i; + igraph_real_t step; + igraph_real_t phi; + + if (center < 0 || center >= no_of_nodes) { + IGRAPH_ERROR("The given center is not a vertex of the graph.", IGRAPH_EINVAL); + } + if (order && igraph_vector_size(order) != no_of_nodes) { + IGRAPH_ERROR("Invalid order vector length.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); + + if (no_of_nodes == 1) { + MATRIX(*res, 0, 0) = MATRIX(*res, 0, 1) = 0.0; + } else { + for (i = 0, step = 2 * M_PI / (no_of_nodes - 1), phi = 0; + i < no_of_nodes; i++) { + long int node = order ? (long int) VECTOR(*order)[i] : i; + if (order && (node < 0 || node >= no_of_nodes)) { + IGRAPH_ERROR("Elements in the order vector are not all vertices of the graph.", IGRAPH_EINVAL); + } + if (node != c) { + MATRIX(*res, node, 0) = cos(phi); + MATRIX(*res, node, 1) = sin(phi); + phi += step; + } else { + MATRIX(*res, node, 0) = MATRIX(*res, node, 1) = 0.0; + } + } + } + + return 0; +} + +/** + * \function igraph_layout_sphere + * \brief Places vertices (more or less) uniformly on a sphere. + * + * + * The algorithm was described in the following paper: + * Distributing many points on a sphere by E.B. Saff and + * A.B.J. Kuijlaars, \emb Mathematical Intelligencer \eme 19.1 (1997) + * 5--11. + * + * \param graph Pointer to an initialized graph object. + * \param res Pointer to an initialized matrix object. This will + * contain the result and will be resized as needed. + * \return Error code. The current implementation always returns with + * success. + * + * Added in version 0.2. + * + * Time complexity: O(|V|), the number of vertices in the graph. + */ +int igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res) { + + long int no_of_nodes = igraph_vcount(graph); + long int i; + igraph_real_t h; + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); + + if (no_of_nodes != 0) { + MATRIX(*res, 0, 0) = M_PI; + MATRIX(*res, 0, 1) = 0; + } + for (i = 1; i < no_of_nodes - 1; i++) { + h = -1 + 2 * i / (double)(no_of_nodes - 1); + MATRIX(*res, i, 0) = acos(h); + MATRIX(*res, i, 1) = fmod((MATRIX(*res, i - 1, 1) + + 3.6 / sqrt(no_of_nodes * (1 - h * h))), 2 * M_PI); + IGRAPH_ALLOW_INTERRUPTION(); + } + if (no_of_nodes >= 2) { + MATRIX(*res, no_of_nodes - 1, 0) = 0; + MATRIX(*res, no_of_nodes - 1, 1) = 0; + } + + for (i = 0; i < no_of_nodes; i++) { + igraph_real_t x = cos(MATRIX(*res, i, 1)) * sin(MATRIX(*res, i, 0)); + igraph_real_t y = sin(MATRIX(*res, i, 1)) * sin(MATRIX(*res, i, 0)); + igraph_real_t z = cos(MATRIX(*res, i, 0)); + MATRIX(*res, i, 0) = x; + MATRIX(*res, i, 1) = y; + MATRIX(*res, i, 2) = z; + IGRAPH_ALLOW_INTERRUPTION(); + } + + return 0; +} diff --git a/src/rigraph/core/layout/davidson_harel.c b/src/rigraph/core/layout/davidson_harel.c new file mode 100644 index 0000000..78ae484 --- /dev/null +++ b/src/rigraph/core/layout/davidson_harel.c @@ -0,0 +1,464 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" +#include "igraph_random.h" + +#include "../core/math.h" +#include "../core/interruption.h" +#include "layout_internal.h" + +#include + +/* not 'static', used in tests */ +igraph_bool_t igraph_i_layout_segments_intersect(float p0_x, float p0_y, + float p1_x, float p1_y, + float p2_x, float p2_y, + float p3_x, float p3_y) { + float s1_x = p1_x - p0_x; + float s1_y = p1_y - p0_y; + float s2_x = p3_x - p2_x; + float s2_y = p3_y - p2_y; + + float s1, s2, t1, t2, s, t; + s1 = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)); + s2 = (-s2_x * s1_y + s1_x * s2_y); + if (s2 == 0) { + return 0; + } + t1 = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)); + t2 = (-s2_x * s1_y + s1_x * s2_y); + s = s1 / s2; + t = t1 / t2; + + return s >= 0 && s <= 1 && t >= 0 && t <= 1 ? 1 : 0; +} + +/* not 'static', used in tests */ +float igraph_i_layout_point_segment_dist2(float v_x, float v_y, + float u1_x, float u1_y, + float u2_x, float u2_y) { + + float dx = u2_x - u1_x; + float dy = u2_y - u1_y; + float l2 = dx * dx + dy * dy; + float t, p_x, p_y; + if (l2 == 0) { + return (v_x - u1_x) * (v_x - u1_x) + (v_y - u1_y) * (v_y - u1_y); + } + t = ((v_x - u1_x) * dx + (v_y - u1_y) * dy) / l2; + if (t < 0.0) { + return (v_x - u1_x) * (v_x - u1_x) + (v_y - u1_y) * (v_y - u1_y); + } else if (t > 1.0) { + return (v_x - u2_x) * (v_x - u2_x) + (v_y - u2_y) * (v_y - u2_y); + } + p_x = u1_x + t * dx; + p_y = u1_y + t * dy; + return (v_x - p_x) * (v_x - p_x) + (v_y - p_y) * (v_y - p_y); +} + +/** + * \function igraph_layout_davidson_harel + * Davidson-Harel layout algorithm + * + * This function implements the algorithm by Davidson and Harel, + * see Ron Davidson, David Harel: Drawing Graphs Nicely Using + * Simulated Annealing. ACM Transactions on Graphics 15(4), + * pp. 301-331, 1996. + * + * + * The algorithm uses simulated annealing and a sophisticated + * energy function, which is unfortunately hard to parameterize + * for different graphs. The original publication did not disclose any + * parameter values, and the ones below were determined by + * experimentation. + * + * + * The algorithm consists of two phases, an annealing phase, and a + * fine-tuning phase. There is no simulated annealing in the second + * phase. + * + * + * Our implementation tries to follow the original publication, as + * much as possible. The only major difference is that coordinates are + * explicitly kept within the bounds of the rectangle of the layout. + * + * \param graph The input graph, edge directions are ignored. + * \param res A matrix, the result is stored here. It can be used to + * supply start coordinates, see \p use_seed. + * \param use_seed Boolean, whether to use the supplied \p res as + * start coordinates. + * \param maxiter The maximum number of annealing iterations. A + * reasonable value for smaller graphs is 10. + * \param fineiter The number of fine tuning iterations. A reasonable + * value is max(10, log2(n)) where n is the number of vertices. + * \param cool_fact Cooling factor. A reasonable value is 0.75. + * \param weight_node_dist Weight for the node-node distances + * component of the energy function. Reasonable value: 1.0. + * \param weight_border Weight for the distance from the border + * component of the energy function. It can be set to zero, if + * vertices are allowed to sit on the border. + * \param weight_edge_lengths Weight for the edge length component + * of the energy function, a reasonable value is the density of + * the graph divided by 10. + * \param weight_edge_crossings Weight for the edge crossing component + * of the energy function, a reasonable default is 1 minus the + * square root of the density of the graph. + * \param weight_node_edge_dist Weight for the node-edge distance + * component of the energy function. A reasonable value is + * 1 minus the density, divided by 5. + * \return Error code. + * + * Time complexity: one first phase iteration has time complexity + * O(n^2+m^2), one fine tuning iteration has time complexity O(mn). + * Time complexity might be smaller if some of the weights of the + * components of the energy function are set to zero. + * + */ + +int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, + igraph_bool_t use_seed, igraph_integer_t maxiter, + igraph_integer_t fineiter, igraph_real_t cool_fact, + igraph_real_t weight_node_dist, igraph_real_t weight_border, + igraph_real_t weight_edge_lengths, + igraph_real_t weight_edge_crossings, + igraph_real_t weight_node_edge_dist) { + + igraph_integer_t no_nodes = igraph_vcount(graph); + igraph_integer_t no_edges = igraph_ecount(graph); + float width = sqrt(no_nodes) * 10, height = width; + igraph_vector_int_t perm; + igraph_bool_t fine_tuning = 0; + igraph_integer_t round, i; + igraph_vector_float_t try_x, try_y; + igraph_vector_int_t try_idx; + float move_radius = width / 2; + float fine_tuning_factor = 0.01f; + igraph_vector_t neis; + float min_x = width / 2, max_x = -width / 2, min_y = height / 2, max_y = -height / 2; + + igraph_integer_t no_tries = 30; + float w_node_dist = weight_node_dist ; /* 1.0 */ + float w_borderlines = weight_border; /* 0.0 */ + float w_edge_lengths = weight_edge_lengths; /* 0.0001; */ + float w_edge_crossings = weight_edge_crossings; /* 1.0 */ + float w_node_edge_dist = weight_node_edge_dist; /* 0.2 */ + + if (use_seed && (igraph_matrix_nrow(res) != no_nodes || + igraph_matrix_ncol(res) != 2)) { + IGRAPH_ERROR("Invalid start position matrix size in " + "Davidson-Harel layout", IGRAPH_EINVAL); + } + if (maxiter < 0) { + IGRAPH_ERROR("Number of iterations must be non-negative in " + "Davidson-Harel layout", IGRAPH_EINVAL); + } + if (fineiter < 0) { + IGRAPH_ERROR("Number of fine tuning iterations must be non-negative in " + "Davidson-Harel layout", IGRAPH_EINVAL); + } + if (cool_fact <= 0 || cool_fact >= 1) { + IGRAPH_ERROR("Cooling factor must be in (0,1) in " + "Davidson-Harel layout", IGRAPH_EINVAL); + } + + if (no_nodes == 0) { + return 0; + } + + IGRAPH_CHECK(igraph_vector_int_init_seq(&perm, 0, no_nodes - 1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &perm); + IGRAPH_CHECK(igraph_vector_float_init(&try_x, no_tries)); + IGRAPH_FINALLY(igraph_vector_float_destroy, &try_x); + IGRAPH_CHECK(igraph_vector_float_init(&try_y, no_tries)); + IGRAPH_FINALLY(igraph_vector_float_destroy, &try_y); + IGRAPH_CHECK(igraph_vector_int_init_seq(&try_idx, 0, no_tries - 1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &try_idx); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 100); + + RNG_BEGIN(); + + if (!use_seed) { + IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2)); + for (i = 0; i < no_nodes; i++) { + float x, y; + x = MATRIX(*res, i, 0) = RNG_UNIF(-width / 2, width / 2); + y = MATRIX(*res, i, 1) = RNG_UNIF(-height / 2, height / 2); + if (x < min_x) { + min_x = x; + } else if (x > max_x) { + max_x = x; + } + if (y < min_y) { + min_y = y; + } else if (y > max_y) { + max_y = y; + } + } + } else { + min_x = IGRAPH_INFINITY; max_x = IGRAPH_NEGINFINITY; + min_y = IGRAPH_INFINITY; max_y = IGRAPH_NEGINFINITY; + for (i = 0; i < no_nodes; i++) { + float x = MATRIX(*res, i, 0); + float y = MATRIX(*res, i, 1); + if (x < min_x) { + min_x = x; + } else if (x > max_x) { + max_x = x; + } + if (y < min_y) { + min_y = y; + } else if (y > max_y) { + max_y = y; + } + } + } + + for (i = 0; i < no_tries; i++) { + float phi = 2 * M_PI / no_tries * i; + VECTOR(try_x)[i] = cos(phi); + VECTOR(try_y)[i] = sin(phi); + } + + for (round = 0; round < maxiter + fineiter; round++) { + igraph_integer_t p; + igraph_vector_int_shuffle(&perm); + + IGRAPH_ALLOW_INTERRUPTION(); + + fine_tuning = round >= maxiter; + if (fine_tuning) { + float fx = fine_tuning_factor * (max_x - min_x); + float fy = fine_tuning_factor * (max_y - min_y); + move_radius = fx < fy ? fx : fy; + } + + for (p = 0; p < no_nodes; p++) { + igraph_integer_t t; + igraph_integer_t v = VECTOR(perm)[p]; + igraph_vector_int_shuffle(&try_idx); + + for (t = 0; t < no_tries; t++) { + float diff_energy = 0.0; + int ti = VECTOR(try_idx)[t]; + + /* Try moving it */ + float old_x = MATRIX(*res, v, 0); + float old_y = MATRIX(*res, v, 1); + float new_x = old_x + move_radius * VECTOR(try_x)[ti]; + float new_y = old_y + move_radius * VECTOR(try_y)[ti]; + + if (new_x < -width / 2) { + new_x = -width / 2 - 1e-6; + } + if (new_x > width / 2) { + new_x = width / 2 - 1e-6; + } + if (new_y < -height / 2) { + new_y = -height / 2 - 1e-6; + } + if (new_y > height / 2) { + new_y = height / 2 - 1e-6; + } + + if (w_node_dist != 0) { + igraph_integer_t u; + for (u = 0; u < no_nodes; u++) { + float odx, ody, odist2, dx, dy, dist2; + if (u == v) { + continue; + } + odx = old_x - MATRIX(*res, u, 0); + ody = old_y - MATRIX(*res, u, 1); + dx = new_x - MATRIX(*res, u, 0); + dy = new_y - MATRIX(*res, u, 1); + odist2 = odx * odx + ody * ody; + dist2 = dx * dx + dy * dy; + diff_energy += w_node_dist / dist2 - w_node_dist / odist2; + } + } + + if (w_borderlines != 0) { + float odx1 = width / 2 - old_x, odx2 = old_x + width / 2; + float ody1 = height / 2 - old_y, ody2 = old_y + height / 2; + float dx1 = width / 2 - new_x, dx2 = new_x + width / 2; + float dy1 = height / 2 - new_y, dy2 = new_y + height / 2; + if (odx1 < 0) { + odx1 = 2; + } if (odx2 < 0) { + odx2 = 2; + } + if (ody1 < 0) { + ody1 = 2; + } if (ody2 < 0) { + ody2 = 2; + } + if (dx1 < 0) { + dx1 = 2; + } if (dx2 < 0) { + dx2 = 2; + } + if (dy1 < 0) { + dy1 = 2; + } if (dy2 < 0) { + dy2 = 2; + } + diff_energy -= w_borderlines * + (1.0 / (odx1 * odx1) + 1.0 / (odx2 * odx2) + + 1.0 / (ody1 * ody1) + 1.0 / (ody2 * ody2)); + diff_energy += w_borderlines * + (1.0 / (dx1 * dx1) + 1.0 / (dx2 * dx2) + + 1.0 / (dy1 * dy1) + 1.0 / (dy2 * dy2)); + } + + if (w_edge_lengths != 0) { + igraph_integer_t len, j; + igraph_neighbors(graph, &neis, v, IGRAPH_ALL); + len = igraph_vector_size(&neis); + for (j = 0; j < len; j++) { + igraph_integer_t u = VECTOR(neis)[j]; + float odx = old_x - MATRIX(*res, u, 0); + float ody = old_y - MATRIX(*res, u, 1); + float odist2 = odx * odx + ody * ody; + float dx = new_x - MATRIX(*res, u, 0); + float dy = new_y - MATRIX(*res, u, 1); + float dist2 = dx * dx + dy * dy; + diff_energy += w_edge_lengths * (dist2 - odist2); + } + } + + if (w_edge_crossings != 0) { + igraph_integer_t len, j, no = 0; + igraph_neighbors(graph, &neis, v, IGRAPH_ALL); + len = igraph_vector_size(&neis); + for (j = 0; j < len; j++) { + igraph_integer_t u = VECTOR(neis)[j]; + float u_x = MATRIX(*res, u, 0); + float u_y = MATRIX(*res, u, 1); + igraph_integer_t e; + for (e = 0; e < no_edges; e++) { + igraph_integer_t u1 = IGRAPH_FROM(graph, e); + igraph_integer_t u2 = IGRAPH_TO(graph, e); + float u1_x, u1_y, u2_x, u2_y; + if (u1 == v || u2 == v || u1 == u || u2 == u) { + continue; + } + u1_x = MATRIX(*res, u1, 0); + u1_y = MATRIX(*res, u1, 1); + u2_x = MATRIX(*res, u2, 0); + u2_y = MATRIX(*res, u2, 1); + no -= igraph_i_layout_segments_intersect(old_x, old_y, u_x, u_y, + u1_x, u1_y, u2_x, u2_y); + no += igraph_i_layout_segments_intersect(new_x, new_y, u_x, u_y, + u1_x, u1_y, u2_x, u2_y); + } + } + diff_energy += w_edge_crossings * no; + } + + if (w_node_edge_dist != 0 && fine_tuning) { + igraph_integer_t e, no; + + /* All non-incident edges from the moved 'v' */ + for (e = 0; e < no_edges; e++) { + igraph_integer_t u1 = IGRAPH_FROM(graph, e); + igraph_integer_t u2 = IGRAPH_TO(graph, e); + float u1_x, u1_y, u2_x, u2_y, d_ev; + if (u1 == v || u2 == v) { + continue; + } + u1_x = MATRIX(*res, u1, 0); + u1_y = MATRIX(*res, u1, 1); + u2_x = MATRIX(*res, u2, 0); + u2_y = MATRIX(*res, u2, 1); + d_ev = igraph_i_layout_point_segment_dist2(old_x, old_y, u1_x, u1_y, + u2_x, u2_y); + diff_energy -= w_node_edge_dist / d_ev; + d_ev = igraph_i_layout_point_segment_dist2(new_x, new_y, u1_x, u1_y, + u2_x, u2_y); + diff_energy += w_node_edge_dist / d_ev; + } + + /* All other nodes from all of v's incident edges */ + igraph_incident(graph, &neis, v, IGRAPH_ALL); + no = igraph_vector_size(&neis); + for (e = 0; e < no; e++) { + igraph_integer_t mye = VECTOR(neis)[e]; + igraph_integer_t u = IGRAPH_OTHER(graph, mye, v); + float u_x = MATRIX(*res, u, 0); + float u_y = MATRIX(*res, u, 1); + igraph_integer_t w; + for (w = 0; w < no_nodes; w++) { + float w_x, w_y, d_ev; + if (w == v || w == u) { + continue; + } + w_x = MATRIX(*res, w, 0); + w_y = MATRIX(*res, w, 1); + d_ev = igraph_i_layout_point_segment_dist2(w_x, w_y, old_x, + old_y, u_x, u_y); + diff_energy -= w_node_edge_dist / d_ev; + d_ev = igraph_i_layout_point_segment_dist2(w_x, w_y, new_x, new_y, + u_x, u_y); + diff_energy += w_node_edge_dist / d_ev; + } + } + } /* w_node_edge_dist != 0 && fine_tuning */ + + if (diff_energy < 0 || + (!fine_tuning && RNG_UNIF01() < exp(-diff_energy / move_radius))) { + MATRIX(*res, v, 0) = new_x; + MATRIX(*res, v, 1) = new_y; + if (new_x < min_x) { + min_x = new_x; + } else if (new_x > max_x) { + max_x = new_x; + } + if (new_y < min_y) { + min_y = new_y; + } else if (new_y > max_y) { + max_y = new_y; + } + } + + } /* t < no_tries */ + + } /* p < no_nodes */ + + move_radius *= cool_fact; + + } /* round < maxiter */ + + RNG_END(); + + igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&try_idx); + igraph_vector_float_destroy(&try_x); + igraph_vector_float_destroy(&try_y); + igraph_vector_int_destroy(&perm); + IGRAPH_FINALLY_CLEAN(5); + + return 0; +} diff --git a/src/rigraph/core/layout/drl/DensityGrid.cpp b/src/rigraph/core/layout/drl/DensityGrid.cpp new file mode 100644 index 0000000..1678db3 --- /dev/null +++ b/src/rigraph/core/layout/drl/DensityGrid.cpp @@ -0,0 +1,281 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// This file contains the member definitions of the DensityGrid.h class +// This code is modified from the original code by B.N. Wylie + +#include "drl_Node.h" +#include "DensityGrid.h" +#include "igraph_error.h" + +#include +#include + +using namespace std; + +#define GET_BIN(y, x) (Bins[y*GRID_SIZE+x]) + +namespace drl { + +//******************************************************* +// Density Grid Destructor -- deallocates memory used +// for Density matrix, fall_off matrix, and node deque. + +DensityGrid::~DensityGrid () { + delete[] Density; + delete[] fall_off; + delete[] Bins; +} + +/********************************************* +* Function: Density_Grid::Reset * +* Description: Reset the density grid * +*********************************************/ +// changed from reset to init since we will only +// call this once in the parallel version of layout + +void DensityGrid::Init() { + + try { + Density = new float[GRID_SIZE][GRID_SIZE]; + fall_off = new float[RADIUS * 2 + 1][RADIUS * 2 + 1]; + Bins = new deque[GRID_SIZE * GRID_SIZE]; + } catch (bad_alloc&) { + // cout << "Error: Out of memory! Program stopped." << endl; +#ifdef MUSE_MPI + MPI_Abort ( MPI_COMM_WORLD, 1 ); +#else + igraph_error("DrL is out of memory", IGRAPH_FILE_BASENAME, __LINE__, + IGRAPH_ENOMEM); + return; +#endif + } + + // Clear Grid + int i; + for (i = 0; i < GRID_SIZE; i++) + for (int j = 0; j < GRID_SIZE; j++) { + Density[i][j] = 0; + GET_BIN(i, j).erase(GET_BIN(i, j).begin(), GET_BIN(i, j).end()); + } + + // Compute fall off + for (i = -RADIUS; i <= RADIUS; i++) + for (int j = -RADIUS; j <= RADIUS; j++) { + fall_off[i + RADIUS][j + RADIUS] = (float)((RADIUS - fabs((float)i)) / RADIUS) * + (float)((RADIUS - fabs((float)j)) / RADIUS); + } + +} + +/*************************************************** + * Function: DensityGrid::GetDensity * + * Description: Get_Density from density grid * + **************************************************/ +float DensityGrid::GetDensity(float Nx, float Ny, bool fineDensity) { + deque::iterator BI; + int x_grid, y_grid; + float x_dist, y_dist, distance, density = 0; + int boundary = 10; // boundary around plane + + + /* Where to look */ + x_grid = (int)((Nx + HALF_VIEW + .5) * VIEW_TO_GRID); + y_grid = (int)((Ny + HALF_VIEW + .5) * VIEW_TO_GRID); + + // Check for edges of density grid (10000 is arbitrary high density) + if (x_grid > GRID_SIZE - boundary || x_grid < boundary) { + return 10000; + } + if (y_grid > GRID_SIZE - boundary || y_grid < boundary) { + return 10000; + } + + // Fine density? + if (fineDensity) { + + // Go through nearest bins + for (int i = y_grid - 1; i <= y_grid + 1; i++) + for (int j = x_grid - 1; j <= x_grid + 1; j++) { + + // Look through bin and add fine repulsions + for (BI = GET_BIN(i, j).begin(); BI != GET_BIN(i, j).end(); ++BI) { + x_dist = Nx - (BI->x); + y_dist = Ny - (BI->y); + distance = x_dist * x_dist + y_dist * y_dist; + density += 1e-4 / (distance + 1e-50); + } + } + // Course density + } else { + + // Add rough estimate + density = Density[y_grid][x_grid]; + density *= density; + } + + return density; +} + +/// Wrapper functions for the Add and subtract methods +/// Nodes should all be passed by constant ref + +void DensityGrid::Add(Node &n, bool fineDensity) { + if (fineDensity) { + fineAdd(n); + } else { + Add(n); + } +} + +void DensityGrid::Subtract( Node &n, bool first_add, + bool fine_first_add, bool fineDensity) { + if ( fineDensity && !fine_first_add ) { + fineSubtract (n); + } else if ( !first_add ) { + Subtract(n); + } +} + + +/*************************************************** + * Function: DensityGrid::Subtract * + * Description: Subtract a node from density grid * + **************************************************/ +void DensityGrid::Subtract(Node &N) { + int x_grid, y_grid, diam; + float *den_ptr, *fall_ptr; + + /* Where to subtract */ + x_grid = (int)((N.sub_x + HALF_VIEW + .5) * VIEW_TO_GRID); + y_grid = (int)((N.sub_y + HALF_VIEW + .5) * VIEW_TO_GRID); + x_grid -= RADIUS; + y_grid -= RADIUS; + diam = 2 * RADIUS; + + // check to see that we are inside grid + if ( (x_grid >= GRID_SIZE) || (x_grid < 0) || + (y_grid >= GRID_SIZE) || (y_grid < 0) ) { +#ifdef MUSE_MPI + MPI_Abort ( MPI_COMM_WORLD, 1 ); +#else + igraph_error("Exceeded density grid in DrL", IGRAPH_FILE_BASENAME, + __LINE__, IGRAPH_EDRL); + return; +#endif + } + + /* Subtract density values */ + den_ptr = &Density[y_grid][x_grid]; + fall_ptr = &fall_off[0][0]; + for (int i = 0; i <= diam; i++) { + for (int j = 0; j <= diam; j++) { + *den_ptr++ -= *fall_ptr++; + } + den_ptr += GRID_SIZE - (diam + 1); + } +} + +/*************************************************** + * Function: DensityGrid::Add * + * Description: Add a node to the density grid * + **************************************************/ +void DensityGrid::Add(Node &N) { + + int x_grid, y_grid, diam; + float *den_ptr, *fall_ptr; + + + /* Where to add */ + x_grid = (int)((N.x + HALF_VIEW + .5) * VIEW_TO_GRID); + y_grid = (int)((N.y + HALF_VIEW + .5) * VIEW_TO_GRID); + + N.sub_x = N.x; + N.sub_y = N.y; + + x_grid -= RADIUS; + y_grid -= RADIUS; + diam = 2 * RADIUS; + + // check to see that we are inside grid + if ( (x_grid >= GRID_SIZE) || (x_grid < 0) || + (y_grid >= GRID_SIZE) || (y_grid < 0) ) { +#ifdef MUSE_MPI + MPI_Abort ( MPI_COMM_WORLD, 1 ); +#else + igraph_error("Exceeded density grid in DrL", IGRAPH_FILE_BASENAME, + __LINE__, IGRAPH_EDRL); + return; +#endif + } + + /* Add density values */ + den_ptr = &Density[y_grid][x_grid]; + fall_ptr = &fall_off[0][0]; + for (int i = 0; i <= diam; i++) { + for (int j = 0; j <= diam; j++) { + *den_ptr++ += *fall_ptr++; + } + den_ptr += GRID_SIZE - (diam + 1); + } + +} + +/*************************************************** + * Function: DensityGrid::fineSubtract * + * Description: Subtract a node from bins * + **************************************************/ +void DensityGrid::fineSubtract(Node &N) { + int x_grid, y_grid; + + /* Where to subtract */ + x_grid = (int)((N.sub_x + HALF_VIEW + .5) * VIEW_TO_GRID); + y_grid = (int)((N.sub_y + HALF_VIEW + .5) * VIEW_TO_GRID); + GET_BIN(y_grid, x_grid).pop_front(); +} + +/*************************************************** + * Function: DensityGrid::fineAdd * + * Description: Add a node to the bins * + **************************************************/ +void DensityGrid::fineAdd(Node &N) { + int x_grid, y_grid; + + /* Where to add */ + x_grid = (int)((N.x + HALF_VIEW + .5) * VIEW_TO_GRID); + y_grid = (int)((N.y + HALF_VIEW + .5) * VIEW_TO_GRID); + N.sub_x = N.x; + N.sub_y = N.y; + GET_BIN(y_grid, x_grid).push_back(N); +} + +} // namespace drl diff --git a/src/rigraph/core/layout/drl/DensityGrid.h b/src/rigraph/core/layout/drl/DensityGrid.h new file mode 100644 index 0000000..3db6725 --- /dev/null +++ b/src/rigraph/core/layout/drl/DensityGrid.h @@ -0,0 +1,84 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __DENSITY_GRID_H__ +#define __DENSITY_GRID_H__ + + +// Compile time adjustable parameters + +#include "drl_layout.h" +#include "drl_Node.h" +#ifdef MUSE_MPI + #include +#endif + +#include + +namespace drl { + +class DensityGrid { + +public: + + // Methods + void Init(); + void Subtract(Node &n, bool first_add, bool fine_first_add, bool fineDensity); + void Add(Node &n, bool fineDensity ); + float GetDensity(float Nx, float Ny, bool fineDensity); + + // Contructor/Destructor + DensityGrid() {}; + ~DensityGrid(); + +private: + + // Private Members + void Subtract( Node &N ); + void Add( Node &N ); + void fineSubtract( Node &N ); + void fineAdd( Node &N ); + + // new dynamic variables -- SBM + float (*fall_off)[RADIUS * 2 + 1]; + float (*Density)[GRID_SIZE]; + std::deque* Bins; + + // old static variables + //float fall_off[RADIUS*2+1][RADIUS*2+1]; + //float Density[GRID_SIZE][GRID_SIZE]; + //deque Bins[GRID_SIZE][GRID_SIZE]; +}; + +} // namespace drl + +#endif // __DENSITY_GRID_H__ diff --git a/src/rigraph/core/layout/drl/DensityGrid_3d.cpp b/src/rigraph/core/layout/drl/DensityGrid_3d.cpp new file mode 100644 index 0000000..0f02f15 --- /dev/null +++ b/src/rigraph/core/layout/drl/DensityGrid_3d.cpp @@ -0,0 +1,305 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// This file contains the member definitions of the DensityGrid.h class +// This code is modified from the original code by B.N. Wylie + +#include "drl_Node_3d.h" +#include "DensityGrid_3d.h" +#include "igraph_error.h" + +#include +#include + +using namespace std; + +#define GET_BIN(z, y, x) (Bins[(z*GRID_SIZE+y)*GRID_SIZE+x]) + +namespace drl3d { + +//******************************************************* +// Density Grid Destructor -- deallocates memory used +// for Density matrix, fall_off matrix, and node deque. + +DensityGrid::~DensityGrid () { + delete[] Density; + delete[] fall_off; + delete[] Bins; +} + +/********************************************* +* Function: Density_Grid::Reset * +* Description: Reset the density grid * +*********************************************/ +// changed from reset to init since we will only +// call this once in the parallel version of layout + +void DensityGrid::Init() { + + try { + Density = new float[GRID_SIZE][GRID_SIZE][GRID_SIZE]; + fall_off = new float[RADIUS * 2 + 1][RADIUS * 2 + 1][RADIUS * 2 + 1]; + Bins = new deque[GRID_SIZE * GRID_SIZE * GRID_SIZE]; + } catch (bad_alloc&) { + // cout << "Error: Out of memory! Program stopped." << endl; +#ifdef MUSE_MPI + MPI_Abort ( MPI_COMM_WORLD, 1 ); +#else + igraph_error("DrL is out of memory", IGRAPH_FILE_BASENAME, __LINE__, + IGRAPH_ENOMEM); + return; +#endif + } + + // Clear Grid + int i; + for (i = 0; i < GRID_SIZE; i++) + for (int j = 0; j < GRID_SIZE; j++) + for (int k = 0; k < GRID_SIZE; k++) { + Density[i][j][k] = 0; + GET_BIN(i, j, k).erase(GET_BIN(i, j, k).begin(), GET_BIN(i, j, k).end()); + } + + // Compute fall off + for (i = -RADIUS; i <= RADIUS; i++) + for (int j = -RADIUS; j <= RADIUS; j++) + for (int k = -RADIUS; k <= RADIUS; k++) { + fall_off[i + RADIUS][j + RADIUS][k + RADIUS] = + (float)((RADIUS - fabs((float)i)) / RADIUS) * + (float)((RADIUS - fabs((float)j)) / RADIUS) * + (float)((RADIUS - fabs((float)k)) / RADIUS); + } + +} + + +/*************************************************** + * Function: DensityGrid::GetDensity * + * Description: Get_Density from density grid * + **************************************************/ +float DensityGrid::GetDensity(float Nx, float Ny, float Nz, bool fineDensity) { + deque::iterator BI; + int x_grid, y_grid, z_grid; + float x_dist, y_dist, z_dist, distance, density = 0; + int boundary = 10; // boundary around plane + + + /* Where to look */ + x_grid = (int)((Nx + HALF_VIEW + .5) * VIEW_TO_GRID); + y_grid = (int)((Ny + HALF_VIEW + .5) * VIEW_TO_GRID); + z_grid = (int)((Nz + HALF_VIEW + .5) * VIEW_TO_GRID); + + // Check for edges of density grid (10000 is arbitrary high density) + if (x_grid > GRID_SIZE - boundary || x_grid < boundary) { + return 10000; + } + if (y_grid > GRID_SIZE - boundary || y_grid < boundary) { + return 10000; + } + if (z_grid > GRID_SIZE - boundary || z_grid < boundary) { + return 10000; + } + + // Fine density? + if (fineDensity) { + + // Go through nearest bins + for (int k = z_grid - 1; k <= z_grid + 1; k++) + for (int i = y_grid - 1; i <= y_grid + 1; i++) + for (int j = x_grid - 1; j <= x_grid + 1; j++) { + + // Look through bin and add fine repulsions + for (BI = GET_BIN(k, i, j).begin(); BI < GET_BIN(k, i, j).end(); ++BI) { + x_dist = Nx - (BI->x); + y_dist = Ny - (BI->y); + z_dist = Nz - (BI->z); + distance = x_dist * x_dist + y_dist * y_dist + z_dist * z_dist; + density += 1e-4 / (distance + 1e-50); + } + } + + // Course density + } else { + + // Add rough estimate + density = Density[z_grid][y_grid][x_grid]; + density *= density; + } + + return density; +} + +/// Wrapper functions for the Add and subtract methods +/// Nodes should all be passed by constant ref + +void DensityGrid::Add(Node &n, bool fineDensity) { + if (fineDensity) { + fineAdd(n); + } else { + Add(n); + } +} + +void DensityGrid::Subtract( Node &n, bool first_add, + bool fine_first_add, bool fineDensity) { + if ( fineDensity && !fine_first_add ) { + fineSubtract (n); + } else if ( !first_add ) { + Subtract(n); + } +} + + +/*************************************************** + * Function: DensityGrid::Subtract * + * Description: Subtract a node from density grid * + **************************************************/ +void DensityGrid::Subtract(Node &N) { + int x_grid, y_grid, z_grid, diam; + float *den_ptr, *fall_ptr; + + /* Where to subtract */ + x_grid = (int)((N.sub_x + HALF_VIEW + .5) * VIEW_TO_GRID); + y_grid = (int)((N.sub_y + HALF_VIEW + .5) * VIEW_TO_GRID); + z_grid = (int)((N.sub_z + HALF_VIEW + .5) * VIEW_TO_GRID); + x_grid -= RADIUS; + y_grid -= RADIUS; + z_grid -= RADIUS; + diam = 2 * RADIUS; + + // check to see that we are inside grid + if ( (x_grid >= GRID_SIZE) || (x_grid < 0) || + (y_grid >= GRID_SIZE) || (y_grid < 0) || + (z_grid >= GRID_SIZE) || (z_grid < 0) ) { +#ifdef MUSE_MPI + MPI_Abort ( MPI_COMM_WORLD, 1 ); +#else + igraph_error("Exceeded density grid in DrL", IGRAPH_FILE_BASENAME, + __LINE__, IGRAPH_EDRL); + return; +#endif + } + + /* Subtract density values */ + den_ptr = &Density[z_grid][y_grid][x_grid]; + fall_ptr = &fall_off[0][0][0]; + for (int i = 0; i <= diam; i++) { + for (int j = 0; j <= diam; j++) + for (int k = 0; k <= diam; k++) { + *den_ptr++ -= *fall_ptr++; + } + den_ptr += GRID_SIZE - (diam + 1); + } +} + +/*************************************************** + * Function: DensityGrid::Add * + * Description: Add a node to the density grid * + **************************************************/ +void DensityGrid::Add(Node &N) { + + int x_grid, y_grid, z_grid, diam; + float *den_ptr, *fall_ptr; + + + /* Where to add */ + x_grid = (int)((N.x + HALF_VIEW + .5) * VIEW_TO_GRID); + y_grid = (int)((N.y + HALF_VIEW + .5) * VIEW_TO_GRID); + z_grid = (int)((N.z + HALF_VIEW + .5) * VIEW_TO_GRID); + + N.sub_x = N.x; + N.sub_y = N.y; + N.sub_z = N.z; + + x_grid -= RADIUS; + y_grid -= RADIUS; + z_grid -= RADIUS; + diam = 2 * RADIUS; + + // check to see that we are inside grid + if ( (x_grid >= GRID_SIZE) || (x_grid < 0) || + (y_grid >= GRID_SIZE) || (y_grid < 0) || + (z_grid >= GRID_SIZE) || (z_grid < 0) ) { +#ifdef MUSE_MPI + MPI_Abort ( MPI_COMM_WORLD, 1 ); +#else + igraph_error("Exceeded density grid in DrL", IGRAPH_FILE_BASENAME, + __LINE__, IGRAPH_EDRL); + return; +#endif + } + + /* Add density values */ + den_ptr = &Density[z_grid][y_grid][x_grid]; + fall_ptr = &fall_off[0][0][0]; + for (int i = 0; i <= diam; i++) { + for (int j = 0; j <= diam; j++) + for (int k = 0; k <= diam; k++) { + *den_ptr++ += *fall_ptr++; + } + den_ptr += GRID_SIZE - (diam + 1); + } + +} + +/*************************************************** + * Function: DensityGrid::fineSubtract * + * Description: Subtract a node from bins * + **************************************************/ +void DensityGrid::fineSubtract(Node &N) { + int x_grid, y_grid, z_grid; + + /* Where to subtract */ + x_grid = (int)((N.sub_x + HALF_VIEW + .5) * VIEW_TO_GRID); + y_grid = (int)((N.sub_y + HALF_VIEW + .5) * VIEW_TO_GRID); + z_grid = (int)((N.sub_z + HALF_VIEW + .5) * VIEW_TO_GRID); + GET_BIN(z_grid, y_grid, x_grid).pop_front(); +} + +/*************************************************** + * Function: DensityGrid::fineAdd * + * Description: Add a node to the bins * + **************************************************/ +void DensityGrid::fineAdd(Node &N) { + int x_grid, y_grid, z_grid; + + /* Where to add */ + x_grid = (int)((N.x + HALF_VIEW + .5) * VIEW_TO_GRID); + y_grid = (int)((N.y + HALF_VIEW + .5) * VIEW_TO_GRID); + z_grid = (int)((N.z + HALF_VIEW + .5) * VIEW_TO_GRID); + N.sub_x = N.x; + N.sub_y = N.y; + N.sub_z = N.z; + GET_BIN(z_grid, y_grid, x_grid).push_back(N); +} + +} // namespace drl3d diff --git a/src/rigraph/core/layout/drl/DensityGrid_3d.h b/src/rigraph/core/layout/drl/DensityGrid_3d.h new file mode 100644 index 0000000..f02ea32 --- /dev/null +++ b/src/rigraph/core/layout/drl/DensityGrid_3d.h @@ -0,0 +1,84 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __DENSITY_GRID_H__ +#define __DENSITY_GRID_H__ + + +// Compile time adjustable parameters + +#include "drl_layout_3d.h" +#include "drl_Node_3d.h" +#ifdef MUSE_MPI + #include +#endif + +#include + +namespace drl3d { + +class DensityGrid { + +public: + + // Methods + void Init(); + void Subtract(Node &n, bool first_add, bool fine_first_add, bool fineDensity); + void Add(Node &n, bool fineDensity ); + float GetDensity(float Nx, float Ny, float Nz, bool fineDensity); + + // Contructor/Destructor + DensityGrid() {}; + ~DensityGrid(); + +private: + + // Private Members + void Subtract( Node &N ); + void Add( Node &N ); + void fineSubtract( Node &N ); + void fineAdd( Node &N ); + + // new dynamic variables -- SBM + float (*fall_off)[RADIUS * 2 + 1][RADIUS * 2 + 1]; + float (*Density)[GRID_SIZE][GRID_SIZE]; + std::deque* Bins; + + // old static variables + //float fall_off[RADIUS*2+1][RADIUS*2+1]; + //float Density[GRID_SIZE][GRID_SIZE]; + //deque Bins[GRID_SIZE][GRID_SIZE]; +}; + +} // namespace drl3d + +#endif // __DENSITY_GRID_H__ diff --git a/src/rigraph/core/layout/drl/drl_Node.h b/src/rigraph/core/layout/drl/drl_Node.h new file mode 100644 index 0000000..bc894c2 --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_Node.h @@ -0,0 +1,68 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __NODE_H__ +#define __NODE_H__ + +// The node class contains information about a given node for +// use by the density server process. + +// structure coord used to pass position information between +// density server and graph class + +namespace drl { + +class Node { + +public: + + bool fixed; // if true do not change the + // position of this node + int id; + + float x, y; + float sub_x, sub_y; + float energy; + +public: + + Node( int node_id ) { + x = y = 0.0; fixed = false; + id = node_id; + } + ~Node() { } + +}; + +} // namespace drl + +#endif //__NODE_H__ diff --git a/src/rigraph/core/layout/drl/drl_Node_3d.h b/src/rigraph/core/layout/drl/drl_Node_3d.h new file mode 100644 index 0000000..e373d9a --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_Node_3d.h @@ -0,0 +1,68 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __NODE_H__ +#define __NODE_H__ + +// The node class contains information about a given node for +// use by the density server process. + +// structure coord used to pass position information between +// density server and graph class + +namespace drl3d { + +class Node { + +public: + + bool fixed; // if true do not change the + // position of this node + int id; + + float x, y, z; + float sub_x, sub_y, sub_z; + float energy; + +public: + + Node( int node_id ) { + x = y = z = 0.0; fixed = false; + id = node_id; + } + ~Node() { } + +}; + +} // namespace drl3d + +#endif //__NODE_H__ diff --git a/src/rigraph/core/layout/drl/drl_graph.cpp b/src/rigraph/core/layout/drl/drl_graph.cpp new file mode 100644 index 0000000..25427f1 --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_graph.cpp @@ -0,0 +1,1306 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// This file contains the member definitions of the master class + + +#include +#include +#include + +using namespace std; + +#include "drl_graph.h" +#include "igraph_random.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "../../core/interruption.h" +#ifdef MUSE_MPI + #include +#endif + +namespace drl { + +// constructor -- initializes the schedule variables (as in +// graph constructor) + +// graph::graph ( int proc_id, int tot_procs, char *int_file ) +// { + +// // MPI parameters +// myid = proc_id; +// num_procs = tot_procs; + +// // initial annealing parameters +// STAGE = 0; +// iterations = 0; +// temperature = 2000; +// attraction = 10; +// damping_mult = 1.0; +// min_edges = 20; +// first_add = fine_first_add = true; +// fineDensity = false; + +// // Brian's original Vx schedule +// liquid.iterations = 200; +// liquid.temperature = 2000; +// liquid.attraction = 2; +// liquid.damping_mult = 1.0; +// liquid.time_elapsed = 0; + +// expansion.iterations = 200; +// expansion.temperature = 2000; +// expansion.attraction = 10; +// expansion.damping_mult = 1.0; +// expansion.time_elapsed = 0; + +// cooldown.iterations = 200; +// cooldown.temperature = 2000; +// cooldown.attraction = 1; +// cooldown.damping_mult = .1; +// cooldown.time_elapsed = 0; + +// crunch.iterations = 50; +// crunch.temperature = 250; +// crunch.attraction = 1; +// crunch. damping_mult = .25; +// crunch.time_elapsed = 0; + +// simmer.iterations = 100; +// simmer.temperature = 250; +// simmer.attraction = .5; +// simmer.damping_mult = 0.0; +// simmer.time_elapsed = 0; + +// // scan .int file for node info +// scan_int ( int_file ); + +// // populate node positions and ids +// positions.reserve ( num_nodes ); +// map < int, int >::iterator cat_iter; +// for ( cat_iter = id_catalog.begin(); +// cat_iter != id_catalog.end(); +// cat_iter++ ) +// positions.push_back ( Node( cat_iter->first ) ); + +// /* +// // output positions .ids for debugging +// for ( int id = 0; id < num_nodes; id++ ) +// cout << positions[id].id << endl; +// */ + +// // read .int file for graph info +// read_int ( int_file ); + +// // initialize density server +// density_server.Init(); + +// } + +graph::graph(const igraph_t *igraph, + const igraph_layout_drl_options_t *options, + const igraph_vector_t *weights) { + myid = 0; + num_procs = 1; + + STAGE = 0; + iterations = options->init_iterations; + temperature = options->init_temperature; + attraction = options->init_attraction; + damping_mult = options->init_damping_mult; + min_edges = 20; + first_add = fine_first_add = true; + fineDensity = false; + + // Brian's original Vx schedule + liquid.iterations = options->liquid_iterations; + liquid.temperature = options->liquid_temperature; + liquid.attraction = options->liquid_attraction; + liquid.damping_mult = options->liquid_damping_mult; + liquid.time_elapsed = 0; + + expansion.iterations = options->expansion_iterations; + expansion.temperature = options->expansion_temperature; + expansion.attraction = options->expansion_attraction; + expansion.damping_mult = options->expansion_damping_mult; + expansion.time_elapsed = 0; + + cooldown.iterations = options->cooldown_iterations; + cooldown.temperature = options->cooldown_temperature; + cooldown.attraction = options->cooldown_attraction; + cooldown.damping_mult = options->cooldown_damping_mult; + cooldown.time_elapsed = 0; + + crunch.iterations = options->crunch_iterations; + crunch.temperature = options->crunch_temperature; + crunch.attraction = options->crunch_attraction; + crunch.damping_mult = options->crunch_damping_mult; + crunch.time_elapsed = 0; + + simmer.iterations = options->simmer_iterations; + simmer.temperature = options->simmer_temperature; + simmer.attraction = options->simmer_attraction; + simmer.damping_mult = options->simmer_damping_mult; + simmer.time_elapsed = 0; + + // scan .int file for node info + highest_sim = 1.0; + num_nodes = igraph_vcount(igraph); + long int no_of_edges = igraph_ecount(igraph); + for (long int i = 0; i < num_nodes; i++) { + id_catalog[i] = 1; + } + map< int, int>::iterator cat_iter; + for ( cat_iter = id_catalog.begin(); + cat_iter != id_catalog.end(); cat_iter++) { + cat_iter->second = cat_iter->first; + } + + // populate node positions and ids + positions.reserve ( num_nodes ); + for ( cat_iter = id_catalog.begin(); + cat_iter != id_catalog.end(); + cat_iter++ ) { + positions.push_back ( Node( cat_iter->first ) ); + } + + // read .int file for graph info + long int node_1, node_2; + double weight; + for (long int i = 0; i < no_of_edges; i++) { + node_1 = IGRAPH_FROM(igraph, i); + node_2 = IGRAPH_TO(igraph, i); + weight = weights ? VECTOR(*weights)[i] : 1.0 ; + (neighbors[id_catalog[node_1]])[id_catalog[node_2]] = weight; + (neighbors[id_catalog[node_2]])[id_catalog[node_1]] = weight; + } + + // initialize density server + density_server.Init(); + +} + +// The following subroutine scans the .int file for the following +// information: number nodes, node ids, and highest similarity. The +// corresponding graph globals are populated: num_nodes, id_catalog, +// and highest_sim. + +// void graph::scan_int ( char *filename ) +// { + +// cout << "Proc. " << myid << " scanning .int file ..." << endl; + +// // Open (sim) File +// ifstream fp ( filename ); +// if ( !fp ) +// { +// cout << "Error: could not open " << filename << ". Program terminated." << endl; +// #ifdef MUSE_MPI +// MPI_Abort ( MPI_COMM_WORLD, 1 ); +// #else +// exit (1); +// #endif +// } + +// // Read file, parse, and add into data structure +// int id1, id2; +// float edge_weight; +// highest_sim = -1.0; +// while ( !fp.eof () ) +// { +// fp >> id1 >> id2 >> edge_weight; + +// // ignore negative weights! +// if ( edge_weight <= 0 ) +// { +// cout << "Error: found negative edge weight in " << filename << ". Program stopped." << endl; +// #ifdef MUSE_MPI +// MPI_Abort ( MPI_COMM_WORLD, 1 ); +// #else +// exit (1); +// #endif +// } + +// if ( highest_sim < edge_weight ) +// highest_sim = edge_weight; + +// id_catalog[id1] = 1; +// id_catalog[id2] = 1; +// } + +// fp.close(); + +// if ( id_catalog.size() == 0 ) +// { +// cout << "Error: Proc. " << myid << ": " << filename << " is empty. Program terminated." << endl; +// #ifdef MUSE_MPI +// MPI_Abort ( MPI_COMM_WORLD, 1 ); +// #else +// exit (1); +// #endif +// } + +// // label nodes with sequential integers starting at 0 +// map< int, int>::iterator cat_iter; +// int id_label; +// for ( cat_iter = id_catalog.begin(), id_label = 0; +// cat_iter != id_catalog.end(); cat_iter++, id_label++ ) +// cat_iter->second = id_label; + +// /* +// // output id_catalog for debugging: +// for ( cat_iter = id_catalog.begin(); +// cat_iter != id_catalog.end(); +// cat_iter++ ) +// cout << cat_iter->first << "\t" << cat_iter->second << endl; +// */ + +// num_nodes = id_catalog.size(); +// } + +// read in .parms file, if present + +/* +void graph::read_parms ( char *parms_file ) +{ + + // read from .parms file + ifstream parms_in ( parms_file ); + if ( !parms_in ) + { + cout << "Error: could not open .parms file! Program stopped." << endl; + #ifdef MUSE_MPI + MPI_Abort ( MPI_COMM_WORLD, 1 ); + #else + exit (1); + #endif + } + + cout << "Processor " << myid << " reading .parms file." << endl; + + // read in stage parameters + string parm_label; // this is ignored in the .parms file + + // initial parameters + parms_in >> parm_label >> iterations; + parms_in >> parm_label >> temperature; + parms_in >> parm_label >> attraction; + parms_in >> parm_label >> damping_mult; + + // liquid stage + parms_in >> parm_label >> liquid.iterations; + parms_in >> parm_label >> liquid.temperature; + parms_in >> parm_label >> liquid.attraction; + parms_in >> parm_label >> liquid.damping_mult; + + // expansion stage + parms_in >> parm_label >> expansion.iterations; + parms_in >> parm_label >> expansion.temperature; + parms_in >> parm_label >> expansion.attraction; + parms_in >> parm_label >> expansion.damping_mult; + + // cooldown stage + parms_in >> parm_label >> cooldown.iterations; + parms_in >> parm_label >> cooldown.temperature; + parms_in >> parm_label >> cooldown.attraction; + parms_in >> parm_label >> cooldown.damping_mult; + + // crunch stage + parms_in >> parm_label >> crunch.iterations; + parms_in >> parm_label >> crunch.temperature; + parms_in >> parm_label >> crunch.attraction; + parms_in >> parm_label >> crunch.damping_mult; + + // simmer stage + parms_in >> parm_label >> simmer.iterations; + parms_in >> parm_label >> simmer.temperature; + parms_in >> parm_label >> simmer.attraction; + parms_in >> parm_label >> simmer.damping_mult; + + parms_in.close(); + + // print out parameters for double checking + if ( myid == 0 ) + { + cout << "Processor 0 reports the following inputs:" << endl; + cout << "inital.iterations = " << iterations << endl; + cout << "initial.temperature = " << temperature << endl; + cout << "initial.attraction = " << attraction << endl; + cout << "initial.damping_mult = " << damping_mult << endl; + cout << " ..." << endl; + cout << "liquid.iterations = " << liquid.iterations << endl; + cout << "liquid.temperature = " << liquid.temperature << endl; + cout << "liquid.attraction = " << liquid.attraction << endl; + cout << "liquid.damping_mult = " << liquid.damping_mult << endl; + cout << " ..." << endl; + cout << "simmer.iterations = " << simmer.iterations << endl; + cout << "simmer.temperature = " << simmer.temperature << endl; + cout << "simmer.attraction = " << simmer.attraction << endl; + cout << "simmer.damping_mult = " << simmer.damping_mult << endl; + } + +} +*/ + +// init_parms -- this subroutine initializes the edge_cut variables +// used in the original VxOrd starting with the edge_cut parameter. +// In our version, edge_cut = 0 means no cutting, 1 = maximum cut. +// We also set the random seed here. + +void graph::init_parms ( int rand_seed, float edge_cut, float real_parm ) { + IGRAPH_UNUSED(rand_seed); + + // first we translate edge_cut the former tcl sliding scale + //CUT_END = cut_length_end = 39000.0 * (1.0 - edge_cut) + 1000.0; + CUT_END = cut_length_end = 40000.0 * (1.0 - edge_cut); + + // cut_length_end cannot actually be 0 + if ( cut_length_end <= 1.0 ) { + cut_length_end = 1.0; + } + + float cut_length_start = 4.0 * cut_length_end; + + // now we set the parameters used by ReCompute + cut_off_length = cut_length_start; + cut_rate = ( cut_length_start - cut_length_end ) / 400.0; + + // finally set the number of iterations to leave .real coords fixed + int full_comp_iters; + full_comp_iters = liquid.iterations + expansion.iterations + + cooldown.iterations + crunch.iterations + 3; + + // adjust real parm to iterations (do not enter simmer halfway) + if ( real_parm < 0 ) { + real_iterations = (int)real_parm; + } else if ( real_parm == 1) { + real_iterations = full_comp_iters + simmer.iterations + 100; + } else { + real_iterations = (int)(real_parm * full_comp_iters); + } + + tot_iterations = 0; + if ( real_iterations > 0 ) { + real_fixed = true; + } else { + real_fixed = false; + } + + // calculate total expected iterations (for progress bar display) + tot_expected_iterations = liquid.iterations + + expansion.iterations + cooldown.iterations + + crunch.iterations + simmer.iterations; + + /* + // output edge_cutting parms (for debugging) + cout << "Processor " << myid << ": " + << "cut_length_end = CUT_END = " << cut_length_end + << ", cut_length_start = " << cut_length_start + << ", cut_rate = " << cut_rate << endl; + */ + + // set random seed + // srand ( rand_seed ); // Don't need this in igraph + +} + +void graph::init_parms(const igraph_layout_drl_options_t *options) { + double rand_seed = 0.0; + double real_in = -1.0; + init_parms(rand_seed, options->edge_cut, real_in); +} + +// The following subroutine reads a .real file to obtain initial +// coordinates. If a node is missing coordinates the coordinates +// are computed + +// void graph::read_real ( char *real_file ) +// { +// cout << "Processor " << myid << " reading .real file ..." << endl; + +// // read in .real file and mark as fixed +// ifstream real_in ( real_file ); +// if ( !real_in ) +// { +// cout << "Error: proc. " << myid << " could not open .real file." << endl; +// #ifdef MUSE_MPI +// MPI_Abort ( MPI_COMM_WORLD, 1 ); +// #else +// exit (1); +// #endif +// } + +// int real_id; +// float real_x, real_y; +// while ( !real_in.eof () ) +// { +// real_id = -1; +// real_in >> real_id >> real_x >> real_y; +// if ( real_id >= 0 ) +// { +// positions[id_catalog[real_id]].x = real_x; +// positions[id_catalog[real_id]].y = real_y; +// positions[id_catalog[real_id]].fixed = true; + +// /* +// // output positions read (for debugging) +// cout << id_catalog[real_id] << " (" << positions[id_catalog[real_id]].x +// << ", " << positions[id_catalog[real_id]].y << ") " +// << positions[id_catalog[real_id]].fixed << endl; +// */ + +// // add node to density grid +// if ( real_iterations > 0 ) +// density_server.Add ( positions[id_catalog[real_id]], fineDensity ); +// } + +// } + +// real_in.close(); +// } + +int graph::read_real ( const igraph_matrix_t *real_mat, + const igraph_vector_bool_t *fixed) { + long int n = igraph_matrix_nrow(real_mat); + for (long int i = 0; i < n; i++) { + positions[id_catalog[i]].x = MATRIX(*real_mat, i, 0); + positions[id_catalog[i]].y = MATRIX(*real_mat, i, 1); + positions[id_catalog[i]].fixed = fixed ? VECTOR(*fixed)[i] : false; + + if ( real_iterations > 0 ) { + density_server.Add ( positions[id_catalog[i]], fineDensity ); + } + } + + return 0; +} + +// The read_part_int subroutine reads the .int +// file produced by convert_sim and gathers the nodes and their +// neighbors in the range start_ind to end_ind. + +// void graph::read_int ( char *file_name ) +// { + +// ifstream int_file; + +// int_file.open ( file_name ); +// if ( !int_file ) +// { +// cout << "Error (worker process " << myid << "): could not open .int file." << endl; +// #ifdef MUSE_MPI +// MPI_Abort ( MPI_COMM_WORLD, 1 ); +// #else +// exit (1); +// #endif +// } + +// cout << "Processor " << myid << " reading .int file ..." << endl; + +// int node_1, node_2; +// float weight; + +// while ( !int_file.eof() ) +// { +// weight = 0; // all weights should be >= 0 +// int_file >> node_1 >> node_2 >> weight; +// if ( weight ) // otherwise we are at end of file +// // or it is a self-connected node +// { +// // normalization from original vxord +// weight /= highest_sim; +// weight = weight*fabs(weight); + +// // initialize graph +// if ( ( node_1 % num_procs ) == myid ) +// (neighbors[id_catalog[node_1]])[id_catalog[node_2]] = weight; +// if ( ( node_2 % num_procs ) == myid ) +// (neighbors[id_catalog[node_2]])[id_catalog[node_1]] = weight; +// } +// } +// int_file.close(); + +// /* +// // the following code outputs the contents of the neighbors structure +// // (to be used for debugging) + +// map >::iterator i; +// map::iterator j; + +// for ( i = neighbors.begin(); i != neighbors.end(); i++ ) { +// cout << myid << ": " << i->first << " "; +// for (j = (i->second).begin(); j != (i->second).end(); j++ ) +// cout << j->first << " (" << j->second << ") "; +// cout << endl; +// } +// */ + +// } + +/********************************************* + * Function: ReCompute * + * Description: Compute the graph locations * + * Modified from original code by B. Wylie * + ********************************************/ + +int graph::ReCompute( ) { + + // carryover from original VxOrd + int MIN = 1; + + /* + // output parameters (for debugging) + cout << "ReCompute is using the following parameters: "<< endl; + cout << "STAGE: " << STAGE << ", iter: " << iterations << ", temp = " << temperature + << ", attract = " << attraction << ", damping_mult = " << damping_mult + << ", min_edges = " << min_edges << ", cut_off_length = " << cut_off_length + << ", fineDensity = " << fineDensity << endl; + */ + + /* igraph progress report */ + float progress = (tot_iterations * 100.0 / tot_expected_iterations); + + switch (STAGE) { + case 0: + if (iterations == 0) { + IGRAPH_PROGRESS("DrL layout (initialization stage)", progress, 0); + } else { + IGRAPH_PROGRESS("DrL layout (liquid stage)", progress, 0); + } + break; + case 1: + IGRAPH_PROGRESS("DrL layout (expansion stage)", progress, 0); break; + case 2: + IGRAPH_PROGRESS("DrL layout (cooldown and cluster phase)", progress, 0); break; + case 3: + IGRAPH_PROGRESS("DrL layout (crunch phase)", progress, 0); break; + case 5: + IGRAPH_PROGRESS("DrL layout (simmer phase)", progress, 0); break; + case 6: + IGRAPH_PROGRESS("DrL layout (final phase)", 100.0, 0); break; + default: + IGRAPH_PROGRESS("DrL layout (unknown phase)", 0.0, 0); break; + } + + /* Compute Energies for individual nodes */ + update_nodes (); + + // check to see if we need to free fixed nodes + tot_iterations++; + if ( tot_iterations >= real_iterations ) { + real_fixed = false; + } + + + // **************************************** + // AUTOMATIC CONTROL SECTION + // **************************************** + + // STAGE 0: LIQUID + if (STAGE == 0) { + + if ( iterations == 0 ) { + start_time = time( NULL ); +// if ( myid == 0 ) +// cout << "Entering liquid stage ..."; + } + + if (iterations < liquid.iterations) { + temperature = liquid.temperature; + attraction = liquid.attraction; + damping_mult = liquid.damping_mult; + iterations++; +// if ( myid == 0 ) +// cout << "." << flush; + + } else { + + stop_time = time( NULL ); + liquid.time_elapsed = liquid.time_elapsed + (stop_time - start_time); + temperature = expansion.temperature; + attraction = expansion.attraction; + damping_mult = expansion.damping_mult; + iterations = 0; + + // go to next stage + STAGE = 1; + start_time = time( NULL ); + +// if ( myid == 0 ) +// cout << "Entering expansion stage ..."; + } + } + + // STAGE 1: EXPANSION + if (STAGE == 1) { + + if (iterations < expansion.iterations) { + + // Play with vars + if (attraction > 1) { + attraction -= .05f; + } + if (min_edges > 12) { + min_edges -= .05f; + } + cut_off_length -= cut_rate; + if (damping_mult > .1) { + damping_mult -= .005f; + } + iterations++; +// if ( myid == 0 ) cout << "." << flush; + + } else { + + stop_time = time( NULL ); + expansion.time_elapsed = expansion.time_elapsed + (stop_time - start_time); + min_edges = 12; + damping_mult = cooldown.damping_mult; + + STAGE = 2; + attraction = cooldown.attraction; + temperature = cooldown.temperature; + iterations = 0; + start_time = time( NULL ); + +// if ( myid == 0 ) +// cout << "Entering cool-down stage ..."; + } + } + + // STAGE 2: Cool down and cluster + else if (STAGE == 2) { + + if (iterations < cooldown.iterations) { + + // Reduce temperature + if (temperature > 50) { + temperature -= 10; + } + + // Reduce cut length + if (cut_off_length > cut_length_end) { + cut_off_length -= cut_rate * 2; + } + if (min_edges > MIN) { + min_edges -= .2f; + } + //min_edges = 99; + iterations++; +// if ( myid == 0 ) +// cout << "." << flush; + + } else { + + stop_time = time( NULL ); + cooldown.time_elapsed = cooldown.time_elapsed + (stop_time - start_time); + cut_off_length = cut_length_end; + temperature = crunch.temperature; + damping_mult = crunch.damping_mult; + min_edges = MIN; + //min_edges = 99; // In other words: no more cutting + + STAGE = 3; + iterations = 0; + attraction = crunch.attraction; + start_time = time( NULL ); + +// if ( myid == 0 ) +// cout << "Entering crunch stage ..."; + } + } + + // STAGE 3: Crunch + else if (STAGE == 3) { + + if (iterations < crunch.iterations) { + iterations++; +// if ( myid == 0 ) cout << "." << flush; + } else { + + stop_time = time( NULL ); + crunch.time_elapsed = crunch.time_elapsed + (stop_time - start_time); + iterations = 0; + temperature = simmer.temperature; + attraction = simmer.attraction; + damping_mult = simmer.damping_mult; + min_edges = 99; + fineDensity = true; + + STAGE = 5; + start_time = time( NULL ); + +// if ( myid == 0 ) +// cout << "Entering simmer stage ..."; + } + } + + // STAGE 5: Simmer + else if ( STAGE == 5 ) { + + if (iterations < simmer.iterations) { + if (temperature > 50) { + temperature -= 2; + } + iterations++; +// if ( myid == 0 ) cout << "." << flush; + } else { + stop_time = time( NULL ); + simmer.time_elapsed = simmer.time_elapsed + (stop_time - start_time); + + STAGE = 6; + +// if ( myid == 0 ) +// cout << "Layout calculation completed in " << +// ( liquid.time_elapsed + expansion.time_elapsed + +// cooldown.time_elapsed + crunch.time_elapsed + +// simmer.time_elapsed ) +// << " seconds (not including I/O)." +// << endl; + } + } + + // STAGE 6: All Done! + else if ( STAGE == 6) { + + /* + // output parameters (for debugging) + cout << "ReCompute is using the following parameters: "<< endl; + cout << "STAGE: " << STAGE << ", iter: " << iterations << ", temp = " << temperature + << ", attract = " << attraction << ", damping_mult = " << damping_mult + << ", min_edges = " << min_edges << ", cut_off_length = " << cut_off_length + << ", fineDensity = " << fineDensity << endl; + */ + + return 0; + } + + // **************************************** + // END AUTOMATIC CONTROL SECTION + // **************************************** + + // Still need more recomputation + return 1; + +} + +// update_nodes -- this function will complete the primary node update +// loop in layout's recompute routine. It follows exactly the same +// sequence to ensure similarity of parallel layout to the standard layout + +void graph::update_nodes ( ) { + + vector node_indices; // node list of nodes currently being updated + float old_positions[2 * MAX_PROCS]; // positions before update + float new_positions[2 * MAX_PROCS]; // positions after update + + bool all_fixed; // check if all nodes are fixed + + // initial node list consists of 0,1,...,num_procs + for ( int i = 0; i < num_procs; i++ ) { + node_indices.push_back( i ); + } + + // next we calculate the number of nodes there would be if the + // num_nodes by num_procs schedule grid were perfectly square + int square_num_nodes = (int)(num_procs + num_procs * floor ((float)(num_nodes - 1) / (float)num_procs )); + + for ( int i = myid; i < square_num_nodes; i += num_procs ) { + + // get old positions + get_positions ( node_indices, old_positions ); + + // default new position is old position + get_positions ( node_indices, new_positions ); + + if ( i < num_nodes ) { + + // advance random sequence according to myid + for ( int j = 0; j < 2 * myid; j++ ) { + RNG_UNIF01(); + } + // rand(); + + // calculate node energy possibilities + if ( !(positions[i].fixed && real_fixed) ) { + update_node_pos ( i, old_positions, new_positions ); + } + + // advance random sequence for next iteration + for ( unsigned int j = 2 * myid; j < 2 * (node_indices.size() - 1); j++ ) { + RNG_UNIF01(); + } + // rand(); + + } else { + // advance random sequence according to use by + // the other processors + for ( unsigned int j = 0; j < 2 * (node_indices.size()); j++ ) { + RNG_UNIF01(); + } + //rand(); + } + + // check if anything was actually updated (e.g. everything was fixed) + all_fixed = true; + for ( unsigned int j = 0; j < node_indices.size (); j++ ) + if ( !(positions [ node_indices[j] ].fixed && real_fixed) ) { + all_fixed = false; + } + + // update positions across processors (if not all fixed) + if ( !all_fixed ) { +#ifdef MUSE_MPI + MPI_Allgather ( &new_positions[2 * myid], 2, MPI_FLOAT, + new_positions, 2, MPI_FLOAT, MPI_COMM_WORLD ); +#endif + + // update positions (old to new) + update_density ( node_indices, old_positions, new_positions ); + } + + /* + if ( myid == 0 ) + { + // output node list (for debugging) + for ( unsigned int j = 0; j < node_indices.size(); j++ ) + cout << node_indices[j] << " "; + cout << endl; + } + */ + + // compute node list for next update + for ( unsigned int j = 0; j < node_indices.size(); j++ ) { + node_indices [j] += num_procs; + } + + while ( !node_indices.empty() && node_indices.back() >= num_nodes ) { + node_indices.pop_back ( ); + } + + } + + // update first_add and fine_first_add + first_add = false; + if ( fineDensity ) { + fine_first_add = false; + } + +} + +// The get_positions function takes the node_indices list +// and returns the corresponding positions in an array. + +void graph::get_positions ( vector &node_indices, + float return_positions[2 * MAX_PROCS] ) { + + // fill positions + for (unsigned int i = 0; i < node_indices.size(); i++) { + return_positions[2 * i] = positions[ node_indices[i] ].x; + return_positions[2 * i + 1] = positions[ node_indices[i] ].y; + } + +} + +// update_node_pos -- this subroutine does the actual work of computing +// the new position of a given node. num_act_proc gives the number +// of active processes at this level for use by the random number +// generators. + +void graph::update_node_pos ( int node_ind, + float old_positions[2 * MAX_PROCS], + float new_positions[2 * MAX_PROCS] ) { + + float energies[2]; // node energies for possible positions + float updated_pos[2][2]; // possible positions + float pos_x, pos_y; + + // old VxOrd parameter + float jump_length = .010 * temperature; + + // subtract old node + density_server.Subtract ( positions[node_ind], first_add, fine_first_add, fineDensity ); + + // compute node energy for old solution + energies[0] = Compute_Node_Energy ( node_ind ); + + // move node to centroid position + Solve_Analytic ( node_ind, pos_x, pos_y ); + positions[node_ind].x = updated_pos[0][0] = pos_x; + positions[node_ind].y = updated_pos[0][1] = pos_y; + + /* + // ouput random numbers (for debugging) + int rand_0, rand_1; + rand_0 = rand(); + rand_1 = rand(); + cout << myid << ": " << rand_0 << ", " << rand_1 << endl; + */ + + // Do random method (RAND_MAX is C++ maximum random number) + updated_pos[1][0] = updated_pos[0][0] + (.5 - RNG_UNIF01()) * jump_length; + updated_pos[1][1] = updated_pos[0][1] + (.5 - RNG_UNIF01()) * jump_length; + + // compute node energy for random position + positions[node_ind].x = updated_pos[1][0]; + positions[node_ind].y = updated_pos[1][1]; + energies[1] = Compute_Node_Energy ( node_ind ); + + /* + // output update possiblities (debugging): + cout << node_ind << ": (" << updated_pos[0][0] << "," << updated_pos[0][1] + << "), " << energies[0] << "; (" << updated_pos[1][0] << "," + << updated_pos[1][1] << "), " << energies[1] << endl; + */ + + // add back old position + positions[node_ind].x = old_positions[2 * myid]; + positions[node_ind].y = old_positions[2 * myid + 1]; + if ( !fineDensity && !first_add ) { + density_server.Add ( positions[node_ind], fineDensity ); + } else if ( !fine_first_add ) { + density_server.Add ( positions[node_ind], fineDensity ); + } + + // choose updated node position with lowest energy + if ( energies[0] < energies[1] ) { + new_positions[2 * myid] = updated_pos[0][0]; + new_positions[2 * myid + 1] = updated_pos[0][1]; + positions[node_ind].energy = energies[0]; + } else { + new_positions[2 * myid] = updated_pos[1][0]; + new_positions[2 * myid + 1] = updated_pos[1][1]; + positions[node_ind].energy = energies[1]; + } + +} + +// update_density takes a sequence of node_indices and their positions and +// updates the positions by subtracting the old positions and adding the +// new positions to the density grid. + +void graph::update_density ( vector &node_indices, + float old_positions[2 * MAX_PROCS], + float new_positions[2 * MAX_PROCS] ) { + + // go through each node and subtract old position from + // density grid before adding new position + for ( unsigned int i = 0; i < node_indices.size(); i++ ) { + positions[node_indices[i]].x = old_positions[2 * i]; + positions[node_indices[i]].y = old_positions[2 * i + 1]; + density_server.Subtract ( positions[node_indices[i]], + first_add, fine_first_add, fineDensity ); + + positions[node_indices[i]].x = new_positions[2 * i]; + positions[node_indices[i]].y = new_positions[2 * i + 1]; + density_server.Add ( positions[node_indices[i]], fineDensity ); + } + +} + +/******************************************** +* Function: Compute_Node_Energy * +* Description: Compute the node energy * +* This code has been modified from the * +* original code by B. Wylie. * +*********************************************/ + +float graph::Compute_Node_Energy( int node_ind ) { + + /* Want to expand 4th power range of attraction */ + float attraction_factor = attraction * attraction * + attraction * attraction * 2e-2; + + map ::iterator EI; + float x_dis, y_dis; + float energy_distance, weight; + float node_energy = 0; + + // Add up all connection energies + for (EI = neighbors[node_ind].begin(); EI != neighbors[node_ind].end(); ++EI) { + + // Get edge weight + weight = EI->second; + + // Compute x,y distance + x_dis = positions[ node_ind ].x - positions[ EI->first ].x; + y_dis = positions[ node_ind ].y - positions[ EI->first ].y; + + // Energy Distance + energy_distance = x_dis * x_dis + y_dis * y_dis; + if (STAGE < 2) { + energy_distance *= energy_distance; + } + + // In the liquid phase we want to discourage long link distances + if (STAGE == 0) { + energy_distance *= energy_distance; + } + + node_energy += weight * attraction_factor * energy_distance; + } + + // output effect of density (debugging) + //cout << "[before: " << node_energy; + + // add density + node_energy += density_server.GetDensity ( positions[ node_ind ].x, positions[ node_ind ].y, + fineDensity ); + + // after calling density server (debugging) + //cout << ", after: " << node_energy << "]" << endl; + + // return computated energy + return node_energy; +} + + +/********************************************* +* Function: Solve_Analytic * +* Description: Compute the node position * +* This is a modified version of the function * +* originally written by B. Wylie * +*********************************************/ + +void graph::Solve_Analytic( int node_ind, float &pos_x, float &pos_y ) { + + map ::iterator EI; + float total_weight = 0; + float x_dis, y_dis, x_cen = 0, y_cen = 0; + float x = 0, y = 0, dis; + float damping, weight; + + // Sum up all connections + for (EI = neighbors[node_ind].begin(); EI != neighbors[node_ind].end(); ++EI) { + weight = EI->second; + total_weight += weight; + x += weight * positions[ EI->first ].x; + y += weight * positions[ EI->first ].y; + } + + // Now set node position + if (total_weight > 0) { + + // Compute centriod + x_cen = x / total_weight; + y_cen = y / total_weight; + damping = 1.0 - damping_mult; + pos_x = damping * positions[ node_ind ].x + (1.0 - damping) * x_cen; + pos_y = damping * positions[ node_ind ].y + (1.0 - damping) * y_cen; + } else { + pos_x = positions[ node_ind ].x; + pos_y = positions[ node_ind ].y; + } + + // No cut edge flag (?) + if (min_edges == 99) { + return; + } + + // Don't cut at end of scale + if ( CUT_END >= 39500 ) { + return; + } + + float num_connections = sqrt((double)neighbors[node_ind].size()); + float maxLength = 0; + + map::iterator maxIndex; + + // Go through nodes edges... cutting if necessary + for (EI = maxIndex = neighbors[node_ind].begin(); + EI != neighbors[node_ind].end(); ++EI) { + + // Check for at least min edges + if (neighbors[node_ind].size() < min_edges) { + continue; + } + + x_dis = x_cen - positions[ EI->first ].x; + y_dis = y_cen - positions[ EI->first ].y; + dis = x_dis * x_dis + y_dis * y_dis; + dis *= num_connections; + + // Store maximum edge + if (dis > maxLength) { + maxLength = dis; + maxIndex = EI; + } + } + + // If max length greater than cut_length then cut + if (maxLength > cut_off_length) { + neighbors[ node_ind ].erase( maxIndex ); + } + +} + + +// write_coord writes out the coordinate file of the final solutions + +// void graph::write_coord( const char *file_name ) +// { + +// ofstream coordOUT( file_name ); +// if ( !coordOUT ) +// { +// cout << "Could not open " << file_name << ". Program terminated." << endl; +// #ifdef MUSE_MPI +// MPI_Abort ( MPI_COMM_WORLD, 1 ); +// #else +// exit (1); +// #endif +// } + +// cout << "Writing out solution to " << file_name << " ..." << endl; + +// for (unsigned int i = 0; i < positions.size(); i++) { +// coordOUT << positions[i].id << "\t" << positions[i].x << "\t" << positions[i].y < >::iterator i; + map::iterator j; + + for ( i = neighbors.begin(); i != neighbors.end(); i++ ) + for (j = (i->second).begin(); j != (i->second).end(); j++ ) + simOUT << positions[i->first].id << "\t" + << positions[j->first].id << "\t" + << j->second << endl; + + simOUT.close(); + +} +*/ + +// get_tot_energy adds up the energy for each node to give an estimate of the +// quality of the minimization. + +float graph::get_tot_energy ( ) { + + float my_tot_energy, tot_energy; + my_tot_energy = 0; + for ( int i = myid; i < num_nodes; i += num_procs ) { + my_tot_energy += positions[i].energy; + } + + //vector::iterator i; + //for ( i = positions.begin(); i != positions.end(); i++ ) + // tot_energy += i->energy; + +#ifdef MUSE_MPI + MPI_Reduce ( &my_tot_energy, &tot_energy, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD ); +#else + tot_energy = my_tot_energy; +#endif + + return tot_energy; + +} + + +// The following subroutine draws the graph with possible intermediate +// output (int_out is set to 0 if not proc. 0). int_out is the parameter +// passed by the user, and coord_file is the .coord file. + +// void graph::draw_graph ( int int_out, char *coord_file ) +// { + +// // layout graph (with possible intermediate output) +// int count_iter = 0, count_file = 1; +// char int_coord_file [MAX_FILE_NAME + MAX_INT_LENGTH]; +// while ( ReCompute( ) ) +// if ( (int_out > 0) && (count_iter == int_out) ) +// { +// // output intermediate solution +// sprintf ( int_coord_file, "%s.%d", coord_file, count_file ); +// write_coord ( int_coord_file ); + +// count_iter = 0; +// count_file++; +// } +// else +// count_iter++; + +// } + +int graph::draw_graph(igraph_matrix_t *res) { + int count_iter = 0; + while (ReCompute()) { + IGRAPH_ALLOW_INTERRUPTION(); + count_iter++; + } + long int n = positions.size(); + IGRAPH_CHECK(igraph_matrix_resize(res, n, 2)); + for (long int i = 0; i < n; i++) { + MATRIX(*res, i, 0) = positions[i].x; + MATRIX(*res, i, 1) = positions[i].y; + } + return 0; +} + +} // namespace drl diff --git a/src/rigraph/core/layout/drl/drl_graph.h b/src/rigraph/core/layout/drl/drl_graph.h new file mode 100644 index 0000000..1a2804d --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_graph.h @@ -0,0 +1,132 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// The graph class contains the methods necessary to draw the +// graph. It calls on the density server class to obtain +// position and density information + +#include "DensityGrid.h" +#include "igraph_layout.h" + +#include +#include +#include + +namespace drl { + +// layout schedule information +struct layout_schedule { + int iterations; + float temperature; + float attraction; + float damping_mult; + time_t time_elapsed; +}; + +class graph { + +public: + + // Methods + void init_parms ( int rand_seed, float edge_cut, float real_parm ); + void init_parms ( const igraph_layout_drl_options_t *options ); + void read_parms ( char *parms_file ); + void read_real ( char *real_file ); + int read_real ( const igraph_matrix_t *real_mat, + const igraph_vector_bool_t *fixed); + void scan_int ( char *filename ); + void read_int ( char *file_name ); + void draw_graph ( int int_out, char *coord_file ); + int draw_graph (igraph_matrix_t *res); + void write_coord ( const char *file_name ); + void write_sim ( const char *file_name ); + float get_tot_energy ( ); + + // Con/Decon + graph( int proc_id, int tot_procs, char *int_file ); + ~graph( ) { } + graph( const igraph_t *igraph, + const igraph_layout_drl_options_t *options, + const igraph_vector_t *weights); + +private: + + // Methods + int ReCompute ( ); + void update_nodes ( ); + float Compute_Node_Energy ( int node_ind ); + void Solve_Analytic ( int node_ind, float &pos_x, float &pos_y ); + void get_positions ( std::vector &node_indices, float return_positions[2 * MAX_PROCS] ); + void update_density ( std::vector &node_indices, + float old_positions[2 * MAX_PROCS], + float new_positions[2 * MAX_PROCS] ); + void update_node_pos ( int node_ind, + float old_positions[2 * MAX_PROCS], + float new_positions[2 * MAX_PROCS] ); + + // MPI information + int myid, num_procs; + + // graph decomposition information + int num_nodes; // number of nodes in graph + float highest_sim; // highest sim for normalization + std::map id_catalog; // id_catalog[file id] = internal id + std::map > neighbors; // neighbors of nodes on this proc. + + // graph layout information + std::vector positions; + DensityGrid density_server; + + // original VxOrd information + int STAGE, iterations; + float temperature, attraction, damping_mult; + float min_edges, CUT_END, cut_length_end, cut_off_length, cut_rate; + bool first_add, fine_first_add, fineDensity; + + // scheduling variables + layout_schedule liquid; + layout_schedule expansion; + layout_schedule cooldown; + layout_schedule crunch; + layout_schedule simmer; + + // timing statistics + time_t start_time, stop_time; + + // online clustering information + int real_iterations; // number of iterations to hold .real input fixed + int tot_iterations; + int tot_expected_iterations; // for progress bar + bool real_fixed; +}; + +} // namespace drl diff --git a/src/rigraph/core/layout/drl/drl_graph_3d.cpp b/src/rigraph/core/layout/drl/drl_graph_3d.cpp new file mode 100644 index 0000000..7eb4a32 --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_graph_3d.cpp @@ -0,0 +1,873 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// This file contains the member definitions of the master class + +#include +#include +#include + +using namespace std; + +#include "drl_graph_3d.h" +#include "igraph_random.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "../../core/interruption.h" +#ifdef MUSE_MPI + #include +#endif + +namespace drl3d { + +graph::graph(const igraph_t *igraph, + const igraph_layout_drl_options_t *options, + const igraph_vector_t *weights) { + myid = 0; + num_procs = 1; + + STAGE = 0; + iterations = options->init_iterations; + temperature = options->init_temperature; + attraction = options->init_attraction; + damping_mult = options->init_damping_mult; + min_edges = 20; + first_add = fine_first_add = true; + fineDensity = false; + + // Brian's original Vx schedule + liquid.iterations = options->liquid_iterations; + liquid.temperature = options->liquid_temperature; + liquid.attraction = options->liquid_attraction; + liquid.damping_mult = options->liquid_damping_mult; + liquid.time_elapsed = 0; + + expansion.iterations = options->expansion_iterations; + expansion.temperature = options->expansion_temperature; + expansion.attraction = options->expansion_attraction; + expansion.damping_mult = options->expansion_damping_mult; + expansion.time_elapsed = 0; + + cooldown.iterations = options->cooldown_iterations; + cooldown.temperature = options->cooldown_temperature; + cooldown.attraction = options->cooldown_attraction; + cooldown.damping_mult = options->cooldown_damping_mult; + cooldown.time_elapsed = 0; + + crunch.iterations = options->crunch_iterations; + crunch.temperature = options->crunch_temperature; + crunch.attraction = options->crunch_attraction; + crunch.damping_mult = options->crunch_damping_mult; + crunch.time_elapsed = 0; + + simmer.iterations = options->simmer_iterations; + simmer.temperature = options->simmer_temperature; + simmer.attraction = options->simmer_attraction; + simmer.damping_mult = options->simmer_damping_mult; + simmer.time_elapsed = 0; + + // scan .int file for node info + highest_sim = 1.0; + num_nodes = igraph_vcount(igraph); + long int no_of_edges = igraph_ecount(igraph); + for (long int i = 0; i < num_nodes; i++) { + id_catalog[i] = 1; + } + map< int, int>::iterator cat_iter; + for ( cat_iter = id_catalog.begin(); + cat_iter != id_catalog.end(); cat_iter++) { + cat_iter->second = cat_iter->first; + } + + // populate node positions and ids + positions.reserve ( num_nodes ); + for ( cat_iter = id_catalog.begin(); + cat_iter != id_catalog.end(); + cat_iter++ ) { + positions.push_back ( Node( cat_iter->first ) ); + } + + // read .int file for graph info + long int node_1, node_2; + double weight; + for (long int i = 0; i < no_of_edges; i++) { + node_1 = IGRAPH_FROM(igraph, i); + node_2 = IGRAPH_TO(igraph, i); + weight = weights ? VECTOR(*weights)[i] : 1.0 ; + (neighbors[id_catalog[node_1]])[id_catalog[node_2]] = weight; + (neighbors[id_catalog[node_2]])[id_catalog[node_1]] = weight; + } + + // initialize density server + density_server.Init(); + +} + +// init_parms -- this subroutine initializes the edge_cut variables +// used in the original VxOrd starting with the edge_cut parameter. +// In our version, edge_cut = 0 means no cutting, 1 = maximum cut. +// We also set the random seed here. + +void graph::init_parms ( int rand_seed, float edge_cut, float real_parm ) { + + IGRAPH_UNUSED(rand_seed); + // first we translate edge_cut the former tcl sliding scale + //CUT_END = cut_length_end = 39000.0 * (1.0 - edge_cut) + 1000.0; + CUT_END = cut_length_end = 40000.0 * (1.0 - edge_cut); + + // cut_length_end cannot actually be 0 + if ( cut_length_end <= 1.0 ) { + cut_length_end = 1.0; + } + + float cut_length_start = 4.0 * cut_length_end; + + // now we set the parameters used by ReCompute + cut_off_length = cut_length_start; + cut_rate = ( cut_length_start - cut_length_end ) / 400.0; + + // finally set the number of iterations to leave .real coords fixed + int full_comp_iters; + full_comp_iters = liquid.iterations + expansion.iterations + + cooldown.iterations + crunch.iterations + 3; + + // adjust real parm to iterations (do not enter simmer halfway) + if ( real_parm < 0 ) { + real_iterations = (int)real_parm; + } else if ( real_parm == 1) { + real_iterations = full_comp_iters + simmer.iterations + 100; + } else { + real_iterations = (int)(real_parm * full_comp_iters); + } + + tot_iterations = 0; + if ( real_iterations > 0 ) { + real_fixed = true; + } else { + real_fixed = false; + } + + // calculate total expected iterations (for progress bar display) + tot_expected_iterations = liquid.iterations + + expansion.iterations + cooldown.iterations + + crunch.iterations + simmer.iterations; + + /* + // output edge_cutting parms (for debugging) + cout << "Processor " << myid << ": " + << "cut_length_end = CUT_END = " << cut_length_end + << ", cut_length_start = " << cut_length_start + << ", cut_rate = " << cut_rate << endl; + */ + + // set random seed + // srand ( rand_seed ); // Don't need this in igraph + +} + +void graph::init_parms(const igraph_layout_drl_options_t *options) { + double rand_seed = 0.0; + double real_in = -1.0; + init_parms(rand_seed, options->edge_cut, real_in); +} + +int graph::read_real ( const igraph_matrix_t *real_mat, + const igraph_vector_bool_t *fixed) { + long int n = igraph_matrix_nrow(real_mat); + for (long int i = 0; i < n; i++) { + positions[id_catalog[i]].x = MATRIX(*real_mat, i, 0); + positions[id_catalog[i]].y = MATRIX(*real_mat, i, 1); + positions[id_catalog[i]].z = MATRIX(*real_mat, i, 2); + positions[id_catalog[i]].fixed = fixed ? VECTOR(*fixed)[i] : false; + + if ( real_iterations > 0 ) { + density_server.Add ( positions[id_catalog[i]], fineDensity ); + } + } + + return 0; +} + +/********************************************* + * Function: ReCompute * + * Description: Compute the graph locations * + * Modified from original code by B. Wylie * + ********************************************/ + +int graph::ReCompute( ) { + + // carryover from original VxOrd + int MIN = 1; + + /* + // output parameters (for debugging) + cout << "ReCompute is using the following parameters: "<< endl; + cout << "STAGE: " << STAGE << ", iter: " << iterations << ", temp = " << temperature + << ", attract = " << attraction << ", damping_mult = " << damping_mult + << ", min_edges = " << min_edges << ", cut_off_length = " << cut_off_length + << ", fineDensity = " << fineDensity << endl; + */ + + /* igraph progress report */ + float progress = (tot_iterations * 100.0 / tot_expected_iterations); + + switch (STAGE) { + case 0: + if (iterations == 0) { + IGRAPH_PROGRESS("DrL layout (initialization stage)", progress, 0); + } else { + IGRAPH_PROGRESS("DrL layout (liquid stage)", progress, 0); + } + break; + case 1: + IGRAPH_PROGRESS("DrL layout (expansion stage)", progress, 0); break; + case 2: + IGRAPH_PROGRESS("DrL layout (cooldown and cluster phase)", progress, 0); break; + case 3: + IGRAPH_PROGRESS("DrL layout (crunch phase)", progress, 0); break; + case 5: + IGRAPH_PROGRESS("DrL layout (simmer phase)", progress, 0); break; + case 6: + IGRAPH_PROGRESS("DrL layout (final phase)", 100.0, 0); break; + default: + IGRAPH_PROGRESS("DrL layout (unknown phase)", 0.0, 0); break; + } + + /* Compute Energies for individual nodes */ + update_nodes (); + + // check to see if we need to free fixed nodes + tot_iterations++; + if ( tot_iterations >= real_iterations ) { + real_fixed = false; + } + + + // **************************************** + // AUTOMATIC CONTROL SECTION + // **************************************** + + // STAGE 0: LIQUID + if (STAGE == 0) { + + if ( iterations == 0 ) { + start_time = time( NULL ); +// if ( myid == 0 ) +// cout << "Entering liquid stage ..."; + } + + if (iterations < liquid.iterations) { + temperature = liquid.temperature; + attraction = liquid.attraction; + damping_mult = liquid.damping_mult; + iterations++; +// if ( myid == 0 ) +// cout << "." << flush; + + } else { + + stop_time = time( NULL ); + liquid.time_elapsed = liquid.time_elapsed + (stop_time - start_time); + temperature = expansion.temperature; + attraction = expansion.attraction; + damping_mult = expansion.damping_mult; + iterations = 0; + + // go to next stage + STAGE = 1; + start_time = time( NULL ); + +// if ( myid == 0 ) +// cout << "Entering expansion stage ..."; + } + } + + // STAGE 1: EXPANSION + if (STAGE == 1) { + + if (iterations < expansion.iterations) { + + // Play with vars + if (attraction > 1) { + attraction -= .05f; + } + if (min_edges > 12) { + min_edges -= .05f; + } + cut_off_length -= cut_rate; + if (damping_mult > .1) { + damping_mult -= .005f; + } + iterations++; +// if ( myid == 0 ) cout << "." << flush; + + } else { + + stop_time = time( NULL ); + expansion.time_elapsed = expansion.time_elapsed + (stop_time - start_time); + min_edges = 12; + damping_mult = cooldown.damping_mult; + + STAGE = 2; + attraction = cooldown.attraction; + temperature = cooldown.temperature; + iterations = 0; + start_time = time( NULL ); + +// if ( myid == 0 ) +// cout << "Entering cool-down stage ..."; + } + } + + // STAGE 2: Cool down and cluster + else if (STAGE == 2) { + + if (iterations < cooldown.iterations) { + + // Reduce temperature + if (temperature > 50) { + temperature -= 10; + } + + // Reduce cut length + if (cut_off_length > cut_length_end) { + cut_off_length -= cut_rate * 2; + } + if (min_edges > MIN) { + min_edges -= .2f; + } + //min_edges = 99; + iterations++; +// if ( myid == 0 ) +// cout << "." << flush; + + } else { + + stop_time = time( NULL ); + cooldown.time_elapsed = cooldown.time_elapsed + (stop_time - start_time); + cut_off_length = cut_length_end; + temperature = crunch.temperature; + damping_mult = crunch.damping_mult; + min_edges = MIN; + //min_edges = 99; // In other words: no more cutting + + STAGE = 3; + iterations = 0; + attraction = crunch.attraction; + start_time = time( NULL ); + +// if ( myid == 0 ) +// cout << "Entering crunch stage ..."; + } + } + + // STAGE 3: Crunch + else if (STAGE == 3) { + + if (iterations < crunch.iterations) { + iterations++; +// if ( myid == 0 ) cout << "." << flush; + } else { + + stop_time = time( NULL ); + crunch.time_elapsed = crunch.time_elapsed + (stop_time - start_time); + iterations = 0; + temperature = simmer.temperature; + attraction = simmer.attraction; + damping_mult = simmer.damping_mult; + min_edges = 99; + fineDensity = true; + + STAGE = 5; + start_time = time( NULL ); + +// if ( myid == 0 ) +// cout << "Entering simmer stage ..."; + } + } + + // STAGE 5: Simmer + else if ( STAGE == 5 ) { + + if (iterations < simmer.iterations) { + if (temperature > 50) { + temperature -= 2; + } + iterations++; +// if ( myid == 0 ) cout << "." << flush; + } else { + stop_time = time( NULL ); + simmer.time_elapsed = simmer.time_elapsed + (stop_time - start_time); + + STAGE = 6; + +// if ( myid == 0 ) +// cout << "Layout calculation completed in " << +// ( liquid.time_elapsed + expansion.time_elapsed + +// cooldown.time_elapsed + crunch.time_elapsed + +// simmer.time_elapsed ) +// << " seconds (not including I/O)." +// << endl; + } + } + + // STAGE 6: All Done! + else if ( STAGE == 6) { + + /* + // output parameters (for debugging) + cout << "ReCompute is using the following parameters: "<< endl; + cout << "STAGE: " << STAGE << ", iter: " << iterations << ", temp = " << temperature + << ", attract = " << attraction << ", damping_mult = " << damping_mult + << ", min_edges = " << min_edges << ", cut_off_length = " << cut_off_length + << ", fineDensity = " << fineDensity << endl; + */ + + return 0; + } + + // **************************************** + // END AUTOMATIC CONTROL SECTION + // **************************************** + + // Still need more recomputation + return 1; + +} + +// update_nodes -- this function will complete the primary node update +// loop in layout's recompute routine. It follows exactly the same +// sequence to ensure similarity of parallel layout to the standard layout + +void graph::update_nodes ( ) { + + vector node_indices; // node list of nodes currently being updated + float old_positions[2 * MAX_PROCS]; // positions before update + float new_positions[2 * MAX_PROCS]; // positions after update + + bool all_fixed; // check if all nodes are fixed + + // initial node list consists of 0,1,...,num_procs + for ( int i = 0; i < num_procs; i++ ) { + node_indices.push_back( i ); + } + + // next we calculate the number of nodes there would be if the + // num_nodes by num_procs schedule grid were perfectly square + int square_num_nodes = (int)(num_procs + num_procs * floor ((float)(num_nodes - 1) / (float)num_procs )); + + for ( int i = myid; i < square_num_nodes; i += num_procs ) { + + // get old positions + get_positions ( node_indices, old_positions ); + + // default new position is old position + get_positions ( node_indices, new_positions ); + + if ( i < num_nodes ) { + + // advance random sequence according to myid + for ( int j = 0; j < 2 * myid; j++ ) { + RNG_UNIF01(); + } + // rand(); + + // calculate node energy possibilities + if ( !(positions[i].fixed && real_fixed) ) { + update_node_pos ( i, old_positions, new_positions ); + } + + // advance random sequence for next iteration + for ( unsigned int j = 2 * myid; j < 2 * (node_indices.size() - 1); j++ ) { + RNG_UNIF01(); + } + // rand(); + + } else { + // advance random sequence according to use by + // the other processors + for ( unsigned int j = 0; j < 2 * (node_indices.size()); j++ ) { + RNG_UNIF01(); + } + //rand(); + } + + // check if anything was actually updated (e.g. everything was fixed) + all_fixed = true; + for ( unsigned int j = 0; j < node_indices.size (); j++ ) + if ( !(positions [ node_indices[j] ].fixed && real_fixed) ) { + all_fixed = false; + } + + // update positions across processors (if not all fixed) + if ( !all_fixed ) { +#ifdef MUSE_MPI + MPI_Allgather ( &new_positions[2 * myid], 2, MPI_FLOAT, + new_positions, 2, MPI_FLOAT, MPI_COMM_WORLD ); +#endif + + // update positions (old to new) + update_density ( node_indices, old_positions, new_positions ); + } + + /* + if ( myid == 0 ) + { + // output node list (for debugging) + for ( unsigned int j = 0; j < node_indices.size(); j++ ) + cout << node_indices[j] << " "; + cout << endl; + } + */ + + // compute node list for next update + for ( unsigned int j = 0; j < node_indices.size(); j++ ) { + node_indices [j] += num_procs; + } + + while ( !node_indices.empty() && node_indices.back() >= num_nodes ) { + node_indices.pop_back ( ); + } + + } + + // update first_add and fine_first_add + first_add = false; + if ( fineDensity ) { + fine_first_add = false; + } + +} + +// The get_positions function takes the node_indices list +// and returns the corresponding positions in an array. + +void graph::get_positions ( vector &node_indices, + float return_positions[3 * MAX_PROCS] ) { + + // fill positions + for (unsigned int i = 0; i < node_indices.size(); i++) { + return_positions[3 * i] = positions[ node_indices[i] ].x; + return_positions[3 * i + 1] = positions[ node_indices[i] ].y; + return_positions[3 * i + 2] = positions[ node_indices[i] ].z; + } + +} + +// update_node_pos -- this subroutine does the actual work of computing +// the new position of a given node. num_act_proc gives the number +// of active processes at this level for use by the random number +// generators. + +void graph::update_node_pos ( int node_ind, + float old_positions[3 * MAX_PROCS], + float new_positions[3 * MAX_PROCS] ) { + + float energies[2]; // node energies for possible positions + float updated_pos[2][3]; // possible positions + float pos_x, pos_y, pos_z; + + // old VxOrd parameter + float jump_length = .010 * temperature; + + // subtract old node + density_server.Subtract ( positions[node_ind], first_add, fine_first_add, fineDensity ); + + // compute node energy for old solution + energies[0] = Compute_Node_Energy ( node_ind ); + + // move node to centroid position + Solve_Analytic ( node_ind, pos_x, pos_y, pos_z ); + positions[node_ind].x = updated_pos[0][0] = pos_x; + positions[node_ind].y = updated_pos[0][1] = pos_y; + positions[node_ind].z = updated_pos[0][2] = pos_z; + + /* + // ouput random numbers (for debugging) + int rand_0, rand_1; + rand_0 = rand(); + rand_1 = rand(); + cout << myid << ": " << rand_0 << ", " << rand_1 << endl; + */ + + // Do random method (RAND_MAX is C++ maximum random number) + updated_pos[1][0] = updated_pos[0][0] + (.5 - RNG_UNIF01()) * jump_length; + updated_pos[1][1] = updated_pos[0][1] + (.5 - RNG_UNIF01()) * jump_length; + updated_pos[1][2] = updated_pos[0][2] + (.5 - RNG_UNIF01()) * jump_length; + + // compute node energy for random position + positions[node_ind].x = updated_pos[1][0]; + positions[node_ind].y = updated_pos[1][1]; + positions[node_ind].z = updated_pos[1][2]; + energies[1] = Compute_Node_Energy ( node_ind ); + + /* + // output update possiblities (debugging): + cout << node_ind << ": (" << updated_pos[0][0] << "," << updated_pos[0][1] + << "), " << energies[0] << "; (" << updated_pos[1][0] << "," + << updated_pos[1][1] << "), " << energies[1] << endl; + */ + + // add back old position + positions[node_ind].x = old_positions[3 * myid]; + positions[node_ind].y = old_positions[3 * myid + 1]; + positions[node_ind].z = old_positions[3 * myid + 2]; + if ( !fineDensity && !first_add ) { + density_server.Add ( positions[node_ind], fineDensity ); + } else if ( !fine_first_add ) { + density_server.Add ( positions[node_ind], fineDensity ); + } + + // choose updated node position with lowest energy + if ( energies[0] < energies[1] ) { + new_positions[3 * myid] = updated_pos[0][0]; + new_positions[3 * myid + 1] = updated_pos[0][1]; + new_positions[3 * myid + 2] = updated_pos[0][2]; + positions[node_ind].energy = energies[0]; + } else { + new_positions[3 * myid] = updated_pos[1][0]; + new_positions[3 * myid + 1] = updated_pos[1][1]; + new_positions[3 * myid + 2] = updated_pos[1][2]; + positions[node_ind].energy = energies[1]; + } + +} + +// update_density takes a sequence of node_indices and their positions and +// updates the positions by subtracting the old positions and adding the +// new positions to the density grid. + +void graph::update_density ( vector &node_indices, + float old_positions[3 * MAX_PROCS], + float new_positions[3 * MAX_PROCS] ) { + + // go through each node and subtract old position from + // density grid before adding new position + for ( unsigned int i = 0; i < node_indices.size(); i++ ) { + positions[node_indices[i]].x = old_positions[3 * i]; + positions[node_indices[i]].y = old_positions[3 * i + 1]; + positions[node_indices[i]].z = old_positions[3 * i + 2]; + density_server.Subtract ( positions[node_indices[i]], + first_add, fine_first_add, fineDensity ); + + positions[node_indices[i]].x = new_positions[3 * i]; + positions[node_indices[i]].y = new_positions[3 * i + 1]; + positions[node_indices[i]].z = new_positions[3 * i + 2]; + density_server.Add ( positions[node_indices[i]], fineDensity ); + } + +} + +/******************************************** +* Function: Compute_Node_Energy * +* Description: Compute the node energy * +* This code has been modified from the * +* original code by B. Wylie. * +*********************************************/ + +float graph::Compute_Node_Energy( int node_ind ) { + + /* Want to expand 4th power range of attraction */ + float attraction_factor = attraction * attraction * + attraction * attraction * 2e-2; + + map ::iterator EI; + float x_dis, y_dis, z_dis; + float energy_distance, weight; + float node_energy = 0; + + // Add up all connection energies + for (EI = neighbors[node_ind].begin(); EI != neighbors[node_ind].end(); ++EI) { + + // Get edge weight + weight = EI->second; + + // Compute x,y distance + x_dis = positions[ node_ind ].x - positions[ EI->first ].x; + y_dis = positions[ node_ind ].y - positions[ EI->first ].y; + z_dis = positions[ node_ind ].z - positions[ EI->first ].z; + + // Energy Distance + energy_distance = x_dis * x_dis + y_dis * y_dis + z_dis * z_dis; + if (STAGE < 2) { + energy_distance *= energy_distance; + } + + // In the liquid phase we want to discourage long link distances + if (STAGE == 0) { + energy_distance *= energy_distance; + } + + node_energy += weight * attraction_factor * energy_distance; + } + + // output effect of density (debugging) + //cout << "[before: " << node_energy; + + // add density + node_energy += density_server.GetDensity ( positions[ node_ind ].x, positions[ node_ind ].y, + positions[ node_ind ].z, fineDensity ); + + // after calling density server (debugging) + //cout << ", after: " << node_energy << "]" << endl; + + // return computated energy + return node_energy; +} + + +/********************************************* +* Function: Solve_Analytic * +* Description: Compute the node position * +* This is a modified version of the function * +* originally written by B. Wylie * +*********************************************/ + +void graph::Solve_Analytic( int node_ind, float &pos_x, float &pos_y, + float &pos_z) { + + map ::iterator EI; + float total_weight = 0; + float x_dis, y_dis, z_dis, x_cen = 0, y_cen = 0, z_cen = 0; + float x = 0, y = 0, z = 0, dis; + float damping, weight; + + // Sum up all connections + for (EI = neighbors[node_ind].begin(); EI != neighbors[node_ind].end(); ++EI) { + weight = EI->second; + total_weight += weight; + x += weight * positions[ EI->first ].x; + y += weight * positions[ EI->first ].y; + z += weight * positions[ EI->first ].z; + } + + // Now set node position + if (total_weight > 0) { + + // Compute centriod + x_cen = x / total_weight; + y_cen = y / total_weight; + z_cen = z / total_weight; + damping = 1.0 - damping_mult; + pos_x = damping * positions[ node_ind ].x + (1.0 - damping) * x_cen; + pos_y = damping * positions[ node_ind ].y + (1.0 - damping) * y_cen; + pos_z = damping * positions[ node_ind ].z + (1.0 - damping) * z_cen; + } + + // No cut edge flag (?) + if (min_edges == 99) { + return; + } + + // Don't cut at end of scale + if ( CUT_END >= 39500 ) { + return; + } + + float num_connections = (float)sqrt((float)neighbors[node_ind].size()); + float maxLength = 0; + + map::iterator maxIndex; + + // Go through nodes edges... cutting if necessary + for (EI = maxIndex = neighbors[node_ind].begin(); + EI != neighbors[node_ind].end(); ++EI) { + + // Check for at least min edges + if (neighbors[node_ind].size() < min_edges) { + continue; + } + + x_dis = x_cen - positions[ EI->first ].x; + y_dis = y_cen - positions[ EI->first ].y; + z_dis = z_cen - positions[ EI->first ].z; + dis = x_dis * x_dis + y_dis * y_dis + z_dis * z_dis; + dis *= num_connections; + + // Store maximum edge + if (dis > maxLength) { + maxLength = dis; + maxIndex = EI; + } + } + + // If max length greater than cut_length then cut + if (maxLength > cut_off_length) { + neighbors[ node_ind ].erase( maxIndex ); + } + +} + + +// get_tot_energy adds up the energy for each node to give an estimate of the +// quality of the minimization. + +float graph::get_tot_energy ( ) { + + float my_tot_energy, tot_energy; + my_tot_energy = 0; + for ( int i = myid; i < num_nodes; i += num_procs ) { + my_tot_energy += positions[i].energy; + } + + //vector::iterator i; + //for ( i = positions.begin(); i != positions.end(); i++ ) + // tot_energy += i->energy; + +#ifdef MUSE_MPI + MPI_Reduce ( &my_tot_energy, &tot_energy, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD ); +#else + tot_energy = my_tot_energy; +#endif + + return tot_energy; + +} + + +int graph::draw_graph(igraph_matrix_t *res) { + int count_iter = 0; + while (ReCompute()) { + IGRAPH_ALLOW_INTERRUPTION(); + count_iter++; + } + long int n = positions.size(); + IGRAPH_CHECK(igraph_matrix_resize(res, n, 3)); + for (long int i = 0; i < n; i++) { + MATRIX(*res, i, 0) = positions[i].x; + MATRIX(*res, i, 1) = positions[i].y; + MATRIX(*res, i, 2) = positions[i].z; + } + return 0; +} + +} // namespace drl3d diff --git a/src/rigraph/core/layout/drl/drl_graph_3d.h b/src/rigraph/core/layout/drl/drl_graph_3d.h new file mode 100644 index 0000000..c61612c --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_graph_3d.h @@ -0,0 +1,124 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// The graph class contains the methods necessary to draw the +// graph. It calls on the density server class to obtain +// position and density information + +#include "DensityGrid_3d.h" +#include "igraph_layout.h" + +#include +#include +#include + +namespace drl3d { + +// layout schedule information +struct layout_schedule { + int iterations; + float temperature; + float attraction; + float damping_mult; + time_t time_elapsed; +}; + +class graph { + +public: + + // Methods + void init_parms ( int rand_seed, float edge_cut, float real_parm ); + void init_parms ( const igraph_layout_drl_options_t *options ); + int read_real ( const igraph_matrix_t *real_mat, + const igraph_vector_bool_t *fixed); + int draw_graph (igraph_matrix_t *res); + float get_tot_energy ( ); + + // Con/Decon + graph( const igraph_t *igraph, + const igraph_layout_drl_options_t *options, + const igraph_vector_t *weights); + ~graph( ) { } + +private: + + // Methods + int ReCompute ( ); + void update_nodes ( ); + float Compute_Node_Energy ( int node_ind ); + void Solve_Analytic ( int node_ind, float &pos_x, float &pos_y, float &pos_z ); + void get_positions ( std::vector &node_indices, float return_positions[3 * MAX_PROCS] ); + void update_density ( std::vector &node_indices, + float old_positions[3 * MAX_PROCS], + float new_positions[3 * MAX_PROCS] ); + void update_node_pos ( int node_ind, + float old_positions[3 * MAX_PROCS], + float new_positions[3 * MAX_PROCS] ); + + // MPI information + int myid, num_procs; + + // graph decomposition information + int num_nodes; // number of nodes in graph + float highest_sim; // highest sim for normalization + std::map id_catalog; // id_catalog[file id] = internal id + std::map > neighbors; // neighbors of nodes on this proc. + + // graph layout information + std::vector positions; + DensityGrid density_server; + + // original VxOrd information + int STAGE, iterations; + float temperature, attraction, damping_mult; + float min_edges, CUT_END, cut_length_end, cut_off_length, cut_rate; + bool first_add, fine_first_add, fineDensity; + + // scheduling variables + layout_schedule liquid; + layout_schedule expansion; + layout_schedule cooldown; + layout_schedule crunch; + layout_schedule simmer; + + // timing statistics + time_t start_time, stop_time; + + // online clustering information + int real_iterations; // number of iterations to hold .real input fixed + int tot_iterations; + int tot_expected_iterations; // for progress bar + bool real_fixed; +}; + +} // namespace drl3d diff --git a/src/rigraph/core/layout/drl/drl_layout.cpp b/src/rigraph/core/layout/drl/drl_layout.cpp new file mode 100644 index 0000000..92f3b0f --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_layout.cpp @@ -0,0 +1,495 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// Layout +// +// This program implements a parallel force directed graph drawing +// algorithm. The algorithm used is based upon a random decomposition +// of the graph and simulated shared memory of node position and density. +// In this version, the simulated shared memory is spread among all processors +// +// The structure of the inputs and outputs of this code will be displayed +// if the program is called without parameters, or if an erroneous +// parameter is passed to the program. +// +// S. Martin +// 5/6/2005 + +// C++ library routines +#include +#include + +using namespace std; + +// layout routines and constants +#include "drl_layout.h" +#include "drl_parse.h" +#include "drl_graph.h" + +// MPI +#ifdef MUSE_MPI + #include +#endif + +using namespace drl; +#include "igraph_layout.h" +#include "igraph_random.h" +#include "igraph_interface.h" + +#include "../../core/exceptions.h" + +namespace drl { + +// int main(int argc, char **argv) { + + +// // initialize MPI +// int myid, num_procs; + +// #ifdef MUSE_MPI +// MPI_Init ( &argc, &argv ); +// MPI_Comm_size ( MPI_COMM_WORLD, &num_procs ); +// MPI_Comm_rank ( MPI_COMM_WORLD, &myid ); +// #else +// myid = 0; +// num_procs = 1; +// #endif + +// // parameters that must be broadcast to all processors +// int rand_seed; +// float edge_cut; + +// char int_file[MAX_FILE_NAME]; +// char coord_file[MAX_FILE_NAME]; +// char real_file[MAX_FILE_NAME]; +// char parms_file[MAX_FILE_NAME]; + +// int int_out = 0; +// int edges_out = 0; +// int parms_in = 0; +// float real_in = -1.0; + +// // user interaction is handled by processor 0 +// if ( myid == 0 ) +// { +// if ( num_procs > MAX_PROCS ) +// { +// cout << "Error: Maximum number of processors is " << MAX_PROCS << "." << endl; +// cout << "Adjust compile time parameter." << endl; +// #ifdef MUSE_MPI +// MPI_Abort ( MPI_COMM_WORLD, 1 ); +// #else +// exit (1); +// #endif +// } + +// // get user input +// parse command_line ( argc, argv ); +// rand_seed = command_line.rand_seed; +// edge_cut = command_line.edge_cut; +// int_out = command_line.int_out; +// edges_out = command_line.edges_out; +// parms_in = command_line.parms_in; +// real_in = command_line.real_in; +// strcpy ( coord_file, command_line.coord_file.c_str() ); +// strcpy ( int_file, command_line.sim_file.c_str() ); +// strcpy ( real_file, command_line.real_file.c_str() ); +// strcpy ( parms_file, command_line.parms_file.c_str() ); + +// } + +// // now we initialize all processors by reading .int file +// #ifdef MUSE_MPI +// MPI_Bcast ( &int_file, MAX_FILE_NAME, MPI_CHAR, 0, MPI_COMM_WORLD ); +// #endif +// graph neighbors ( myid, num_procs, int_file ); + +// // check for user supplied parameters +// #ifdef MUSE_MPI +// MPI_Bcast ( &parms_in, 1, MPI_INT, 0, MPI_COMM_WORLD ); +// #endif +// if ( parms_in ) +// { +// #ifdef MUSE_MPI +// MPI_Bcast ( &parms_file, MAX_FILE_NAME, MPI_CHAR, 0, MPI_COMM_WORLD ); +// #endif +// neighbors.read_parms ( parms_file ); +// } + +// // set random seed, edge cutting, and real iterations parameters +// #ifdef MUSE_MPI +// MPI_Bcast ( &rand_seed, 1, MPI_INT, 0, MPI_COMM_WORLD ); +// MPI_Bcast ( &edge_cut, 1, MPI_FLOAT, 0, MPI_COMM_WORLD ); +// MPI_Bcast ( &real_in, 1, MPI_INT, 0, MPI_COMM_WORLD ); +// #endif +// neighbors.init_parms ( rand_seed, edge_cut, real_in ); + +// // check for .real file with existing coordinates +// if ( real_in >= 0 ) +// { +// #ifdef MUSE_MPI +// MPI_Bcast ( &real_file, MAX_FILE_NAME, MPI_CHAR, 0, MPI_COMM_WORLD ); +// #endif +// neighbors.read_real ( real_file ); +// } + +// neighbors.draw_graph ( int_out, coord_file ); + +// // do we have to write out the edges? +// #ifdef MUSE_MPI +// MPI_Bcast ( &edges_out, 1, MPI_INT, 0, MPI_COMM_WORLD ); +// #endif +// if ( edges_out ) +// { +// #ifdef MUSE_MPI +// MPI_Bcast ( &coord_file, MAX_FILE_NAME, MPI_CHAR, 0, MPI_COMM_WORLD ); +// #endif +// for ( int i = 0; i < num_procs; i++ ) +// { +// if ( myid == i ) +// neighbors.write_sim ( coord_file ); +// #ifdef MUSE_MPI +// MPI_Barrier ( MPI_COMM_WORLD ); +// #endif +// } +// } + +// // finally we output file and quit +// float tot_energy; +// tot_energy = neighbors.get_tot_energy (); +// if ( myid == 0 ) +// { +// neighbors.write_coord ( coord_file ); +// cout << "Total Energy: " << tot_energy << "." << endl +// << "Program terminated successfully." << endl; +// } + +// // MPI finalize +// #ifdef MUSE_MPI +// MPI_Finalize (); +// #endif + +// return 0; +// } + +} // namespace drl + +/** + * \section about_drl + * + * + * DrL is a sophisticated layout generator developed and implemented by + * Shawn Martin et al. As of October 2012 the original DrL homepage is + * unfortunately not available. You can read more about this algorithm + * in the following technical report: Martin, S., Brown, W.M., + * Klavans, R., Boyack, K.W., DrL: Distributed Recursive (Graph) + * Layout. SAND Reports, 2008. 2936: p. 1-10. + * + * + * + * Only a subset of the complete DrL functionality is + * included in igraph, parallel runs and recursive, multi-level + * layouting is not supported. + * + * + * + * The parameters of the layout are stored in an \ref + * igraph_layout_drl_options_t structure, this can be initialized by + * calling the function \ref igraph_layout_drl_options_init(). + * The fields of this structure can then be adjusted by hand if needed. + * The layout is calculated by an \ref igraph_layout_drl() call. + * + */ + +/** + * \function igraph_layout_drl_options_init + * Initialize parameters for the DrL layout generator + * + * This function can be used to initialize the struct holding the + * parameters for the DrL layout generator. There are a number of + * predefined templates available, it is a good idea to start from one + * of these by modifying some parameters. + * \param options The struct to initialize. + * \param templ The template to use. Currently the following templates + * are supplied: \c IGRAPH_LAYOUT_DRL_DEFAULT, \c + * IGRAPH_LAYOUT_DRL_COARSEN, \c IGRAPH_LAYOUT_DRL_COARSEST, + * \c IGRAPH_LAYOUT_DRL_REFINE and \c IGRAPH_LAYOUT_DRL_FINAL. + * \return Error code. + * + * Time complexity: O(1). + */ + +int igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, + igraph_layout_drl_default_t templ) { + + options->edge_cut = 32.0 / 40.0; + + switch (templ) { + case IGRAPH_LAYOUT_DRL_DEFAULT: + options->init_iterations = 0; + options->init_temperature = 2000; + options->init_attraction = 10; + options->init_damping_mult = 1.0; + + options->liquid_iterations = 200; + options->liquid_temperature = 2000; + options->liquid_attraction = 10; + options->liquid_damping_mult = 1.0; + + options->expansion_iterations = 200; + options->expansion_temperature = 2000; + options->expansion_attraction = 2; + options->expansion_damping_mult = 1.0; + + options->cooldown_iterations = 200; + options->cooldown_temperature = 2000; + options->cooldown_attraction = 1; + options->cooldown_damping_mult = .1; + + options->crunch_iterations = 50; + options->crunch_temperature = 250; + options->crunch_attraction = 1; + options->crunch_damping_mult = 0.25; + + options->simmer_iterations = 100; + options->simmer_temperature = 250; + options->simmer_attraction = .5; + options->simmer_damping_mult = 0; + + break; + case IGRAPH_LAYOUT_DRL_COARSEN: + options->init_iterations = 0; + options->init_temperature = 2000; + options->init_attraction = 10; + options->init_damping_mult = 1.0; + + options->liquid_iterations = 200; + options->liquid_temperature = 2000; + options->liquid_attraction = 2; + options->liquid_damping_mult = 1.0; + + options->expansion_iterations = 200; + options->expansion_temperature = 2000; + options->expansion_attraction = 10; + options->expansion_damping_mult = 1.0; + + options->cooldown_iterations = 200; + options->cooldown_temperature = 2000; + options->cooldown_attraction = 1; + options->cooldown_damping_mult = .1; + + options->crunch_iterations = 50; + options->crunch_temperature = 250; + options->crunch_attraction = 1; + options->crunch_damping_mult = 0.25; + + options->simmer_iterations = 100; + options->simmer_temperature = 250; + options->simmer_attraction = .5; + options->simmer_damping_mult = 0; + + break; + case IGRAPH_LAYOUT_DRL_COARSEST: + options->init_iterations = 0; + options->init_temperature = 2000; + options->init_attraction = 10; + options->init_damping_mult = 1.0; + + options->liquid_iterations = 200; + options->liquid_temperature = 2000; + options->liquid_attraction = 2; + options->liquid_damping_mult = 1.0; + + options->expansion_iterations = 200; + options->expansion_temperature = 2000; + options->expansion_attraction = 10; + options->expansion_damping_mult = 1.0; + + options->cooldown_iterations = 200; + options->cooldown_temperature = 2000; + options->cooldown_attraction = 1; + options->cooldown_damping_mult = .1; + + options->crunch_iterations = 200; + options->crunch_temperature = 250; + options->crunch_attraction = 1; + options->crunch_damping_mult = 0.25; + + options->simmer_iterations = 100; + options->simmer_temperature = 250; + options->simmer_attraction = .5; + options->simmer_damping_mult = 0; + + break; + case IGRAPH_LAYOUT_DRL_REFINE: + options->init_iterations = 0; + options->init_temperature = 50; + options->init_attraction = .5; + options->init_damping_mult = 0; + + options->liquid_iterations = 0; + options->liquid_temperature = 2000; + options->liquid_attraction = 2; + options->liquid_damping_mult = 1.0; + + options->expansion_iterations = 50; + options->expansion_temperature = 500; + options->expansion_attraction = .1; + options->expansion_damping_mult = .25; + + options->cooldown_iterations = 50; + options->cooldown_temperature = 200; + options->cooldown_attraction = 1; + options->cooldown_damping_mult = .1; + + options->crunch_iterations = 50; + options->crunch_temperature = 250; + options->crunch_attraction = 1; + options->crunch_damping_mult = 0.25; + + options->simmer_iterations = 0; + options->simmer_temperature = 250; + options->simmer_attraction = .5; + options->simmer_damping_mult = 0; + + break; + case IGRAPH_LAYOUT_DRL_FINAL: + options->init_iterations = 0; + options->init_temperature = 50; + options->init_attraction = .5; + options->init_damping_mult = 0; + + options->liquid_iterations = 0; + options->liquid_temperature = 2000; + options->liquid_attraction = 2; + options->liquid_damping_mult = 1.0; + + options->expansion_iterations = 50; + options->expansion_temperature = 50; + options->expansion_attraction = .1; + options->expansion_damping_mult = .25; + + options->cooldown_iterations = 50; + options->cooldown_temperature = 200; + options->cooldown_attraction = 1; + options->cooldown_damping_mult = .1; + + options->crunch_iterations = 50; + options->crunch_temperature = 250; + options->crunch_attraction = 1; + options->crunch_damping_mult = 0.25; + + options->simmer_iterations = 25; + options->simmer_temperature = 250; + options->simmer_attraction = .5; + options->simmer_damping_mult = 0; + + break; + default: + IGRAPH_ERROR("Unknown DrL template", IGRAPH_EINVAL); + break; + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_layout_drl + * The DrL layout generator + * + * This function implements the force-directed DrL layout generator. + * Please see more in the following technical report: Martin, S., + * Brown, W.M., Klavans, R., Boyack, K.W., DrL: Distributed Recursive + * (Graph) Layout. SAND Reports, 2008. 2936: p. 1-10. + * \param graph The input graph. + * \param use_seed Logical scalar, if true, then the coordinates + * supplied in the \p res argument are used as starting points. + * \param res Pointer to a matrix, the result layout is stored + * here. It will be resized as needed. + * \param options The parameters to pass to the layout generator. + * \param weights Edge weights, pointer to a vector. If this is a null + * pointer then every edge will have the same weight. + * \param fixed Pointer to a logical vector, or a null pointer. Originally, + * this argument was used in the DrL algorithm to keep the nodes marked + * with this argument as fixed; fixed nodes would then keep their + * positions in the initial stages of the algorithm. However, due to how + * the DrL code imported into igraph is organized, it seems that the + * argument does not do anything and we are not sure whether this is a + * bug or a feature in DrL. We are leaving the argument here in order not + * to break the API, but note that at the present stage it has no effect. + * \return Error code. + * + * Time complexity: ???. + */ + +int igraph_layout_drl(const igraph_t *graph, igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_layout_drl_options_t *options, + const igraph_vector_t *weights, + const igraph_vector_bool_t *fixed) { + const char msg[] = "Damping multipliers cannot be negative, got %f."; + + if (options->init_damping_mult < 0) { + IGRAPH_ERRORF(msg, IGRAPH_EINVAL, options->init_damping_mult); + } + if (options->liquid_damping_mult < 0) { + IGRAPH_ERRORF(msg, IGRAPH_EINVAL, options->liquid_damping_mult); + } + if (options->expansion_damping_mult < 0) { + IGRAPH_ERRORF(msg, IGRAPH_EINVAL, options->expansion_damping_mult); + } + if (options->cooldown_damping_mult < 0) { + IGRAPH_ERRORF(msg, IGRAPH_EINVAL, options->cooldown_damping_mult); + } + if (options->crunch_damping_mult < 0) { + IGRAPH_ERRORF(msg, IGRAPH_EINVAL, options->crunch_damping_mult); + } + if (options->simmer_damping_mult < 0) { + IGRAPH_ERRORF(msg, IGRAPH_EINVAL, options->simmer_damping_mult); + } + + IGRAPH_HANDLE_EXCEPTIONS( + RNG_BEGIN(); + + drl::graph neighbors(graph, options, weights); + neighbors.init_parms(options); + if (use_seed) { + IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 2)); + neighbors.read_real(res, fixed); + } + neighbors.draw_graph(res); + + RNG_END(); + ); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/layout/drl/drl_layout.h b/src/rigraph/core/layout/drl/drl_layout.h new file mode 100644 index 0000000..8d3cd29 --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_layout.h @@ -0,0 +1,65 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// This file contains compile time parameters which affect the entire +// DrL program. + +#define DRL_VERSION "3.2 5/5/2006" + +// compile time parameters for MPI message passing +#define MAX_PROCS 256 // maximum number of processors +#define MAX_FILE_NAME 250 // max length of filename +#define MAX_INT_LENGTH 4 // max length of integer suffix of intermediate .coord file + +// Compile time adjustable parameters for the Density grid + +#define GRID_SIZE 1000 // size of Density grid +#define VIEW_SIZE 4000.0 // actual physical size of layout plane +// these values use more memory but have +// little effect on performance or layout + +#define RADIUS 10 // radius for density fall-off: +// larger values tends to slow down +// the program and clump the data + +#define HALF_VIEW 2000 // 1/2 of VIEW_SIZE +#define VIEW_TO_GRID .25 // ratio of GRID_SIZE to VIEW_SIZE + +/* +// original values for VxOrd +#define GRID_SIZE 400 // size of VxOrd Density grid +#define VIEW_SIZE 1600.0 // actual physical size of VxOrd plane +#define RADIUS 10 // radius for density fall-off + +#define HALF_VIEW 800 // 1/2 of VIEW_SIZE +#define VIEW_TO_GRID .25 // ratio of GRID_SIZE to VIEW_SIZE +*/ diff --git a/src/rigraph/core/layout/drl/drl_layout_3d.cpp b/src/rigraph/core/layout/drl/drl_layout_3d.cpp new file mode 100644 index 0000000..0df4fe7 --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_layout_3d.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// Layout +// +// This program implements a parallel force directed graph drawing +// algorithm. The algorithm used is based upon a random decomposition +// of the graph and simulated shared memory of node position and density. +// In this version, the simulated shared memory is spread among all processors +// +// The structure of the inputs and outputs of this code will be displayed +// if the program is called without parameters, or if an erroneous +// parameter is passed to the program. +// +// S. Martin +// 5/6/2005 + +// C++ library routines +#include +#include + +using namespace std; + +// layout routines and constants +#include "drl_layout_3d.h" +#include "drl_parse.h" +#include "drl_graph_3d.h" + +// MPI +#ifdef MUSE_MPI + #include +#endif + +using namespace drl3d; +#include "igraph_layout.h" +#include "igraph_random.h" +#include "igraph_interface.h" + +#include "../../core/exceptions.h" + +/** + * \function igraph_layout_drl_3d + * The DrL layout generator, 3d version. + * + * This function implements the force-directed DrL layout generator. + * Please see more in the technical report: Martin, S., Brown, W.M., + * Klavans, R., Boyack, K.W., DrL: Distributed Recursive (Graph) + * Layout. SAND Reports, 2008. 2936: p. 1-10. + * + * This function uses a modified DrL generator that does + * the layout in three dimensions. + * \param graph The input graph. + * \param use_seed Logical scalar, if true, then the coordinates + * supplied in the \p res argument are used as starting points. + * \param res Pointer to a matrix, the result layout is stored + * here. It will be resized as needed. + * \param options The parameters to pass to the layout generator. + * \param weights Edge weights, pointer to a vector. If this is a null + * pointer then every edge will have the same weight. + * \param fixed Pointer to a logical vector, or a null pointer. This + * can be used to fix the position of some vertices. Vertices for + * which it is true will not be moved, but stay at the coordinates + * given in the \p res matrix. This argument is ignored if it is a + * null pointer or if use_seed is false. + * \return Error code. + * + * Time complexity: ???. + * + * \sa \ref igraph_layout_drl() for the standard 2d version. + */ + +int igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_layout_drl_options_t *options, + const igraph_vector_t *weights, + const igraph_vector_bool_t *fixed) { + IGRAPH_HANDLE_EXCEPTIONS( + RNG_BEGIN(); + + drl3d::graph neighbors(graph, options, weights); + neighbors.init_parms(options); + if (use_seed) { + IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 3)); + neighbors.read_real(res, fixed); + } + neighbors.draw_graph(res); + + RNG_END(); + ); + + return 0; +} diff --git a/src/rigraph/core/layout/drl/drl_layout_3d.h b/src/rigraph/core/layout/drl/drl_layout_3d.h new file mode 100644 index 0000000..d9b0095 --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_layout_3d.h @@ -0,0 +1,65 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// This file contains compile time parameters which affect the entire +// DrL program. + +#define DRL_VERSION "3.2 5/5/2006" + +// compile time parameters for MPI message passing +#define MAX_PROCS 256 // maximum number of processors +#define MAX_FILE_NAME 250 // max length of filename +#define MAX_INT_LENGTH 4 // max length of integer suffix of intermediate .coord file + +// Compile time adjustable parameters for the Density grid + +#define GRID_SIZE 100 // size of Density grid +#define VIEW_SIZE 250.0 // actual physical size of layout plane +// these values use more memory but have +// little effect on performance or layout + +#define RADIUS 10 // radius for density fall-off: +// larger values tends to slow down +// the program and clump the data + +#define HALF_VIEW 125.0 // 1/2 of VIEW_SIZE +#define VIEW_TO_GRID .4 // ratio of GRID_SIZE to VIEW_SIZE + +/* +// original values for VxOrd +#define GRID_SIZE 400 // size of VxOrd Density grid +#define VIEW_SIZE 1600.0 // actual physical size of VxOrd plane +#define RADIUS 10 // radius for density fall-off + +#define HALF_VIEW 800 // 1/2 of VIEW_SIZE +#define VIEW_TO_GRID .25 // ratio of GRID_SIZE to VIEW_SIZE +*/ diff --git a/src/rigraph/core/layout/drl/drl_parse.cpp b/src/rigraph/core/layout/drl/drl_parse.cpp new file mode 100644 index 0000000..c0e98cd --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_parse.cpp @@ -0,0 +1,197 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// This file contains the methods for the parse.h class + +#include "drl_layout.h" +#include "drl_parse.h" + +namespace drl { + +// void parse::print_syntax( const char *error_string ) +// { +// cout << endl << "Error: " << error_string << endl; +// cout << endl << "Layout" << endl +// << "------" << endl +// << "S. Martin" << endl +// << "Version " << DRL_VERSION << endl << endl +// << "This program provides a parallel adaptation of a force directed" << endl +// << "graph layout algorithm for use with large datasets." << endl << endl +// << "Usage: layout [options] root_file" << endl << endl +// << "root_file -- the root name of the file being processed." << endl << endl +// << "INPUT" << endl +// << "-----" << endl +// << "root_file.int -- the input file containing the graph to draw using layout." << endl +// << " The .int file must have the suffix \".int\" and each line of .int file" << endl +// << " should have the form" << endl +// << "\tnode_id node_id weight" << endl +// << " where node_id's are integers in sequence starting from 0, and" << endl +// << " weight is a float > 0." << endl << endl +// << "OUTPUT" << endl +// << "------" << endl +// << "root_file.icoord -- the resulting output file, containing an ordination" << endl +// << " of the graph. The .icoord file will have the suffix \".icoord\" and" << endl +// << " each line of the .icoord file will be of the form" << endl +// << "\tnode_id x-coord y-coord" << endl << endl +// << "Options:" << endl << endl +// << "\t-s {int>=0} random seed (default value is 0)" << endl +// << "\t-c {real[0,1]} edge cutting (default 32/40 = .8)" << endl +// << "\t (old max was 39/40 = .975)" << endl +// << "\t-p input parameters from .parms file" << endl +// << "\t-r {real[0,1]} input coordinates from .real file" << endl +// << "\t (hold fixed until fraction of optimization schedule reached)" << endl +// << "\t-i {int>=0} intermediate output interval (default 0: no output)" << endl +// << "\t-e output .iedges file (same prefix as .coord file)" << endl << endl; + +// #ifdef MUSE_MPI +// MPI_Abort ( MPI_COMM_WORLD, 1 ); +// #else +// exit (1); +// #endif +// } + +// parse::parse ( int argc, char** argv) +// { +// map m; + +// // make sure there is at least one argument +// if ( argc < 2) +// print_syntax ( "not enough arguments!" ); + +// // make sure coord_file ends in ".coord" +// parms_file = real_file = sim_file = coord_file = argv[argc-1]; +// parms_file = parms_file + ".parms"; +// real_file = real_file + ".real"; +// sim_file = sim_file + ".int"; +// coord_file = coord_file + ".icoord"; + +// char error_string[200]; +// sprintf ( error_string, "%s %d %s", "root file name cannot be longer than", MAX_FILE_NAME-7, +// "characters."); +// if ( coord_file.length() > MAX_FILE_NAME ) +// print_syntax ( error_string ); + +// // echo sim_file and coord_file +// cout << "Using " << sim_file << " for .int file, and " << coord_file << " for .icoord file." << endl; + +// // set defaults +// rand_seed = 0; +// //edge_cut = 32.0/39.0; // (old default) +// edge_cut = 32.0/40.0; +// int_out = 0; +// edges_out = 0; +// parms_in = 0; +// real_in = -1.0; + +// // now check for optional arguments +// string arg; +// for( int i = 1; i= (argc-1) ) +// print_syntax ( "-s flag has no argument." ); +// else +// { +// rand_seed = atoi ( argv[i] ); +// if ( rand_seed < 0 ) +// print_syntax ( "random seed must be >= 0." ); +// } +// } +// // check for edge cutting +// else if ( arg == "-c" ) +// { +// i++; +// if ( i >= (argc-1) ) +// print_syntax ( "-c flag has no argument." ); +// else +// { +// edge_cut = atof ( argv[i] ); +// if ( (edge_cut < 0) || (edge_cut > 1) ) +// print_syntax ( "edge cut must be between 0 and 1." ); +// } +// } +// // check for intermediate output +// else if ( arg == "-i" ) +// { +// i++; +// if ( i >= (argc-1) ) +// print_syntax ( "-i flag has no argument." ); +// else +// { +// int_out = atoi ( argv[i] ); +// if ( int_out < 0 ) +// print_syntax ( "intermediate output must be >= 0." ); +// } +// } +// // check for .real input +// else if ( arg == "-r" ) +// { +// i++; +// if ( i >= (argc-1) ) +// print_syntax ( "-r flag has no argument." ); +// else +// { +// real_in = atof ( argv[i] ); +// if ( (real_in < 0) || (real_in > 1) ) +// print_syntax ( "real iteration fraction must be from 0 to 1." ); +// } +// } +// else if ( arg == "-e" ) +// edges_out = 1; +// else if ( arg == "-p" ) +// parms_in = 1; +// else +// print_syntax ( "unrecongized option!" ); +// } + +// if ( parms_in ) +// cout << "Using " << parms_file << " for .parms file." << endl; + +// if ( real_in >= 0 ) +// cout << "Using " << real_file << " for .real file." << endl; + +// // echo arguments input or default +// cout << "Using random seed = " << rand_seed << endl +// << " edge_cutting = " << edge_cut << endl +// << " intermediate output = " << int_out << endl +// << " output .iedges file = " << edges_out << endl; +// if ( real_in >= 0 ) +// cout << " holding .real fixed until iterations = " << real_in << endl; + +// } + +} // namespace drl diff --git a/src/rigraph/core/layout/drl/drl_parse.h b/src/rigraph/core/layout/drl/drl_parse.h new file mode 100644 index 0000000..a772478 --- /dev/null +++ b/src/rigraph/core/layout/drl/drl_parse.h @@ -0,0 +1,72 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// The parse class contains the methods necessary to parse +// the command line, print help, and do error checking + +#ifdef MUSE_MPI + #include +#endif + +#include + +namespace drl { + +class parse { + +public: + + // Methods + + parse ( int argc, char **argv ); + ~parse () {} + + // user parameters + std::string sim_file; // .sim file + std::string coord_file; // .coord file + std::string parms_file; // .parms file + std::string real_file; // .real file + + int rand_seed; // random seed int >= 0 + float edge_cut; // edge cutting real [0,1] + int int_out; // intermediate output, int >= 1 + int edges_out; // true if .edges file is requested + int parms_in; // true if .parms file is to be read + float real_in; // true if .real file is to be read + +private: + + void print_syntax ( const char *error_string ); + +}; + +} // namespace drl diff --git a/src/rigraph/core/layout/fruchterman_reingold.c b/src/rigraph/core/layout/fruchterman_reingold.c new file mode 100644 index 0000000..307b5fe --- /dev/null +++ b/src/rigraph/core/layout/fruchterman_reingold.c @@ -0,0 +1,709 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_random.h" +#include "igraph_interface.h" +#include "igraph_components.h" + +#include "../core/grid.h" +#include "../core/interruption.h" + +static int igraph_layout_i_fr(const igraph_t *graph, + igraph_matrix_t *res, + igraph_bool_t use_seed, + igraph_integer_t niter, + igraph_real_t start_temp, + const igraph_vector_t *weight, + const igraph_vector_t *minx, + const igraph_vector_t *maxx, + const igraph_vector_t *miny, + const igraph_vector_t *maxy) { + + igraph_integer_t no_nodes = igraph_vcount(graph); + igraph_integer_t no_edges = igraph_ecount(graph); + igraph_integer_t i; + igraph_vector_float_t dispx, dispy; + igraph_real_t temp = start_temp; + igraph_real_t difftemp = start_temp / niter; + float width = sqrtf(no_nodes), height = width; + igraph_bool_t conn = 1; + float C = 0; + + IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); + if (!conn) { + C = no_nodes * sqrtf(no_nodes); + } + + RNG_BEGIN(); + + if (!use_seed) { + IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2)); + for (i = 0; i < no_nodes; i++) { + igraph_real_t x1 = minx ? VECTOR(*minx)[i] : -width / 2; + igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] : width / 2; + igraph_real_t y1 = miny ? VECTOR(*miny)[i] : -height / 2; + igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] : height / 2; + if (!igraph_finite(x1)) { + x1 = -sqrt(no_nodes) / 2; + } + if (!igraph_finite(x2)) { + x2 = sqrt(no_nodes) / 2; + } + if (!igraph_finite(y1)) { + y1 = -sqrt(no_nodes) / 2; + } + if (!igraph_finite(y2)) { + y2 = sqrt(no_nodes) / 2; + } + MATRIX(*res, i, 0) = RNG_UNIF(x1, x2); + MATRIX(*res, i, 1) = RNG_UNIF(y1, y2); + } + } + + IGRAPH_CHECK(igraph_vector_float_init(&dispx, no_nodes)); + IGRAPH_FINALLY(igraph_vector_float_destroy, &dispx); + IGRAPH_CHECK(igraph_vector_float_init(&dispy, no_nodes)); + IGRAPH_FINALLY(igraph_vector_float_destroy, &dispy); + + for (i = 0; i < niter; i++) { + igraph_integer_t v, u, e; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* calculate repulsive forces, we have a special version + for unconnected graphs */ + igraph_vector_float_null(&dispx); + igraph_vector_float_null(&dispy); + if (conn) { + for (v = 0; v < no_nodes; v++) { + for (u = v + 1; u < no_nodes; u++) { + float dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0); + float dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1); + float dlen = dx * dx + dy * dy; + + if (dlen == 0) { + dx = RNG_UNIF01() * 1e-9; + dy = RNG_UNIF01() * 1e-9; + dlen = dx * dx + dy * dy; + } + + VECTOR(dispx)[v] += dx / dlen; + VECTOR(dispy)[v] += dy / dlen; + VECTOR(dispx)[u] -= dx / dlen; + VECTOR(dispy)[u] -= dy / dlen; + } + } + } else { + for (v = 0; v < no_nodes; v++) { + for (u = v + 1; u < no_nodes; u++) { + float dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0); + float dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1); + float dlen, rdlen; + + dlen = dx * dx + dy * dy; + if (dlen == 0) { + dx = RNG_UNIF(0, 1e-6); + dy = RNG_UNIF(0, 1e-6); + dlen = dx * dx + dy * dy; + } + + rdlen = sqrt(dlen); + + VECTOR(dispx)[v] += dx * (C - dlen * rdlen) / (dlen * C); + VECTOR(dispy)[v] += dy * (C - dlen * rdlen) / (dlen * C); + VECTOR(dispx)[u] -= dx * (C - dlen * rdlen) / (dlen * C); + VECTOR(dispy)[u] -= dy * (C - dlen * rdlen) / (dlen * C); + } + } + } + + /* calculate attractive forces */ + for (e = 0; e < no_edges; e++) { + /* each edges is an ordered pair of vertices v and u */ + igraph_integer_t v = IGRAPH_FROM(graph, e); + igraph_integer_t u = IGRAPH_TO(graph, e); + igraph_real_t dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0); + igraph_real_t dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1); + igraph_real_t w = weight ? VECTOR(*weight)[e] : 1.0; + igraph_real_t dlen = sqrt(dx * dx + dy * dy) * w; + VECTOR(dispx)[v] -= (dx * dlen); + VECTOR(dispy)[v] -= (dy * dlen); + VECTOR(dispx)[u] += (dx * dlen); + VECTOR(dispy)[u] += (dy * dlen); + } + + /* limit max displacement to temperature t and prevent from + displacement outside frame */ + for (v = 0; v < no_nodes; v++) { + igraph_real_t dx = VECTOR(dispx)[v] + RNG_UNIF01() * 1e-9; + igraph_real_t dy = VECTOR(dispy)[v] + RNG_UNIF01() * 1e-9; + igraph_real_t displen = sqrt(dx * dx + dy * dy); + igraph_real_t mx = fabs(dx) < temp ? dx : temp; + igraph_real_t my = fabs(dy) < temp ? dy : temp; + if (displen > 0) { + MATRIX(*res, v, 0) += (dx / displen) * mx; + MATRIX(*res, v, 1) += (dy / displen) * my; + } + if (minx && MATRIX(*res, v, 0) < VECTOR(*minx)[v]) { + MATRIX(*res, v, 0) = VECTOR(*minx)[v]; + } + if (maxx && MATRIX(*res, v, 0) > VECTOR(*maxx)[v]) { + MATRIX(*res, v, 0) = VECTOR(*maxx)[v]; + } + if (miny && MATRIX(*res, v, 1) < VECTOR(*miny)[v]) { + MATRIX(*res, v, 1) = VECTOR(*miny)[v]; + } + if (maxy && MATRIX(*res, v, 1) > VECTOR(*maxy)[v]) { + MATRIX(*res, v, 1) = VECTOR(*maxy)[v]; + } + } + + temp -= difftemp; + } + + RNG_END(); + + igraph_vector_float_destroy(&dispx); + igraph_vector_float_destroy(&dispy); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +static int igraph_layout_i_grid_fr( + const igraph_t *graph, + igraph_matrix_t *res, igraph_bool_t use_seed, + igraph_integer_t niter, igraph_real_t start_temp, + const igraph_vector_t *weight, const igraph_vector_t *minx, + const igraph_vector_t *maxx, const igraph_vector_t *miny, + const igraph_vector_t *maxy) { + + igraph_integer_t no_nodes = igraph_vcount(graph); + igraph_integer_t no_edges = igraph_ecount(graph); + float width = sqrtf(no_nodes), height = width; + igraph_2dgrid_t grid; + igraph_vector_float_t dispx, dispy; + igraph_real_t temp = start_temp; + igraph_real_t difftemp = start_temp / niter; + igraph_2dgrid_iterator_t vidit; + igraph_integer_t i; + const float cellsize = 2.0; + + RNG_BEGIN(); + + if (!use_seed) { + IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2)); + for (i = 0; i < no_nodes; i++) { + igraph_real_t x1 = minx ? VECTOR(*minx)[i] : -width / 2; + igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] : width / 2; + igraph_real_t y1 = miny ? VECTOR(*miny)[i] : -height / 2; + igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] : height / 2; + if (!igraph_finite(x1)) { + x1 = -sqrt(no_nodes) / 2; + } + if (!igraph_finite(x2)) { + x2 = sqrt(no_nodes) / 2; + } + if (!igraph_finite(y1)) { + y1 = -sqrt(no_nodes) / 2; + } + if (!igraph_finite(y2)) { + y2 = sqrt(no_nodes) / 2; + } + MATRIX(*res, i, 0) = RNG_UNIF(x1, x2); + MATRIX(*res, i, 1) = RNG_UNIF(y1, y2); + } + } + + /* make grid */ + IGRAPH_CHECK(igraph_2dgrid_init(&grid, res, -width / 2, width / 2, cellsize, + -height / 2, height / 2, cellsize)); + IGRAPH_FINALLY(igraph_2dgrid_destroy, &grid); + + /* place vertices on grid */ + for (i = 0; i < no_nodes; i++) { + igraph_2dgrid_add2(&grid, i); + } + + IGRAPH_CHECK(igraph_vector_float_init(&dispx, no_nodes)); + IGRAPH_FINALLY(igraph_vector_float_destroy, &dispx); + IGRAPH_CHECK(igraph_vector_float_init(&dispy, no_nodes)); + IGRAPH_FINALLY(igraph_vector_float_destroy, &dispy); + + for (i = 0; i < niter; i++) { + igraph_integer_t v, u, e; + + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_vector_float_null(&dispx); + igraph_vector_float_null(&dispy); + + /* repulsion */ + igraph_2dgrid_reset(&grid, &vidit); + while ( (v = igraph_2dgrid_next(&grid, &vidit) - 1) != -1) { + while ( (u = igraph_2dgrid_next_nei(&grid, &vidit) - 1) != -1) { + float dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0); + float dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1); + float dlen = dx * dx + dy * dy; + if (dlen < cellsize * cellsize) { + VECTOR(dispx)[v] += dx / dlen; + VECTOR(dispy)[v] += dy / dlen; + VECTOR(dispx)[u] -= dx / dlen; + VECTOR(dispy)[u] -= dy / dlen; + } + } + } + + /* attraction */ + for (e = 0; e < no_edges; e++) { + igraph_integer_t v = IGRAPH_FROM(graph, e); + igraph_integer_t u = IGRAPH_TO(graph, e); + igraph_real_t dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0); + igraph_real_t dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1); + igraph_real_t w = weight ? VECTOR(*weight)[e] : 1.0; + igraph_real_t dlen = sqrt(dx * dx + dy * dy) * w; + VECTOR(dispx)[v] -= (dx * dlen); + VECTOR(dispy)[v] -= (dy * dlen); + VECTOR(dispx)[u] += (dx * dlen); + VECTOR(dispy)[u] += (dy * dlen); + } + + /* update */ + for (v = 0; v < no_nodes; v++) { + igraph_real_t dx = VECTOR(dispx)[v] + RNG_UNIF01() * 1e-9; + igraph_real_t dy = VECTOR(dispy)[v] + RNG_UNIF01() * 1e-9; + igraph_real_t displen = sqrt(dx * dx + dy * dy); + igraph_real_t mx = fabs(dx) < temp ? dx : temp; + igraph_real_t my = fabs(dy) < temp ? dy : temp; + if (displen > 0) { + MATRIX(*res, v, 0) += (dx / displen) * mx; + MATRIX(*res, v, 1) += (dy / displen) * my; + } + if (minx && MATRIX(*res, v, 0) < VECTOR(*minx)[v]) { + MATRIX(*res, v, 0) = VECTOR(*minx)[v]; + } + if (maxx && MATRIX(*res, v, 0) > VECTOR(*maxx)[v]) { + MATRIX(*res, v, 0) = VECTOR(*maxx)[v]; + } + if (miny && MATRIX(*res, v, 1) < VECTOR(*miny)[v]) { + MATRIX(*res, v, 1) = VECTOR(*miny)[v]; + } + if (maxy && MATRIX(*res, v, 1) > VECTOR(*maxy)[v]) { + MATRIX(*res, v, 1) = VECTOR(*maxy)[v]; + } + } + + temp -= difftemp; + } + + igraph_vector_float_destroy(&dispx); + igraph_vector_float_destroy(&dispy); + igraph_2dgrid_destroy(&grid); + IGRAPH_FINALLY_CLEAN(3); + return 0; +} + +/** + * \ingroup layout + * \function igraph_layout_fruchterman_reingold + * \brief Places the vertices on a plane according to the Fruchterman-Reingold algorithm. + * + * + * This is a force-directed layout, see Fruchterman, T.M.J. and + * Reingold, E.M.: Graph Drawing by Force-directed Placement. + * Software -- Practice and Experience, 21/11, 1129--1164, + * 1991. + * \param graph Pointer to an initialized graph object. + * \param res Pointer to an initialized matrix object. This will + * contain the result and will be resized as needed. + * \param use_seed Logical, if true the supplied values in the + * \p res argument are used as an initial layout, if + * false a random initial layout is used. + * \param niter The number of iterations to do. A reasonable + * default value is 500. + * \param start_temp Start temperature. This is the maximum amount + * of movement allowed along one axis, within one step, for a + * vertex. Currently it is decreased linearly to zero during + * the iteration. + * \param grid Whether to use the (fast but less accurate) grid based + * version of the algorithm. Possible values: \c + * IGRAPH_LAYOUT_GRID, \c IGRAPH_LAYOUT_NOGRID, \c + * IGRAPH_LAYOUT_AUTOGRID. The last one uses the grid based + * version only for large graphs, currently the ones with + * more than 1000 vertices. + * \param weight Pointer to a vector containing edge weights, + * the attraction along the edges will be multiplied by these. + * It will be ignored if it is a null-pointer. + * \param minx Pointer to a vector, or a \c NULL pointer. If not a + * \c NULL pointer then the vector gives the minimum + * \quote x \endquote coordinate for every vertex. + * \param maxx Same as \p minx, but the maximum \quote x \endquote + * coordinates. + * \param miny Pointer to a vector, or a \c NULL pointer. If not a + * \c NULL pointer then the vector gives the minimum + * \quote y \endquote coordinate for every vertex. + * \param maxy Same as \p miny, but the maximum \quote y \endquote + * coordinates. + * \return Error code. + * + * Time complexity: O(|V|^2) in each + * iteration, |V| is the number of + * vertices in the graph. + */ + +int igraph_layout_fruchterman_reingold(const igraph_t *graph, + igraph_matrix_t *res, + igraph_bool_t use_seed, + igraph_integer_t niter, + igraph_real_t start_temp, + igraph_layout_grid_t grid, + const igraph_vector_t *weight, + const igraph_vector_t *minx, + const igraph_vector_t *maxx, + const igraph_vector_t *miny, + const igraph_vector_t *maxy) { + + igraph_integer_t no_nodes = igraph_vcount(graph); + + if (niter < 0) { + IGRAPH_ERROR("Number of iterations must be non-negative in " + "Fruchterman-Reingold layout.", IGRAPH_EINVAL); + } + + if (use_seed && (igraph_matrix_nrow(res) != no_nodes || + igraph_matrix_ncol(res) != 2)) { + IGRAPH_ERROR("Invalid start position matrix size in " + "Fruchterman-Reingold layout.", IGRAPH_EINVAL); + } + + if (weight && igraph_vector_size(weight) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + + if (minx && igraph_vector_size(minx) != no_nodes) { + IGRAPH_ERROR("Invalid minx vector length.", IGRAPH_EINVAL); + } + if (maxx && igraph_vector_size(maxx) != no_nodes) { + IGRAPH_ERROR("Invalid maxx vector length.", IGRAPH_EINVAL); + } + if (minx && maxx && !igraph_vector_all_le(minx, maxx)) { + IGRAPH_ERROR("minx must not be greater than maxx.", IGRAPH_EINVAL); + } + if (miny && igraph_vector_size(miny) != no_nodes) { + IGRAPH_ERROR("Invalid miny vector length.", IGRAPH_EINVAL); + } + if (maxy && igraph_vector_size(maxy) != no_nodes) { + IGRAPH_ERROR("Invalid maxy vector length.", IGRAPH_EINVAL); + } + if (miny && maxy && !igraph_vector_all_le(miny, maxy)) { + IGRAPH_ERROR("miny must not be greater than maxy.", IGRAPH_EINVAL); + } + + if (grid == IGRAPH_LAYOUT_AUTOGRID) { + if (no_nodes > 1000) { + grid = IGRAPH_LAYOUT_GRID; + } else { + grid = IGRAPH_LAYOUT_NOGRID; + } + } + + if (grid == IGRAPH_LAYOUT_GRID) { + return igraph_layout_i_grid_fr(graph, res, use_seed, niter, start_temp, + weight, minx, maxx, miny, maxy); + } else { + return igraph_layout_i_fr(graph, res, use_seed, niter, start_temp, + weight, minx, maxx, miny, maxy); + } +} + +/** + * \function igraph_layout_fruchterman_reingold_3d + * \brief 3D Fruchterman-Reingold algorithm. + * + * This is the 3D version of the force based + * Fruchterman-Reingold layout (see \ref + * igraph_layout_fruchterman_reingold for the 2D version + * + * \param graph Pointer to an initialized graph object. + * \param res Pointer to an initialized matrix object. This will + * contain the result and will be resized as needed. + * \param use_seed Logical, if true the supplied values in the + * \p res argument are used as an initial layout, if + * false a random initial layout is used. + * \param niter The number of iterations to do. A reasonable + * default value is 500. + * \param start_temp Start temperature. This is the maximum amount + * of movement alloved along one axis, within one step, for a + * vertex. Currently it is decreased linearly to zero during + * the iteration. + * \param weight Pointer to a vector containing edge weights, + * the attraction along the edges will be multiplied by these. + * It will be ignored if it is a null-pointer. + * \param minx Pointer to a vector, or a \c NULL pointer. If not a + * \c NULL pointer then the vector gives the minimum + * \quote x \endquote coordinate for every vertex. + * \param maxx Same as \p minx, but the maximum \quote x \endquote + * coordinates. + * \param miny Pointer to a vector, or a \c NULL pointer. If not a + * \c NULL pointer then the vector gives the minimum + * \quote y \endquote coordinate for every vertex. + * \param maxy Same as \p miny, but the maximum \quote y \endquote + * coordinates. + * \param minz Pointer to a vector, or a \c NULL pointer. If not a + * \c NULL pointer then the vector gives the minimum + * \quote z \endquote coordinate for every vertex. + * \param maxz Same as \p minz, but the maximum \quote z \endquote + * coordinates. + * \return Error code. + * + * Added in version 0.2. + * + * Time complexity: O(|V|^2) in each + * iteration, |V| is the number of + * vertices in the graph. + * + */ + +int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, + igraph_matrix_t *res, + igraph_bool_t use_seed, + igraph_integer_t niter, + igraph_real_t start_temp, + const igraph_vector_t *weight, + const igraph_vector_t *minx, + const igraph_vector_t *maxx, + const igraph_vector_t *miny, + const igraph_vector_t *maxy, + const igraph_vector_t *minz, + const igraph_vector_t *maxz) { + + igraph_integer_t no_nodes = igraph_vcount(graph); + igraph_integer_t no_edges = igraph_ecount(graph); + igraph_integer_t i; + igraph_vector_float_t dispx, dispy, dispz; + igraph_real_t temp = start_temp; + igraph_real_t difftemp = start_temp / niter; + float width = sqrtf(no_nodes), height = width, depth = width; + igraph_bool_t conn = 1; + float C = 0; + + if (niter < 0) { + IGRAPH_ERROR("Number of iterations must be non-negative in " + "Fruchterman-Reingold layout", IGRAPH_EINVAL); + } + + if (use_seed && (igraph_matrix_nrow(res) != no_nodes || + igraph_matrix_ncol(res) != 3)) { + IGRAPH_ERROR("Invalid start position matrix size in " + "Fruchterman-Reingold layout", IGRAPH_EINVAL); + } + + if (weight && igraph_vector_size(weight) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + } + + if (minx && igraph_vector_size(minx) != no_nodes) { + IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL); + } + if (maxx && igraph_vector_size(maxx) != no_nodes) { + IGRAPH_ERROR("Invalid maxx vector length", IGRAPH_EINVAL); + } + if (minx && maxx && !igraph_vector_all_le(minx, maxx)) { + IGRAPH_ERROR("minx must not be greater than maxx", IGRAPH_EINVAL); + } + if (miny && igraph_vector_size(miny) != no_nodes) { + IGRAPH_ERROR("Invalid miny vector length", IGRAPH_EINVAL); + } + if (maxy && igraph_vector_size(maxy) != no_nodes) { + IGRAPH_ERROR("Invalid maxy vector length", IGRAPH_EINVAL); + } + if (miny && maxy && !igraph_vector_all_le(miny, maxy)) { + IGRAPH_ERROR("miny must not be greater than maxy", IGRAPH_EINVAL); + } + if (minz && igraph_vector_size(minz) != no_nodes) { + IGRAPH_ERROR("Invalid minz vector length", IGRAPH_EINVAL); + } + if (maxz && igraph_vector_size(maxz) != no_nodes) { + IGRAPH_ERROR("Invalid maxz vector length", IGRAPH_EINVAL); + } + if (minz && maxz && !igraph_vector_all_le(minz, maxz)) { + IGRAPH_ERROR("minz must not be greater than maxz", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); + if (!conn) { + C = no_nodes * sqrtf(no_nodes); + } + + RNG_BEGIN(); + + if (!use_seed) { + IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 3)); + for (i = 0; i < no_nodes; i++) { + igraph_real_t x1 = minx ? VECTOR(*minx)[i] : -width / 2; + igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] : width / 2; + igraph_real_t y1 = miny ? VECTOR(*miny)[i] : -height / 2; + igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] : height / 2; + igraph_real_t z1 = minz ? VECTOR(*minz)[i] : -depth / 2; + igraph_real_t z2 = maxz ? VECTOR(*maxz)[i] : depth / 2; + MATRIX(*res, i, 0) = RNG_UNIF(x1, x2); + MATRIX(*res, i, 1) = RNG_UNIF(y1, y2); + MATRIX(*res, i, 2) = RNG_UNIF(z1, z2); + } + } + + IGRAPH_CHECK(igraph_vector_float_init(&dispx, no_nodes)); + IGRAPH_FINALLY(igraph_vector_float_destroy, &dispx); + IGRAPH_CHECK(igraph_vector_float_init(&dispy, no_nodes)); + IGRAPH_FINALLY(igraph_vector_float_destroy, &dispy); + IGRAPH_CHECK(igraph_vector_float_init(&dispz, no_nodes)); + IGRAPH_FINALLY(igraph_vector_float_destroy, &dispz); + + for (i = 0; i < niter; i++) { + igraph_integer_t v, u, e; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* calculate repulsive forces, we have a special version + for unconnected graphs */ + igraph_vector_float_null(&dispx); + igraph_vector_float_null(&dispy); + igraph_vector_float_null(&dispz); + if (conn) { + for (v = 0; v < no_nodes; v++) { + for (u = v + 1; u < no_nodes; u++) { + float dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0); + float dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1); + float dz = MATRIX(*res, v, 2) - MATRIX(*res, u, 2); + float dlen = dx * dx + dy * dy + dz * dz; + + if (dlen == 0) { + dx = RNG_UNIF01() * 1e-9; + dy = RNG_UNIF01() * 1e-9; + dz = RNG_UNIF01() * 1e-9; + dlen = dx * dx + dy * dy + dz * dz; + } + + VECTOR(dispx)[v] += dx / dlen; + VECTOR(dispy)[v] += dy / dlen; + VECTOR(dispz)[v] += dz / dlen; + VECTOR(dispx)[u] -= dx / dlen; + VECTOR(dispy)[u] -= dy / dlen; + VECTOR(dispz)[u] -= dz / dlen; + } + } + } else { + for (v = 0; v < no_nodes; v++) { + for (u = v + 1; u < no_nodes; u++) { + float dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0); + float dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1); + float dz = MATRIX(*res, v, 2) - MATRIX(*res, u, 2); + float dlen, rdlen; + + dlen = dx * dx + dy * dy + dz * dz; + if (dlen == 0) { + dx = RNG_UNIF01() * 1e-9; + dy = RNG_UNIF01() * 1e-9; + dz = RNG_UNIF01() * 1e-9; + dlen = dx * dx + dy * dy + dz * dz; + } + + rdlen = sqrt(dlen); + + VECTOR(dispx)[v] += dx * (C - dlen * rdlen) / (dlen * C); + VECTOR(dispy)[v] += dy * (C - dlen * rdlen) / (dlen * C); + VECTOR(dispy)[v] += dz * (C - dlen * rdlen) / (dlen * C); + VECTOR(dispx)[u] -= dx * (C - dlen * rdlen) / (dlen * C); + VECTOR(dispy)[u] -= dy * (C - dlen * rdlen) / (dlen * C); + VECTOR(dispz)[u] -= dz * (C - dlen * rdlen) / (dlen * C); + } + } + } + + /* calculate attractive forces */ + for (e = 0; e < no_edges; e++) { + /* each edges is an ordered pair of vertices v and u */ + igraph_integer_t v = IGRAPH_FROM(graph, e); + igraph_integer_t u = IGRAPH_TO(graph, e); + igraph_real_t dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0); + igraph_real_t dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1); + igraph_real_t dz = MATRIX(*res, v, 2) - MATRIX(*res, u, 2); + igraph_real_t w = weight ? VECTOR(*weight)[e] : 1.0; + igraph_real_t dlen = sqrt(dx * dx + dy * dy + dz * dz) * w; + VECTOR(dispx)[v] -= (dx * dlen); + VECTOR(dispy)[v] -= (dy * dlen); + VECTOR(dispz)[v] -= (dz * dlen); + VECTOR(dispx)[u] += (dx * dlen); + VECTOR(dispy)[u] += (dy * dlen); + VECTOR(dispz)[u] += (dz * dlen); + } + + /* limit max displacement to temperature t and prevent from + displacement outside frame */ + for (v = 0; v < no_nodes; v++) { + igraph_real_t dx = VECTOR(dispx)[v] + RNG_UNIF01() * 1e-9; + igraph_real_t dy = VECTOR(dispy)[v] + RNG_UNIF01() * 1e-9; + igraph_real_t dz = VECTOR(dispz)[v] + RNG_UNIF01() * 1e-9; + igraph_real_t displen = sqrt(dx * dx + dy * dy + dz * dz); + igraph_real_t mx = fabs(dx) < temp ? dx : temp; + igraph_real_t my = fabs(dy) < temp ? dy : temp; + igraph_real_t mz = fabs(dz) < temp ? dz : temp; + if (displen > 0) { + MATRIX(*res, v, 0) += (dx / displen) * mx; + MATRIX(*res, v, 1) += (dy / displen) * my; + MATRIX(*res, v, 2) += (dz / displen) * mz; + } + if (minx && MATRIX(*res, v, 0) < VECTOR(*minx)[v]) { + MATRIX(*res, v, 0) = VECTOR(*minx)[v]; + } + if (maxx && MATRIX(*res, v, 0) > VECTOR(*maxx)[v]) { + MATRIX(*res, v, 0) = VECTOR(*maxx)[v]; + } + if (miny && MATRIX(*res, v, 1) < VECTOR(*miny)[v]) { + MATRIX(*res, v, 1) = VECTOR(*miny)[v]; + } + if (maxy && MATRIX(*res, v, 1) > VECTOR(*maxy)[v]) { + MATRIX(*res, v, 1) = VECTOR(*maxy)[v]; + } + if (minz && MATRIX(*res, v, 2) < VECTOR(*minz)[v]) { + MATRIX(*res, v, 2) = VECTOR(*minz)[v]; + } + if (maxz && MATRIX(*res, v, 2) > VECTOR(*maxz)[v]) { + MATRIX(*res, v, 2) = VECTOR(*maxz)[v]; + } + } + + temp -= difftemp; + } + + RNG_END(); + + igraph_vector_float_destroy(&dispx); + igraph_vector_float_destroy(&dispy); + igraph_vector_float_destroy(&dispz); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} diff --git a/src/rigraph/core/layout/gem.c b/src/rigraph/core/layout/gem.c new file mode 100644 index 0000000..b2e4288 --- /dev/null +++ b/src/rigraph/core/layout/gem.c @@ -0,0 +1,250 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" +#include "igraph_random.h" + +#include "../core/math.h" +#include "../core/interruption.h" + +/** + * \ingroup layout + * \function igraph_layout_gem + * + * The GEM layout algorithm, as described in Arne Frick, Andreas Ludwig, + * Heiko Mehldau: A Fast Adaptive Layout Algorithm for Undirected Graphs, + * Proc. Graph Drawing 1994, LNCS 894, pp. 388-403, 1995. + * \param graph The input graph. Edge directions are ignored in + * directed graphs. + * \param res The result is stored here. If the \p use_seed argument + * is true (non-zero), then this matrix is also used as the + * starting point of the algorithm. + * \param use_seed Boolean, whether to use the supplied coordinates in + * \p res as the starting point. If false (zero), then a + * uniform random starting point is used. + * \param maxiter The maximum number of iterations to + * perform. Updating a single vertex counts as an iteration. + * A reasonable default is 40 * n * n, where n is the number of + * vertices. The original paper suggests 4 * n * n, but this + * usually only works if the other parameters are set up carefully. + * \param temp_max The maximum allowed local temperature. A reasonable + * default is the number of vertices. + * \param temp_min The global temperature at which the algorithm + * terminates (even before reaching \p maxiter iterations). A + * reasonable default is 1/10. + * \param temp_init Initial local temperature of all vertices. A + * reasonable default is the square root of the number of + * vertices. + * \return Error code. + * + * Time complexity: O(t * n * (n+e)), where n is the number of vertices, + * e is the number of edges and t is the number of time steps + * performed. + */ + +int igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, + igraph_bool_t use_seed, igraph_integer_t maxiter, + igraph_real_t temp_max, igraph_real_t temp_min, + igraph_real_t temp_init) { + + igraph_integer_t no_nodes = igraph_vcount(graph); + igraph_vector_int_t perm; + igraph_vector_float_t impulse_x, impulse_y, temp, skew_gauge; + igraph_integer_t i; + float temp_global; + igraph_integer_t perm_pointer = 0; + float barycenter_x = 0, barycenter_y = 0; + igraph_vector_t phi; + igraph_vector_t neis; + const float elen_des2 = 128 * 128; + const float gamma = 1 / 16.0f; + const float alpha_o = (float)M_PI; + const float alpha_r = (float)M_PI / 3.0f; + const float sigma_o = 1.0f / 3.0f; + const float sigma_r = 1.0f / 2.0f / no_nodes; + + if (maxiter < 0) { + IGRAPH_ERROR("Number of iterations must be non-negative in GEM layout", + IGRAPH_EINVAL); + } + if (use_seed && (igraph_matrix_nrow(res) != no_nodes || + igraph_matrix_ncol(res) != 2)) { + IGRAPH_ERROR("Invalid start position matrix size in GEM layout", + IGRAPH_EINVAL); + } + if (temp_max <= 0) { + IGRAPH_ERROR("Maximum temperature should be positive in GEM layout", + IGRAPH_EINVAL); + } + if (temp_min <= 0) { + IGRAPH_ERROR("Minimum temperature should be positive in GEM layout", + IGRAPH_EINVAL); + } + if (temp_init <= 0) { + IGRAPH_ERROR("Initial temperature should be positive in GEM layout", + IGRAPH_EINVAL); + } + if (temp_max < temp_init || temp_init < temp_min) { + IGRAPH_ERROR("Minimum <= Initial <= Maximum temperature is required " + "in GEM layout", IGRAPH_EINVAL); + } + + if (no_nodes == 0) { + return 0; + } + + IGRAPH_CHECK(igraph_vector_float_init(&impulse_x, no_nodes)); + IGRAPH_FINALLY(igraph_vector_float_destroy, &impulse_x); + IGRAPH_CHECK(igraph_vector_float_init(&impulse_y, no_nodes)); + IGRAPH_FINALLY(igraph_vector_float_destroy, &impulse_y); + IGRAPH_CHECK(igraph_vector_float_init(&temp, no_nodes)); + IGRAPH_FINALLY(igraph_vector_float_destroy, &temp); + IGRAPH_CHECK(igraph_vector_float_init(&skew_gauge, no_nodes)); + IGRAPH_FINALLY(igraph_vector_float_destroy, &skew_gauge); + IGRAPH_CHECK(igraph_vector_int_init_seq(&perm, 0, no_nodes - 1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &perm); + IGRAPH_VECTOR_INIT_FINALLY(&phi, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 10); + + RNG_BEGIN(); + + /* Initialization */ + igraph_degree(graph, &phi, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + if (!use_seed) { + const igraph_real_t width_half = no_nodes * 100, height_half = width_half; + IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2)); + for (i = 0; i < no_nodes; i++) { + MATRIX(*res, i, 0) = RNG_UNIF(-width_half, width_half); + MATRIX(*res, i, 1) = RNG_UNIF(-height_half, height_half); + barycenter_x += MATRIX(*res, i, 0); + barycenter_y += MATRIX(*res, i, 1); + VECTOR(phi)[i] *= (VECTOR(phi)[i] / 2.0 + 1.0); + } + } else { + for (i = 0; i < no_nodes; i++) { + barycenter_x += MATRIX(*res, i, 0); + barycenter_y += MATRIX(*res, i, 1); + VECTOR(phi)[i] *= (VECTOR(phi)[i] / 2.0 + 1.0); + } + } + igraph_vector_float_fill(&temp, temp_init); + temp_global = temp_init * no_nodes; + + while (temp_global > temp_min * no_nodes && maxiter > 0) { + igraph_integer_t u, v, nlen, j; + float px, py, pvx, pvy; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* choose a vertex v to update */ + if (!perm_pointer) { + igraph_vector_int_shuffle(&perm); + perm_pointer = no_nodes - 1; + } + v = VECTOR(perm)[perm_pointer--]; + + /* compute v's impulse */ + px = (barycenter_x / no_nodes - MATRIX(*res, v, 0)) * gamma * VECTOR(phi)[v]; + py = (barycenter_y / no_nodes - MATRIX(*res, v, 1)) * gamma * VECTOR(phi)[v]; + px += RNG_UNIF(-32.0, 32.0); + py += RNG_UNIF(-32.0, 32.0); + + for (u = 0; u < no_nodes; u++) { + float dx, dy, dist2; + if (u == v) { + continue; + } + dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0); + dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1); + dist2 = dx * dx + dy * dy; + if (dist2 != 0) { + px += dx * elen_des2 / dist2; + py += dy * elen_des2 / dist2; + } + } + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL)); + nlen = igraph_vector_size(&neis); + for (j = 0; j < nlen; j++) { + igraph_integer_t u = VECTOR(neis)[j]; + float dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0); + float dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1); + float dist2 = dx * dx + dy * dy; + px -= dx * dist2 / (elen_des2 * VECTOR(phi)[v]); + py -= dy * dist2 / (elen_des2 * VECTOR(phi)[v]); + } + + /* update v's position and temperature */ + if (px != 0 || py != 0) { + float plen = sqrtf(px * px + py * py); + px *= VECTOR(temp)[v] / plen; + py *= VECTOR(temp)[v] / plen; + MATRIX(*res, v, 0) += px; + MATRIX(*res, v, 1) += py; + barycenter_x += px; + barycenter_y += py; + } + + pvx = VECTOR(impulse_x)[v]; pvy = VECTOR(impulse_y)[v]; + if (pvx != 0 || pvy != 0) { + float beta = atan2f(pvy - py, pvx - px); + float sin_beta = sinf(beta); + float sign_sin_beta = (sin_beta > 0) ? 1 : ((sin_beta < 0) ? -1 : 0); + float cos_beta = cosf(beta); + float abs_cos_beta = fabsf(cos_beta); + float old_temp = VECTOR(temp)[v]; + if (sin(beta) >= sin(M_PI_2 + alpha_r / 2.0)) { + VECTOR(skew_gauge)[v] += sigma_r * sign_sin_beta; + } + if (abs_cos_beta >= cosf(alpha_o / 2.0)) { + VECTOR(temp)[v] *= sigma_o * cos_beta; + } + VECTOR(temp)[v] *= (1 - fabsf(VECTOR(skew_gauge)[v])); + if (VECTOR(temp)[v] > temp_max) { + VECTOR(temp)[v] = temp_max; + } + VECTOR(impulse_x)[v] = px; + VECTOR(impulse_y)[v] = py; + temp_global += VECTOR(temp)[v] - old_temp; + } + + maxiter--; + + } /* while temp && iter */ + + + RNG_END(); + + igraph_vector_destroy(&neis); + igraph_vector_destroy(&phi); + igraph_vector_int_destroy(&perm); + igraph_vector_float_destroy(&skew_gauge); + igraph_vector_float_destroy(&temp); + igraph_vector_float_destroy(&impulse_y); + igraph_vector_float_destroy(&impulse_x); + IGRAPH_FINALLY_CLEAN(7); + + return 0; +} diff --git a/src/rigraph/core/layout/graphopt.c b/src/rigraph/core/layout/graphopt.c new file mode 100644 index 0000000..e72b6a1 --- /dev/null +++ b/src/rigraph/core/layout/graphopt.c @@ -0,0 +1,444 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" +#include "igraph_progress.h" + +#include "../core/interruption.h" + +#define COULOMBS_CONSTANT 8987500000.0 + + +static igraph_real_t igraph_i_distance_between( + const igraph_matrix_t *c, + long int a, long int b); + +static int igraph_i_determine_electric_axal_forces( + const igraph_matrix_t *pos, + igraph_real_t *x, + igraph_real_t *y, + igraph_real_t directed_force, + igraph_real_t distance, + long int other_node, + long int this_node); + +static int igraph_i_apply_electrical_force( + const igraph_matrix_t *pos, + igraph_vector_t *pending_forces_x, + igraph_vector_t *pending_forces_y, + long int other_node, long int this_node, + igraph_real_t node_charge, + igraph_real_t distance); + +static int igraph_i_determine_spring_axal_forces( + const igraph_matrix_t *pos, + igraph_real_t *x, igraph_real_t *y, + igraph_real_t directed_force, + igraph_real_t distance, + igraph_real_t spring_length, + long int other_node, + long int this_node); + +static int igraph_i_apply_spring_force( + const igraph_matrix_t *pos, + igraph_vector_t *pending_forces_x, + igraph_vector_t *pending_forces_y, + long int other_node, + long int this_node, igraph_real_t spring_length, + igraph_real_t spring_constant); + +static int igraph_i_move_nodes( + igraph_matrix_t *pos, + const igraph_vector_t *pending_forces_x, + const igraph_vector_t *pending_forces_y, + igraph_real_t node_mass, + igraph_real_t max_sa_movement); + +static igraph_real_t igraph_i_distance_between( + const igraph_matrix_t *c, + long int a, long int b) { + igraph_real_t diffx = MATRIX(*c, a, 0) - MATRIX(*c, b, 0); + igraph_real_t diffy = MATRIX(*c, a, 1) - MATRIX(*c, b, 1); + return sqrt( diffx * diffx + diffy * diffy ); +} + +static int igraph_i_determine_electric_axal_forces(const igraph_matrix_t *pos, + igraph_real_t *x, + igraph_real_t *y, + igraph_real_t directed_force, + igraph_real_t distance, + long int other_node, + long int this_node) { + + // We know what the directed force is. We now need to translate it + // into the appropriate x and y components. + // First, assume: + // other_node + // /| + // directed_force / | + // / | y + // /______| + // this_node x + // + // other_node.x > this_node.x + // other_node.y > this_node.y + // the force will be on this_node away from other_node + + // the proportion (distance/y_distance) is equal to the proportion + // (directed_force/y_force), as the two triangles are similar. + // therefore, the magnitude of y_force = (directed_force*y_distance)/distance + // the sign of y_force is negative, away from other_node + + igraph_real_t x_distance, y_distance; + y_distance = MATRIX(*pos, other_node, 1) - MATRIX(*pos, this_node, 1); + if (y_distance < 0) { + y_distance = -y_distance; + } + *y = -1 * ((directed_force * y_distance) / distance); + + // the x component works in exactly the same way. + x_distance = MATRIX(*pos, other_node, 0) - MATRIX(*pos, this_node, 0); + if (x_distance < 0) { + x_distance = -x_distance; + } + *x = -1 * ((directed_force * x_distance) / distance); + + // Now we need to reverse the polarity of our answers based on the falsness + // of our assumptions. + if (MATRIX(*pos, other_node, 0) < MATRIX(*pos, this_node, 0)) { + *x = *x * -1; + } + if (MATRIX(*pos, other_node, 1) < MATRIX(*pos, this_node, 1)) { + *y = *y * -1; + } + + return 0; +} + +static int igraph_i_apply_electrical_force( + const igraph_matrix_t *pos, + igraph_vector_t *pending_forces_x, + igraph_vector_t *pending_forces_y, + long int other_node, long int this_node, + igraph_real_t node_charge, + igraph_real_t distance) { + + igraph_real_t directed_force = COULOMBS_CONSTANT * + ((node_charge * node_charge) / (distance * distance)); + + igraph_real_t x_force, y_force; + igraph_i_determine_electric_axal_forces(pos, &x_force, &y_force, + directed_force, distance, + other_node, this_node); + + VECTOR(*pending_forces_x)[this_node] += x_force; + VECTOR(*pending_forces_y)[this_node] += y_force; + VECTOR(*pending_forces_x)[other_node] -= x_force; + VECTOR(*pending_forces_y)[other_node] -= y_force; + + return 0; +} + +static int igraph_i_determine_spring_axal_forces( + const igraph_matrix_t *pos, + igraph_real_t *x, igraph_real_t *y, + igraph_real_t directed_force, + igraph_real_t distance, + igraph_real_t spring_length, + long int other_node, long int this_node) { + + // if the spring is just the right size, the forces will be 0, so we can + // skip the computation. + // + // if the spring is too long, our forces will be identical to those computed + // by determine_electrical_axal_forces() (this_node will be pulled toward + // other_node). + // + // if the spring is too short, our forces will be the opposite of those + // computed by determine_electrical_axal_forces() (this_node will be pushed + // away from other_node) + // + // finally, since both nodes are movable, only one-half of the total force + // should be applied to each node, so half the forces for our answer. + + if (distance == spring_length) { + *x = 0.0; + *y = 0.0; + } else { + igraph_i_determine_electric_axal_forces(pos, x, y, directed_force, distance, + other_node, this_node); + if (distance < spring_length) { + *x = -1 * *x; + *y = -1 * *y; + } + *x = 0.5 * *x; + *y = 0.5 * *y; + } + + return 0; +} + +static int igraph_i_apply_spring_force( + const igraph_matrix_t *pos, + igraph_vector_t *pending_forces_x, + igraph_vector_t *pending_forces_y, + long int other_node, + long int this_node, igraph_real_t spring_length, + igraph_real_t spring_constant) { + + // determined using Hooke's Law: + // force = -kx + // where: + // k = spring constant + // x = displacement from ideal length in meters + + igraph_real_t distance, displacement, directed_force, x_force, y_force; + distance = igraph_i_distance_between(pos, other_node, this_node); + // let's protect ourselves from division by zero by ignoring two nodes that + // happen to be in the same place. Since we separate all nodes before we + // work on any of them, this will only happen in extremely rare circumstances, + // and when it does, electrical force will probably push one or both of them + // one way or another anyway. + if (distance == 0.0) { + return 0; + } + + displacement = distance - spring_length; + if (displacement < 0) { + displacement = -displacement; + } + directed_force = -1 * spring_constant * displacement; + // remember, this is force directed away from the spring; + // a negative number is back towards the spring (or, in our case, back towards + // the other node) + + // get the force that should be applied to >this< node + igraph_i_determine_spring_axal_forces(pos, &x_force, &y_force, + directed_force, distance, spring_length, + other_node, this_node); + + VECTOR(*pending_forces_x)[this_node] += x_force; + VECTOR(*pending_forces_y)[this_node] += y_force; + VECTOR(*pending_forces_x)[other_node] -= x_force; + VECTOR(*pending_forces_y)[other_node] -= y_force; + + return 0; +} + +static int igraph_i_move_nodes( + igraph_matrix_t *pos, + const igraph_vector_t *pending_forces_x, + const igraph_vector_t *pending_forces_y, + igraph_real_t node_mass, + igraph_real_t max_sa_movement) { + + // Since each iteration is isolated, time is constant at 1. + // Therefore: + // Force effects acceleration. + // acceleration (d(velocity)/time) = velocity + // velocity (d(displacement)/time) = displacement + // displacement = acceleration + + // determined using Newton's second law: + // sum(F) = ma + // therefore: + // acceleration = force / mass + // velocity = force / mass + // displacement = force / mass + + long int this_node, no_of_nodes = igraph_vector_size(pending_forces_x); + + for (this_node = 0; this_node < no_of_nodes; this_node++) { + + igraph_real_t x_movement, y_movement; + + x_movement = VECTOR(*pending_forces_x)[this_node] / node_mass; + if (x_movement > max_sa_movement) { + x_movement = max_sa_movement; + } else if (x_movement < -max_sa_movement) { + x_movement = -max_sa_movement; + } + + y_movement = VECTOR(*pending_forces_y)[this_node] / node_mass; + if (y_movement > max_sa_movement) { + y_movement = max_sa_movement; + } else if (y_movement < -max_sa_movement) { + y_movement = -max_sa_movement; + } + + MATRIX(*pos, this_node, 0) += x_movement; + MATRIX(*pos, this_node, 1) += y_movement; + + } + return 0; +} + +/** + * \function igraph_layout_graphopt + * \brief Optimizes vertex layout via the graphopt algorithm. + * + * + * This is a port of the graphopt layout algorithm by Michael Schmuhl. + * graphopt version 0.4.1 was rewritten in C and the support for + * layers was removed (might be added later) and a code was a bit + * reorganized to avoid some unnecessary steps is the node charge (see below) + * is zero. + * + * + * Graphopt uses physical analogies for defining attracting and repelling + * forces among the vertices and then the physical system is simulated + * until it reaches an equilibrium. (There is no simulated annealing or + * anything like that, so a stable fixed point is not guaranteed.) + * + * + * See also http://www.schmuhl.org/graphopt/ for the original graphopt. + * \param graph The input graph. + * \param res Pointer to an initialized matrix, the result will be stored here + * and its initial contents are used as the starting point of the simulation + * if the \p use_seed argument is true. Note that in this case the + * matrix should have the proper size, otherwise a warning is issued and + * the supplied values are ignored. If no starting positions are given + * (or they are invalid) then a random starting position is used. + * The matrix will be resized if needed. + * \param niter Integer constant, the number of iterations to perform. + * Should be a couple of hundred in general. If you have a large graph + * then you might want to only do a few iterations and then check the + * result. If it is not good enough you can feed it in again in + * the \p res argument. The original graphopt default is 500. + * \param node_charge The charge of the vertices, used to calculate electric + * repulsion. The original graphopt default is 0.001. + * \param node_mass The mass of the vertices, used for the spring forces. + * The original graphopt defaults to 30. + * \param spring_length The length of the springs. + * The original graphopt defaults to zero. + * \param spring_constant The spring constant, the original graphopt defaults + * to one. + * \param max_sa_movement Real constant, it gives the maximum amount of movement + * allowed in a single step along a single axis. The original graphopt + * default is 5. + * \param use_seed Logical scalar, whether to use the positions in \p res as + * a starting configuration. See also \p res above. + * \return Error code. + * + * Time complexity: O(n (|V|^2+|E|) ), n is the number of iterations, + * |V| is the number of vertices, |E| the number + * of edges. If \p node_charge is zero then it is only O(n|E|). + */ +int igraph_layout_graphopt(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t niter, + igraph_real_t node_charge, igraph_real_t node_mass, + igraph_real_t spring_length, + igraph_real_t spring_constant, + igraph_real_t max_sa_movement, + igraph_bool_t use_seed) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_vector_t pending_forces_x, pending_forces_y; + /* Set a flag to calculate (or not) the electrical forces that the nodes */ + /* apply on each other based on if both node types' charges are zero. */ + igraph_bool_t apply_electric_charges = (node_charge != 0); + + long int this_node, other_node, edge; + igraph_real_t distance; + long int i; + + IGRAPH_VECTOR_INIT_FINALLY(&pending_forces_x, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&pending_forces_y, no_of_nodes); + + if (use_seed) { + if (igraph_matrix_nrow(res) != no_of_nodes || + igraph_matrix_ncol(res) != 2) { + IGRAPH_WARNING("Invalid size for initial matrix, starting from random layout."); + IGRAPH_CHECK(igraph_layout_random(graph, res)); + } + } else { + IGRAPH_CHECK(igraph_layout_random(graph, res)); + } + + IGRAPH_PROGRESS("Graphopt layout", 0, NULL); + for (i = niter; i > 0; i--) { + /* Report progress in approx. every 100th step */ + if (i % 10 == 0) { + IGRAPH_PROGRESS("Graphopt layout", 100.0 - 100.0 * i / niter, NULL); + } + + /* Clear pending forces on all nodes */ + igraph_vector_null(&pending_forces_x); + igraph_vector_null(&pending_forces_y); + + // Apply electrical force applied by all other nodes + if (apply_electric_charges) { + // Iterate through all nodes + for (this_node = 0; this_node < no_of_nodes; this_node++) { + IGRAPH_ALLOW_INTERRUPTION(); + for (other_node = this_node + 1; + other_node < no_of_nodes; + other_node++) { + distance = igraph_i_distance_between(res, this_node, other_node); + // let's protect ourselves from division by zero by ignoring + // two nodes that happen to be in the same place. Since we + // separate all nodes before we work on any of them, this + // will only happen in extremely rare circumstances, and when + // it does, springs will probably pull them apart anyway. + // also, if we are more than 50 away, the electric force + // will be negligible. + // ***** may not always be desirable **** + if ((distance != 0.0) && (distance < 500.0)) { + // if (distance != 0.0) { + // Apply electrical force from node(counter2) on + // node(counter) + igraph_i_apply_electrical_force(res, &pending_forces_x, + &pending_forces_y, + other_node, this_node, + node_charge, + distance); + } + } + } + } + + // Apply force from springs + for (edge = 0; edge < no_of_edges; edge++) { + long int tthis_node = IGRAPH_FROM(graph, edge); + long int oother_node = IGRAPH_TO(graph, edge); + // Apply spring force on both nodes + igraph_i_apply_spring_force(res, &pending_forces_x, &pending_forces_y, + oother_node, tthis_node, spring_length, + spring_constant); + } + + // Effect the movement of the nodes based on all pending forces + igraph_i_move_nodes(res, &pending_forces_x, &pending_forces_y, node_mass, + max_sa_movement); + } + IGRAPH_PROGRESS("Graphopt layout", 100, NULL); + + igraph_vector_destroy(&pending_forces_y); + igraph_vector_destroy(&pending_forces_x); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/layout/kamada_kawai.c b/src/rigraph/core/layout/kamada_kawai.c new file mode 100644 index 0000000..5db2e9b --- /dev/null +++ b/src/rigraph/core/layout/kamada_kawai.c @@ -0,0 +1,718 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" +#include "igraph_paths.h" +#include "igraph_random.h" + +#include "../core/interruption.h" + +/** + * \ingroup layout + * \function igraph_layout_kamada_kawai + * \brief Places the vertices on a plane according to the Kamada-Kawai algorithm. + * + * This is a force-directed layout. A spring is inserted between all pairs + * of vertices, both those which are directly connected and those that are not. + * The unstretched length of springs is chosen based on the graph distance + * between the corresponding pair of vertices. Thus, in a weighted graph, increasing + * the weight between two vertices pushes them apart. The Young modulus of springs + * is inversely proportional to the graph distance, ensuring that springs between + * far-apart veritces will have a smaller effect on the layout. + * + * + * This layout algorithm is not suitable for large graphs. The memory + * requirements are of the order O(|V|^2). + * + * + * Reference: + * + * + * Kamada, T. and Kawai, S.: + * An Algorithm for Drawing General Undirected Graphs. + * Information Processing Letters, 31/1, 7--15, 1989. + * https://doi.org/10.1016/0020-0190(89)90102-6 + * + * \param graph A graph object. + * \param res Pointer to an initialized matrix object. This will + * contain the result (x-positions in column zero and + * y-positions in column one) and will be resized if needed. + * \param use_seed Boolean, whether to use the values supplied in the + * \p res argument as the initial configuration. If zero and there + * are any limits on the X or Y coordinates, then a random initial + * configuration is used. Otherwise the vertices are placed on a + * circle of radius 1 as the initial configuration. + * \param maxiter The maximum number of iterations to perform. A reasonable + * default value is at least ten (or more) times the number of + * vertices. + * \param epsilon Stop the iteration, if the maximum delta value of the + * algorithm is smaller than still. It is safe to leave it at zero, + * and then \p maxiter iterations are performed. + * \param kkconst The Kamada-Kawai vertex attraction constant. + * Typical value: number of vertices. + * \param weights Edge weights, larger values will result longer edges. + * \param minx Pointer to a vector, or a \c NULL pointer. If not a + * \c NULL pointer then the vector gives the minimum + * \quote x \endquote coordinate for every vertex. + * \param maxx Same as \p minx, but the maximum \quote x \endquote + * coordinates. + * \param miny Pointer to a vector, or a \c NULL pointer. If not a + * \c NULL pointer then the vector gives the minimum + * \quote y \endquote coordinate for every vertex. + * \param maxy Same as \p miny, but the maximum \quote y \endquote + * coordinates. + * \return Error code. + * + * Time complexity: O(|V|) for each iteration, after an O(|V|^2 + * log|V|) initialization step. |V| is the number of vertices in the + * graph. + */ + +int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, + igraph_bool_t use_seed, igraph_integer_t maxiter, + igraph_real_t epsilon, igraph_real_t kkconst, + const igraph_vector_t *weights, + const igraph_vector_t *minx, const igraph_vector_t *maxx, + const igraph_vector_t *miny, const igraph_vector_t *maxy) { + + igraph_integer_t no_nodes = igraph_vcount(graph); + igraph_integer_t no_edges = igraph_ecount(graph); + igraph_real_t L, L0 = sqrt(no_nodes); + igraph_matrix_t dij, lij, kij; + igraph_real_t max_dij; + igraph_vector_t D1, D2; + igraph_integer_t i, j, m; + + if (maxiter < 0) { + IGRAPH_ERROR("Number of iterations must be non-negatice in " + "Kamada-Kawai layout", IGRAPH_EINVAL); + } + if (kkconst <= 0) { + IGRAPH_ERROR("`K' constant must be positive in Kamada-Kawai layout", + IGRAPH_EINVAL); + } + + if (use_seed && (igraph_matrix_nrow(res) != no_nodes || + igraph_matrix_ncol(res) != 2)) { + IGRAPH_ERROR("Invalid start position matrix size in " + "Kamada-Kawai layout", IGRAPH_EINVAL); + } + if (weights && igraph_vector_size(weights) != no_edges) { + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + } + + if (minx && igraph_vector_size(minx) != no_nodes) { + IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL); + } + if (maxx && igraph_vector_size(maxx) != no_nodes) { + IGRAPH_ERROR("Invalid maxx vector length", IGRAPH_EINVAL); + } + if (minx && maxx && !igraph_vector_all_le(minx, maxx)) { + IGRAPH_ERROR("minx must not be greater than maxx", IGRAPH_EINVAL); + } + if (miny && igraph_vector_size(miny) != no_nodes) { + IGRAPH_ERROR("Invalid miny vector length", IGRAPH_EINVAL); + } + if (maxy && igraph_vector_size(maxy) != no_nodes) { + IGRAPH_ERROR("Invalid maxy vector length", IGRAPH_EINVAL); + } + if (miny && maxy && !igraph_vector_all_le(miny, maxy)) { + IGRAPH_ERROR("miny must not be greater than maxy", IGRAPH_EINVAL); + } + + if (!use_seed) { + if (minx || maxx || miny || maxy) { + const igraph_real_t width = sqrt(no_nodes), height = width; + IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2)); + RNG_BEGIN(); + for (i = 0; i < no_nodes; i++) { + igraph_real_t x1 = minx ? VECTOR(*minx)[i] : -width / 2; + igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] : width / 2; + igraph_real_t y1 = miny ? VECTOR(*miny)[i] : -height / 2; + igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] : height / 2; + if (!igraph_finite(x1)) { + x1 = -width / 2; + } + if (!igraph_finite(x2)) { + x2 = width / 2; + } + if (!igraph_finite(y1)) { + y1 = -height / 2; + } + if (!igraph_finite(y2)) { + y2 = height / 2; + } + MATRIX(*res, i, 0) = RNG_UNIF(x1, x2); + MATRIX(*res, i, 1) = RNG_UNIF(y1, y2); + } + RNG_END(); + } else { + igraph_layout_circle(graph, res, /* order= */ igraph_vss_all()); + } + } + + if (no_nodes <= 1) { + return 0; + } + + IGRAPH_MATRIX_INIT_FINALLY(&dij, no_nodes, no_nodes); + IGRAPH_MATRIX_INIT_FINALLY(&kij, no_nodes, no_nodes); + IGRAPH_MATRIX_INIT_FINALLY(&lij, no_nodes, no_nodes); + + if (weights && no_edges > 0 && igraph_vector_min(weights) < 0) { + IGRAPH_CHECK(igraph_shortest_paths_bellman_ford(graph, &dij, igraph_vss_all(), + igraph_vss_all(), weights, + IGRAPH_ALL)); + } else { + + IGRAPH_CHECK(igraph_shortest_paths_dijkstra(graph, &dij, igraph_vss_all(), + igraph_vss_all(), weights, + IGRAPH_ALL)); + } + + max_dij = 0.0; + for (i = 0; i < no_nodes; i++) { + for (j = i + 1; j < no_nodes; j++) { + if (!igraph_finite(MATRIX(dij, i, j))) { + continue; + } + if (MATRIX(dij, i, j) > max_dij) { + max_dij = MATRIX(dij, i, j); + } + } + } + for (i = 0; i < no_nodes; i++) { + for (j = 0; j < no_nodes; j++) { + if (MATRIX(dij, i, j) > max_dij) { + MATRIX(dij, i, j) = max_dij; + } + } + } + + L = L0 / max_dij; + for (i = 0; i < no_nodes; i++) { + for (j = 0; j < no_nodes; j++) { + igraph_real_t tmp = MATRIX(dij, i, j) * MATRIX(dij, i, j); + if (i == j) { + continue; + } + MATRIX(kij, i, j) = kkconst / tmp; + MATRIX(lij, i, j) = L * MATRIX(dij, i, j); + } + } + + /* Initialize delta */ + IGRAPH_VECTOR_INIT_FINALLY(&D1, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&D2, no_nodes); + for (m = 0; m < no_nodes; m++) { + igraph_real_t myD1 = 0.0, myD2 = 0.0; + for (i = 0; i < no_nodes; i++) { + igraph_real_t dx, dy, mi_dist; + if (i == m) { + continue; + } + dx = MATRIX(*res, m, 0) - MATRIX(*res, i, 0); + dy = MATRIX(*res, m, 1) - MATRIX(*res, i, 1); + mi_dist = sqrt(dx * dx + dy * dy); + myD1 += MATRIX(kij, m, i) * (dx - MATRIX(lij, m, i) * dx / mi_dist); + myD2 += MATRIX(kij, m, i) * (dy - MATRIX(lij, m, i) * dy / mi_dist); + } + VECTOR(D1)[m] = myD1; + VECTOR(D2)[m] = myD2; + } + + for (j = 0; j < maxiter; j++) { + igraph_real_t myD1, myD2, A, B, C; + igraph_real_t max_delta, delta_x, delta_y; + igraph_real_t old_x, old_y, new_x, new_y; + + IGRAPH_ALLOW_INTERRUPTION(); + + myD1 = 0.0, myD2 = 0.0, A = 0.0, B = 0.0, C = 0.0; + + /* Select maximal delta */ + m = 0; max_delta = -1; + for (i = 0; i < no_nodes; i++) { + igraph_real_t delta = (VECTOR(D1)[i] * VECTOR(D1)[i] + + VECTOR(D2)[i] * VECTOR(D2)[i]); + if (delta > max_delta) { + m = i; max_delta = delta; + } + } + if (max_delta < epsilon) { + break; + } + old_x = MATRIX(*res, m, 0); + old_y = MATRIX(*res, m, 1); + + /* Calculate D1 and D2, A, B, C */ + for (i = 0; i < no_nodes; i++) { + igraph_real_t dx, dy, dist, den; + if (i == m) { + continue; + } + dx = old_x - MATRIX(*res, i, 0); + dy = old_y - MATRIX(*res, i, 1); + dist = sqrt(dx * dx + dy * dy); + den = dist * (dx * dx + dy * dy); + A += MATRIX(kij, m, i) * (1 - MATRIX(lij, m, i) * dy * dy / den); + B += MATRIX(kij, m, i) * MATRIX(lij, m, i) * dx * dy / den; + C += MATRIX(kij, m, i) * (1 - MATRIX(lij, m, i) * dx * dx / den); + } + myD1 = VECTOR(D1)[m]; + myD2 = VECTOR(D2)[m]; + + /* Need to solve some linear equations */ + delta_y = (B * myD1 - myD2 * A) / (C * A - B * B); + delta_x = - (myD1 + B * delta_y) / A; + + new_x = old_x + delta_x; + new_y = old_y + delta_y; + + /* Limits, if given */ + if (minx && new_x < VECTOR(*minx)[m]) { + new_x = VECTOR(*minx)[m]; + } + if (maxx && new_x > VECTOR(*maxx)[m]) { + new_x = VECTOR(*maxx)[m]; + } + if (miny && new_y < VECTOR(*miny)[m]) { + new_y = VECTOR(*miny)[m]; + } + if (maxy && new_y > VECTOR(*maxy)[m]) { + new_y = VECTOR(*maxy)[m]; + } + + /* Update delta, only with/for the affected node */ + VECTOR(D1)[m] = VECTOR(D2)[m] = 0.0; + for (i = 0; i < no_nodes; i++) { + igraph_real_t old_dx, old_dy, new_dx, new_dy, new_mi_dist, old_mi_dist; + if (i == m) { + continue; + } + old_dx = old_x - MATRIX(*res, i, 0); + old_dy = old_y - MATRIX(*res, i, 1); + old_mi_dist = sqrt(old_dx * old_dx + old_dy * old_dy); + new_dx = new_x - MATRIX(*res, i, 0); + new_dy = new_y - MATRIX(*res, i, 1); + new_mi_dist = sqrt(new_dx * new_dx + new_dy * new_dy); + + VECTOR(D1)[i] -= MATRIX(kij, m, i) * + (-old_dx + MATRIX(lij, m, i) * old_dx / old_mi_dist); + VECTOR(D2)[i] -= MATRIX(kij, m, i) * + (-old_dy + MATRIX(lij, m, i) * old_dy / old_mi_dist); + VECTOR(D1)[i] += MATRIX(kij, m, i) * + (-new_dx + MATRIX(lij, m, i) * new_dx / new_mi_dist); + VECTOR(D2)[i] += MATRIX(kij, m, i) * + (-new_dy + MATRIX(lij, m, i) * new_dy / new_mi_dist); + + VECTOR(D1)[m] += MATRIX(kij, m, i) * + (new_dx - MATRIX(lij, m, i) * new_dx / new_mi_dist); + VECTOR(D2)[m] += MATRIX(kij, m, i) * + (new_dy - MATRIX(lij, m, i) * new_dy / new_mi_dist); + } + + /* Update coordinates*/ + MATRIX(*res, m, 0) = new_x; + MATRIX(*res, m, 1) = new_y; + } + + igraph_vector_destroy(&D2); + igraph_vector_destroy(&D1); + igraph_matrix_destroy(&lij); + igraph_matrix_destroy(&kij); + igraph_matrix_destroy(&dij); + IGRAPH_FINALLY_CLEAN(5); + + return 0; +} + +/** + * \ingroup layout + * \function igraph_layout_kamada_kawai_3d + * \brief 3D version of the Kamada-Kawai layout generator. + * + * This is the 3D version of igraph_layout_kamada_kawai(). + * See the documentation of that function for more information. + * + * + * This layout algorithm is not suitable for large graphs. The memory + * requirements are of the order O(|V|^2). + * + * \param graph A graph object. + * \param res Pointer to an initialized matrix object. This will + * contain the result (x-, y- and z-positions in columns one + * through three) and will be resized if needed. + * \param use_seed Boolean, whether to use the values supplied in the + * \p res argument as the initial configuration. If zero and there + * are any limits on the z, y or z coordinates, then a random initial + * configuration is used. Otherwise the vertices are placed uniformly + * on a sphere of radius 1 as the initial configuration. + * \param maxiter The maximum number of iterations to perform. A reasonable + * default value is at least ten (or more) times the number of + * vertices. + * \param epsilon Stop the iteration, if the maximum delta value of the + * algorithm is smaller than still. It is safe to leave it at zero, + * and then \p maxiter iterations are performed. + * \param kkconst The Kamada-Kawai vertex attraction constant. + * Typical value: number of vertices. + * \param weights Edge weights, larger values will result longer edges. + * \param minx Pointer to a vector, or a \c NULL pointer. If not a + * \c NULL pointer then the vector gives the minimum + * \quote x \endquote coordinate for every vertex. + * \param maxx Same as \p minx, but the maximum \quote x \endquote + * coordinates. + * \param miny Pointer to a vector, or a \c NULL pointer. If not a + * \c NULL pointer then the vector gives the minimum + * \quote y \endquote coordinate for every vertex. + * \param maxy Same as \p miny, but the maximum \quote y \endquote + * coordinates. + * \param minz Pointer to a vector, or a \c NULL pointer. If not a + * \c NULL pointer then the vector gives the minimum + * \quote z \endquote coordinate for every vertex. + * \param maxz Same as \p minz, but the maximum \quote z \endquote + * coordinates. + * \return Error code. + * + * Time complexity: O(|V|) for each iteration, after an O(|V|^2 + * log|V|) initialization step. |V| is the number of vertices in the + * graph. + */ + +int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, + igraph_bool_t use_seed, igraph_integer_t maxiter, + igraph_real_t epsilon, igraph_real_t kkconst, + const igraph_vector_t *weights, + const igraph_vector_t *minx, const igraph_vector_t *maxx, + const igraph_vector_t *miny, const igraph_vector_t *maxy, + const igraph_vector_t *minz, const igraph_vector_t *maxz) { + + igraph_integer_t no_nodes = igraph_vcount(graph); + igraph_integer_t no_edges = igraph_ecount(graph); + igraph_real_t L, L0 = sqrt(no_nodes); + igraph_matrix_t dij, lij, kij; + igraph_real_t max_dij; + igraph_vector_t D1, D2, D3; + igraph_integer_t i, j, m; + + if (maxiter < 0) { + IGRAPH_ERROR("Number of iterations must be non-negatice in " + "Kamada-Kawai layout", IGRAPH_EINVAL); + } + if (kkconst <= 0) { + IGRAPH_ERROR("`K' constant must be positive in Kamada-Kawai layout", + IGRAPH_EINVAL); + } + + if (use_seed && (igraph_matrix_nrow(res) != no_nodes || + igraph_matrix_ncol(res) != 3)) { + IGRAPH_ERROR("Invalid start position matrix size in " + "3d Kamada-Kawai layout", IGRAPH_EINVAL); + } + if (weights && igraph_vector_size(weights) != no_edges) { + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + } + + if (minx && igraph_vector_size(minx) != no_nodes) { + IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL); + } + if (maxx && igraph_vector_size(maxx) != no_nodes) { + IGRAPH_ERROR("Invalid maxx vector length", IGRAPH_EINVAL); + } + if (minx && maxx && !igraph_vector_all_le(minx, maxx)) { + IGRAPH_ERROR("minx must not be greater than maxx", IGRAPH_EINVAL); + } + if (miny && igraph_vector_size(miny) != no_nodes) { + IGRAPH_ERROR("Invalid miny vector length", IGRAPH_EINVAL); + } + if (maxy && igraph_vector_size(maxy) != no_nodes) { + IGRAPH_ERROR("Invalid maxy vector length", IGRAPH_EINVAL); + } + if (miny && maxy && !igraph_vector_all_le(miny, maxy)) { + IGRAPH_ERROR("miny must not be greater than maxy", IGRAPH_EINVAL); + } + if (minz && igraph_vector_size(minz) != no_nodes) { + IGRAPH_ERROR("Invalid minz vector length", IGRAPH_EINVAL); + } + if (maxz && igraph_vector_size(maxz) != no_nodes) { + IGRAPH_ERROR("Invalid maxz vector length", IGRAPH_EINVAL); + } + if (minz && maxz && !igraph_vector_all_le(minz, maxz)) { + IGRAPH_ERROR("minz must not be greater than maxz", IGRAPH_EINVAL); + } + + if (!use_seed) { + if (minx || maxx || miny || maxy || minz || maxz) { + const igraph_real_t width = sqrt(no_nodes), height = width, depth = width; + IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 3)); + RNG_BEGIN(); + for (i = 0; i < no_nodes; i++) { + igraph_real_t x1 = minx ? VECTOR(*minx)[i] : -width / 2; + igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] : width / 2; + igraph_real_t y1 = miny ? VECTOR(*miny)[i] : -height / 2; + igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] : height / 2; + igraph_real_t z1 = minz ? VECTOR(*minz)[i] : -depth / 2; + igraph_real_t z2 = maxz ? VECTOR(*maxz)[i] : depth / 2; + if (!igraph_finite(x1)) { + x1 = -width / 2; + } + if (!igraph_finite(x2)) { + x2 = width / 2; + } + if (!igraph_finite(y1)) { + y1 = -height / 2; + } + if (!igraph_finite(y2)) { + y2 = height / 2; + } + if (!igraph_finite(z1)) { + z1 = -depth / 2; + } + if (!igraph_finite(z2)) { + z2 = depth / 2; + } + MATRIX(*res, i, 0) = RNG_UNIF(x1, x2); + MATRIX(*res, i, 1) = RNG_UNIF(y1, y2); + MATRIX(*res, i, 2) = RNG_UNIF(z1, z2); + } + RNG_END(); + } else { + igraph_layout_sphere(graph, res); + } + } + + if (no_nodes <= 1) { + return 0; + } + + IGRAPH_MATRIX_INIT_FINALLY(&dij, no_nodes, no_nodes); + IGRAPH_MATRIX_INIT_FINALLY(&kij, no_nodes, no_nodes); + IGRAPH_MATRIX_INIT_FINALLY(&lij, no_nodes, no_nodes); + IGRAPH_CHECK(igraph_shortest_paths_dijkstra(graph, &dij, igraph_vss_all(), + igraph_vss_all(), weights, + IGRAPH_ALL)); + + max_dij = 0.0; + for (i = 0; i < no_nodes; i++) { + for (j = i + 1; j < no_nodes; j++) { + if (!igraph_finite(MATRIX(dij, i, j))) { + continue; + } + if (MATRIX(dij, i, j) > max_dij) { + max_dij = MATRIX(dij, i, j); + } + } + } + for (i = 0; i < no_nodes; i++) { + for (j = 0; j < no_nodes; j++) { + if (MATRIX(dij, i, j) > max_dij) { + MATRIX(dij, i, j) = max_dij; + } + } + } + + L = L0 / max_dij; + for (i = 0; i < no_nodes; i++) { + for (j = 0; j < no_nodes; j++) { + igraph_real_t tmp = MATRIX(dij, i, j) * MATRIX(dij, i, j); + if (i == j) { + continue; + } + MATRIX(kij, i, j) = kkconst / tmp; + MATRIX(lij, i, j) = L * MATRIX(dij, i, j); + } + } + + /* Initialize delta */ + IGRAPH_VECTOR_INIT_FINALLY(&D1, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&D2, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&D3, no_nodes); + for (m = 0; m < no_nodes; m++) { + igraph_real_t dx, dy, dz, mi_dist; + igraph_real_t myD1 = 0.0, myD2 = 0.0, myD3 = 0.0; + for (i = 0; i < no_nodes; i++) { + if (i == m) { + continue; + } + dx = MATRIX(*res, m, 0) - MATRIX(*res, i, 0); + dy = MATRIX(*res, m, 1) - MATRIX(*res, i, 1); + dz = MATRIX(*res, m, 2) - MATRIX(*res, i, 2); + mi_dist = sqrt(dx * dx + dy * dy + dz * dz); + myD1 += MATRIX(kij, m, i) * (dx - MATRIX(lij, m, i) * dx / mi_dist); + myD2 += MATRIX(kij, m, i) * (dy - MATRIX(lij, m, i) * dy / mi_dist); + myD3 += MATRIX(kij, m, i) * (dz - MATRIX(lij, m, i) * dz / mi_dist); + } + VECTOR(D1)[m] = myD1; + VECTOR(D2)[m] = myD2; + VECTOR(D3)[m] = myD3; + } + + for (j = 0; j < maxiter; j++) { + + igraph_real_t Ax = 0.0, Ay = 0.0, Az = 0.0; + igraph_real_t Axx = 0.0, Axy = 0.0, Axz = 0.0, Ayy = 0.0, Ayz = 0.0, Azz = 0.0; + igraph_real_t max_delta, delta_x, delta_y, delta_z; + igraph_real_t old_x, old_y, old_z, new_x, new_y, new_z; + igraph_real_t detnum; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Select maximal delta */ + m = 0; max_delta = -1; + for (i = 0; i < no_nodes; i++) { + igraph_real_t delta = (VECTOR(D1)[i] * VECTOR(D1)[i] + + VECTOR(D2)[i] * VECTOR(D2)[i] + + VECTOR(D3)[i] * VECTOR(D3)[i]); + if (delta > max_delta) { + m = i; max_delta = delta; + } + } + if (max_delta < epsilon) { + break; + } + old_x = MATRIX(*res, m, 0); + old_y = MATRIX(*res, m, 1); + old_z = MATRIX(*res, m, 2); + + /* Calculate D1, D2 and D3, and other coefficients */ + for (i = 0; i < no_nodes; i++) { + igraph_real_t dx, dy, dz, dist, den, k_mi, l_mi; + if (i == m) { + continue; + } + dx = old_x - MATRIX(*res, i, 0); + dy = old_y - MATRIX(*res, i, 1); + dz = old_z - MATRIX(*res, i, 2); + dist = sqrt(dx * dx + dy * dy + dz * dz); + den = dist * (dx * dx + dy * dy + dz * dz); + + k_mi = MATRIX(kij, m, i); + l_mi = MATRIX(lij, m, i); + Axx += k_mi * (1 - l_mi * (dy * dy + dz * dz) / den); + Ayy += k_mi * (1 - l_mi * (dx * dx + dz * dz) / den); + Azz += k_mi * (1 - l_mi * (dx * dx + dy * dy) / den); + Axy += k_mi * l_mi * dx * dy / den; + Axz += k_mi * l_mi * dx * dz / den; + Ayz += k_mi * l_mi * dy * dz / den; + } + Ax = -VECTOR(D1)[m]; + Ay = -VECTOR(D2)[m]; + Az = -VECTOR(D3)[m]; + + /* Need to solve some linear equations, we just use Cramer's rule */ +#define DET(a,b,c,d,e,f,g,h,i) ((a*e*i+b*f*g+c*d*h)-(c*e*g+b*d*i+a*f*h)) + + detnum = DET(Axx, Axy, Axz, Axy, Ayy, Ayz, Axz, Ayz, Azz); + if (detnum != 0) { + delta_x = DET(Ax, Ay, Az, Axy, Ayy, Ayz, Axz, Ayz, Azz) / detnum; + delta_y = DET(Axx, Axy, Axz, Ax, Ay, Az, Axz, Ayz, Azz) / detnum; + delta_z = DET(Axx, Axy, Axz, Axy, Ayy, Ayz, Ax, Ay, Az ) / detnum; + } else { + /* No new stable position for node m; this can happen in rare + * cases, e.g., if the graph has two nodes only. It's best to leave + * the node where it is. */ + delta_x = delta_y = delta_z = 0; + } + + new_x = old_x + delta_x; + new_y = old_y + delta_y; + new_z = old_z + delta_z; + + /* Limits, if given */ + if (minx && new_x < VECTOR(*minx)[m]) { + new_x = VECTOR(*minx)[m]; + } + if (maxx && new_x > VECTOR(*maxx)[m]) { + new_x = VECTOR(*maxx)[m]; + } + if (miny && new_y < VECTOR(*miny)[m]) { + new_y = VECTOR(*miny)[m]; + } + if (maxy && new_y > VECTOR(*maxy)[m]) { + new_y = VECTOR(*maxy)[m]; + } + if (minz && new_z < VECTOR(*minz)[m]) { + new_z = VECTOR(*minz)[m]; + } + if (maxz && new_z > VECTOR(*maxz)[m]) { + new_z = VECTOR(*maxz)[m]; + } + + /* Update delta, only with/for the affected node */ + VECTOR(D1)[m] = VECTOR(D2)[m] = VECTOR(D3)[m] = 0.0; + for (i = 0; i < no_nodes; i++) { + igraph_real_t old_dx, old_dy, old_dz, old_mi_dist, new_dx, new_dy, new_dz, new_mi_dist; + if (i == m) { + continue; + } + old_dx = old_x - MATRIX(*res, i, 0); + old_dy = old_y - MATRIX(*res, i, 1); + old_dz = old_z - MATRIX(*res, i, 2); + old_mi_dist = sqrt(old_dx * old_dx + old_dy * old_dy + + old_dz * old_dz); + new_dx = new_x - MATRIX(*res, i, 0); + new_dy = new_y - MATRIX(*res, i, 1); + new_dz = new_z - MATRIX(*res, i, 2); + new_mi_dist = sqrt(new_dx * new_dx + new_dy * new_dy + + new_dz * new_dz); + + VECTOR(D1)[i] -= MATRIX(kij, m, i) * + (-old_dx + MATRIX(lij, m, i) * old_dx / old_mi_dist); + VECTOR(D2)[i] -= MATRIX(kij, m, i) * + (-old_dy + MATRIX(lij, m, i) * old_dy / old_mi_dist); + VECTOR(D3)[i] -= MATRIX(kij, m, i) * + (-old_dz + MATRIX(lij, m, i) * old_dz / old_mi_dist); + + VECTOR(D1)[i] += MATRIX(kij, m, i) * + (-new_dx + MATRIX(lij, m, i) * new_dx / new_mi_dist); + VECTOR(D2)[i] += MATRIX(kij, m, i) * + (-new_dy + MATRIX(lij, m, i) * new_dy / new_mi_dist); + VECTOR(D3)[i] += MATRIX(kij, m, i) * + (-new_dz + MATRIX(lij, m, i) * new_dz / new_mi_dist); + + VECTOR(D1)[m] += MATRIX(kij, m, i) * + (new_dx - MATRIX(lij, m, i) * new_dx / new_mi_dist); + VECTOR(D2)[m] += MATRIX(kij, m, i) * + (new_dy - MATRIX(lij, m, i) * new_dy / new_mi_dist); + VECTOR(D3)[m] += MATRIX(kij, m, i) * + (new_dz - MATRIX(lij, m, i) * new_dz / new_mi_dist); + } + + /* Update coordinates*/ + MATRIX(*res, m, 0) = new_x; + MATRIX(*res, m, 1) = new_y; + MATRIX(*res, m, 2) = new_z; + } + + igraph_vector_destroy(&D3); + igraph_vector_destroy(&D2); + igraph_vector_destroy(&D1); + igraph_matrix_destroy(&lij); + igraph_matrix_destroy(&kij); + igraph_matrix_destroy(&dij); + IGRAPH_FINALLY_CLEAN(6); + + return 0; +} diff --git a/src/rigraph/core/layout/large_graph.c b/src/rigraph/core/layout/large_graph.c new file mode 100644 index 0000000..cd7cddb --- /dev/null +++ b/src/rigraph/core/layout/large_graph.c @@ -0,0 +1,349 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_random.h" +#include "igraph_structural.h" +#include "igraph_visitor.h" + +#include "../core/grid.h" +#include "../core/interruption.h" +#include "../core/math.h" + +static void igraph_i_norm2d(igraph_real_t *x, igraph_real_t *y) { + igraph_real_t len = sqrt((*x) * (*x) + (*y) * (*y)); + if (len != 0) { + *x /= len; + *y /= len; + } +} + +/** + * \function igraph_layout_lgl + * \brief Force based layout algorithm for large graphs. + * + * + * This is a layout generator similar to the Large Graph Layout + * algorithm and program + * (http://lgl.sourceforge.net/). But unlike LGL, this + * version uses a Fruchterman-Reingold style simulated annealing + * algorithm for placing the vertices. The speedup is achieved by + * placing the vertices on a grid and calculating the repulsion only + * for vertices which are closer to each other than a limit. + * + * \param graph The (initialized) graph object to place. + * \param res Pointer to an initialized matrix object to hold the + * result. It will be resized if needed. + * \param maxit The maximum number of cooling iterations to perform + * for each layout step. A reasonable default is 150. + * \param maxdelta The maximum length of the move allowed for a vertex + * in a single iteration. A reasonable default is the number of + * vertices. + * \param area This parameter gives the area of the square on which + * the vertices will be placed. A reasonable default value is the + * number of vertices squared. + * \param coolexp The cooling exponent. A reasonable default value is + * 1.5. + * \param repulserad Determines the radius at which vertex-vertex + * repulsion cancels out attraction of adjacent vertices. A + * reasonable default value is \p area times the number of vertices. + * \param cellsize The size of the grid cells, one side of the + * square. A reasonable default value is the fourth root of + * \p area (or the square root of the number of vertices if \p area + * is also left at its default value). + * \param proot The root vertex, this is placed first, its neighbors + * in the first iteration, second neighbors in the second, etc. If + * negative then a random vertex is chosen. + * \return Error code. + * + * Added in version 0.2. + * + * Time complexity: ideally O(dia*maxit*(|V|+|E|)), |V| is the number + * of vertices, + * dia is the diameter of the graph, worst case complexity is still + * O(dia*maxit*(|V|^2+|E|)), this is the case when all vertices happen to be + * in the same grid cell. + */ + +int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t maxit, igraph_real_t maxdelta, + igraph_real_t area, igraph_real_t coolexp, + igraph_real_t repulserad, igraph_real_t cellsize, + igraph_integer_t proot) { + + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_t mst; + long int root; + long int no_of_layers, actlayer = 0; + igraph_vector_t vids; + igraph_vector_t layers; + igraph_vector_t parents; + igraph_vector_t edges; + igraph_2dgrid_t grid; + igraph_vector_t eids; + igraph_vector_t forcex; + igraph_vector_t forcey; + + igraph_real_t frk = sqrt(area / no_of_nodes); + igraph_real_t H_n = 0; + + IGRAPH_CHECK(igraph_minimum_spanning_tree_unweighted(graph, &mst)); + IGRAPH_FINALLY(igraph_destroy, &mst); + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); + + /* Determine the root vertex, random pick right now */ + if (proot < 0) { + root = RNG_INTEGER(0, no_of_nodes - 1); + } else { + root = proot; + } + + /* Assign the layers */ + IGRAPH_VECTOR_INIT_FINALLY(&vids, 0); + IGRAPH_VECTOR_INIT_FINALLY(&layers, 0); + IGRAPH_VECTOR_INIT_FINALLY(&parents, 0); + IGRAPH_CHECK(igraph_bfs_simple(&mst, (igraph_integer_t) root, IGRAPH_ALL, &vids, + &layers, &parents)); + no_of_layers = igraph_vector_size(&layers) - 1; + + /* We don't need the mst any more */ + igraph_destroy(&mst); + igraph_empty(&mst, 0, IGRAPH_UNDIRECTED); /* to make finalization work */ + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges)); + IGRAPH_VECTOR_INIT_FINALLY(&eids, 0); + IGRAPH_VECTOR_INIT_FINALLY(&forcex, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&forcey, no_of_nodes); + + /* Place the vertices randomly */ + IGRAPH_CHECK(igraph_layout_random(graph, res)); + igraph_matrix_scale(res, 1e6); + + /* This is the grid for calculating the vertices near to a given vertex */ + IGRAPH_CHECK(igraph_2dgrid_init(&grid, res, + -sqrt(area / M_PI), sqrt(area / M_PI), cellsize, + -sqrt(area / M_PI), sqrt(area / M_PI), cellsize)); + IGRAPH_FINALLY(igraph_2dgrid_destroy, &grid); + + /* Place the root vertex */ + igraph_2dgrid_add(&grid, root, 0, 0); + + for (actlayer = 1; actlayer < no_of_layers; actlayer++) { + H_n += 1.0 / actlayer; + } + + for (actlayer = 1; actlayer < no_of_layers; actlayer++) { + + igraph_real_t c = 1; + long int i, j; + igraph_real_t massx, massy; + igraph_real_t px, py; + igraph_real_t sx, sy; + + long int it = 0; + igraph_real_t epsilon = 10e-6; + igraph_real_t maxchange = epsilon + 1; + long int pairs; + igraph_real_t sconst = sqrt(area / M_PI) / H_n; + igraph_2dgrid_iterator_t vidit; + + /* printf("Layer %li:\n", actlayer); */ + + /*-----------------------------------------*/ + /* Step 1: place the next layer on spheres */ + /*-----------------------------------------*/ + + RNG_BEGIN(); + + j = (long int) VECTOR(layers)[actlayer]; + for (i = (long int) VECTOR(layers)[actlayer - 1]; + i < VECTOR(layers)[actlayer]; i++) { + + long int vid = (long int) VECTOR(vids)[i]; + long int par = (long int) VECTOR(parents)[vid]; + IGRAPH_ALLOW_INTERRUPTION(); + igraph_2dgrid_getcenter(&grid, &massx, &massy); + igraph_i_norm2d(&massx, &massy); + px = MATRIX(*res, vid, 0) - MATRIX(*res, par, 0); + py = MATRIX(*res, vid, 1) - MATRIX(*res, par, 1); + igraph_i_norm2d(&px, &py); + sx = c * (massx + px) + MATRIX(*res, vid, 0); + sy = c * (massy + py) + MATRIX(*res, vid, 1); + + /* The neighbors of 'vid' */ + while (j < VECTOR(layers)[actlayer + 1] && + VECTOR(parents)[(long int)VECTOR(vids)[j]] == vid) { + igraph_real_t rx, ry; + if (actlayer == 1) { + igraph_real_t phi = 2 * M_PI / (VECTOR(layers)[2] - 1) * (j - 1); + rx = cos(phi); + ry = sin(phi); + } else { + rx = RNG_UNIF(-1, 1); + ry = RNG_UNIF(-1, 1); + } + igraph_i_norm2d(&rx, &ry); + rx = rx / actlayer * sconst; + ry = ry / actlayer * sconst; + igraph_2dgrid_add(&grid, (long int) VECTOR(vids)[j], sx + rx, sy + ry); + j++; + } + } + + RNG_END(); + + /*-----------------------------------------*/ + /* Step 2: add the edges of the next layer */ + /*-----------------------------------------*/ + + for (j = (long int) VECTOR(layers)[actlayer]; + j < VECTOR(layers)[actlayer + 1]; j++) { + long int vid = (long int) VECTOR(vids)[j]; + long int k; + IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_CHECK(igraph_incident(graph, &eids, (igraph_integer_t) vid, + IGRAPH_ALL)); + for (k = 0; k < igraph_vector_size(&eids); k++) { + long int eid = (long int) VECTOR(eids)[k]; + igraph_integer_t from, to; + igraph_edge(graph, (igraph_integer_t) eid, &from, &to); + if ((from != vid && igraph_2dgrid_in(&grid, from)) || + (to != vid && igraph_2dgrid_in(&grid, to))) { + igraph_vector_push_back(&edges, eid); + } + } + } + + /*-----------------------------------------*/ + /* Step 3: let the springs spring */ + /*-----------------------------------------*/ + + maxchange = epsilon + 1; + while (it < maxit && maxchange > epsilon) { + long int jj; + igraph_real_t t = maxdelta * pow((maxit - it) / (double)maxit, coolexp); + long int vid, nei; + + IGRAPH_PROGRESS("Large graph layout", + 100.0 * ((actlayer - 1.0) / (no_of_layers - 1.0) + ((float)it) / (maxit * (no_of_layers - 1.0))), + 0); + + /* init */ + igraph_vector_null(&forcex); + igraph_vector_null(&forcey); + maxchange = 0; + + /* attractive "forces" along the edges */ + for (jj = 0; jj < igraph_vector_size(&edges); jj++) { + igraph_integer_t from, to; + igraph_real_t xd, yd, dist, force; + IGRAPH_ALLOW_INTERRUPTION(); + igraph_edge(graph, (igraph_integer_t) VECTOR(edges)[jj], &from, &to); + xd = MATRIX(*res, (long int)from, 0) - MATRIX(*res, (long int)to, 0); + yd = MATRIX(*res, (long int)from, 1) - MATRIX(*res, (long int)to, 1); + dist = sqrt(xd * xd + yd * yd); + if (dist != 0) { + xd /= dist; + yd /= dist; + } + force = dist * dist / frk; + VECTOR(forcex)[(long int)from] -= xd * force; + VECTOR(forcex)[(long int)to] += xd * force; + VECTOR(forcey)[(long int)from] -= yd * force; + VECTOR(forcey)[(long int)to] += yd * force; + } + + /* repulsive "forces" of the vertices nearby */ + pairs = 0; + igraph_2dgrid_reset(&grid, &vidit); + while ( (vid = igraph_2dgrid_next(&grid, &vidit) - 1) != -1) { + while ( (nei = igraph_2dgrid_next_nei(&grid, &vidit) - 1) != -1) { + igraph_real_t xd = MATRIX(*res, (long int)vid, 0) - + MATRIX(*res, (long int)nei, 0); + igraph_real_t yd = MATRIX(*res, (long int)vid, 1) - + MATRIX(*res, (long int)nei, 1); + igraph_real_t dist = sqrt(xd * xd + yd * yd); + igraph_real_t force; + if (dist < cellsize) { + pairs++; + if (dist == 0) { + dist = epsilon; + }; + xd /= dist; yd /= dist; + force = frk * frk * (1.0 / dist - dist * dist / repulserad); + VECTOR(forcex)[(long int)vid] += xd * force; + VECTOR(forcex)[(long int)nei] -= xd * force; + VECTOR(forcey)[(long int)vid] += yd * force; + VECTOR(forcey)[(long int)nei] -= yd * force; + } + } + } + + /* printf("verties: %li iterations: %li\n", */ + /* (long int) VECTOR(layers)[actlayer+1], pairs); */ + + /* apply the changes */ + for (jj = 0; jj < VECTOR(layers)[actlayer + 1]; jj++) { + long int vvid = (long int) VECTOR(vids)[jj]; + igraph_real_t fx = VECTOR(forcex)[vvid]; + igraph_real_t fy = VECTOR(forcey)[vvid]; + igraph_real_t ded = sqrt(fx * fx + fy * fy); + if (ded > t) { + ded = t / ded; + fx *= ded; fy *= ded; + } + igraph_2dgrid_move(&grid, vvid, fx, fy); + if (fx > maxchange) { + maxchange = fx; + } + if (fy > maxchange) { + maxchange = fy; + } + } + it++; + /* printf("%li iterations, maxchange: %f\n", it, (double)maxchange); */ + } + } + + IGRAPH_PROGRESS("Large graph layout", 100.0, 0); + igraph_destroy(&mst); + igraph_vector_destroy(&vids); + igraph_vector_destroy(&layers); + igraph_vector_destroy(&parents); + igraph_vector_destroy(&edges); + igraph_2dgrid_destroy(&grid); + igraph_vector_destroy(&eids); + igraph_vector_destroy(&forcex); + igraph_vector_destroy(&forcey); + IGRAPH_FINALLY_CLEAN(9); + return 0; + +} diff --git a/src/rigraph/core/layout/layout_bipartite.c b/src/rigraph/core/layout/layout_bipartite.c new file mode 100644 index 0000000..ebd4540 --- /dev/null +++ b/src/rigraph/core/layout/layout_bipartite.c @@ -0,0 +1,81 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" + +/** + * \function igraph_layout_bipartite + * Simple layout for bipartite graphs. + * + * The layout is created by first placing the vertices in two rows, + * according to their types. Then the positions within the rows are + * optimized to minimize edge crossings, by calling \ref + * igraph_layout_sugiyama(). + * + * \param graph The input graph. + * \param types A boolean vector containing ones and zeros, the vertex + * types. Its length must match the number of vertices in the graph. + * \param res Pointer to an initialized matrix, the result, the x and + * y coordinates are stored here. + * \param hgap The preferred minimum horizontal gap between vertices + * in the same layer (i.e. vertices of the same type). + * \param vgap The distance between layers. + * \param maxiter Maximum number of iterations in the crossing + * minimization stage. 100 is a reasonable default; if you feel + * that you have too many edge crossings, increase this. + * \return Error code. + * + * \sa \ref igraph_layout_sugiyama(). + */ +int igraph_layout_bipartite(const igraph_t *graph, + const igraph_vector_bool_t *types, + igraph_matrix_t *res, igraph_real_t hgap, + igraph_real_t vgap, long int maxiter) { + + long int i, no_of_nodes = igraph_vcount(graph); + igraph_vector_t layers; + + if (igraph_vector_bool_size(types) != no_of_nodes) { + IGRAPH_ERRORF("The vertex type vector size (%ld) should be equal to the number of nodes (%ld).", + IGRAPH_EINVAL, igraph_vector_bool_size(types), no_of_nodes); + } + if (hgap < 0) { + IGRAPH_ERRORF("The horizontal gap cannot be negative, got %f.", IGRAPH_EINVAL, hgap); + } + + IGRAPH_VECTOR_INIT_FINALLY(&layers, no_of_nodes); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(layers)[i] = VECTOR(*types)[i] ? 0 : 1; + } + + IGRAPH_CHECK(igraph_layout_sugiyama(graph, res, /*extd_graph=*/ 0, + /*extd_to_orig_eids=*/ 0, &layers, hgap, + vgap, maxiter, /*weights=*/ 0)); + + igraph_vector_destroy(&layers); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/layout/layout_grid.c b/src/rigraph/core/layout/layout_grid.c new file mode 100644 index 0000000..144fdb6 --- /dev/null +++ b/src/rigraph/core/layout/layout_grid.c @@ -0,0 +1,113 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" + +/** + * \ingroup layout + * \function igraph_layout_grid + * \brief Places the vertices on a regular grid on the plane. + * + * \param graph Pointer to an initialized graph object. + * \param res Pointer to an initialized matrix object. This will + * contain the result and will be resized as needed. + * \param width The number of vertices in a single row of the grid. + * When zero or negative, the width of the grid will be the + * square root of the number of vertices, rounded up if needed. + * \return Error code. The current implementation always returns with + * success. + * + * Time complexity: O(|V|), the number of vertices. + */ +int igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, long int width) { + long int i, no_of_nodes = igraph_vcount(graph); + igraph_real_t x, y; + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); + + if (width <= 0) { + width = (long int) ceil(sqrt(no_of_nodes)); + } + + x = y = 0; + for (i = 0; i < no_of_nodes; i++) { + MATRIX(*res, i, 0) = x++; + MATRIX(*res, i, 1) = y; + if (x == width) { + x = 0; y++; + } + } + + return 0; +} + +/** + * \ingroup layout + * \function igraph_layout_grid_3d + * \brief Places the vertices on a regular grid in the 3D space. + * + * \param graph Pointer to an initialized graph object. + * \param res Pointer to an initialized matrix object. This will + * contain the result and will be resized as needed. + * \param width The number of vertices in a single row of the grid. When + * zero or negative, the width is determined automatically. + * \param height The number of vertices in a single column of the grid. When + * zero or negative, the height is determined automatically. + * + * \return Error code. The current implementation always returns with + * success. + * + * Time complexity: O(|V|), the number of vertices. + */ +int igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res, + long int width, long int height) { + long int i, no_of_nodes = igraph_vcount(graph); + igraph_real_t x, y, z; + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); + + if (width <= 0 && height <= 0) { + width = height = (long int) ceil(pow(no_of_nodes, 1.0 / 3)); + } else if (width <= 0) { + width = (long int) ceil(sqrt(no_of_nodes / (double)height)); + } else if (height <= 0) { + height = (long int) ceil(sqrt(no_of_nodes / (double)width)); + } + + x = y = z = 0; + for (i = 0; i < no_of_nodes; i++) { + MATRIX(*res, i, 0) = x++; + MATRIX(*res, i, 1) = y; + MATRIX(*res, i, 2) = z; + if (x == width) { + x = 0; y++; + if (y == height) { + y = 0; z++; + } + } + } + + return 0; +} diff --git a/src/rigraph/core/layout/layout_internal.h b/src/rigraph/core/layout/layout_internal.h new file mode 100644 index 0000000..bfc52ec --- /dev/null +++ b/src/rigraph/core/layout/layout_internal.h @@ -0,0 +1,55 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_LAYOUT_INTERNAL_H +#define IGRAPH_LAYOUT_INTERNAL_H + +#include "igraph_types.h" + +#include "merge_grid.h" + +__BEGIN_DECLS + +IGRAPH_PRIVATE_EXPORT int igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid, + long int actg, igraph_real_t *x, igraph_real_t *y, igraph_real_t r, + igraph_real_t cx, igraph_real_t cy, igraph_real_t startr, + igraph_real_t killr); + +IGRAPH_PRIVATE_EXPORT int igraph_i_layout_sphere_2d(igraph_matrix_t *coords, igraph_real_t *x, + igraph_real_t *y, igraph_real_t *r); + +IGRAPH_PRIVATE_EXPORT int igraph_i_layout_sphere_3d(igraph_matrix_t *coords, igraph_real_t *x, + igraph_real_t *y, igraph_real_t *z, + igraph_real_t *r); + +IGRAPH_PRIVATE_EXPORT float igraph_i_layout_point_segment_dist2(float v_x, float v_y, + float u1_x, float u1_y, + float u2_x, float u2_y); + +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_i_layout_segments_intersect(float p0_x, float p0_y, + float p1_x, float p1_y, + float p2_x, float p2_y, + float p3_x, float p3_y); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/layout/layout_random.c b/src/rigraph/core/layout/layout_random.c new file mode 100644 index 0000000..7c19c74 --- /dev/null +++ b/src/rigraph/core/layout/layout_random.c @@ -0,0 +1,98 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_interface.h" +#include "igraph_random.h" + +/** + * \ingroup layout + * \function igraph_layout_random + * \brief Places the vertices uniform randomly on a plane. + * + * \param graph Pointer to an initialized graph object. + * \param res Pointer to an initialized matrix object. This will + * contain the result and will be resized as needed. + * \return Error code. The current implementation always returns with + * success. + * + * Time complexity: O(|V|), the + * number of vertices. + */ +int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) { + + long int no_of_nodes = igraph_vcount(graph); + long int i; + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); + + RNG_BEGIN(); + + for (i = 0; i < no_of_nodes; i++) { + MATRIX(*res, i, 0) = RNG_UNIF(-1, 1); + MATRIX(*res, i, 1) = RNG_UNIF(-1, 1); + } + + RNG_END(); + + return 0; +} + +/** + * \function igraph_layout_random_3d + * \brief Places the vertices uniform randomly in a cube. + * + * + * Vertex coordinates range from -1 to 1, and are placed in 3 columns + * of a matrix, with a row for each vertex. + * + * \param graph The graph to place. + * \param res Pointer to an initialized matrix object. It will be + * resized to hold the result. + * \return Error code. The current implementation always returns with + * success. + * + * Added in version 0.2. + * + * Time complexity: O(|V|), the number of vertices. + */ +int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res) { + + long int no_of_nodes = igraph_vcount(graph); + long int i; + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); + + RNG_BEGIN(); + + for (i = 0; i < no_of_nodes; i++) { + MATRIX(*res, i, 0) = RNG_UNIF(-1, 1); + MATRIX(*res, i, 1) = RNG_UNIF(-1, 1); + MATRIX(*res, i, 2) = RNG_UNIF(-1, 1); + } + + RNG_END(); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/layout/mds.c b/src/rigraph/core/layout/mds.c new file mode 100644 index 0000000..677b3ab --- /dev/null +++ b/src/rigraph/core/layout/mds.c @@ -0,0 +1,305 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_blas.h" +#include "igraph_components.h" +#include "igraph_eigen.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_paths.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +static int igraph_i_layout_mds_step(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra); + +static int igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, + igraph_matrix_t *dist, long int dim); + +static int igraph_i_layout_mds_step(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + igraph_matrix_t* matrix = (igraph_matrix_t*)extra; + IGRAPH_UNUSED(n); + igraph_blas_dgemv_array(0, 1, matrix, from, 0, to); + return 0; +} + +/* MDS layout for a connected graph, with no error checking on the + * input parameters. The distance matrix will be modified in-place. */ +int igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, + igraph_matrix_t *dist, long int dim) { + + long int no_of_nodes = igraph_vcount(graph); + long int nev = dim; + igraph_matrix_t vectors; + igraph_vector_t values, row_means; + igraph_real_t grand_mean; + long int i, j, k; + igraph_eigen_which_t which; + + /* Handle the trivial cases */ + if (no_of_nodes == 1) { + IGRAPH_CHECK(igraph_matrix_resize(res, 1, dim)); + igraph_matrix_fill(res, 0); + return IGRAPH_SUCCESS; + } + if (no_of_nodes == 2) { + IGRAPH_CHECK(igraph_matrix_resize(res, 2, dim)); + igraph_matrix_fill(res, 0); + for (j = 0; j < dim; j++) { + MATRIX(*res, 1, j) = 1; + } + return IGRAPH_SUCCESS; + } + + /* Initialize some stuff */ + IGRAPH_VECTOR_INIT_FINALLY(&values, no_of_nodes); + IGRAPH_CHECK(igraph_matrix_init(&vectors, no_of_nodes, dim)); + IGRAPH_FINALLY(igraph_matrix_destroy, &vectors); + + /* Take the square of the distance matrix */ + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < no_of_nodes; j++) { + MATRIX(*dist, i, j) *= MATRIX(*dist, i, j); + } + } + + /* Double centering of the distance matrix */ + IGRAPH_VECTOR_INIT_FINALLY(&row_means, no_of_nodes); + igraph_vector_fill(&values, 1.0 / no_of_nodes); + igraph_blas_dgemv(0, 1, dist, &values, 0, &row_means); + grand_mean = igraph_vector_sum(&row_means) / no_of_nodes; + igraph_matrix_add_constant(dist, grand_mean); + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < no_of_nodes; j++) { + MATRIX(*dist, i, j) -= VECTOR(row_means)[i] + VECTOR(row_means)[j]; + MATRIX(*dist, i, j) *= -0.5; + } + } + igraph_vector_destroy(&row_means); + IGRAPH_FINALLY_CLEAN(1); + + /* Calculate the top `dim` eigenvectors. */ + which.pos = IGRAPH_EIGEN_LA; + which.howmany = (int) nev; + IGRAPH_CHECK(igraph_eigen_matrix_symmetric(/*A=*/ 0, /*sA=*/ 0, + /*fun=*/ igraph_i_layout_mds_step, + /*n=*/ (int) no_of_nodes, /*extra=*/ dist, + /*algorithm=*/ IGRAPH_EIGEN_LAPACK, + &which, /*options=*/ 0, /*storage=*/ 0, + &values, &vectors)); + + /* Calculate and normalize the final coordinates */ + for (j = 0; j < nev; j++) { + VECTOR(values)[j] = sqrt(fabs(VECTOR(values)[j])); + } + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, dim)); + for (i = 0; i < no_of_nodes; i++) { + for (j = 0, k = nev - 1; j < nev; j++, k--) { + MATRIX(*res, i, k) = VECTOR(values)[j] * MATRIX(vectors, i, j); + } + } + + igraph_matrix_destroy(&vectors); + igraph_vector_destroy(&values); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_layout_mds + * \brief Place the vertices on a plane using multidimensional scaling. + * + * + * This layout requires a distance matrix, where the intersection of + * row i and column j specifies the desired distance between vertex i + * and vertex j. The algorithm will try to place the vertices in a + * space having a given number of dimensions in a way that approximates + * the distance relations prescribed in the distance matrix. igraph + * uses the classical multidimensional scaling by Torgerson; for more + * details, see Cox & Cox: Multidimensional Scaling (1994), Chapman + * and Hall, London. + * + * + * If the input graph is disconnected, igraph will decompose it + * first into its subgraphs, lay out the subgraphs one by one + * using the appropriate submatrices of the distance matrix, and + * then merge the layouts using \ref igraph_layout_merge_dla. + * Since \ref igraph_layout_merge_dla works for 2D layouts only, + * you cannot run the MDS layout on disconnected graphs for + * more than two dimensions. + * + * + * Warning: if the graph is symmetric to the exchange of two vertices + * (as is the case with leaves of a tree connecting to the same parent), + * classical multidimensional scaling may assign the same coordinates to + * these vertices. + * + * \param graph A graph object. + * \param res Pointer to an initialized matrix object. This will + * contain the result and will be resized if needed. + * \param dist The distance matrix. It must be symmetric and this + * function does not check whether the matrix is indeed + * symmetric. Results are unspecified if you pass a non-symmetric + * matrix here. You can set this parameter to null; in this + * case, the shortest path lengths between vertices will be + * used as distances. + * \param dim The number of dimensions in the embedding space. For + * 2D layouts, supply 2 here. + * \return Error code. + * + * Added in version 0.6. + * + * + * Time complexity: usually around O(|V|^2 dim). + */ + +int igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res, + const igraph_matrix_t *dist, long int dim) { + long int i, no_of_nodes = igraph_vcount(graph); + igraph_matrix_t m; + igraph_bool_t conn; + + RNG_BEGIN(); + + /* Check the distance matrix */ + if (dist && (igraph_matrix_nrow(dist) != no_of_nodes || + igraph_matrix_ncol(dist) != no_of_nodes)) { + IGRAPH_ERROR("invalid distance matrix size", IGRAPH_EINVAL); + } + + /* Check the number of dimensions */ + if (dim <= 1) { + IGRAPH_ERROR("dim must be positive", IGRAPH_EINVAL); + } + if (dim > no_of_nodes) { + IGRAPH_ERROR("dim must be less than the number of nodes", IGRAPH_EINVAL); + } + + /* Copy or obtain the distance matrix */ + if (dist == 0) { + IGRAPH_CHECK(igraph_matrix_init(&m, no_of_nodes, no_of_nodes)); + IGRAPH_FINALLY(igraph_matrix_destroy, &m); + IGRAPH_CHECK(igraph_shortest_paths(graph, &m, + igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL)); + } else { + IGRAPH_CHECK(igraph_matrix_copy(&m, dist)); + IGRAPH_FINALLY(igraph_matrix_destroy, &m); + /* Make sure that the diagonal contains zeroes only */ + for (i = 0; i < no_of_nodes; i++) { + MATRIX(m, i, i) = 0.0; + } + } + + /* Check whether the graph is connected */ + IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); + if (conn) { + /* Yes, it is, just do the MDS */ + IGRAPH_CHECK(igraph_i_layout_mds_single(graph, res, &m, dim)); + } else { + /* The graph is not connected, lay out the components one by one */ + igraph_vector_ptr_t layouts; + igraph_vector_t comp, vertex_order; + igraph_t subgraph; + igraph_matrix_t *layout; + igraph_matrix_t dist_submatrix; + igraph_bool_t *seen_vertices; + long int j, n, processed_vertex_count = 0; + + IGRAPH_VECTOR_INIT_FINALLY(&comp, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vertex_order, no_of_nodes); + + IGRAPH_CHECK(igraph_vector_ptr_init(&layouts, 0)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &layouts); + igraph_vector_ptr_set_item_destructor(&layouts, (igraph_finally_func_t*)igraph_matrix_destroy); + + IGRAPH_CHECK(igraph_matrix_init(&dist_submatrix, 0, 0)); + IGRAPH_FINALLY(igraph_matrix_destroy, &dist_submatrix); + + seen_vertices = IGRAPH_CALLOC(no_of_nodes, igraph_bool_t); + if (seen_vertices == 0) { + IGRAPH_ERROR("cannot calculate MDS layout", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, seen_vertices); + + for (i = 0; i < no_of_nodes; i++) { + if (seen_vertices[i]) { + continue; + } + + /* This is a vertex whose component we did not lay out so far */ + IGRAPH_CHECK(igraph_subcomponent(graph, &comp, i, IGRAPH_ALL)); + /* Take the subgraph */ + IGRAPH_CHECK(igraph_induced_subgraph(graph, &subgraph, igraph_vss_vector(&comp), + IGRAPH_SUBGRAPH_AUTO)); + IGRAPH_FINALLY(igraph_destroy, &subgraph); + /* Calculate the submatrix of the distances */ + IGRAPH_CHECK(igraph_matrix_select_rows_cols(&m, &dist_submatrix, + &comp, &comp)); + /* Allocate a new matrix for storing the layout */ + layout = IGRAPH_CALLOC(1, igraph_matrix_t); + if (layout == 0) { + IGRAPH_ERROR("cannot calculate MDS layout", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, layout); + IGRAPH_CHECK(igraph_matrix_init(layout, 0, 0)); + IGRAPH_FINALLY(igraph_matrix_destroy, layout); + /* Lay out the subgraph */ + IGRAPH_CHECK(igraph_i_layout_mds_single(&subgraph, layout, &dist_submatrix, dim)); + /* Store the layout */ + IGRAPH_CHECK(igraph_vector_ptr_push_back(&layouts, layout)); + IGRAPH_FINALLY_CLEAN(2); /* ownership of layout taken by layouts */ + /* Free the newly created subgraph */ + igraph_destroy(&subgraph); + IGRAPH_FINALLY_CLEAN(1); + /* Mark all the vertices in the component as visited */ + n = igraph_vector_size(&comp); + for (j = 0; j < n; j++) { + seen_vertices[(long int)VECTOR(comp)[j]] = 1; + VECTOR(vertex_order)[(long int)VECTOR(comp)[j]] = processed_vertex_count++; + } + } + /* Merge the layouts - reusing dist_submatrix here */ + IGRAPH_CHECK(igraph_layout_merge_dla(0, &layouts, &dist_submatrix)); + /* Reordering the rows of res to match the original graph */ + IGRAPH_CHECK(igraph_matrix_select_rows(&dist_submatrix, res, &vertex_order)); + + igraph_free(seen_vertices); + igraph_matrix_destroy(&dist_submatrix); + igraph_vector_ptr_destroy_all(&layouts); + igraph_vector_destroy(&vertex_order); + igraph_vector_destroy(&comp); + IGRAPH_FINALLY_CLEAN(5); + } + + RNG_END(); + + igraph_matrix_destroy(&m); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/layout/merge_dla.c b/src/rigraph/core/layout/merge_dla.c new file mode 100644 index 0000000..5e15830 --- /dev/null +++ b/src/rigraph/core/layout/merge_dla.c @@ -0,0 +1,295 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" +#include "igraph_progress.h" +#include "igraph_random.h" + +#include "../core/grid.h" +#include "../core/interruption.h" +#include "../core/math.h" +#include "merge_grid.h" +#include "layout_internal.h" + +/** + * \function igraph_layout_merge_dla + * \brief Merge multiple layouts by using a DLA algorithm + * + * + * First each layout is covered by a circle. Then the layout of the + * largest graph is placed at the origin. Then the other layouts are + * placed by the DLA algorithm, larger ones first and smaller ones + * last. + * \param thegraphs Pointer vector containing the graph objects of + * which the layouts will be merged. + * \param coords Pointer vector containing matrix objects with the 2d + * layouts of the graphs in \p thegraphs. + * \param res Pointer to an initialized matrix object, the result will + * be stored here. It will be resized if needed. + * \return Error code. + * + * Added in version 0.2. This function is experimental. + * + * + * Time complexity: TODO. + */ + +int igraph_layout_merge_dla(const igraph_vector_ptr_t *thegraphs, + const igraph_vector_ptr_t *coords, + igraph_matrix_t *res) { + long int graphs = igraph_vector_ptr_size(coords); + igraph_vector_t sizes; + igraph_vector_t x, y, r; + igraph_vector_t nx, ny, nr; + long int allnodes = 0; + long int i, j; + long int actg; + igraph_i_layout_mergegrid_t grid; + long int jpos = 0; + igraph_real_t minx, maxx, miny, maxy; + igraph_real_t area = 0; + igraph_real_t maxr = 0; + long int respos; + + /* Graphs are currently not used, only the coordinates */ + IGRAPH_UNUSED(thegraphs); + + IGRAPH_VECTOR_INIT_FINALLY(&sizes, graphs); + IGRAPH_VECTOR_INIT_FINALLY(&x, graphs); + IGRAPH_VECTOR_INIT_FINALLY(&y, graphs); + IGRAPH_VECTOR_INIT_FINALLY(&r, graphs); + IGRAPH_VECTOR_INIT_FINALLY(&nx, graphs); + IGRAPH_VECTOR_INIT_FINALLY(&ny, graphs); + IGRAPH_VECTOR_INIT_FINALLY(&nr, graphs); + + RNG_BEGIN(); + + for (i = 0; i < igraph_vector_ptr_size(coords); i++) { + igraph_matrix_t *mat = VECTOR(*coords)[i]; + long int size = igraph_matrix_nrow(mat); + + if (igraph_matrix_ncol(mat) != 2) { + IGRAPH_ERROR("igraph_layout_merge_dla works for 2D layouts only", + IGRAPH_EINVAL); + } + + IGRAPH_ALLOW_INTERRUPTION(); + allnodes += size; + VECTOR(sizes)[i] = size; + VECTOR(r)[i] = pow(size, .75); + area += VECTOR(r)[i] * VECTOR(r)[i]; + if (VECTOR(r)[i] > maxr) { + maxr = VECTOR(r)[i]; + } + + igraph_i_layout_sphere_2d(mat, + igraph_vector_e_ptr(&nx, i), + igraph_vector_e_ptr(&ny, i), + igraph_vector_e_ptr(&nr, i)); + + } + igraph_vector_order2(&sizes); /* largest first */ + + /* 0. create grid */ + minx = miny = -sqrt(5 * area); + maxx = maxy = sqrt(5 * area); + igraph_i_layout_mergegrid_init(&grid, minx, maxx, 200, + miny, maxy, 200); + IGRAPH_FINALLY(igraph_i_layout_mergegrid_destroy, &grid); + + /* fprintf(stderr, "Ok, starting DLA\n"); */ + + /* 1. place the largest */ + actg = (long int) VECTOR(sizes)[jpos++]; + igraph_i_layout_merge_place_sphere(&grid, 0, 0, VECTOR(r)[actg], actg); + + IGRAPH_PROGRESS("Merging layouts via DLA", 0.0, NULL); + while (jpos < graphs) { + IGRAPH_ALLOW_INTERRUPTION(); + /* fprintf(stderr, "comp: %li", jpos); */ + IGRAPH_PROGRESS("Merging layouts via DLA", (100.0 * jpos) / graphs, NULL); + + actg = (long int) VECTOR(sizes)[jpos++]; + /* 2. random walk, TODO: tune parameters */ + igraph_i_layout_merge_dla(&grid, actg, + igraph_vector_e_ptr(&x, actg), + igraph_vector_e_ptr(&y, actg), + VECTOR(r)[actg], 0, 0, + maxx, maxx + 5); + + /* 3. place sphere */ + igraph_i_layout_merge_place_sphere(&grid, VECTOR(x)[actg], VECTOR(y)[actg], + VECTOR(r)[actg], actg); + } + IGRAPH_PROGRESS("Merging layouts via DLA", 100.0, NULL); + + /* Create the result */ + IGRAPH_CHECK(igraph_matrix_resize(res, allnodes, 2)); + respos = 0; + for (i = 0; i < graphs; i++) { + long int size = igraph_matrix_nrow(VECTOR(*coords)[i]); + igraph_real_t xx = VECTOR(x)[i]; + igraph_real_t yy = VECTOR(y)[i]; + igraph_real_t rr = VECTOR(r)[i] / VECTOR(nr)[i]; + igraph_matrix_t *mat = VECTOR(*coords)[i]; + IGRAPH_ALLOW_INTERRUPTION(); + if (VECTOR(nr)[i] == 0) { + rr = 1; + } + for (j = 0; j < size; j++) { + MATRIX(*res, respos, 0) = rr * (MATRIX(*mat, j, 0) - VECTOR(nx)[i]); + MATRIX(*res, respos, 1) = rr * (MATRIX(*mat, j, 1) - VECTOR(ny)[i]); + MATRIX(*res, respos, 0) += xx; + MATRIX(*res, respos, 1) += yy; + ++respos; + } + } + + RNG_END(); + + igraph_i_layout_mergegrid_destroy(&grid); + igraph_vector_destroy(&sizes); + igraph_vector_destroy(&x); + igraph_vector_destroy(&y); + igraph_vector_destroy(&r); + igraph_vector_destroy(&nx); + igraph_vector_destroy(&ny); + igraph_vector_destroy(&nr); + IGRAPH_FINALLY_CLEAN(8); + return 0; +} + +int igraph_i_layout_sphere_2d(igraph_matrix_t *coords, + igraph_real_t *x, igraph_real_t *y, + igraph_real_t *r) { + long int nodes = igraph_matrix_nrow(coords); + long int i; + igraph_real_t xmin, xmax, ymin, ymax; + + xmin = xmax = MATRIX(*coords, 0, 0); + ymin = ymax = MATRIX(*coords, 0, 1); + for (i = 1; i < nodes; i++) { + + if (MATRIX(*coords, i, 0) < xmin) { + xmin = MATRIX(*coords, i, 0); + } else if (MATRIX(*coords, i, 0) > xmax) { + xmax = MATRIX(*coords, i, 0); + } + + if (MATRIX(*coords, i, 1) < ymin) { + ymin = MATRIX(*coords, i, 1); + } else if (MATRIX(*coords, i, 1) > ymax) { + ymax = MATRIX(*coords, i, 1); + } + + } + + *x = (xmin + xmax) / 2; + *y = (ymin + ymax) / 2; + *r = sqrt( (xmax - xmin) * (xmax - xmin) + (ymax - ymin) * (ymax - ymin) ) / 2; + + return 0; +} + +int igraph_i_layout_sphere_3d(igraph_matrix_t *coords, + igraph_real_t *x, igraph_real_t *y, + igraph_real_t *z, igraph_real_t *r) { + long int nodes = igraph_matrix_nrow(coords); + long int i; + igraph_real_t xmin, xmax, ymin, ymax, zmin, zmax; + + xmin = xmax = MATRIX(*coords, 0, 0); + ymin = ymax = MATRIX(*coords, 0, 1); + zmin = zmax = MATRIX(*coords, 0, 2); + for (i = 1; i < nodes; i++) { + + if (MATRIX(*coords, i, 0) < xmin) { + xmin = MATRIX(*coords, i, 0); + } else if (MATRIX(*coords, i, 0) > xmax) { + xmax = MATRIX(*coords, i, 0); + } + + if (MATRIX(*coords, i, 1) < ymin) { + ymin = MATRIX(*coords, i, 1); + } else if (MATRIX(*coords, i, 1) > ymax) { + ymax = MATRIX(*coords, i, 1); + } + + if (MATRIX(*coords, i, 2) < zmin) { + zmin = MATRIX(*coords, i, 2); + } else if (MATRIX(*coords, i, 2) > zmax) { + zmax = MATRIX(*coords, i, 2); + } + + } + + *x = (xmin + xmax) / 2; + *y = (ymin + ymax) / 2; + *z = (zmin + zmax) / 2; + *r = sqrt( (xmax - xmin) * (xmax - xmin) + (ymax - ymin) * (ymax - ymin) + + (zmax - zmin) * (zmax - zmin) ) / 2; + + return 0; +} + +#define DIST(x,y) (sqrt(pow((x)-cx,2)+pow((y)-cy,2))) + +int igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid, + long int actg, igraph_real_t *x, igraph_real_t *y, igraph_real_t r, + igraph_real_t cx, igraph_real_t cy, igraph_real_t startr, + igraph_real_t killr) { + long int sp = -1; + igraph_real_t angle, len; + long int steps = 0; + + /* The graph is not used, only its coordinates */ + IGRAPH_UNUSED(actg); + + while (sp < 0) { + /* start particle */ + do { + steps++; + angle = RNG_UNIF(0, 2 * M_PI); + len = RNG_UNIF(.5 * startr, startr); + *x = cx + len * cos(angle); + *y = cy + len * sin(angle); + sp = igraph_i_layout_mergegrid_get_sphere(grid, *x, *y, r); + } while (sp >= 0); + + while (sp < 0 && DIST(*x, *y) < killr) { + igraph_real_t nx, ny; + steps++; + angle = RNG_UNIF(0, 2 * M_PI); + len = RNG_UNIF(0, startr / 100); + nx = *x + len * cos(angle); + ny = *y + len * sin(angle); + sp = igraph_i_layout_mergegrid_get_sphere(grid, nx, ny, r); + if (sp < 0) { + *x = nx; *y = ny; + } + } + } + + /* fprintf(stderr, "%li ", steps); */ + return 0; +} diff --git a/src/rigraph/core/layout/merge_grid.c b/src/rigraph/core/layout/merge_grid.c new file mode 100644 index 0000000..f7f6538 --- /dev/null +++ b/src/rigraph/core/layout/merge_grid.c @@ -0,0 +1,217 @@ +/* -*- mode: C -*- */ +/* + IGraph package. + Copyright (C) 2006-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_memory.h" + +#include "merge_grid.h" + +static int igraph_i_layout_mergegrid_which(igraph_i_layout_mergegrid_t *grid, + igraph_real_t xc, igraph_real_t yc, + long int *x, long int *y) { + if (xc <= grid->minx) { + *x = 0; + } else if (xc >= grid->maxx) { + *x = grid->stepsx - 1; + } else { + *x = (long int) floor((xc - (grid->minx)) / (grid->deltax)); + } + + if (yc <= grid->miny) { + *y = 0; + } else if (yc >= grid->maxy) { + *y = grid->stepsy - 1; + } else { + *y = (long int) floor((yc - (grid->miny)) / (grid->deltay)); + } + + return 0; +} + +int igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid, + igraph_real_t minx, igraph_real_t maxx, long int stepsx, + igraph_real_t miny, igraph_real_t maxy, long int stepsy) { + grid->minx = minx; + grid->maxx = maxx; + grid->stepsx = stepsx; + grid->deltax = (maxx - minx) / stepsx; + grid->miny = miny; + grid->maxy = maxy; + grid->stepsy = stepsy; + grid->deltay = (maxy - miny) / stepsy; + + grid->data = IGRAPH_CALLOC(stepsx * stepsy, long int); + if (grid->data == 0) { + IGRAPH_ERROR("Cannot create grid", IGRAPH_ENOMEM); + } + return 0; +} + +void igraph_i_layout_mergegrid_destroy(igraph_i_layout_mergegrid_t *grid) { + IGRAPH_FREE(grid->data); +} + +#define MAT(i,j) (grid->data[(grid->stepsy)*(j)+(i)]) +#define DIST2(x2,y2) (sqrt(pow(x-(x2),2)+pow(y-(y2), 2))) + +int igraph_i_layout_merge_place_sphere(igraph_i_layout_mergegrid_t *grid, + igraph_real_t x, igraph_real_t y, igraph_real_t r, + long int id) { + long int cx, cy; + long int i, j; + + igraph_i_layout_mergegrid_which(grid, x, y, &cx, &cy); + + MAT(cx, cy) = id + 1; + +#define DIST(i,j) (DIST2(grid->minx+(cx+(i))*grid->deltax, \ + grid->miny+(cy+(j))*grid->deltay)) + + for (i = 0; cx + i < grid->stepsx && DIST(i, 0) < r; i++) { + for (j = 0; cy + j < grid->stepsy && DIST(i, j) < r; j++) { + MAT(cx + i, cy + j) = id + 1; + } + } + +#undef DIST +#define DIST(i,j) (DIST2(grid->minx+(cx+(i))*grid->deltax, \ + grid->miny+(cy-(j)+1)*grid->deltay)) + + for (i = 0; cx + i < grid->stepsx && DIST(i, 0) < r; i++) { + for (j = 1; cy - j > 0 && DIST(i, j) < r; j++) { + MAT(cx + i, cy - j) = id + 1; + } + } + +#undef DIST +#define DIST(i,j) (DIST2(grid->minx+(cx-(i)+1)*grid->deltax, \ + grid->miny+(cy+(j))*grid->deltay)) + + for (i = 1; cx - i > 0 && DIST(i, 0) < r; i++) { + for (j = 0; cy + j < grid->stepsy && DIST(i, j) < r; j++) { + MAT(cx - i, cy + j) = id + 1; + } + } + +#undef DIST +#define DIST(i,j) (DIST2(grid->minx+(cx-(i)+1)*grid->deltax, \ + grid->miny+(cy-(j)+1)*grid->deltay)) + + for (i = 1; cx - i > 0 && DIST(i, 0) < r; i++) { + for (j = 1; cy - j > 0 && DIST(i, j) < r; j++) { + MAT(cx - i, cy - j) = id + 1; + } + } + +#undef DIST +#undef DIST2 + + return 0; +} + +long int igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid, + igraph_real_t x, igraph_real_t y) { + long int cx, cy; + long int res; + + if (x <= grid->minx || x >= grid->maxx || + y <= grid->miny || y >= grid->maxy) { + res = -1; + } else { + igraph_i_layout_mergegrid_which(grid, x, y, &cx, &cy); + res = MAT(cx, cy) - 1; + } + + return res; +} + +#define DIST2(x2,y2) (sqrt(pow(x-(x2),2)+pow(y-(y2), 2))) + +long int igraph_i_layout_mergegrid_get_sphere(igraph_i_layout_mergegrid_t *grid, + igraph_real_t x, igraph_real_t y, igraph_real_t r) { + long int cx, cy; + long int i, j; + long int ret; + + if (x - r <= grid->minx || x + r >= grid->maxx || + y - r <= grid->miny || y + r >= grid->maxy) { + ret = -1; + } else { + igraph_i_layout_mergegrid_which(grid, x, y, &cx, &cy); + + ret = MAT(cx, cy) - 1; + +#define DIST(i,j) (DIST2(grid->minx+(cx+(i))*grid->deltax, \ + grid->miny+(cy+(j))*grid->deltay)) + + for (i = 0; ret < 0 && cx + i < grid->stepsx && DIST(i, 0) < r; i++) { + for (j = 0; ret < 0 && cy + j < grid->stepsy && DIST(i, j) < r; j++) { + ret = MAT(cx + i, cy + j) - 1; + } + } + +#undef DIST +#define DIST(i,j) (DIST2(grid->minx+(cx+(i))*grid->deltax, \ + grid->miny+(cy-(j)+1)*grid->deltay)) + + for (i = 0; ret < 0 && cx + i < grid->stepsx && DIST(i, 0) < r; i++) { + for (j = 1; ret < 0 && cy - j > 0 && DIST(i, j) < r; j++) { + ret = MAT(cx + i, cy - j) - 1; + } + } + +#undef DIST +#define DIST(i,j) (DIST2(grid->minx+(cx-(i)+1)*grid->deltax, \ + grid->miny+(cy+(j))*grid->deltay)) + + for (i = 1; ret < 0 && cx - i > 0 && DIST(i, 0) < r; i++) { + for (j = 0; ret < 0 && cy + j < grid->stepsy && DIST(i, j) < r; j++) { + ret = MAT(cx - i, cy + j) - 1; + } + } + +#undef DIST +#define DIST(i,j) (DIST2(grid->minx+(cx-(i)+1)*grid->deltax, \ + grid->miny+(cy-(j)+1)*grid->deltay)) + + for (i = 1; ret < 0 && cx + i > 0 && DIST(i, 0) < r; i++) { + for (j = 1; ret < 0 && cy + i > 0 && DIST(i, j) < r; j++) { + ret = MAT(cx - i, cy - j) - 1; + } + } + +#undef DIST + + } + + return ret; +} + +/* int print_grid(igraph_i_layout_mergegrid_t *grid) { */ +/* long int i,j; */ + +/* for (i=0; istepsx; i++) { */ +/* for (j=0; jstepsy; j++) { */ +/* printf("%li ", MAT(i,j)-1); */ +/* } */ +/* printf("\n"); */ +/* } */ +/* } */ diff --git a/src/rigraph/core/layout/merge_grid.h b/src/rigraph/core/layout/merge_grid.h new file mode 100644 index 0000000..70fcfee --- /dev/null +++ b/src/rigraph/core/layout/merge_grid.h @@ -0,0 +1,58 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_LAYOUT_MERGE_GRID_H +#define IGRAPH_LAYOUT_MERGE_GRID_H + +#include "igraph_decls.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +/* A type of grid used for merging layouts; each cell is owned by exactly one graph */ + +typedef struct igraph_i_layout_mergegrid_t { + long int *data; + long int stepsx, stepsy; + igraph_real_t minx, maxx, deltax; + igraph_real_t miny, maxy, deltay; +} igraph_i_layout_mergegrid_t; + +IGRAPH_PRIVATE_EXPORT int igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid, + igraph_real_t minx, igraph_real_t maxx, long int stepsx, + igraph_real_t miny, igraph_real_t maxy, long int stepsy); + +IGRAPH_PRIVATE_EXPORT void igraph_i_layout_mergegrid_destroy(igraph_i_layout_mergegrid_t *grid); + +IGRAPH_PRIVATE_EXPORT int igraph_i_layout_merge_place_sphere(igraph_i_layout_mergegrid_t *grid, + igraph_real_t x, igraph_real_t y, igraph_real_t r, + long int id); + +long int igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid, + igraph_real_t x, igraph_real_t y); + +long int igraph_i_layout_mergegrid_get_sphere(igraph_i_layout_mergegrid_t *g, + igraph_real_t x, igraph_real_t y, igraph_real_t r); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/layout/reingold_tilford.c b/src/rigraph/core/layout/reingold_tilford.c new file mode 100644 index 0000000..5edb013 --- /dev/null +++ b/src/rigraph/core/layout/reingold_tilford.c @@ -0,0 +1,965 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_paths.h" +#include "igraph_progress.h" +#include "igraph_structural.h" + +#include "../core/math.h" + +static int igraph_i_layout_reingold_tilford_unreachable( + const igraph_t *graph, + igraph_neimode_t mode, + long int real_root, + long int no_of_nodes, + igraph_vector_t *pnewedges) { + + long int no_of_newedges; + igraph_vector_t visited; + long int i, j, n; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_adjlist_t allneis; + igraph_vector_int_t *neis; + + igraph_vector_resize(pnewedges, 0); + + /* traverse from real_root and see what nodes you cannot reach */ + no_of_newedges = 0; + IGRAPH_VECTOR_INIT_FINALLY(&visited, no_of_nodes); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + + /* start from real_root and go BFS */ + IGRAPH_CHECK(igraph_dqueue_push(&q, real_root)); + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + neis = igraph_adjlist_get(&allneis, actnode); + n = igraph_vector_int_size(neis); + VECTOR(visited)[actnode] = 1; + for (j = 0; j < n; j++) { + long int neighbor = (long int) VECTOR(*neis)[j]; + if (!(long int)VECTOR(visited)[neighbor]) { + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + } + } + } + + for (j = 0; j < no_of_nodes; j++) { + no_of_newedges += 1 - VECTOR(visited)[j]; + } + + /* if any nodes are unreachable, add edges between them and real_root */ + if (no_of_newedges != 0) { + + igraph_vector_resize(pnewedges, no_of_newedges * 2); + j = 0; + for (i = 0; i < no_of_nodes; i++) { + if (!VECTOR(visited)[i]) { + if (mode != IGRAPH_IN) { + VECTOR(*pnewedges)[2 * j] = real_root; + VECTOR(*pnewedges)[2 * j + 1] = i; + } else { + VECTOR(*pnewedges)[2 * j] = i; + VECTOR(*pnewedges)[2 * j + 1] = real_root; + } + j++; + } + } + } + + igraph_dqueue_destroy(&q); + igraph_adjlist_destroy(&allneis); + igraph_vector_destroy(&visited); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/* Internal structure for Reingold-Tilford layout */ +struct igraph_i_reingold_tilford_vertex { + long int parent; /* Parent node index */ + long int level; /* Level of the node */ + igraph_real_t offset; /* X offset from parent node */ + long int left_contour; /* Next left node of the contour + of the subtree rooted at this node */ + long int right_contour; /* Next right node of the contour + of the subtree rooted at this node */ + igraph_real_t offset_to_left_contour; /* X offset when following the left contour */ + igraph_real_t offset_to_right_contour; /* X offset when following the right contour */ + long int left_extreme; /* Leftmost node on the deepest layer of the subtree rooted at this node */ + long int right_extreme; /* Rightmost node on the deepest layer of the subtree rooted at this node */ + igraph_real_t offset_to_left_extreme; /* X offset when jumping to the left extreme node */ + igraph_real_t offset_to_right_extreme; /* X offset when jumping to the right extreme node */ +}; + +static int igraph_i_layout_reingold_tilford_postorder(struct igraph_i_reingold_tilford_vertex *vdata, + long int node, long int vcount); +static int igraph_i_layout_reingold_tilford_calc_coords(struct igraph_i_reingold_tilford_vertex *vdata, + igraph_matrix_t *res, long int node, + long int vcount, igraph_real_t xpos); + +/* uncomment the next line for debugging the Reingold-Tilford layout */ +/* #define LAYOUT_RT_DEBUG 1 */ + +static int igraph_i_layout_reingold_tilford(const igraph_t *graph, + igraph_matrix_t *res, + igraph_neimode_t mode, + long int root) { + long int no_of_nodes = igraph_vcount(graph); + long int i, n, j; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_adjlist_t allneis; + igraph_vector_int_t *neis; + struct igraph_i_reingold_tilford_vertex *vdata; + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + + vdata = IGRAPH_CALLOC(no_of_nodes, struct igraph_i_reingold_tilford_vertex); + if (vdata == 0) { + IGRAPH_ERROR("igraph_layout_reingold_tilford failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, vdata); + + for (i = 0; i < no_of_nodes; i++) { + vdata[i].parent = -1; + vdata[i].level = -1; + vdata[i].offset = 0.0; + vdata[i].left_contour = -1; + vdata[i].right_contour = -1; + vdata[i].offset_to_left_contour = 0.0; + vdata[i].offset_to_right_contour = 0.0; + vdata[i].left_extreme = i; + vdata[i].right_extreme = i; + vdata[i].offset_to_left_extreme = 0.0; + vdata[i].offset_to_right_extreme = 0.0; + } + vdata[root].parent = root; + vdata[root].level = 0; + MATRIX(*res, root, 1) = 0; + + /* Step 1: assign Y coordinates based on BFS and setup parents vector */ + IGRAPH_CHECK(igraph_dqueue_push(&q, root)); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + long int actdist = (long int) igraph_dqueue_pop(&q); + neis = igraph_adjlist_get(&allneis, actnode); + n = igraph_vector_int_size(neis); + + for (j = 0; j < n; j++) { + long int neighbor = (long int) VECTOR(*neis)[j]; + if (vdata[neighbor].parent >= 0) { + continue; + } + MATRIX(*res, neighbor, 1) = actdist + 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + vdata[neighbor].parent = actnode; + vdata[neighbor].level = actdist + 1; + } + } + + /* Step 2: postorder tree traversal, determines the appropriate X + * offsets for every node */ + igraph_i_layout_reingold_tilford_postorder(vdata, root, no_of_nodes); + + /* Step 3: calculate real coordinates based on X offsets */ + igraph_i_layout_reingold_tilford_calc_coords(vdata, res, root, no_of_nodes, vdata[root].offset); + + igraph_dqueue_destroy(&q); + igraph_adjlist_destroy(&allneis); + igraph_free(vdata); + IGRAPH_FINALLY_CLEAN(3); + + IGRAPH_PROGRESS("Reingold-Tilford tree layout", 100.0, NULL); + +#ifdef LAYOUT_RT_DEBUG + for (i = 0; i < no_of_nodes; i++) { + printf( + "%3ld: offset = %.2f, contours = [%ld, %ld], contour offsets = [%.2f, %.2f]\n", + i, vdata[i].offset, + vdata[i].left_contour, vdata[i].right_contour, + vdata[i].offset_to_left_contour, vdata[i].offset_to_right_contour + ); + if (vdata[i].left_extreme != i || vdata[i].right_extreme != i) { + printf( + " extrema = [%ld, %ld], offsets to extrema = [%.2f, %.2f]\n", + vdata[i].left_extreme, vdata[i].right_extreme, + vdata[i].offset_to_left_extreme, vdata[i].offset_to_right_extreme + ); + } + } +#endif + + return 0; +} + +static int igraph_i_layout_reingold_tilford_calc_coords( + struct igraph_i_reingold_tilford_vertex *vdata, + igraph_matrix_t *res, long int node, + long int vcount, igraph_real_t xpos) { + long int i; + MATRIX(*res, node, 0) = xpos; + for (i = 0; i < vcount; i++) { + if (i == node) { + continue; + } + if (vdata[i].parent == node) { + igraph_i_layout_reingold_tilford_calc_coords(vdata, res, i, vcount, + xpos + vdata[i].offset); + } + } + return 0; +} + +static int igraph_i_layout_reingold_tilford_postorder( + struct igraph_i_reingold_tilford_vertex *vdata, + long int node, long int vcount) { + long int i, j, childcount, leftroot, leftrootidx; + const igraph_real_t minsep = 1; + igraph_real_t avg; + +#ifdef LAYOUT_RT_DEBUG + printf("Starting visiting node %ld\n", node); +#endif + + /* Check whether this node is a leaf node */ + childcount = 0; + for (i = 0; i < vcount; i++) { + if (i == node) { + continue; + } + if (vdata[i].parent == node) { + /* Node i is a child, so visit it recursively */ + childcount++; + igraph_i_layout_reingold_tilford_postorder(vdata, i, vcount); + } + } + + if (childcount == 0) { + return 0; + } + + /* Here we can assume that all of the subtrees have been placed and their + * left and right contours are calculated. Let's place them next to each + * other as close as we can. + * We will take each subtree in an arbitrary order. The root of the + * first one will be placed at offset 0, the next ones will be placed + * as close to each other as possible. leftroot stores the root of the + * rightmost subtree of the already placed subtrees - its right contour + * will be checked against the left contour of the next subtree */ + leftroot = leftrootidx = -1; + avg = 0.0; +#ifdef LAYOUT_RT_DEBUG + printf("Visited node %ld and arranged its subtrees\n", node); +#endif + for (i = 0, j = 0; i < vcount; i++) { + if (i == node) { + continue; + } + if (vdata[i].parent == node) { + if (leftroot >= 0) { + /* Now we will follow the right contour of leftroot and the + * left contour of the subtree rooted at i */ + long lnode, rnode, auxnode; + igraph_real_t loffset, roffset, rootsep, newoffset; + +#ifdef LAYOUT_RT_DEBUG + printf(" Placing child %ld on level %ld, to the right of %ld\n", i, vdata[i].level, leftroot); +#endif + lnode = leftroot; rnode = i; + rootsep = vdata[leftroot].offset + minsep; + loffset = vdata[leftroot].offset; roffset = loffset + minsep; + + /* Keep on updating the right contour now that we have attached + * a new node to the subtree being built */ + vdata[node].right_contour = i; + vdata[node].offset_to_right_contour = rootsep; + +#ifdef LAYOUT_RT_DEBUG + printf(" Contour: [%ld, %ld], offsets: [%lf, %lf], rootsep: %lf\n", + lnode, rnode, loffset, roffset, rootsep); +#endif + while ((lnode >= 0) && (rnode >= 0)) { + /* Step to the next level on the right contour of the left subtree */ + if (vdata[lnode].right_contour >= 0) { + loffset += vdata[lnode].offset_to_right_contour; + lnode = vdata[lnode].right_contour; + } else { + /* Left subtree ended there. The left and right contour + * of the left subtree will continue to the next step + * on the right subtree. */ + if (vdata[rnode].left_contour >= 0) { + auxnode = vdata[node].left_extreme; + + /* this is the "threading" step that the original + * paper is talking about */ + newoffset = (vdata[node].offset_to_right_extreme - vdata[node].offset_to_left_extreme) + minsep + vdata[rnode].offset_to_left_contour; + vdata[auxnode].left_contour = vdata[rnode].left_contour; + vdata[auxnode].right_contour = vdata[rnode].left_contour; + vdata[auxnode].offset_to_left_contour = vdata[auxnode].offset_to_right_contour = newoffset; + + /* since we attached a larger subtree to the + * already placed left subtree, we need to update + * the extrema of the subtree rooted at 'node' */ + vdata[node].left_extreme = vdata[i].left_extreme; + vdata[node].right_extreme = vdata[i].right_extreme; + vdata[node].offset_to_left_extreme = vdata[i].offset_to_left_extreme + rootsep; + vdata[node].offset_to_right_extreme = vdata[i].offset_to_right_extreme + rootsep; +#ifdef LAYOUT_RT_DEBUG + printf(" Left subtree ended earlier, continuing left subtree's left and right contour on right subtree (node %ld gets connected to node %ld)\n", auxnode, vdata[rnode].left_contour); + printf(" New contour following offset for node %ld is %lf\n", auxnode, vdata[auxnode].offset_to_left_contour); +#endif + } else { + /* Both subtrees are ending at the same time; the + * left extreme node of the subtree rooted at + * 'node' remains the same but the right extreme + * will change */ + vdata[node].right_extreme = vdata[i].right_extreme; + vdata[node].offset_to_right_extreme = vdata[i].offset_to_right_extreme + rootsep; + } + lnode = -1; + } + /* Step to the next level on the left contour of the right subtree */ + if (vdata[rnode].left_contour >= 0) { + roffset += vdata[rnode].offset_to_left_contour; + rnode = vdata[rnode].left_contour; + } else { + /* Right subtree ended here. The right contour of the right + * subtree will continue to the next step on the left subtree. + * Note that lnode has already been advanced here */ + if (lnode >= 0) { + auxnode = vdata[i].right_extreme; + + /* this is the "threading" step that the original + * paper is talking about */ + newoffset = loffset - rootsep - vdata[i].offset_to_right_extreme; + vdata[auxnode].left_contour = lnode; + vdata[auxnode].right_contour = lnode; + vdata[auxnode].offset_to_left_contour = vdata[auxnode].offset_to_right_contour = newoffset; + + /* no need to update the extrema of the subtree + * rooted at 'node' because the right subtree was + * smaller */ +#ifdef LAYOUT_RT_DEBUG + printf(" Right subtree ended earlier, continuing right subtree's left and right contour on left subtree (node %ld gets connected to node %ld)\n", auxnode, lnode); + printf(" New contour following offset for node %ld is %lf\n", auxnode, vdata[auxnode].offset_to_left_contour); +#endif + } + rnode = -1; + } +#ifdef LAYOUT_RT_DEBUG + printf(" Contour: [%ld, %ld], offsets: [%lf, %lf], rootsep: %lf\n", + lnode, rnode, loffset, roffset, rootsep); +#endif + + /* Push subtrees away if necessary */ + if ((lnode >= 0) && (rnode >= 0) && (roffset - loffset < minsep)) { +#ifdef LAYOUT_RT_DEBUG + printf(" Pushing right subtree away by %lf\n", minsep-roffset+loffset); +#endif + rootsep += minsep - roffset + loffset; + roffset = loffset + minsep; + vdata[node].offset_to_right_contour = rootsep; + } + } + +#ifdef LAYOUT_RT_DEBUG + printf(" Offset of subtree with root node %ld will be %lf\n", i, rootsep); +#endif + vdata[i].offset = rootsep; + vdata[node].offset_to_right_contour = rootsep; + avg = (avg * j) / (j + 1) + rootsep / (j + 1); + leftrootidx = j; + leftroot = i; + } else { + /* This is the first child of the node being considered so we + * can simply place the subtree on our virtual canvas */ +#ifdef LAYOUT_RT_DEBUG + printf(" Placing child %ld on level %ld as first child\n", i, vdata[i].level); +#endif + leftrootidx = j; + leftroot = i; + vdata[node].left_contour = i; + vdata[node].right_contour = i; + vdata[node].offset_to_left_contour = 0.0; + vdata[node].offset_to_right_contour = 0.0; + vdata[node].left_extreme = vdata[i].left_extreme; + vdata[node].right_extreme = vdata[i].right_extreme; + vdata[node].offset_to_left_extreme = vdata[i].offset_to_left_extreme; + vdata[node].offset_to_right_extreme = vdata[i].offset_to_right_extreme; + avg = vdata[i].offset; + } + j++; + } + } +#ifdef LAYOUT_RT_DEBUG + printf("Shifting node %ld to be centered above children. Shift amount: %lf\n", node, avg); +#endif + vdata[node].offset_to_left_contour -= avg; + vdata[node].offset_to_right_contour -= avg; + vdata[node].offset_to_left_extreme -= avg; + vdata[node].offset_to_right_extreme -= avg; + for (i = 0, j = 0; i < vcount; i++) { + if (i == node) { + continue; + } + if (vdata[i].parent == node) { + vdata[i].offset -= avg; + } + } + + return 0; +} + +/* This function computes the number of outgoing (or incoming) connections + * of clusters, represented as a membership vector. It only works with + * directed graphs. */ +int igraph_i_layout_reingold_tilford_cluster_degrees_directed( + const igraph_t *graph, + const igraph_vector_t *membership, + igraph_integer_t no_comps, + igraph_neimode_t mode, + igraph_vector_t *degrees) { + + igraph_eit_t eit; + + if (! igraph_is_directed(graph) || (mode != IGRAPH_OUT && mode != IGRAPH_IN)) { + IGRAPH_ERROR("Directed graph expected.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_resize(degrees, no_comps)); + igraph_vector_null(degrees); + + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { + igraph_integer_t eid = IGRAPH_EIT_GET(eit); + + igraph_integer_t from = IGRAPH_FROM(graph, eid); + igraph_integer_t to = IGRAPH_TO(graph, eid); + + igraph_integer_t from_cl = VECTOR(*membership)[from]; + igraph_integer_t to_cl = VECTOR(*membership)[to]; + + igraph_integer_t cl = mode == IGRAPH_OUT ? from_cl : to_cl; + + if (from_cl != to_cl) { + VECTOR(*degrees)[cl] += 1; + } + } + + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* Heuristic method to choose "nice" roots for the Reingold-Tilford layout algorithm. + * + * The principle is to select a minimal set of roots so that all other vertices + * will be reachable from them. + * + * In the undirected case, one root is chosen from each connected component. + * In the directed case, one root is chosen from each strongly connected component + * that has no incoming (or outgoing) edges (depending on 'mode'). + * When more than one root choice is possible, nodes are prioritized based on + * either lowest ecccentricity (if 'use_ecccentricity' is true) or based on + * highest degree (out- or in-degree in directed mode). + */ +int igraph_i_layout_reingold_tilford_select_roots( + const igraph_t *graph, + igraph_neimode_t mode, + igraph_vector_t *roots, + igraph_bool_t use_eccentricity) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_t order, membership; + igraph_integer_t no_comps; + long int i, j; + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); + if (use_eccentricity) { + /* Sort vertices by decreasing eccenticity. */ + + igraph_vector_t ecc; + + IGRAPH_VECTOR_INIT_FINALLY(&ecc, no_of_nodes); + IGRAPH_CHECK(igraph_eccentricity(graph, &ecc, igraph_vss_all(), mode)); + IGRAPH_CHECK(igraph_vector_qsort_ind(&ecc, &order, /* descending= */ 0)); + + igraph_vector_destroy(&ecc); + IGRAPH_FINALLY_CLEAN(1); + } else { + /* Sort vertices by decreasing degree (out- or in-degree in directed case). */ + + IGRAPH_CHECK(igraph_sort_vertex_ids_by_degree(graph, &order, + igraph_vss_all(), mode, 0, IGRAPH_DESCENDING, 0)); + } + + IGRAPH_VECTOR_INIT_FINALLY(&membership, no_of_nodes); + IGRAPH_CHECK(igraph_clusters(graph, &membership, /*csize=*/ NULL, + &no_comps, mode == IGRAPH_ALL ? IGRAPH_WEAK : IGRAPH_STRONG)); + + IGRAPH_CHECK(igraph_vector_resize(roots, no_comps)); + igraph_vector_fill(roots, -1); /* -1 signifies a not-yet-determined root for a component */ + + if (mode != IGRAPH_ALL) { + /* Directed case: + * + * We break the graph into strongly-connected components and find those components + * which have no incoming (outgoing) edges. The largest out-degree (in-degree) + * nodes from these components will be chosen as roots. When the graph is a DAG, + * these will simply be the source (sink) nodes. */ + + igraph_vector_t cluster_degrees; + + IGRAPH_VECTOR_INIT_FINALLY(&cluster_degrees, no_of_nodes); + IGRAPH_CHECK(igraph_i_layout_reingold_tilford_cluster_degrees_directed( + graph, &membership, no_comps, + mode == IGRAPH_OUT ? IGRAPH_IN : IGRAPH_OUT, /* reverse direction */ + &cluster_degrees)); + + /* Iterate through nodes in decreasing out-degree (or in-degree) order + * and record largest degree node in each strongly-connected component + * which has no incoming (outgoing) edges. */ + for (i = 0; i < no_of_nodes; ++i) { + long int v = (long int) VECTOR(order)[i]; + long int cl = VECTOR(membership)[v]; + if (VECTOR(cluster_degrees)[cl] == 0 && VECTOR(*roots)[cl] == -1) { + VECTOR(*roots)[cl] = v; + } + } + + igraph_vector_destroy(&cluster_degrees); + IGRAPH_FINALLY_CLEAN(1); + + /* Remove remaining -1 indices. These correspond to components that + * did have some incoming edges. */ + for (i=0, j=0; i < no_comps; ++i) { + if (VECTOR(*roots)[i] == -1) { + continue; + } + VECTOR(*roots)[j++] = VECTOR(*roots)[i]; + } + igraph_vector_resize(roots, j); + + } else { + /* Undirected case: + * + * Select the highest degree node from each component. + */ + + long int no_seen = 0; + + for (i=0; i < no_of_nodes; ++i) { + long int v = VECTOR(order)[i]; + long int cl = VECTOR(membership)[v]; + if (VECTOR(*roots)[cl] == -1) { + no_seen += 1; + VECTOR(*roots)[cl] = v; + } + if (no_seen == no_comps) { + /* All components have roots now. */ + break; + } + } + } + + igraph_vector_destroy(&membership); + igraph_vector_destroy(&order); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_layout_reingold_tilford + * \brief Reingold-Tilford layout for tree graphs + * + * + * Arranges the nodes in a tree where the given node is used as the root. + * The tree is directed downwards and the parents are centered above its + * children. For the exact algorithm, see: + * + * + * Reingold, E and Tilford, J: Tidier drawing of trees. + * IEEE Trans. Softw. Eng., SE-7(2):223--228, 1981. + * https://doi.org/10.1109/TSE.1981.234519 + * + * + * If the given graph is not a tree, a breadth-first search is executed + * first to obtain a possible spanning tree. + * + * \param graph The graph object. + * \param res The result, the coordinates in a matrix. The parameter + * should point to an initialized matrix object and will be resized. + * \param mode Specifies which edges to consider when building the tree. + * If it is \c IGRAPH_OUT then only the outgoing, if it is \c IGRAPH_IN + * then only the incoming edges of a parent are considered. If it is + * \c IGRAPH_ALL then all edges are used (this was the behavior in + * igraph 0.5 and before). This parameter also influences how the root + * vertices are calculated, if they are not given. See the \p roots parameter. + * \param roots The index of the root vertex or root vertices. The set of roots + * should be specified so that all vertices of the graph are reachable from them. + * Simply put, in the udirected case, one root should be given from each + * connected component. If \p roots is \c NULL or a pointer to an empty vector, + * then the roots will be selected automatically. Currently, automatic root + * selection prefers low ecccentricity vertices in graphs with fewer than + * 500 vertices, and high degree vertices (acording to \p mode) in larger graphs. + * The root selecton heuristic may change without notice. To ensure a consistent + * output, please specify the roots manually. + * \param rootlevel This argument can be useful when drawing forests which are + * not trees (i.e. they are unconnected and have tree components). It specifies + * the level of the root vertices for every tree in the forest. It is only + * considered if not a null pointer and the \p roots argument is also given + * (and it is not a null pointer of an empty vector). + * \return Error code. + * + * Added in version 0.2. + * + * \sa \ref igraph_layout_reingold_tilford_circular(). + * + * \example examples/simple/igraph_layout_reingold_tilford.c + */ +int igraph_layout_reingold_tilford(const igraph_t *graph, + igraph_matrix_t *res, + igraph_neimode_t mode, + const igraph_vector_t *roots, + const igraph_vector_t *rootlevel) { + + long int no_of_nodes_orig = igraph_vcount(graph); + long int no_of_nodes = no_of_nodes_orig; + long int real_root; + igraph_t extended; + const igraph_t *pextended = graph; + igraph_vector_t myroots; + const igraph_vector_t *proots = roots; + + long int i; + igraph_vector_t newedges; + + /* TODO: possible speedup could be achieved if we use a table for storing + * the children of each node in the tree. (Now the implementation uses a + * single array containing the parent of each node and a node's children + * are determined by looking for other nodes that have this node as parent) + */ + + /* at various steps it might be necessary to add edges to the graph */ + IGRAPH_VECTOR_INIT_FINALLY(&newedges, 0); + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if ( (!roots || igraph_vector_size(roots) == 0) && + rootlevel && igraph_vector_size(rootlevel) != 0 ) { + IGRAPH_WARNING("Reingold-Tilford layout: 'rootlevel' ignored"); + } + + /* ----------------------------------------------------------------------- */ + /* If root vertices are not given, perform automated root selection. */ + + if (!roots || igraph_vector_size(roots) == 0) { + + IGRAPH_VECTOR_INIT_FINALLY(&myroots, 0); + igraph_i_layout_reingold_tilford_select_roots(graph, mode, &myroots, no_of_nodes < 500); + proots = &myroots; + + } else if (rootlevel && igraph_vector_size(rootlevel) > 0 && + igraph_vector_size(roots) > 1) { + + /* ----------------------------------------------------------------------- */ + /* Many roots were given to us, check 'rootlevel' */ + + long int plus_levels = 0; + long int i; + + if (igraph_vector_size(roots) != igraph_vector_size(rootlevel)) { + IGRAPH_ERROR("Reingold-Tilford: 'roots' and 'rootlevel' lengths differ", + IGRAPH_EINVAL); + } + + /* count the rootlevels that are not zero */ + for (i = 0; i < igraph_vector_size(roots); i++) { + plus_levels += VECTOR(*rootlevel)[i]; + } + + /* make copy of graph, add vertices/edges */ + if (plus_levels != 0) { + long int edgeptr = 0; + + pextended = &extended; + IGRAPH_CHECK(igraph_copy(&extended, graph)); + IGRAPH_FINALLY(igraph_destroy, &extended); + IGRAPH_CHECK(igraph_add_vertices(&extended, + (igraph_integer_t) plus_levels, 0)); + + igraph_vector_resize(&newedges, plus_levels * 2); + + for (i = 0; i < igraph_vector_size(roots); i++) { + long int rl = (long int) VECTOR(*rootlevel)[i]; + long int rn = (long int) VECTOR(*roots)[i]; + long int j; + + /* zero-level roots don't get anything special */ + if (rl == 0) { + continue; + } + + /* for each nonzero-level root, add vertices + and edges at all levels [1, 2, .., rl] + piercing through the graph. If mode=="in" + they pierce the other way */ + if (mode != IGRAPH_IN) { + VECTOR(newedges)[edgeptr++] = no_of_nodes; + VECTOR(newedges)[edgeptr++] = rn; + for (j = 0; j < rl - 1; j++) { + VECTOR(newedges)[edgeptr++] = no_of_nodes + 1; + VECTOR(newedges)[edgeptr++] = no_of_nodes; + no_of_nodes++; + } + } else { + VECTOR(newedges)[edgeptr++] = rn; + VECTOR(newedges)[edgeptr++] = no_of_nodes; + for (j = 0; j < rl - 1; j++) { + VECTOR(newedges)[edgeptr++] = no_of_nodes; + VECTOR(newedges)[edgeptr++] = no_of_nodes + 1; + no_of_nodes++; + } + } + + /* move on to the next root */ + VECTOR(*roots)[i] = no_of_nodes++; + } + + /* actually add the edges to the graph */ + IGRAPH_CHECK(igraph_add_edges(&extended, &newedges, 0)); + } + } + + /* We have root vertices now. If one or more nonzero-level roots were + chosen by the user, we have copied the graph and added a few vertices + and (directed) edges to connect those floating roots to nonfloating, + zero-level equivalent roots. + + Below, the function + + igraph_i_layout_reingold_tilford(pextended, res, mode, real_root) + + calculates the actual rt coordinates of the graph. However, for + simplicity that function requires a connected graph and a single root. + For directed graphs, it needs not be strongly connected, however all + nodes must be reachable from the root following the stream (i.e. the + root must be a "mother vertex"). + + So before we call that function we have to make sure the (copied) graph + satisfies that condition. That requires: + 1. if there is more than one root, defining a single real_root + 2. if a real_root is defined, adding edges to connect all roots to it + 3. ensure real_root is mother of the whole graph. If it is not, + add shortcut edges from real_root to any disconnected node for now. + + NOTE: 3. could be done better, e.g. by topological sorting of some kind. + But for now it's ok like this. + */ + /* if there is only one root, no need for real_root */ + if (igraph_vector_size(proots) == 1) { + real_root = (long int) VECTOR(*proots)[0]; + if (real_root < 0 || real_root >= no_of_nodes) { + IGRAPH_ERROR("Invalid vertex id.", IGRAPH_EINVVID); + } + + /* else, we need to make real_root */ + } else { + long int no_of_newedges; + + /* Make copy of the graph unless it exists already */ + if (pextended == graph) { + pextended = &extended; + IGRAPH_CHECK(igraph_copy(&extended, graph)); + IGRAPH_FINALLY(igraph_destroy, &extended); + } + + /* add real_root to the vertices */ + real_root = no_of_nodes; + IGRAPH_CHECK(igraph_add_vertices(&extended, 1, 0)); + no_of_nodes++; + + /* add edges from the roots to real_root */ + no_of_newedges = igraph_vector_size(proots); + igraph_vector_resize(&newedges, no_of_newedges * 2); + for (i = 0; i < no_of_newedges; i++) { + VECTOR(newedges)[2 * i] = no_of_nodes - 1; + VECTOR(newedges)[2 * i + 1] = VECTOR(*proots)[i]; + } + + IGRAPH_CHECK(igraph_add_edges(&extended, &newedges, 0)); + } + + /* prepare edges to unreachable parts of the graph */ + IGRAPH_CHECK(igraph_i_layout_reingold_tilford_unreachable(pextended, mode, real_root, no_of_nodes, &newedges)); + + if (igraph_vector_size(&newedges) != 0) { + /* Make copy of the graph unless it exists already */ + if (pextended == graph) { + pextended = &extended; + IGRAPH_CHECK(igraph_copy(&extended, graph)); + IGRAPH_FINALLY(igraph_destroy, &extended); + } + + IGRAPH_CHECK(igraph_add_edges(&extended, &newedges, 0)); + } + igraph_vector_destroy(&newedges); + IGRAPH_FINALLY_CLEAN(1); + + /* ----------------------------------------------------------------------- */ + /* Layout */ + IGRAPH_CHECK(igraph_i_layout_reingold_tilford(pextended, res, mode, real_root)); + + /* Remove the new vertices from the layout */ + if (no_of_nodes != no_of_nodes_orig) { + if (no_of_nodes - 1 == no_of_nodes_orig) { + IGRAPH_CHECK(igraph_matrix_remove_row(res, no_of_nodes_orig)); + } else { + igraph_matrix_t tmp; + long int i; + IGRAPH_MATRIX_INIT_FINALLY(&tmp, no_of_nodes_orig, 2); + for (i = 0; i < no_of_nodes_orig; i++) { + MATRIX(tmp, i, 0) = MATRIX(*res, i, 0); + MATRIX(tmp, i, 1) = MATRIX(*res, i, 1); + } + IGRAPH_CHECK(igraph_matrix_update(res, &tmp)); + igraph_matrix_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + } + } + + if (pextended != graph) { + igraph_destroy(&extended); + IGRAPH_FINALLY_CLEAN(1); + } + + /* Remove the roots vector if it was created by us */ + if (proots != roots) { + igraph_vector_destroy(&myroots); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_layout_reingold_tilford_circular + * \brief Circular Reingold-Tilford layout for trees + * + * + * This layout is almost the same as \ref igraph_layout_reingold_tilford(), but + * the tree is drawn in a circular way, with the root vertex in the center. + * + * \param graph The graph object. + * \param res The result, the coordinates in a matrix. The parameter + * should point to an initialized matrix object and will be resized. + * \param mode Specifies which edges to consider when building the tree. + * If it is \c IGRAPH_OUT then only the outgoing, if it is \c IGRAPH_IN + * then only the incoming edges of a parent are considered. If it is + * \c IGRAPH_ALL then all edges are used (this was the behavior in + * igraph 0.5 and before). This parameter also influences how the root + * vertices are calculated, if they are not given. See the \p roots parameter. + * \param roots The index of the root vertex or root vertices. The set of roots + * should be specified so that all vertices of the graph are reachable from them. + * Simply put, in the udirected case, one root should be given from each + * connected component. If \p roots is \c NULL or a pointer to an empty vector, + * then the roots will be selected automatically. Currently, automatic root + * selection prefers low ecccentricity vertices in graphs with fewer than + * 500 vertices, and high degree vertices (acording to \p mode) in larger graphs. + * The root selecton heuristic may change without notice. To ensure a consistent + * output, please specify the roots manually. + * \param rootlevel This argument can be useful when drawing forests which are + * not trees (i.e. they are unconnected and have tree components). It specifies + * the level of the root vertices for every tree in the forest. It is only + * considered if not a null pointer and the \p roots argument is also given + * (and it is not a null pointer or an empty vector). + * \return Error code. + * + * \sa \ref igraph_layout_reingold_tilford(). + */ +int igraph_layout_reingold_tilford_circular(const igraph_t *graph, + igraph_matrix_t *res, + igraph_neimode_t mode, + const igraph_vector_t *roots, + const igraph_vector_t *rootlevel) { + + long int no_of_nodes = igraph_vcount(graph); + long int i; + igraph_real_t ratio; + igraph_real_t minx, maxx; + + IGRAPH_CHECK(igraph_layout_reingold_tilford(graph, res, mode, roots, rootlevel)); + + if (no_of_nodes == 0) { + return IGRAPH_SUCCESS; + } + + ratio = 2 * M_PI * (no_of_nodes - 1.0) / no_of_nodes; + + minx = maxx = MATRIX(*res, 0, 0); + for (i = 1; i < no_of_nodes; i++) { + if (MATRIX(*res, i, 0) > maxx) { + maxx = MATRIX(*res, i, 0); + } + if (MATRIX(*res, i, 0) < minx) { + minx = MATRIX(*res, i, 0); + } + } + if (maxx > minx) { + ratio /= (maxx - minx); + } + for (i = 0; i < no_of_nodes; i++) { + igraph_real_t phi = (MATRIX(*res, i, 0) - minx) * ratio; + igraph_real_t r = MATRIX(*res, i, 1); + MATRIX(*res, i, 0) = r * cos(phi); + MATRIX(*res, i, 1) = r * sin(phi); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/layout/sugiyama.c b/src/rigraph/core/layout/sugiyama.c new file mode 100644 index 0000000..8e9740e --- /dev/null +++ b/src/rigraph/core/layout/sugiyama.c @@ -0,0 +1,1342 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_layout.h" +#include "igraph_centrality.h" +#include "igraph_components.h" +#include "igraph_constants.h" +#include "igraph_constructors.h" +#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_structural.h" +#include "igraph_types.h" + +#include "../internal/glpk_support.h" +#include "../misc/feedback_arc_set.h" + +#include "config.h" + +#include + +/* #define SUGIYAMA_DEBUG */ + +#ifdef _MSC_VER +/* MSVC does not support variadic macros */ +#include +static void debug(const char* fmt, ...) { + va_list args; + va_start(args, fmt); +#ifdef SUGIYAMA_DEBUG + vfprintf(stderr, fmt, args); +#endif + va_end(args); +} +#else +#ifdef SUGIYAMA_DEBUG + #define debug(...) fprintf(stderr, __VA_ARGS__) +#else + #define debug(...) +#endif +#endif + +/* MSVC uses __forceinline instead of inline */ +#ifdef _MSC_VER + #define INLINE __forceinline +#else + #define INLINE inline +#endif + +/* + * Implementation of the Sugiyama layout algorithm as described in: + * + * [1] K. Sugiyama, S. Tagawa and M. Toda, "Methods for Visual Understanding of + * Hierarchical Systems". IEEE Transactions on Systems, Man and Cybernetics + * 11(2):109-125, 1981. + * + * The layering (if not given in advance) is calculated by ... TODO + * + * [2] TODO + * + * The X coordinates of nodes within a layer are calculated using the method of + * Brandes & Köpf: + * + * [3] U. Brandes and B. Köpf, "Fast and Simple Horizontal Coordinate + * Assignment". In: Lecture Notes in Computer Science 2265:31-44, 2002. + * + * Layer compaction is done according to: + * + * [4] N.S. Nikolov and A. Tarassov, "Graph layering by promotion of nodes". + * Journal of Discrete Applied Mathematics, special issue: IV ALIO/EURO + * workshop on applied combinatorial optimization, 154(5). + * + * The steps of the algorithm are as follows: + * + * 1. Cycle removal by finding an approximately minimal feedback arc set + * and reversing the direction of edges in the set. Algorithms for + * finding minimal feedback arc sets are as follows: + * + * - Find a cycle and find its minimum weight edge. Decrease the weight + * of all the edges by w. Remove those edges whose weight became zero. + * Repeat until there are no cycles. Re-introduce removed edges in + * decreasing order of weights, ensuring that no cycles are created. + * + * - Order the vertices somehow and remove edges which point backwards + * in the ordering. Eades et al proposed the following procedure: + * + * 1. Iteratively remove sinks and prepend them to a vertex sequence + * s2. + * + * 2. Iteratively remove sources and append them to a vertex sequence + * s1. + * + * 3. Choose a vertex u s.t. the difference between the number of + * rightward arcs and the number of leftward arcs is the largest, + * remove u and append it to s1. Goto step 1 if there are still + * more vertices. + * + * 4. Concatenate s1 with s2. + * + * This algorithm is known to produce feedback arc sets at most the + * size of m/2 - n/6, where m is the number of edges. Further + * improvements are possible in step 3 which bring down the size of + * the set to at most m/4 for cubic directed graphs, see Eades (1995). + * + * - For undirected graphs, find a maximum weight spanning tree and + * remove all the edges not in the spanning tree. For directed graphs, + * find minimal cuts iteratively and remove edges pointing from A to + * B or from B to A in the cut, depending on which one is smaller. Yes, + * this is time-consuming. + * + * 2. Assigning vertices to layers according to [2]. + * + * 3. Extracting weakly connected components. The remaining steps are + * executed for each component. + * + * 4. Compacting the layering using the method of [4]. TODO + * Steps 2-4 are performed only when no layering is given in advance. + * + * 5. Adding dummy nodes to ensure that each edge spans at most one layer + * only. + * + * 6. Finding an optimal ordering of vertices within a layer using the + * Sugiyama framework [1]. + * + * 7. Assigning horizontal coordinates to each vertex using [3]. + * + * 8. ??? + * + * 9. Profit! + */ + +/** + * Data structure to store a layering of the graph. + */ +typedef struct { + igraph_vector_ptr_t layers; +} igraph_i_layering_t; + +/** + * Initializes a layering. + */ +static int igraph_i_layering_init(igraph_i_layering_t* layering, + const igraph_vector_t* membership) { + long int i, n, num_layers; + + if (igraph_vector_size(membership) == 0) { + num_layers = 0; + } else { + num_layers = (long int) igraph_vector_max(membership) + 1; + } + + IGRAPH_CHECK(igraph_vector_ptr_init(&layering->layers, num_layers)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &layering->layers); + + for (i = 0; i < num_layers; i++) { + igraph_vector_t* vec = IGRAPH_CALLOC(1, igraph_vector_t); + IGRAPH_VECTOR_INIT_FINALLY(vec, 0); + VECTOR(layering->layers)[i] = vec; + IGRAPH_FINALLY_CLEAN(1); + } + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&layering->layers, igraph_vector_destroy); + + n = igraph_vector_size(membership); + for (i = 0; i < n; i++) { + long int l = (long int) VECTOR(*membership)[i]; + igraph_vector_t* vec = VECTOR(layering->layers)[l]; + IGRAPH_CHECK(igraph_vector_push_back(vec, i)); + } + + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * Destroys a layering. + */ +static void igraph_i_layering_destroy(igraph_i_layering_t* layering) { + igraph_vector_ptr_destroy_all(&layering->layers); +} + +/** + * Returns the number of layers in a layering. + */ +static int igraph_i_layering_num_layers(const igraph_i_layering_t* layering) { + return (int) igraph_vector_ptr_size(&layering->layers); +} + +/** + * Returns the list of vertices in a given layer + */ +static igraph_vector_t* igraph_i_layering_get(const igraph_i_layering_t* layering, + long int index) { + return (igraph_vector_t*)VECTOR(layering->layers)[index]; +} + + +/** + * Forward declarations + */ + +static int igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph, + const igraph_vector_t* weights, igraph_vector_t* membership); +static int igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* graph, + igraph_matrix_t* layout, const igraph_i_layering_t* layering, + long int maxiter); +static int igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* graph, + igraph_matrix_t* layout, const igraph_i_layering_t* layering, + igraph_real_t hgap, igraph_integer_t no_of_real_nodes); + +/** + * Calculated the median of four numbers (not necessarily sorted). + */ +static INLINE igraph_real_t igraph_i_median_4(igraph_real_t x1, + igraph_real_t x2, igraph_real_t x3, igraph_real_t x4) { + igraph_real_t arr[4] = { x1, x2, x3, x4 }; + igraph_vector_t vec; + igraph_vector_view(&vec, arr, 4); + igraph_vector_sort(&vec); + return (arr[1] + arr[2]) / 2.0; +} + + +/** + * \ingroup layout + * \function igraph_layout_sugiyama + * \brief Sugiyama layout algorithm for layered directed acyclic graphs. + * + * + * This layout algorithm is designed for directed acyclic graphs where each + * vertex is assigned to a layer. Layers are indexed from zero, and vertices + * of the same layer will be placed on the same horizontal line. The X coordinates + * of vertices within each layer are decided by the heuristic proposed by + * Sugiyama et al to minimize edge crossings. + * + * + * You can also try to lay out undirected graphs, graphs containing cycles, or + * graphs without an a priori layered assignment with this algorithm. igraph + * will try to eliminate cycles and assign vertices to layers, but there is no + * guarantee on the quality of the layout in such cases. + * + * + * The Sugiyama layout may introduce "bends" on the edges in order to obtain a + * visually more pleasing layout. This is achieved by adding dummy nodes to + * edges spanning more than one layer. The resulting layout assigns coordinates + * not only to the nodes of the original graph but also to the dummy nodes. + * The layout algorithm will also return the extended graph with the dummy nodes. + * An edge in the original graph may either be mapped to a single edge in the + * extended graph or a \em path that starts and ends in the original + * source and target vertex and passes through multiple dummy vertices. In + * such cases, the user may also request the mapping of the edges of the extended + * graph back to the edges of the original graph. + * + * + * For more details, see K. Sugiyama, S. Tagawa and M. Toda, "Methods for Visual + * Understanding of Hierarchical Systems". IEEE Transactions on Systems, Man and + * Cybernetics 11(2):109-125, 1981. + * + * \param graph Pointer to an initialized graph object. + * \param res Pointer to an initialized matrix object. This will contain + * the result and will be resized as needed. The first |V| rows + * of the layout will contain the coordinates of the original graph, + * the remaining rows contain the positions of the dummy nodes. + * Therefore, you can use the result both with \p graph or with + * \p extended_graph. + * \param extended_graph Pointer to an uninitialized graph object or \c NULL. + * The extended graph with the added dummy nodes will be + * returned here. In this graph, each edge points downwards + * to lower layers, spans exactly one layer and the first + * |V| vertices coincide with the vertices of the + * original graph. + * \param extd_to_orig_eids Pointer to a vector or \c NULL. If not \c NULL, the + * mapping from the edge IDs of the extended graph back + * to the edge IDs of the original graph will be stored + * here. + * \param layers The layer index for each vertex or \c NULL if the layers should + * be determined automatically by igraph. + * \param hgap The preferred minimum horizontal gap between vertices in the same + * layer. + * \param vgap The distance between layers. + * \param maxiter Maximum number of iterations in the crossing minimization stage. + * 100 is a reasonable default; if you feel that you have too + * many edge crossings, increase this. + * \param weights Weights of the edges. These are used only if the graph contains + * cycles; igraph will tend to reverse edges with smaller + * weights when breaking the cycles. + */ +int igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, + igraph_t *extd_graph, igraph_vector_t *extd_to_orig_eids, + const igraph_vector_t* layers, igraph_real_t hgap, igraph_real_t vgap, + long int maxiter, const igraph_vector_t *weights) { + long int i, j, k, l, m, nei; + long int no_of_nodes = (long int)igraph_vcount(graph); + long int comp_idx; + long int next_extd_vertex_id = no_of_nodes; + igraph_bool_t directed = igraph_is_directed(graph); + igraph_integer_t no_of_components; /* number of components of the original graph */ + igraph_vector_t membership; /* components of the original graph */ + igraph_vector_t extd_edgelist; /* edge list of the extended graph */ + igraph_vector_t layers_own; /* layer indices after having eliminated empty layers */ + igraph_real_t dx = 0, dx2 = 0; /* displacement of the current component on the X axis */ + igraph_vector_t layer_to_y; /* mapping from layer indices to final Y coordinates */ + + if (layers && igraph_vector_size(layers) != no_of_nodes) { + IGRAPH_ERROR("layer vector too short or too long", IGRAPH_EINVAL); + } + + if (extd_graph != 0) { + IGRAPH_VECTOR_INIT_FINALLY(&extd_edgelist, 0); + if (extd_to_orig_eids != 0) { + igraph_vector_clear(extd_to_orig_eids); + } + } + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); + IGRAPH_VECTOR_INIT_FINALLY(&membership, 0); + IGRAPH_VECTOR_INIT_FINALLY(&layer_to_y, 0); + + /* 1. Find a feedback arc set if we don't have a layering yet. If we do have + * a layering, we can leave all the edges as is as they will be re-oriented + * to point downwards only anyway. */ + if (layers == 0) { + IGRAPH_VECTOR_INIT_FINALLY(&layers_own, no_of_nodes); + IGRAPH_CHECK(igraph_i_layout_sugiyama_place_nodes_vertically( + graph, weights, &layers_own)); + } else { + IGRAPH_CHECK(igraph_vector_copy(&layers_own, layers)); + IGRAPH_FINALLY(igraph_vector_destroy, &layers_own); + } + + /* Normalize layering, eliminate empty layers */ + if (no_of_nodes > 0) { + igraph_vector_t inds; + IGRAPH_VECTOR_INIT_FINALLY(&inds, 0); + IGRAPH_CHECK((int) igraph_vector_qsort_ind(&layers_own, &inds, 0)); + j = -1; dx = VECTOR(layers_own)[(long int)VECTOR(inds)[0]] - 1; + for (i = 0; i < no_of_nodes; i++) { + k = (long int)VECTOR(inds)[i]; + if (VECTOR(layers_own)[k] > dx) { + /* New layer starts here */ + dx = VECTOR(layers_own)[k]; + j++; + IGRAPH_CHECK(igraph_vector_push_back(&layer_to_y, dx * vgap)); + } + VECTOR(layers_own)[k] = j; + } + igraph_vector_destroy(&inds); + IGRAPH_FINALLY_CLEAN(1); + } + + /* 2. Find the connected components. */ + IGRAPH_CHECK(igraph_clusters(graph, &membership, 0, &no_of_components, + IGRAPH_WEAK)); + + /* 3. For each component... */ + dx = 0; + for (comp_idx = 0; comp_idx < no_of_components; comp_idx++) { + /* Extract the edges of the comp_idx'th component and add dummy nodes for edges + * spanning more than one layer. */ + long int component_size, next_new_vertex_id; + igraph_vector_t old2new_vertex_ids; + igraph_vector_t new2old_vertex_ids; + igraph_vector_t new_layers; + igraph_vector_t edgelist; + igraph_vector_t neis; + + IGRAPH_VECTOR_INIT_FINALLY(&edgelist, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&new2old_vertex_ids, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&old2new_vertex_ids, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&new_layers, 0); + + igraph_vector_fill(&old2new_vertex_ids, -1); + + /* Construct a mapping from the old vertex ids to the new ones */ + for (i = 0, next_new_vertex_id = 0; i < no_of_nodes; i++) { + if (VECTOR(membership)[i] == comp_idx) { + IGRAPH_CHECK(igraph_vector_push_back(&new_layers, VECTOR(layers_own)[i])); + VECTOR(new2old_vertex_ids)[next_new_vertex_id] = i; + VECTOR(old2new_vertex_ids)[i] = next_new_vertex_id; + next_new_vertex_id++; + } + } + component_size = next_new_vertex_id; + + /* Construct a proper layering of the component in new_graph where each edge + * points downwards and spans exactly one layer. */ + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(membership)[i] != comp_idx) { + continue; + } + + /* Okay, this vertex is in the component we are considering. + * Add the neighbors of this vertex, excluding loops */ + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) i, + IGRAPH_OUT)); + j = igraph_vector_size(&neis); + for (k = 0; k < j; k++) { + long int eid = (long int) VECTOR(neis)[k]; + if (directed) { + nei = IGRAPH_TO(graph, eid); + } else { + nei = IGRAPH_OTHER(graph, eid, i); + if (nei < i) { /* to avoid considering edges twice */ + continue; + } + } + if (VECTOR(layers_own)[i] == VECTOR(layers_own)[nei]) { + /* Edge goes within the same layer, we don't need this in the + * layered graph, but we need it in the extended graph */ + if (extd_graph != 0) { + IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, i)); + IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, nei)); + if (extd_to_orig_eids != 0) { + IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); + } + } + } else if (VECTOR(layers_own)[i] > VECTOR(layers_own)[nei]) { + /* Edge goes upwards, we have to flip it */ + IGRAPH_CHECK(igraph_vector_push_back(&edgelist, + VECTOR(old2new_vertex_ids)[nei])); + for (l = (long int) VECTOR(layers_own)[nei] + 1; + l < VECTOR(layers_own)[i]; l++) { + IGRAPH_CHECK(igraph_vector_push_back(&new_layers, l)); + IGRAPH_CHECK(igraph_vector_push_back(&edgelist, next_new_vertex_id)); + IGRAPH_CHECK(igraph_vector_push_back(&edgelist, next_new_vertex_id++)); + } + IGRAPH_CHECK(igraph_vector_push_back(&edgelist, + VECTOR(old2new_vertex_ids)[i])); + /* Also add the edge to the extended graph if needed, but this time + * with the proper orientation */ + if (extd_graph != 0) { + IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, i)); + next_extd_vertex_id += VECTOR(layers_own)[i] - VECTOR(layers_own)[nei] - 1; + for (l = (long int) VECTOR(layers_own)[i] - 1, m = 1; + l > VECTOR(layers_own)[nei]; l--, m++) { + IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, next_extd_vertex_id - m)); + IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, next_extd_vertex_id - m)); + if (extd_to_orig_eids != 0) { + IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); + } + } + IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, nei)); + if (extd_to_orig_eids != 0) { + IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); + } + } + } else { + /* Edge goes downwards */ + IGRAPH_CHECK(igraph_vector_push_back(&edgelist, + VECTOR(old2new_vertex_ids)[i])); + for (l = (long int) VECTOR(layers_own)[i] + 1; + l < VECTOR(layers_own)[nei]; l++) { + IGRAPH_CHECK(igraph_vector_push_back(&new_layers, l)); + IGRAPH_CHECK(igraph_vector_push_back(&edgelist, next_new_vertex_id)); + IGRAPH_CHECK(igraph_vector_push_back(&edgelist, next_new_vertex_id++)); + } + IGRAPH_CHECK(igraph_vector_push_back(&edgelist, + VECTOR(old2new_vertex_ids)[nei])); + /* Also add the edge to the extended graph */ + if (extd_graph != 0) { + IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, i)); + for (l = (long int) VECTOR(layers_own)[i] + 1; + l < VECTOR(layers_own)[nei]; l++) { + IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, next_extd_vertex_id)); + IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, next_extd_vertex_id++)); + if (extd_to_orig_eids != 0) { + IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); + } + } + IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, nei)); + if (extd_to_orig_eids != 0) { + IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); + } + } + } + } + } + + /* At this point, we have the subgraph with the dummy nodes and + * edges, so we can run Sugiyama's algorithm on it. */ + { + igraph_matrix_t layout; + igraph_i_layering_t layering; + igraph_t subgraph; + + IGRAPH_CHECK(igraph_matrix_init(&layout, next_new_vertex_id, 2)); + IGRAPH_FINALLY(igraph_matrix_destroy, &layout); + IGRAPH_CHECK(igraph_create(&subgraph, &edgelist, (igraph_integer_t) + next_new_vertex_id, 1)); + IGRAPH_FINALLY(igraph_destroy, &subgraph); + + /* + igraph_vector_print(&edgelist); + igraph_vector_print(&new_layers); + */ + + /* Assign the vertical coordinates */ + for (i = 0; i < next_new_vertex_id; i++) { + MATRIX(layout, i, 1) = VECTOR(new_layers)[i]; + } + + /* Create a layering */ + IGRAPH_CHECK(igraph_i_layering_init(&layering, &new_layers)); + IGRAPH_FINALLY(igraph_i_layering_destroy, &layering); + + /* Find the order in which the nodes within a layer should be placed */ + IGRAPH_CHECK(igraph_i_layout_sugiyama_order_nodes_horizontally(&subgraph, &layout, + &layering, maxiter)); + + /* Assign the horizontal coordinates. This is according to the algorithm + * of Brandes & Köpf */ + IGRAPH_CHECK(igraph_i_layout_sugiyama_place_nodes_horizontally(&subgraph, &layout, + &layering, hgap, (igraph_integer_t) component_size)); + + /* Re-assign rows into the result matrix, and at the same time, */ + /* adjust dx so that the next component does not overlap this one */ + j = next_new_vertex_id - component_size; + k = igraph_matrix_nrow(res); + IGRAPH_CHECK(igraph_matrix_add_rows(res, j)); + dx2 = dx; + for (i = 0; i < component_size; i++) { + l = (long int)VECTOR(new2old_vertex_ids)[i]; + MATRIX(*res, l, 0) = MATRIX(layout, i, 0) + dx; + MATRIX(*res, l, 1) = VECTOR(layer_to_y)[(long)MATRIX(layout, i, 1)]; + if (dx2 < MATRIX(*res, l, 0)) { + dx2 = MATRIX(*res, l, 0); + } + } + for (i = component_size; i < next_new_vertex_id; i++) { + MATRIX(*res, k, 0) = MATRIX(layout, i, 0) + dx; + MATRIX(*res, k, 1) = VECTOR(layer_to_y)[(long)MATRIX(layout, i, 1)]; + if (dx2 < MATRIX(*res, k, 0)) { + dx2 = MATRIX(*res, k, 0); + } + k++; + } + dx = dx2 + hgap; + + igraph_destroy(&subgraph); + igraph_i_layering_destroy(&layering); + igraph_matrix_destroy(&layout); + IGRAPH_FINALLY_CLEAN(3); + } + + igraph_vector_destroy(&new_layers); + igraph_vector_destroy(&old2new_vertex_ids); + igraph_vector_destroy(&new2old_vertex_ids); + igraph_vector_destroy(&edgelist); + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(5); + } + + igraph_vector_destroy(&layers_own); + igraph_vector_destroy(&layer_to_y); + igraph_vector_destroy(&membership); + IGRAPH_FINALLY_CLEAN(3); + + if (extd_graph != 0) { + IGRAPH_CHECK(igraph_create(extd_graph, &extd_edgelist, (igraph_integer_t) + next_extd_vertex_id, igraph_is_directed(graph))); + igraph_vector_destroy(&extd_edgelist); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static int igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph, + const igraph_vector_t* weights, igraph_vector_t* membership) { + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + + if (no_of_edges == 0) { + igraph_vector_fill(membership, 0); + return IGRAPH_SUCCESS; + } + +#ifdef HAVE_GLPK + if (igraph_is_directed(graph) && no_of_nodes <= 1000) { + /* Network simplex algorithm of Gansner et al, using the original linear + * programming formulation */ + long int i, j; + igraph_vector_t outdegs, indegs, feedback_edges; + glp_prob *ip; + glp_smcp parm; + + /* Allocate storage and create the problem */ + ip = glp_create_prob(); + IGRAPH_FINALLY(glp_delete_prob, ip); + IGRAPH_VECTOR_INIT_FINALLY(&feedback_edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outdegs, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&indegs, no_of_nodes); + + /* Find an approximate feedback edge set */ + IGRAPH_CHECK(igraph_i_feedback_arc_set_eades(graph, &feedback_edges, weights, 0)); + igraph_vector_sort(&feedback_edges); + + /* Calculate in- and out-strengths for the remaining edges */ + IGRAPH_CHECK(igraph_strength(graph, &indegs, igraph_vss_all(), + IGRAPH_IN, 1, weights)); + IGRAPH_CHECK(igraph_strength(graph, &outdegs, igraph_vss_all(), + IGRAPH_IN, 1, weights)); + j = igraph_vector_size(&feedback_edges); + for (i = 0; i < j; i++) { + long int eid = (long int) VECTOR(feedback_edges)[i]; + long int from = IGRAPH_FROM(graph, eid); + long int to = IGRAPH_TO(graph, eid); + VECTOR(outdegs)[from] -= weights ? VECTOR(*weights)[eid] : 1; + VECTOR(indegs)[to] -= weights ? VECTOR(*weights)[eid] : 1; + } + + /* Configure GLPK */ + glp_term_out(GLP_OFF); + glp_init_smcp(&parm); + parm.msg_lev = GLP_MSG_OFF; + parm.presolve = GLP_OFF; + + /* Set up variables and objective function coefficients */ + glp_set_obj_dir(ip, GLP_MIN); + glp_add_cols(ip, (int) no_of_nodes); + IGRAPH_CHECK(igraph_vector_sub(&outdegs, &indegs)); + for (i = 1; i <= no_of_nodes; i++) { + glp_set_col_kind(ip, (int) i, GLP_IV); + glp_set_col_bnds(ip, (int) i, GLP_LO, 0.0, 0.0); + glp_set_obj_coef(ip, (int) i, VECTOR(outdegs)[i - 1]); + } + igraph_vector_destroy(&indegs); + igraph_vector_destroy(&outdegs); + IGRAPH_FINALLY_CLEAN(2); + + /* Add constraints */ + glp_add_rows(ip, (int) no_of_edges); + IGRAPH_CHECK(igraph_vector_push_back(&feedback_edges, -1)); + j = 0; + for (i = 0; i < no_of_edges; i++) { + int ind[3]; + double val[3] = {0, -1, 1}; + ind[1] = IGRAPH_FROM(graph, i) + 1; + ind[2] = IGRAPH_TO(graph, i) + 1; + + if (ind[1] == ind[2]) { + if (VECTOR(feedback_edges)[j] == i) { + j++; + } + continue; + } + + if (VECTOR(feedback_edges)[j] == i) { + /* This is a feedback edge, add it reversed */ + glp_set_row_bnds(ip, (int) i + 1, GLP_UP, -1, -1); + j++; + } else { + glp_set_row_bnds(ip, (int) i + 1, GLP_LO, 1, 1); + } + glp_set_mat_row(ip, (int) i + 1, 2, ind, val); + } + + /* Solve the problem */ + IGRAPH_GLPK_CHECK(glp_simplex(ip, &parm), + "Vertical arrangement step using IP failed"); + + /* The problem is totally unimodular, therefore the output of the simplex + * solver can be converted to an integer solution easily */ + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*membership)[i] = floor(glp_get_col_prim(ip, (int) i + 1)); + } + + glp_delete_prob(ip); + igraph_vector_destroy(&feedback_edges); + IGRAPH_FINALLY_CLEAN(2); + } else if (igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_feedback_arc_set_eades(graph, 0, weights, membership)); + } else { + IGRAPH_CHECK(igraph_i_feedback_arc_set_undirected(graph, 0, weights, membership)); + } +#else + if (igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_feedback_arc_set_eades(graph, 0, weights, membership)); + } else { + IGRAPH_CHECK(igraph_i_feedback_arc_set_undirected(graph, 0, weights, membership)); + } +#endif + + return IGRAPH_SUCCESS; +} + +static int igraph_i_layout_sugiyama_calculate_barycenters(const igraph_t* graph, + const igraph_i_layering_t* layering, long int layer_index, + igraph_neimode_t direction, const igraph_matrix_t* layout, + igraph_vector_t* barycenters) { + long int i, j, m, n; + igraph_vector_t* layer_members = igraph_i_layering_get(layering, layer_index); + igraph_vector_t neis; + + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + n = igraph_vector_size(layer_members); + IGRAPH_CHECK(igraph_vector_resize(barycenters, n)); + igraph_vector_null(barycenters); + + for (i = 0; i < n; i++) { + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) + VECTOR(*layer_members)[i], direction)); + m = igraph_vector_size(&neis); + if (m == 0) { + /* No neighbors in this direction. Just use the current X coordinate */ + VECTOR(*barycenters)[i] = MATRIX(*layout, i, 0); + } else { + for (j = 0; j < m; j++) { + VECTOR(*barycenters)[i] += MATRIX(*layout, (long)VECTOR(neis)[j], 0); + } + VECTOR(*barycenters)[i] /= m; + } + } + + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * Given a properly layered graph where each edge points downwards and spans + * exactly one layer, arranges the nodes in each layer horizontally in a way + * that strives to minimize edge crossings. + */ +static int igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* graph, + igraph_matrix_t* layout, const igraph_i_layering_t* layering, + long int maxiter) { + long int i, n, nei; + long int no_of_vertices = igraph_vcount(graph); + long int no_of_layers = igraph_i_layering_num_layers(layering); + long int iter, layer_index; + igraph_vector_t* layer_members; + igraph_vector_t neis, barycenters, sort_indices; + igraph_bool_t changed; + + /* The first column of the matrix will serve as the ordering */ + /* Start with a first-seen ordering within each layer */ + { + long int *xs = IGRAPH_CALLOC(no_of_layers, long int); + if (xs == 0) { + IGRAPH_ERROR("cannot order nodes horizontally", IGRAPH_ENOMEM); + } + for (i = 0; i < no_of_vertices; i++) { + MATRIX(*layout, i, 0) = xs[(long int)MATRIX(*layout, i, 1)]++; + } + free(xs); + } + + IGRAPH_VECTOR_INIT_FINALLY(&barycenters, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&sort_indices, 0); + + /* Start the effective part of the Sugiyama algorithm */ + iter = 0; changed = 1; + while (changed && iter < maxiter) { + changed = 0; + + /* Phase 1 */ + + /* Moving downwards and sorting by upper barycenters */ + for (layer_index = 1; layer_index < no_of_layers; layer_index++) { + layer_members = igraph_i_layering_get(layering, layer_index); + n = igraph_vector_size(layer_members); + + igraph_i_layout_sugiyama_calculate_barycenters(graph, + layering, layer_index, IGRAPH_IN, layout, &barycenters); + +#ifdef SUGIYAMA_DEBUG + printf("Layer %ld, aligning to upper barycenters\n", layer_index); + printf("Vertices: "); igraph_vector_print(layer_members); + printf("Barycenters: "); igraph_vector_print(&barycenters); +#endif + IGRAPH_CHECK((int) igraph_vector_qsort_ind(&barycenters, + &sort_indices, 0)); + for (i = 0; i < n; i++) { + nei = (long)VECTOR(*layer_members)[(long)VECTOR(sort_indices)[i]]; + VECTOR(barycenters)[i] = nei; + MATRIX(*layout, nei, 0) = i; + } + if (!igraph_vector_all_e(layer_members, &barycenters)) { + IGRAPH_CHECK(igraph_vector_update(layer_members, &barycenters)); +#ifdef SUGIYAMA_DEBUG + printf("New vertex order: "); igraph_vector_print(layer_members); +#endif + changed = 1; + } else { +#ifdef SUGIYAMA_DEBUG + printf("Order did not change.\n"); +#endif + } + } + + /* Moving upwards and sorting by lower barycenters */ + for (layer_index = no_of_layers - 2; layer_index >= 0; layer_index--) { + layer_members = igraph_i_layering_get(layering, layer_index); + n = igraph_vector_size(layer_members); + + igraph_i_layout_sugiyama_calculate_barycenters(graph, + layering, layer_index, IGRAPH_OUT, layout, &barycenters); + +#ifdef SUGIYAMA_DEBUG + printf("Layer %ld, aligning to lower barycenters\n", layer_index); + printf("Vertices: "); igraph_vector_print(layer_members); + printf("Barycenters: "); igraph_vector_print(&barycenters); +#endif + + IGRAPH_CHECK((int) igraph_vector_qsort_ind(&barycenters, + &sort_indices, 0)); + for (i = 0; i < n; i++) { + nei = (long)VECTOR(*layer_members)[(long)VECTOR(sort_indices)[i]]; + VECTOR(barycenters)[i] = nei; + MATRIX(*layout, nei, 0) = i; + } + if (!igraph_vector_all_e(layer_members, &barycenters)) { + IGRAPH_CHECK(igraph_vector_update(layer_members, &barycenters)); +#ifdef SUGIYAMA_DEBUG + printf("New vertex order: "); igraph_vector_print(layer_members); +#endif + changed = 1; + } else { +#ifdef SUGIYAMA_DEBUG + printf("Order did not change.\n"); +#endif + } + } + +#ifdef SUGIYAMA_DEBUG + printf("==== Finished iteration %ld\n", iter); +#endif + + iter++; + } + + igraph_vector_destroy(&barycenters); + igraph_vector_destroy(&neis); + igraph_vector_destroy(&sort_indices); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +#define IS_DUMMY(v) ((v >= no_of_real_nodes)) +#define IS_INNER_SEGMENT(u, v) (IS_DUMMY(u) && IS_DUMMY(v)) +#define X_POS(v) (MATRIX(*layout, v, 0)) + +static int igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph, + const igraph_i_layering_t* layering, const igraph_matrix_t* layout, + const igraph_vector_bool_t* ignored_edges, + igraph_bool_t reverse, igraph_bool_t align_right, + igraph_vector_t* roots, igraph_vector_t* align); +static int igraph_i_layout_sugiyama_horizontal_compaction(const igraph_t* graph, + const igraph_vector_t* vertex_to_the_left, + const igraph_vector_t* roots, const igraph_vector_t* align, + igraph_real_t hgap, igraph_vector_t* xs); +static int igraph_i_layout_sugiyama_horizontal_compaction_place_block(long int v, + const igraph_vector_t* vertex_to_the_left, + const igraph_vector_t* roots, const igraph_vector_t* align, + igraph_vector_t* sinks, igraph_vector_t* shifts, + igraph_real_t hgap, igraph_vector_t* xs); + +static int igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* graph, + igraph_matrix_t* layout, const igraph_i_layering_t* layering, + igraph_real_t hgap, igraph_integer_t no_of_real_nodes) { + + long int i, j, k, l, n; + long int no_of_layers = igraph_i_layering_num_layers(layering); + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_vector_t neis1, neis2; + igraph_vector_t xs[4]; + igraph_vector_t roots, align; + igraph_vector_t vertex_to_the_left; + igraph_vector_bool_t ignored_edges; + + /* + { + igraph_vector_t edgelist; + IGRAPH_VECTOR_INIT_FINALLY(&edgelist, 0); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, 0)); + igraph_vector_print(&edgelist); + igraph_vector_destroy(&edgelist); + IGRAPH_FINALLY_CLEAN(1); + + for (i = 0; i < no_of_layers; i++) { + igraph_vector_t* layer = igraph_i_layering_get(layering, i); + igraph_vector_print(layer); + } + } + */ + + IGRAPH_CHECK(igraph_vector_bool_init(&ignored_edges, no_of_edges)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &ignored_edges); + + IGRAPH_VECTOR_INIT_FINALLY(&vertex_to_the_left, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&neis1, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis2, 0); + + /* First, find all type 1 conflicts and mark one of the edges participating + * in the conflict as being ignored. If one of the edges in the conflict + * is a non-inner segment and the other is an inner segment, we ignore the + * non-inner segment as we want to keep inner segments vertical. + */ + for (i = 0; i < no_of_layers - 1; i++) { + igraph_vector_t* vertices = igraph_i_layering_get(layering, i); + n = igraph_vector_size(vertices); + + /* Find all the edges from this layer to the next */ + igraph_vector_clear(&neis1); + for (j = 0; j < n; j++) { + IGRAPH_CHECK(igraph_neighbors(graph, &neis2, (igraph_integer_t) + VECTOR(*vertices)[j], IGRAPH_OUT)); + IGRAPH_CHECK(igraph_vector_append(&neis1, &neis2)); + } + + /* Consider all pairs of edges and check whether they are in a type 1 + * conflict */ + n = igraph_vector_size(&neis1); + for (j = 0; j < n; j++) { + long int u = IGRAPH_FROM(graph, j); + long int v = IGRAPH_TO(graph, j); + igraph_bool_t j_inner = IS_INNER_SEGMENT(u, v); + igraph_bool_t crossing; + + for (k = j + 1; k < n; k++) { + long int w = IGRAPH_FROM(graph, k); + long int x = IGRAPH_TO(graph, k); + if (IS_INNER_SEGMENT(w, x) == j_inner) { + continue; + } + /* Do the u --> v and w --> x edges cross? */ + crossing = (u == w || v == x); + if (!crossing) { + if (X_POS(u) <= X_POS(w)) { + crossing = X_POS(v) >= X_POS(x); + } else { + crossing = X_POS(v) <= X_POS(x); + } + } + if (crossing) { + if (j_inner) { + VECTOR(ignored_edges)[k] = 1; + } else { + VECTOR(ignored_edges)[j] = 1; + } + } + } + } + } + + igraph_vector_destroy(&neis1); + igraph_vector_destroy(&neis2); + IGRAPH_FINALLY_CLEAN(2); + + /* + * Prepare vertex_to_the_left where the ith element stores + * the index of the vertex to the left of vertex i, or i itself if the + * vertex is the leftmost vertex in a layer. + */ + for (i = 0; i < no_of_layers; i++) { + igraph_vector_t* vertices = igraph_i_layering_get(layering, i); + n = igraph_vector_size(vertices); + if (n == 0) { + continue; + } + + k = l = (long int)VECTOR(*vertices)[0]; + VECTOR(vertex_to_the_left)[k] = k; + for (j = 1; j < n; j++) { + k = (long int)VECTOR(*vertices)[j]; + VECTOR(vertex_to_the_left)[k] = l; + l = k; + } + } + + /* Type 1 conflicts found, ignored edges chosen, vertex_to_the_left + * prepared. Run vertical alignment for all four combinations */ + for (i = 0; i < 4; i++) { + IGRAPH_VECTOR_INIT_FINALLY(&xs[i], no_of_nodes); + } + + IGRAPH_VECTOR_INIT_FINALLY(&roots, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&align, no_of_nodes); + + for (i = 0; i < 4; i++) { + IGRAPH_CHECK(igraph_i_layout_sugiyama_vertical_alignment(graph, + layering, layout, &ignored_edges, + /* reverse = */ (igraph_bool_t) i / 2, /* align_right = */ i % 2, + &roots, &align)); + IGRAPH_CHECK(igraph_i_layout_sugiyama_horizontal_compaction(graph, + &vertex_to_the_left, &roots, &align, hgap, &xs[i])); + } + + { + igraph_real_t width, min_width, mins[4], maxs[4], diff; + /* Find the alignment with the minimum width */ + min_width = IGRAPH_INFINITY; j = 0; + for (i = 0; i < 4; i++) { + mins[i] = igraph_vector_min(&xs[i]); + maxs[i] = igraph_vector_max(&xs[i]); + width = maxs[i] - mins[i]; + if (width < min_width) { + min_width = width; + j = i; + } + } + + /* Leftmost alignments: align them s.t. the min X coordinate is equal to + * the minimum X coordinate of the alignment with the smallest width. + * Rightmost alignments: align them s.t. the max X coordinate is equal to + * the max X coordinate of the alignment with the smallest width. + */ + for (i = 0; i < 4; i++) { + if (j == i) { + continue; + } + if (i % 2 == 0) { + /* Leftmost alignment */ + diff = mins[j] - mins[i]; + } else { + /* Rightmost alignment */ + diff = maxs[j] - maxs[i]; + } + igraph_vector_add_constant(&xs[i], diff); + } + } + + /* For every vertex, find the median of the X coordinates in the four + * alignments */ + for (i = 0; i < no_of_nodes; i++) { + X_POS(i) = igraph_i_median_4(VECTOR(xs[0])[i], VECTOR(xs[1])[i], + VECTOR(xs[2])[i], VECTOR(xs[3])[i]); + } + + igraph_vector_destroy(&roots); + igraph_vector_destroy(&align); + IGRAPH_FINALLY_CLEAN(2); + + for (i = 0; i < 4; i++) { + igraph_vector_destroy(&xs[i]); + } + IGRAPH_FINALLY_CLEAN(4); + + igraph_vector_destroy(&vertex_to_the_left); + IGRAPH_FINALLY_CLEAN(1); + + igraph_vector_bool_destroy(&ignored_edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph, + const igraph_i_layering_t* layering, const igraph_matrix_t* layout, + const igraph_vector_bool_t* ignored_edges, + igraph_bool_t reverse, igraph_bool_t align_right, + igraph_vector_t* roots, igraph_vector_t* align) { + long int i, j, k, n, di, dj, i_limit, j_limit, r; + long int no_of_layers = igraph_i_layering_num_layers(layering); + long int no_of_nodes = igraph_vcount(graph); + igraph_neimode_t neimode = (reverse ? IGRAPH_OUT : IGRAPH_IN); + igraph_vector_t neis, xs, inds; + + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&xs, 0); + IGRAPH_VECTOR_INIT_FINALLY(&inds, 0); + + IGRAPH_CHECK(igraph_vector_resize(roots, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(align, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*roots)[i] = VECTOR(*align)[i] = i; + } + + /* When reverse = False, we are aligning "upwards" in the tree, hence we + * have to loop i from 1 to no_of_layers-1 (inclusive) and use neimode=IGRAPH_IN. + * When reverse = True, we are aligning "downwards", hence we have to loop + * i from no_of_layers-2 to 0 (inclusive) and use neimode=IGRAPH_OUT. + */ + i = reverse ? (no_of_layers - 2) : 1; + di = reverse ? -1 : 1; + i_limit = reverse ? -1 : no_of_layers; + for (; i != i_limit; i += di) { + igraph_vector_t *layer = igraph_i_layering_get(layering, i); + + /* r = 0 in the paper, but C arrays are indexed from 0 */ + r = align_right ? LONG_MAX : -1; + + /* If align_right is 1, we have to process the layer in reverse order */ + j = align_right ? (igraph_vector_size(layer) - 1) : 0; + dj = align_right ? -1 : 1; + j_limit = align_right ? -1 : igraph_vector_size(layer); + for (; j != j_limit; j += dj) { + long int medians[2]; + long int vertex = (long int) VECTOR(*layer)[j]; + long int pos; + + if (VECTOR(*align)[vertex] != vertex) + /* This vertex is already aligned with some other vertex, + * so there's nothing to do */ + { + continue; + } + + /* Find the neighbors of vertex j in layer i */ + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) vertex, + neimode)); + + n = igraph_vector_size(&neis); + if (n == 0) + /* No neighbors in this direction, continue */ + { + continue; + } + if (n == 1) { + /* Just one neighbor; the median is trivial */ + medians[0] = (long int) VECTOR(neis)[0]; + medians[1] = -1; + } else { + /* Sort the neighbors by their X coordinates */ + IGRAPH_CHECK(igraph_vector_resize(&xs, n)); + for (k = 0; k < n; k++) { + VECTOR(xs)[k] = X_POS((long int)VECTOR(neis)[k]); + } + IGRAPH_CHECK((int) igraph_vector_qsort_ind(&xs, &inds, 0)); + + if (n % 2 == 1) { + /* Odd number of neighbors, so the median is unique */ + medians[0] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2]]; + medians[1] = -1; + } else { + /* Even number of neighbors, so we have two medians. The order + * depends on whether we are processing the layer in leftmost + * or rightmost fashion. */ + if (align_right) { + medians[0] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2]]; + medians[1] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2 - 1]]; + } else { + medians[0] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2 - 1]]; + medians[1] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2]]; + } + } + } + + /* Try aligning with the medians */ + for (k = 0; k < 2; k++) { + igraph_integer_t eid; + if (medians[k] < 0) { + continue; + } + if (VECTOR(*align)[vertex] != vertex) { + /* Vertex already aligned, continue */ + continue; + } + /* Is the edge between medians[k] and vertex ignored + * because of a type 1 conflict? */ + IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) vertex, + (igraph_integer_t) medians[k], 0, 1)); + if (VECTOR(*ignored_edges)[(long int)eid]) { + continue; + } + /* Okay, align with the median if possible */ + pos = (long int) X_POS(medians[k]); + if ((align_right && r > pos) || (!align_right && r < pos)) { + VECTOR(*align)[medians[k]] = vertex; + VECTOR(*roots)[vertex] = VECTOR(*roots)[medians[k]]; + VECTOR(*align)[vertex] = VECTOR(*roots)[medians[k]]; + r = pos; + } + } + } + } + + igraph_vector_destroy(&inds); + igraph_vector_destroy(&neis); + igraph_vector_destroy(&xs); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/* + * Runs a horizontal compaction given a vertical alignment (in `align`) + * and the roots (in `roots`). These come out directly from + * igraph_i_layout_sugiyama_vertical_alignment. + * + * Returns the X coordinates for each vertex in `xs`. + * + * `graph` is the input graph, `layering` is the layering on which we operate. + * `hgap` is the preferred horizontal gap between vertices. + */ +static int igraph_i_layout_sugiyama_horizontal_compaction(const igraph_t* graph, + const igraph_vector_t* vertex_to_the_left, + const igraph_vector_t* roots, const igraph_vector_t* align, + igraph_real_t hgap, igraph_vector_t* xs) { + long int i; + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t sinks, shifts, old_xs; + igraph_real_t shift; + + /* Initialization */ + + IGRAPH_VECTOR_INIT_FINALLY(&sinks, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&shifts, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&old_xs, no_of_nodes); + + IGRAPH_CHECK(igraph_vector_resize(xs, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + VECTOR(sinks)[i] = i; + } + igraph_vector_fill(&shifts, IGRAPH_INFINITY); + igraph_vector_fill(xs, -1); + + /* Calculate the coordinates of the vertices relative to their sinks + * in their own class. At the end of this for loop, xs will contain the + * relative displacement of a vertex from its sink, while the shifts list + * will contain the absolute displacement of the sinks. + * (For the sinks only, of course, the rest is undefined and unused) + */ + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*roots)[i] == i) { + IGRAPH_CHECK( + igraph_i_layout_sugiyama_horizontal_compaction_place_block(i, + vertex_to_the_left, roots, align, &sinks, &shifts, hgap, xs) + ); + } + } + + /* In "sinks", only those indices `i` matter for which `i` is in `roots`. + * All the other values will never be touched. + */ + + /* Calculate the absolute coordinates */ + IGRAPH_CHECK(igraph_vector_update(&old_xs, xs)); + for (i = 0; i < no_of_nodes; i++) { + long int root = (long int) VECTOR(*roots)[i]; + VECTOR(*xs)[i] = VECTOR(old_xs)[root]; + shift = VECTOR(shifts)[(long int)VECTOR(sinks)[root]]; + if (shift < IGRAPH_INFINITY) { + VECTOR(*xs)[i] += shift; + } + } + + igraph_vector_destroy(&sinks); + igraph_vector_destroy(&shifts); + igraph_vector_destroy(&old_xs); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_layout_sugiyama_horizontal_compaction_place_block(long int v, + const igraph_vector_t* vertex_to_the_left, + const igraph_vector_t* roots, const igraph_vector_t* align, + igraph_vector_t* sinks, igraph_vector_t* shifts, + igraph_real_t hgap, igraph_vector_t* xs) { + long int u, w; + long int u_sink, v_sink; + + if (VECTOR(*xs)[v] >= 0) { + return IGRAPH_SUCCESS; + } + + VECTOR(*xs)[v] = 0; + + w = v; + do { + /* Check whether vertex w is the leftmost in its own layer */ + u = (long int) VECTOR(*vertex_to_the_left)[w]; + if (u != w) { + /* Get the root of u (proceeding all the way upwards in the block) */ + u = (long int) VECTOR(*roots)[u]; + /* Place the block of u recursively */ + IGRAPH_CHECK( + igraph_i_layout_sugiyama_horizontal_compaction_place_block(u, + vertex_to_the_left, roots, align, sinks, shifts, hgap, xs) + ); + + u_sink = (long int) VECTOR(*sinks)[u]; + v_sink = (long int) VECTOR(*sinks)[v]; + /* If v is its own sink yet, set its sink to the sink of u */ + if (v_sink == v) { + VECTOR(*sinks)[v] = v_sink = u_sink; + } + /* If v and u have different sinks (i.e. they are in different classes), + * shift the sink of u so that the two blocks are separated by the + * preferred gap + */ + if (v_sink != u_sink) { + if (VECTOR(*shifts)[u_sink] > VECTOR(*xs)[v] - VECTOR(*xs)[u] - hgap) { + VECTOR(*shifts)[u_sink] = VECTOR(*xs)[v] - VECTOR(*xs)[u] - hgap; + } + } else { + /* v and u have the same sink, i.e. they are in the same class. Make sure + * that v is separated from u by at least hgap. + */ + if (VECTOR(*xs)[v] < VECTOR(*xs)[u] + hgap) { + VECTOR(*xs)[v] = VECTOR(*xs)[u] + hgap; + } + } + } + + /* Follow the alignment */ + w = (long int) VECTOR(*align)[w]; + } while (w != v); + + return IGRAPH_SUCCESS; +} + +#undef IS_INNER_SEGMENT +#undef IS_DUMMY +#undef X_POS + +#ifdef SUGIYAMA_DEBUG + #undef SUGIYAMA_DEBUG +#endif diff --git a/src/rigraph/core/linalg/arpack.c b/src/rigraph/core/linalg/arpack.c new file mode 100644 index 0000000..b9bbbb9 --- /dev/null +++ b/src/rigraph/core/linalg/arpack.c @@ -0,0 +1,1438 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 noet: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_arpack.h" +#include "igraph_memory.h" + +#include "arpack_internal.h" + +#include +#include +#include + +/* The ARPACK example file dssimp.f is used as a template */ + +static int igraph_i_arpack_err_dsaupd(int error) { + switch (error) { + case 1: return IGRAPH_ARPACK_MAXIT; + case 3: return IGRAPH_ARPACK_NOSHIFT; + case -1: return IGRAPH_ARPACK_NPOS; + case -2: return IGRAPH_ARPACK_NEVNPOS; + case -3: return IGRAPH_ARPACK_NCVSMALL; + case -4: return IGRAPH_ARPACK_NONPOSI; + case -5: return IGRAPH_ARPACK_WHICHINV; + case -6: return IGRAPH_ARPACK_BMATINV; + case -7: return IGRAPH_ARPACK_WORKLSMALL; + case -8: return IGRAPH_ARPACK_TRIDERR; + case -9: return IGRAPH_ARPACK_ZEROSTART; + case -10: return IGRAPH_ARPACK_MODEINV; + case -11: return IGRAPH_ARPACK_MODEBMAT; + case -12: return IGRAPH_ARPACK_ISHIFT; + case -13: return IGRAPH_ARPACK_NEVBE; + case -9999: return IGRAPH_ARPACK_NOFACT; + default: return IGRAPH_ARPACK_UNKNOWN; + } +} + +static int igraph_i_arpack_err_dseupd(int error) { + switch (error) { + case -1: return IGRAPH_ARPACK_NPOS; + case -2: return IGRAPH_ARPACK_NEVNPOS; + case -3: return IGRAPH_ARPACK_NCVSMALL; + case -5: return IGRAPH_ARPACK_WHICHINV; + case -6: return IGRAPH_ARPACK_BMATINV; + case -7: return IGRAPH_ARPACK_WORKLSMALL; + case -8: return IGRAPH_ARPACK_TRIDERR; + case -9: return IGRAPH_ARPACK_ZEROSTART; + case -10: return IGRAPH_ARPACK_MODEINV; + case -11: return IGRAPH_ARPACK_MODEBMAT; + case -12: return IGRAPH_ARPACK_NEVBE; + case -14: return IGRAPH_ARPACK_FAILED; + case -15: return IGRAPH_ARPACK_HOWMNY; + case -16: return IGRAPH_ARPACK_HOWMNYS; + case -17: return IGRAPH_ARPACK_EVDIFF; + default: return IGRAPH_ARPACK_UNKNOWN; + } + +} + +static int igraph_i_arpack_err_dnaupd(int error) { + switch (error) { + case 1: return IGRAPH_ARPACK_MAXIT; + case 3: return IGRAPH_ARPACK_NOSHIFT; + case -1: return IGRAPH_ARPACK_NPOS; + case -2: return IGRAPH_ARPACK_NEVNPOS; + case -3: return IGRAPH_ARPACK_NCVSMALL; + case -4: return IGRAPH_ARPACK_NONPOSI; + case -5: return IGRAPH_ARPACK_WHICHINV; + case -6: return IGRAPH_ARPACK_BMATINV; + case -7: return IGRAPH_ARPACK_WORKLSMALL; + case -8: return IGRAPH_ARPACK_TRIDERR; + case -9: return IGRAPH_ARPACK_ZEROSTART; + case -10: return IGRAPH_ARPACK_MODEINV; + case -11: return IGRAPH_ARPACK_MODEBMAT; + case -12: return IGRAPH_ARPACK_ISHIFT; + case -9999: return IGRAPH_ARPACK_NOFACT; + default: return IGRAPH_ARPACK_UNKNOWN; + } +} + +static int igraph_i_arpack_err_dneupd(int error) { + switch (error) { + case 1: return IGRAPH_ARPACK_REORDER; + case -1: return IGRAPH_ARPACK_NPOS; + case -2: return IGRAPH_ARPACK_NEVNPOS; + case -3: return IGRAPH_ARPACK_NCVSMALL; + case -5: return IGRAPH_ARPACK_WHICHINV; + case -6: return IGRAPH_ARPACK_BMATINV; + case -7: return IGRAPH_ARPACK_WORKLSMALL; + case -8: return IGRAPH_ARPACK_SHUR; + case -9: return IGRAPH_ARPACK_LAPACK; + case -10: return IGRAPH_ARPACK_MODEINV; + case -11: return IGRAPH_ARPACK_MODEBMAT; + case -12: return IGRAPH_ARPACK_HOWMNYS; + case -13: return IGRAPH_ARPACK_HOWMNY; + case -14: return IGRAPH_ARPACK_FAILED; + case -15: return IGRAPH_ARPACK_EVDIFF; + default: return IGRAPH_ARPACK_UNKNOWN; + } +} + +/** + * \function igraph_arpack_options_init + * Initialize ARPACK options + * + * Initializes ARPACK options, set them to default values. + * You can always pass the initialized \ref igraph_arpack_options_t + * object to built-in igraph functions without any modification. The + * built-in igraph functions modify the options to perform their + * calculation, e.g. \ref igraph_pagerank() always searches for the + * eigenvalue with the largest magnitude, regardless of the supplied + * value. + * + * If you want to implement your own function involving eigenvalue + * calculation using ARPACK, however, you will likely need to set up + * the fields for yourself. + * \param o The \ref igraph_arpack_options_t object to initialize. + * + * Time complexity: O(1). + */ + +void igraph_arpack_options_init(igraph_arpack_options_t *o) { + o->bmat[0] = 'I'; + o->n = 0; /* needs to be updated! */ + o->which[0] = 'X'; o->which[1] = 'X'; + o->nev = 1; + o->tol = 0; + o->ncv = 0; /* 0 means "automatic" */ + o->ldv = o->n; /* will be updated to (real) n */ + o->ishift = 1; + o->mxiter = 3000; + o->nb = 1; + o->mode = 1; + o->start = 0; + o->lworkl = 0; + o->sigma = 0; + o->sigmai = 0; + o->info = o->start; + + o->iparam[0] = o->ishift; o->iparam[1] = 0; o->iparam[2] = o->mxiter; o->iparam[3] = o->nb; + o->iparam[4] = 0; o->iparam[5] = 0; o->iparam[6] = o->mode; o->iparam[7] = 0; + o->iparam[8] = 0; o->iparam[9] = 0; o->iparam[10] = 0; +} + +/** + * \function igraph_arpack_storage_init + * Initialize ARPACK storage + * + * You only need this function if you want to run multiple eigenvalue + * calculations using ARPACK, and want to spare the memory + * allocation/deallocation between each two runs. Otherwise it is safe + * to supply a null pointer as the \c storage argument of both \ref + * igraph_arpack_rssolve() and \ref igraph_arpack_rnsolve() to make + * memory allocated and deallocated automatically. + * + * Don't forget to call the \ref + * igraph_arpack_storage_destroy() function on the storage object if + * you don't need it any more. + * \param s The \ref igraph_arpack_storage_t object to initialize. + * \param maxn The maximum order of the matrices. + * \param maxncv The maximum NCV parameter intended to use. + * \param maxldv The maximum LDV parameter intended to use. + * \param symm Whether symmetric or non-symmetric problems will be + * solved using this \ref igraph_arpack_storage_t. (You cannot use + * the same storage both with symmetric and non-symmetric solvers.) + * \return Error code. + * + * Time complexity: O(maxncv*(maxldv+maxn)). + */ + +int igraph_arpack_storage_init(igraph_arpack_storage_t *s, long int maxn, + long int maxncv, long int maxldv, + igraph_bool_t symm) { + + /* TODO: check arguments */ + s->maxn = (int) maxn; + s->maxncv = (int) maxncv; + s->maxldv = (int) maxldv; + +#define CHECKMEM(x) \ + if (!x) { \ + IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \ + } \ + IGRAPH_FINALLY(igraph_free, x); + + s->v = IGRAPH_CALLOC(maxldv * maxncv, igraph_real_t); CHECKMEM(s->v); + s->workd = IGRAPH_CALLOC(3 * maxn, igraph_real_t); CHECKMEM(s->workd); + s->d = IGRAPH_CALLOC(2 * maxncv, igraph_real_t); CHECKMEM(s->d); + s->resid = IGRAPH_CALLOC(maxn, igraph_real_t); CHECKMEM(s->resid); + s->ax = IGRAPH_CALLOC(maxn, igraph_real_t); CHECKMEM(s->ax); + s->select = IGRAPH_CALLOC(maxncv, int); CHECKMEM(s->select); + + if (symm) { + s->workl = IGRAPH_CALLOC(maxncv * (maxncv + 8), igraph_real_t); CHECKMEM(s->workl); + s->di = 0; + s->workev = 0; + } else { + s->workl = IGRAPH_CALLOC(3 * maxncv * (maxncv + 2), igraph_real_t); CHECKMEM(s->workl); + s->di = IGRAPH_CALLOC(2 * maxncv, igraph_real_t); CHECKMEM(s->di); + s->workev = IGRAPH_CALLOC(3 * maxncv, igraph_real_t); CHECKMEM(s->workev); + IGRAPH_FINALLY_CLEAN(2); + } + +#undef CHECKMEM + + IGRAPH_FINALLY_CLEAN(7); + return 0; +} + +/** + * \function igraph_arpack_storage_destroy + * Deallocate ARPACK storage + * + * \param s The \ref igraph_arpack_storage_t object for which the + * memory will be deallocated. + * + * Time complexity: operating system dependent. + */ + +void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s) { + + if (s->di) { + IGRAPH_FREE(s->di); + } + if (s->workev) { + IGRAPH_FREE(s->workev); + } + + IGRAPH_FREE(s->workl); + IGRAPH_FREE(s->select); + IGRAPH_FREE(s->ax); + IGRAPH_FREE(s->resid); + IGRAPH_FREE(s->d); + IGRAPH_FREE(s->workd); + IGRAPH_FREE(s->v); +} + +/** + * "Solver" for 1x1 eigenvalue problems since ARPACK sometimes blows up with + * these. + */ +static int igraph_i_arpack_rssolve_1x1(igraph_arpack_function_t *fun, void *extra, + igraph_arpack_options_t* options, + igraph_vector_t* values, igraph_matrix_t* vectors) { + igraph_real_t a, b; + int nev = options->nev; + + if (nev <= 0) { + IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS); + } + + /* Probe the value in the matrix */ + a = 1; + if (fun(&b, &a, 1, extra)) { + IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", + IGRAPH_ARPACK_PROD); + } + + options->nconv = nev; + + if (values != 0) { + IGRAPH_CHECK(igraph_vector_resize(values, 1)); + VECTOR(*values)[0] = b; + } + + if (vectors != 0) { + IGRAPH_CHECK(igraph_matrix_resize(vectors, 1, 1)); + MATRIX(*vectors, 0, 0) = 1; + } + + return IGRAPH_SUCCESS; +} + +/** + * "Solver" for 1x1 eigenvalue problems since ARPACK sometimes blows up with + * these. + */ +static int igraph_i_arpack_rnsolve_1x1(igraph_arpack_function_t *fun, void *extra, + igraph_arpack_options_t* options, + igraph_matrix_t* values, igraph_matrix_t* vectors) { + igraph_real_t a, b; + int nev = options->nev; + + if (nev <= 0) { + IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS); + } + + /* Probe the value in the matrix */ + a = 1; + if (fun(&b, &a, 1, extra)) { + IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", + IGRAPH_ARPACK_PROD); + } + + options->nconv = nev; + + if (values != 0) { + IGRAPH_CHECK(igraph_matrix_resize(values, 1, 2)); + MATRIX(*values, 0, 0) = b; MATRIX(*values, 0, 1) = 0; + } + + if (vectors != 0) { + IGRAPH_CHECK(igraph_matrix_resize(vectors, 1, 1)); + MATRIX(*vectors, 0, 0) = 1; + } + + return IGRAPH_SUCCESS; +} + +/** + * "Solver" for 2x2 nonsymmetric eigenvalue problems since ARPACK sometimes + * blows up with these. + */ +static int igraph_i_arpack_rnsolve_2x2(igraph_arpack_function_t *fun, void *extra, + igraph_arpack_options_t* options, igraph_matrix_t* values, + igraph_matrix_t* vectors) { + igraph_real_t vec[2], mat[4]; + igraph_real_t a, b, c, d; + igraph_real_t trace, det, tsq4_minus_d; + igraph_complex_t eval1, eval2; + igraph_complex_t evec1[2], evec2[2]; + igraph_bool_t swap_evals = 0; + igraph_bool_t complex_evals = 0; + int nev = options->nev; + + if (nev <= 0) { + IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS); + } + if (nev > 2) { + nev = 2; + } + + /* Probe the values in the matrix */ + vec[0] = 1; vec[1] = 0; + if (fun(mat, vec, 2, extra)) { + IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", + IGRAPH_ARPACK_PROD); + } + vec[0] = 0; vec[1] = 1; + if (fun(mat + 2, vec, 2, extra)) { + IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", + IGRAPH_ARPACK_PROD); + } + a = mat[0]; b = mat[2]; c = mat[1]; d = mat[3]; + + /* Get the trace and the determinant */ + trace = a + d; + det = a * d - b * c; + tsq4_minus_d = trace * trace / 4 - det; + + /* Calculate the eigenvalues */ + complex_evals = tsq4_minus_d < 0; + eval1 = igraph_complex_sqrt_real(tsq4_minus_d); + if (complex_evals) { + eval2 = igraph_complex_mul_real(eval1, -1); + } else { + /* to avoid having -0 in the imaginary part */ + eval2 = igraph_complex(-IGRAPH_REAL(eval1), 0); + } + eval1 = igraph_complex_add_real(eval1, trace / 2); + eval2 = igraph_complex_add_real(eval2, trace / 2); + + if (c != 0) { + evec1[0] = igraph_complex_sub_real(eval1, d); + evec1[1] = igraph_complex(c, 0); + evec2[0] = igraph_complex_sub_real(eval2, d); + evec2[1] = igraph_complex(c, 0); + } else if (b != 0) { + evec1[0] = igraph_complex(b, 0); + evec1[1] = igraph_complex_sub_real(eval1, a); + evec2[0] = igraph_complex(b, 0); + evec2[1] = igraph_complex_sub_real(eval2, a); + } else { + evec1[0] = igraph_complex(1, 0); + evec1[1] = igraph_complex(0, 0); + evec2[0] = igraph_complex(0, 0); + evec2[1] = igraph_complex(1, 0); + } + + /* Sometimes we have to swap eval1 with eval2 and evec1 with eval2; + * determine whether we have to do it now */ + if (options->which[0] == 'S') { + if (options->which[1] == 'M') { + /* eval1 must be the one with the smallest magnitude */ + swap_evals = (igraph_complex_mod(eval1) > igraph_complex_mod(eval2)); + } else if (options->which[1] == 'R') { + /* eval1 must be the one with the smallest real part */ + swap_evals = (IGRAPH_REAL(eval1) > IGRAPH_REAL(eval2)); + } else if (options->which[1] == 'I') { + /* eval1 must be the one with the smallest imaginary part */ + swap_evals = (IGRAPH_IMAG(eval1) > IGRAPH_IMAG(eval2)); + } else { + IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV); + } + } else if (options->which[0] == 'L') { + if (options->which[1] == 'M') { + /* eval1 must be the one with the largest magnitude */ + swap_evals = (igraph_complex_mod(eval1) < igraph_complex_mod(eval2)); + } else if (options->which[1] == 'R') { + /* eval1 must be the one with the largest real part */ + swap_evals = (IGRAPH_REAL(eval1) < IGRAPH_REAL(eval2)); + } else if (options->which[1] == 'I') { + /* eval1 must be the one with the largest imaginary part */ + swap_evals = (IGRAPH_IMAG(eval1) < IGRAPH_IMAG(eval2)); + } else { + IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV); + } + } else if (options->which[0] == 'X' && options->which[1] == 'X') { + /* No preference on the ordering of eigenvectors */ + } else { + /* fprintf(stderr, "%c%c\n", options->which[0], options->which[1]); */ + IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV); + } + + options->nconv = nev; + + if (swap_evals) { + igraph_complex_t dummy; + dummy = eval1; eval1 = eval2; eval2 = dummy; + dummy = evec1[0]; evec1[0] = evec2[0]; evec2[0] = dummy; + dummy = evec1[1]; evec1[1] = evec2[1]; evec2[1] = dummy; + } + + if (complex_evals) { + /* The eigenvalues are conjugate pairs, so we store only the + * one with positive imaginary part */ + if (IGRAPH_IMAG(eval1) < 0) { + eval1 = eval2; + evec1[0] = evec2[0]; evec1[1] = evec2[1]; + } + } + + if (values != 0) { + IGRAPH_CHECK(igraph_matrix_resize(values, nev, 2)); + MATRIX(*values, 0, 0) = IGRAPH_REAL(eval1); + MATRIX(*values, 0, 1) = IGRAPH_IMAG(eval1); + if (nev > 1) { + MATRIX(*values, 1, 0) = IGRAPH_REAL(eval2); + MATRIX(*values, 1, 1) = IGRAPH_IMAG(eval2); + } + } + + if (vectors != 0) { + if (complex_evals) { + IGRAPH_CHECK(igraph_matrix_resize(vectors, 2, 2)); + MATRIX(*vectors, 0, 0) = IGRAPH_REAL(evec1[0]); + MATRIX(*vectors, 1, 0) = IGRAPH_REAL(evec1[1]); + MATRIX(*vectors, 0, 1) = IGRAPH_IMAG(evec1[0]); + MATRIX(*vectors, 1, 1) = IGRAPH_IMAG(evec1[1]); + } else { + IGRAPH_CHECK(igraph_matrix_resize(vectors, 2, nev)); + MATRIX(*vectors, 0, 0) = IGRAPH_REAL(evec1[0]); + MATRIX(*vectors, 1, 0) = IGRAPH_REAL(evec1[1]); + if (nev > 1) { + MATRIX(*vectors, 0, 1) = IGRAPH_REAL(evec2[0]); + MATRIX(*vectors, 1, 1) = IGRAPH_REAL(evec2[1]); + } + } + } + + return IGRAPH_SUCCESS; +} + +/** + * "Solver" for symmetric 2x2 eigenvalue problems since ARPACK sometimes blows + * up with these. + */ +static int igraph_i_arpack_rssolve_2x2(igraph_arpack_function_t *fun, void *extra, + igraph_arpack_options_t* options, igraph_vector_t* values, + igraph_matrix_t* vectors) { + igraph_real_t vec[2], mat[4]; + igraph_real_t a, b, c, d; + igraph_real_t trace, det, tsq4_minus_d; + igraph_real_t eval1, eval2; + int nev = options->nev; + + if (nev <= 0) { + IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS); + } + if (nev > 2) { + nev = 2; + } + + /* Probe the values in the matrix */ + vec[0] = 1; vec[1] = 0; + if (fun(mat, vec, 2, extra)) { + IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", + IGRAPH_ARPACK_PROD); + } + vec[0] = 0; vec[1] = 1; + if (fun(mat + 2, vec, 2, extra)) { + IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", + IGRAPH_ARPACK_PROD); + } + a = mat[0]; b = mat[2]; c = mat[1]; d = mat[3]; + + /* Get the trace and the determinant */ + trace = a + d; + det = a * d - b * c; + tsq4_minus_d = trace * trace / 4 - det; + + if (tsq4_minus_d >= 0) { + /* Both eigenvalues are real */ + eval1 = trace / 2 + sqrt(tsq4_minus_d); + eval2 = trace / 2 - sqrt(tsq4_minus_d); + if (c != 0) { + mat[0] = eval1 - d; mat[2] = eval2 - d; + mat[1] = c; mat[3] = c; + } else if (b != 0) { + mat[0] = b; mat[2] = b; + mat[1] = eval1 - a; mat[3] = eval2 - a; + } else { + mat[0] = 1; mat[2] = 0; + mat[1] = 0; mat[3] = 1; + } + } else { + /* Both eigenvalues are complex. Should not happen with symmetric + * matrices. */ + IGRAPH_ERROR("ARPACK error, 2x2 matrix is not symmetric", IGRAPH_EINVAL); + } + + /* eval1 is always the larger eigenvalue. If we want the smaller + * one, we have to swap eval1 with eval2 and also the columns of mat */ + if (options->which[0] == 'S') { + trace = eval1; eval1 = eval2; eval2 = trace; + trace = mat[0]; mat[0] = mat[2]; mat[2] = trace; + trace = mat[1]; mat[1] = mat[3]; mat[3] = trace; + } else if (options->which[0] == 'L' || options->which[0] == 'B') { + /* Nothing to do here */ + } else if (options->which[0] == 'X' && options->which[1] == 'X') { + /* No preference on the ordering of eigenvectors */ + } else { + IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV); + } + + options->nconv = nev; + + if (values != 0) { + IGRAPH_CHECK(igraph_vector_resize(values, nev)); + VECTOR(*values)[0] = eval1; + if (nev > 1) { + VECTOR(*values)[1] = eval2; + } + } + + if (vectors != 0) { + IGRAPH_CHECK(igraph_matrix_resize(vectors, 2, nev)); + MATRIX(*vectors, 0, 0) = mat[0]; + MATRIX(*vectors, 1, 0) = mat[1]; + if (nev > 1) { + MATRIX(*vectors, 0, 1) = mat[2]; + MATRIX(*vectors, 1, 1) = mat[3]; + } + } + + return IGRAPH_SUCCESS; +} + +int igraph_arpack_rssort(igraph_vector_t *values, igraph_matrix_t *vectors, + const igraph_arpack_options_t *options, + igraph_real_t *d, const igraph_real_t *v) { + + igraph_vector_t order; + char sort[2]; + int apply = 1; + unsigned int n = (unsigned int) options->n; + int nconv = options->nconv; + int nev = options->nev; + unsigned int nans = (unsigned int) (nconv < nev ? nconv : nev); + unsigned int i; + +#define which(a,b) (options->which[0]==a && options->which[1]==b) + + if (which('L', 'A')) { + sort[0] = 'S'; sort[1] = 'A'; + } else if (which('S', 'A')) { + sort[0] = 'L'; sort[1] = 'A'; + } else if (which('L', 'M')) { + sort[0] = 'S'; sort[1] = 'M'; + } else if (which('S', 'M')) { + sort[0] = 'L'; sort[1] = 'M'; + } else if (which('B', 'E')) { + sort[0] = 'L'; sort[1] = 'A'; + } + + IGRAPH_CHECK(igraph_vector_init_seq(&order, 0, nconv - 1)); + IGRAPH_FINALLY(igraph_vector_destroy, &order); +#ifdef HAVE_GFORTRAN + igraphdsortr_(sort, &apply, &nconv, d, VECTOR(order), /*which_len=*/ 2); +#else + igraphdsortr_(sort, &apply, &nconv, d, VECTOR(order)); +#endif + + /* BE is special */ + if (which('B', 'E')) { + int w = 0, l1 = 0, l2 = nev - 1; + igraph_vector_t order2, d2; + IGRAPH_VECTOR_INIT_FINALLY(&order2, nev); + IGRAPH_VECTOR_INIT_FINALLY(&d2, nev); + while (l1 <= l2) { + VECTOR(order2)[w] = VECTOR(order)[l1]; + VECTOR(d2)[w] = d[l1]; + w++; l1++; + if (l1 <= l2) { + VECTOR(order2)[w] = VECTOR(order)[l2]; + VECTOR(d2)[w] = d[l2]; + w++; l2--; + } + } + igraph_vector_update(&order, &order2); + igraph_vector_copy_to(&d2, d); + igraph_vector_destroy(&order2); + igraph_vector_destroy(&d2); + IGRAPH_FINALLY_CLEAN(2); + } + +#undef which + + /* Copy values */ + if (values) { + IGRAPH_CHECK(igraph_vector_resize(values, nans)); + memcpy(VECTOR(*values), d, sizeof(igraph_real_t) * nans); + } + + /* Reorder vectors */ + if (vectors) { + IGRAPH_CHECK(igraph_matrix_resize(vectors, n, nans)); + for (i = 0; i < nans; i++) { + unsigned int idx = (unsigned int) VECTOR(order)[i]; + const igraph_real_t *ptr = v + n * idx; + memcpy(&MATRIX(*vectors, 0, i), ptr, sizeof(igraph_real_t) * n); + } + } + + igraph_vector_destroy(&order); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +int igraph_arpack_rnsort(igraph_matrix_t *values, igraph_matrix_t *vectors, + const igraph_arpack_options_t *options, + igraph_real_t *dr, igraph_real_t *di, + igraph_real_t *v) { + + igraph_vector_t order; + char sort[2]; + int apply = 1; + unsigned int n = (unsigned int) options->n; + int nconv = options->nconv; + int nev = options->nev; + unsigned int nans = (unsigned int) (nconv < nev ? nconv : nev); + unsigned int i; + +#define which(a,b) (options->which[0]==a && options->which[1]==b) + + if (which('L', 'M')) { + sort[0] = 'S'; sort[1] = 'M'; + } else if (which('S', 'M')) { + sort[0] = 'L'; sort[1] = 'M'; + } else if (which('L', 'R')) { + sort[0] = 'S'; sort[1] = 'R'; + } else if (which('S', 'R')) { + sort[0] = 'L'; sort[1] = 'R'; + } else if (which('L', 'I')) { + sort[0] = 'S'; sort[1] = 'I'; + } else if (which('S', 'I')) { + sort[0] = 'L'; sort[1] = 'I'; + } + +#undef which + + IGRAPH_CHECK(igraph_vector_init_seq(&order, 0, nconv - 1)); + IGRAPH_FINALLY(igraph_vector_destroy, &order); +#ifdef HAVE_GFORTRAN + igraphdsortc_(sort, &apply, &nconv, dr, di, VECTOR(order), /*which_len=*/ 2); +#else + igraphdsortc_(sort, &apply, &nconv, dr, di, VECTOR(order)); +#endif + + if (values) { + IGRAPH_CHECK(igraph_matrix_resize(values, nans, 2)); + memcpy(&MATRIX(*values, 0, 0), dr, sizeof(igraph_real_t) * nans); + memcpy(&MATRIX(*values, 0, 1), di, sizeof(igraph_real_t) * nans); + } + + if (vectors) { + int nc = 0, nr = 0, ncol, vx = 0; + for (i = 0; i < nans; i++) { + if (di[i] == 0) { + nr++; + } else { + nc++; + } + } + ncol = (nc / 2) * 2 + (nc % 2) * 2 + nr; + IGRAPH_CHECK(igraph_matrix_resize(vectors, n, ncol)); + + for (i = 0; i < nans; i++) { + unsigned int idx; + + idx = (unsigned int) VECTOR(order)[i]; + + if (di[i] == 0) { + /* real eigenvalue, single eigenvector */ + memcpy(&MATRIX(*vectors, 0, vx), v + n * idx, sizeof(igraph_real_t) * n); + vx++; + } else if (di[i] > 0) { + /* complex eigenvalue, positive imaginary part encountered first. + * ARPACK stores its eigenvector directly in two consecutive columns. + * The complex conjugate pair of the eigenvalue (if any) will be in + * the next column and we will skip it because we advance 'i' below */ + memcpy(&MATRIX(*vectors, 0, vx), v + n * idx, sizeof(igraph_real_t) * 2 * n); + vx += 2; + i++; + } else { + /* complex eigenvalue, negative imaginary part encountered first. + * The positive one will be the next one, but we need to copy the + * eigenvector corresponding to the eigenvalue with the positive + * imaginary part. */ + idx = (unsigned int) VECTOR(order)[i + 1]; + memcpy(&MATRIX(*vectors, 0, vx), v + n * idx, sizeof(igraph_real_t) * 2 * n); + vx += 2; + i++; + } + } + } + + igraph_vector_destroy(&order); + IGRAPH_FINALLY_CLEAN(1); + + if (values) { + /* Strive to include complex conjugate eigenvalue pairs in a way that the + * positive imaginary part comes first */ + for (i = 0; i < nans; i++) { + if (MATRIX(*values, i, 1) == 0) { + /* Real eigenvalue, nothing to do */ + } else if (MATRIX(*values, i, 1) < 0) { + /* Negative imaginary part came first; negate the imaginary part for + * this eigenvalue and the next one (which is the complex conjugate + * pair), and skip it */ + MATRIX(*values, i, 1) *= -1; + i++; + if (i < nans) { + MATRIX(*values, i, 1) *= -1; + } + } else { + /* Positive imaginary part; skip the next eigenvalue, which is the + * complex conjugate pair */ + i++; + } + } + } + + return 0; +} + +/** + * \function igraph_i_arpack_auto_ncv + * \brief Tries to set up the value of \c ncv in an \c igraph_arpack_options_t + * automagically. + */ +static void igraph_i_arpack_auto_ncv(igraph_arpack_options_t* options) { + /* This is similar to how Octave determines the value of ncv, with some + * modifications. */ + int min_ncv = options->nev * 2 + 1; + + /* Use twice the number of desired eigenvectors plus one by default */ + options->ncv = min_ncv; + /* ...but use at least 20 Lanczos vectors... */ + if (options->ncv < 20) { + options->ncv = 20; + } + /* ...but having ncv close to n leads to some problems with small graphs + * (example: PageRank of "A <--> C, D <--> E, B"), so we don't let it + * to be larger than n / 2... + */ + if (options->ncv > options->n / 2) { + options->ncv = options->n / 2; + } + /* ...but we need at least min_ncv. */ + if (options->ncv < min_ncv) { + options->ncv = min_ncv; + } + /* ...but at most n */ + if (options->ncv > options->n) { + options->ncv = options->n; + } +} + +/** + * \function igraph_i_arpack_report_no_convergence + * \brief Prints a warning that informs the user that the ARPACK solver + * did not converge. + */ +static void igraph_i_arpack_report_no_convergence(const igraph_arpack_options_t* options) { + char buf[1024]; + snprintf(buf, sizeof(buf), "ARPACK solver failed to converge (%d iterations, " + "%d/%d eigenvectors converged)", options->iparam[2], + options->iparam[4], options->nev); + IGRAPH_WARNING(buf); +} + +/** + * \function igraph_arpack_rssolve + * \brief ARPACK solver for symmetric matrices + * + * This is the ARPACK solver for symmetric matrices. Please use + * \ref igraph_arpack_rnsolve() for non-symmetric matrices. + * \param fun Pointer to an \ref igraph_arpack_function_t object, + * the function that performs the matrix-vector multiplication. + * \param extra An extra argument to be passed to \c fun. + * \param options An \ref igraph_arpack_options_t object. + * \param storage An \ref igraph_arpack_storage_t object, or a null + * pointer. In the latter case memory allocation and deallocation + * is performed automatically. Either this or the \p vectors argument + * must be non-null if the ARPACK iteration is started from a + * given starting vector. If both are given \p vectors take + * precedence. + * \param values If not a null pointer, then it should be a pointer to an + * initialized vector. The eigenvalues will be stored here. The + * vector will be resized as needed. + * \param vectors If not a null pointer, then it must be a pointer to + * an initialized matrix. The eigenvectors will be stored in the + * columns of the matrix. The matrix will be resized as needed. + * Either this or the \p vectors argument must be non-null if the + * ARPACK iteration is started from a given starting vector. If + * both are given \p vectors take precedence. + * \return Error code. + * + * Time complexity: depends on the matrix-vector + * multiplication. Usually a small number of iterations is enough, so + * if the matrix is sparse and the matrix-vector multiplication can be + * done in O(n) time (the number of vertices), then the eigenvalues + * are found in O(n) time as well. + */ + +int igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, + igraph_arpack_options_t *options, + igraph_arpack_storage_t *storage, + igraph_vector_t *values, igraph_matrix_t *vectors) { + + igraph_real_t *v, *workl, *workd, *d, *resid, *ax; + igraph_bool_t free_them = 0; + int *select, i; + + int ido = 0; + int rvec = vectors || storage ? 1 : 0; /* calculate eigenvectors? */ + char *all = "All"; + + int origldv = options->ldv, origlworkl = options->lworkl, + orignev = options->nev, origncv = options->ncv; + igraph_real_t origtol = options->tol; + char origwhich[2]; + + origwhich[0] = options->which[0]; + origwhich[1] = options->which[1]; + + /* Special case for 1x1 and 2x2 matrices in mode 1 */ + if (options->mode == 1 && options->n == 1) { + return igraph_i_arpack_rssolve_1x1(fun, extra, options, values, vectors); + } else if (options->mode == 1 && options->n == 2) { + return igraph_i_arpack_rssolve_2x2(fun, extra, options, values, vectors); + } + + /* Brush up options if needed */ + if (options->ldv == 0) { + options->ldv = options->n; + } + if (options->ncv == 0) { + igraph_i_arpack_auto_ncv(options); + } + if (options->lworkl == 0) { + options->lworkl = options->ncv * (options->ncv + 8); + } + if (options->which[0] == 'X') { + options->which[0] = 'L'; + options->which[1] = 'M'; + } + + if (storage) { + /* Storage provided */ + if (storage->maxn < options->n) { + IGRAPH_ERROR("Not enough storage for ARPACK (`n')", IGRAPH_EINVAL); + } + if (storage->maxncv < options->ncv) { + IGRAPH_ERROR("Not enough storage for ARPACK (`ncv')", IGRAPH_EINVAL); + } + if (storage->maxldv < options->ldv) { + IGRAPH_ERROR("Not enough storage for ARPACK (`ldv')", IGRAPH_EINVAL); + } + + v = storage->v; + workl = storage->workl; + workd = storage->workd; + d = storage->d; + resid = storage->resid; + ax = storage->ax; + select = storage->select; + + } else { + /* Storage not provided */ + free_them = 1; + +#define CHECKMEM(x) \ + if (!x) { \ + IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \ + } \ + IGRAPH_FINALLY(igraph_free, x); + + v = IGRAPH_CALLOC(options->ldv * options->ncv, igraph_real_t); CHECKMEM(v); + workl = IGRAPH_CALLOC(options->lworkl, igraph_real_t); CHECKMEM(workl); + workd = IGRAPH_CALLOC(3 * options->n, igraph_real_t); CHECKMEM(workd); + d = IGRAPH_CALLOC(2 * options->ncv, igraph_real_t); CHECKMEM(d); + resid = IGRAPH_CALLOC(options->n, igraph_real_t); CHECKMEM(resid); + ax = IGRAPH_CALLOC(options->n, igraph_real_t); CHECKMEM(ax); + select = IGRAPH_CALLOC(options->ncv, int); CHECKMEM(select); + +#undef CHECKMEM + + } + + /* Set final bits */ + options->bmat[0] = 'I'; + options->iparam[0] = options->ishift; + options->iparam[1] = 0; // not referenced + options->iparam[2] = options->mxiter; + options->iparam[3] = 1; // currently dsaupd() works only for nb=1 + options->iparam[4] = 0; + options->iparam[5] = 0; // not referenced + options->iparam[6] = options->mode; + options->iparam[7] = 0; // return value + options->iparam[8] = 0; // return value + options->iparam[9] = 0; // return value + options->iparam[10] = 0; // return value + options->info = options->start; + if (options->start) { + if (!storage && !vectors) { + IGRAPH_ERROR("Starting vector not given", IGRAPH_EINVAL); + } + if (vectors && (igraph_matrix_nrow(vectors) != options->n || + igraph_matrix_ncol(vectors) != 1)) { + IGRAPH_ERROR("Invalid starting vector size", IGRAPH_EINVAL); + } + if (vectors) { + for (i = 0; i < options->n; i++) { + resid[i] = MATRIX(*vectors, i, 0); + } + } + } + + /* Ok, we have everything */ + while (1) { +#ifdef HAVE_GFORTRAN + igraphdsaupd_(&ido, options->bmat, &options->n, options->which, + &options->nev, &options->tol, + resid, &options->ncv, v, &options->ldv, + options->iparam, options->ipntr, + workd, workl, &options->lworkl, &options->info, + /*bmat_len=*/ 1, /*which_len=*/ 2); +#else + igraphdsaupd_(&ido, options->bmat, &options->n, options->which, + &options->nev, &options->tol, + resid, &options->ncv, v, &options->ldv, + options->iparam, options->ipntr, + workd, workl, &options->lworkl, &options->info); +#endif + + if (ido == -1 || ido == 1) { + igraph_real_t *from = workd + options->ipntr[0] - 1; + igraph_real_t *to = workd + options->ipntr[1] - 1; + if (fun(to, from, options->n, extra) != 0) { + IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", + IGRAPH_ARPACK_PROD); + } + + } else { + break; + } + } + + if (options->info == 1) { + igraph_i_arpack_report_no_convergence(options); + } + if (options->info != 0) { + IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dsaupd(options->info)); + } + + options->ierr = 0; +#ifdef HAVE_GFORTRAN + igraphdseupd_(&rvec, all, select, d, v, &options->ldv, + &options->sigma, options->bmat, &options->n, + options->which, &options->nev, &options->tol, + resid, &options->ncv, v, &options->ldv, options->iparam, + options->ipntr, workd, workl, &options->lworkl, + &options->ierr, /*howmny_len=*/ 1, /*bmat_len=*/ 1, + /*which_len=*/ 2); +#else + igraphdseupd_(&rvec, all, select, d, v, &options->ldv, + &options->sigma, options->bmat, &options->n, + options->which, &options->nev, &options->tol, + resid, &options->ncv, v, &options->ldv, options->iparam, + options->ipntr, workd, workl, &options->lworkl, + &options->ierr); +#endif + + if (options->ierr != 0) { + IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dseupd(options->ierr)); + } + + /* Save the result */ + + options->noiter = options->iparam[2]; + options->nconv = options->iparam[4]; + options->numop = options->iparam[8]; + options->numopb = options->iparam[9]; + options->numreo = options->iparam[10]; + + if (options->nconv < options->nev) { + IGRAPH_WARNING("Not enough eigenvalues/vectors in symmetric ARPACK " + "solver"); + } + + if (values || vectors) { + IGRAPH_CHECK(igraph_arpack_rssort(values, vectors, options, d, v)); + } + + options->ldv = origldv; + options->ncv = origncv; + options->lworkl = origlworkl; + options->which[0] = origwhich[0]; options->which[1] = origwhich[1]; + options->tol = origtol; + options->nev = orignev; + + /* Clean up if needed */ + if (free_them) { + IGRAPH_FREE(select); + IGRAPH_FREE(ax); + IGRAPH_FREE(resid); + IGRAPH_FREE(d); + IGRAPH_FREE(workd); + IGRAPH_FREE(workl); + IGRAPH_FREE(v); + IGRAPH_FINALLY_CLEAN(7); + } + return 0; +} + +/** + * \function igraph_arpack_rnsolve + * \brief ARPACK solver for non-symmetric matrices + * + * Please always consider calling \ref igraph_arpack_rssolve() if your + * matrix is symmetric, it is much faster. + * \ref igraph_arpack_rnsolve() for non-symmetric matrices. + * + * Note that ARPACK is not called for 2x2 matrices as an exact algebraic + * solution exists in these cases. + * + * \param fun Pointer to an \ref igraph_arpack_function_t object, + * the function that performs the matrix-vector multiplication. + * \param extra An extra argument to be passed to \c fun. + * \param options An \ref igraph_arpack_options_t object. + * \param storage An \ref igraph_arpack_storage_t object, or a null + * pointer. In the latter case memory allocation and deallocation + * is performed automatically. + * \param values If not a null pointer, then it should be a pointer to an + * initialized matrix. The (possibly complex) eigenvalues will be + * stored here. The matrix will have two columns, the first column + * contains the real, the second the imaginary parts of the + * eigenvalues. + * The matrix will be resized as needed. + * \param vectors If not a null pointer, then it must be a pointer to + * an initialized matrix. The eigenvectors will be stored in the + * columns of the matrix. The matrix will be resized as needed. + * Note that real eigenvalues will have real eigenvectors in a single + * column in this matrix; however, complex eigenvalues come in conjugate + * pairs and the result matrix will store the eigenvector corresponding to + * the eigenvalue with \em positive imaginary part only. Since in this case + * the eigenvector is also complex, it will occupy \em two columns in the + * eigenvector matrix (the real and the imaginary parts, in this order). + * Caveat: if the eigenvalue vector returns only the eigenvalue with the + * \em negative imaginary part for a complex conjugate eigenvalue pair, the + * result vector will \em still store the eigenvector corresponding to the + * eigenvalue with the positive imaginary part (since this is how ARPACK + * works). + * \return Error code. + * + * Time complexity: depends on the matrix-vector + * multiplication. Usually a small number of iterations is enough, so + * if the matrix is sparse and the matrix-vector multiplication can be + * done in O(n) time (the number of vertices), then the eigenvalues + * are found in O(n) time as well. + */ + +int igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, + igraph_arpack_options_t *options, + igraph_arpack_storage_t *storage, + igraph_matrix_t *values, igraph_matrix_t *vectors) { + + igraph_real_t *v, *workl, *workd, *dr, *di, *resid, *workev; + igraph_bool_t free_them = 0; + int *select, i; + + int ido = 0; + int rvec = vectors || storage ? 1 : 0; + char *all = "All"; + + int origldv = options->ldv, origlworkl = options->lworkl, + orignev = options->nev, origncv = options->ncv; + igraph_real_t origtol = options->tol; + int d_size; + char origwhich[2]; + + origwhich[0] = options->which[0]; + origwhich[1] = options->which[1]; + + /* Special case for 1x1 and 2x2 matrices in mode 1 */ + if (options->mode == 1 && options->n == 1) { + return igraph_i_arpack_rnsolve_1x1(fun, extra, options, values, vectors); + } else if (options->mode == 1 && options->n == 2) { + return igraph_i_arpack_rnsolve_2x2(fun, extra, options, values, vectors); + } + + /* Brush up options if needed */ + if (options->ldv == 0) { + options->ldv = options->n; + } + if (options->ncv == 0) { + igraph_i_arpack_auto_ncv(options); + } + if (options->lworkl == 0) { + options->lworkl = 3 * options->ncv * (options->ncv + 2); + } + if (options->which[0] == 'X') { + options->which[0] = 'L'; + options->which[1] = 'M'; + } + + if (storage) { + /* Storage provided */ + if (storage->maxn < options->n) { + IGRAPH_ERROR("Not enough storage for ARPACK (`n')", IGRAPH_EINVAL); + } + if (storage->maxncv < options->ncv) { + IGRAPH_ERROR("Not enough storage for ARPACK (`ncv')", IGRAPH_EINVAL); + } + if (storage->maxldv < options->ldv) { + IGRAPH_ERROR("Not enough storage for ARPACK (`ldv')", IGRAPH_EINVAL); + } + + v = storage->v; + workl = storage->workl; + workd = storage->workd; + workev = storage->workev; + dr = storage->d; + di = storage->di; + d_size = options->n; + resid = storage->resid; + select = storage->select; + + } else { + /* Storage not provided */ + free_them = 1; + +#define CHECKMEM(x) \ + if (!x) { \ + IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \ + } \ + IGRAPH_FINALLY(igraph_free, x); + + v = IGRAPH_CALLOC(options->n * options->ncv, igraph_real_t); CHECKMEM(v); + workl = IGRAPH_CALLOC(options->lworkl, igraph_real_t); CHECKMEM(workl); + workd = IGRAPH_CALLOC(3 * options->n, igraph_real_t); CHECKMEM(workd); + d_size = 2 * options->nev + 1 > options->ncv ? 2 * options->nev + 1 : options->ncv; + dr = IGRAPH_CALLOC(d_size, igraph_real_t); CHECKMEM(dr); + di = IGRAPH_CALLOC(d_size, igraph_real_t); CHECKMEM(di); + resid = IGRAPH_CALLOC(options->n, igraph_real_t); CHECKMEM(resid); + select = IGRAPH_CALLOC(options->ncv, int); CHECKMEM(select); + workev = IGRAPH_CALLOC(3 * options->ncv, igraph_real_t); CHECKMEM(workev); + +#undef CHECKMEM + + } + + /* Set final bits */ + options->bmat[0] = 'I'; + options->iparam[0] = options->ishift; + options->iparam[1] = 0; // not referenced + options->iparam[2] = options->mxiter; + options->iparam[3] = 1; // currently dnaupd() works only for nb=1 + options->iparam[4] = 0; + options->iparam[5] = 0; // not referenced + options->iparam[6] = options->mode; + options->iparam[7] = 0; // return value + options->iparam[8] = 0; // return value + options->iparam[9] = 0; // return value + options->iparam[10] = 0; // return value + options->info = options->start; + if (options->start) { + if (!storage && !vectors) { + IGRAPH_ERROR("Starting vector not given", IGRAPH_EINVAL); + } + if (vectors && (igraph_matrix_nrow(vectors) != options->n || + igraph_matrix_ncol(vectors) != 1)) { + IGRAPH_ERROR("Invalid starting vector size", IGRAPH_EINVAL); + } + if (vectors) { + for (i = 0; i < options->n; i++) { + resid[i] = MATRIX(*vectors, i, 0); + } + } + } + + /* Ok, we have everything */ + while (1) { +#ifdef HAVE_GFORTRAN + igraphdnaupd_(&ido, options->bmat, &options->n, options->which, + &options->nev, &options->tol, + resid, &options->ncv, v, &options->ldv, + options->iparam, options->ipntr, + workd, workl, &options->lworkl, &options->info, + /*bmat_len=*/ 1, /*which_len=*/ 2); +#else + igraphdnaupd_(&ido, options->bmat, &options->n, options->which, + &options->nev, &options->tol, + resid, &options->ncv, v, &options->ldv, + options->iparam, options->ipntr, + workd, workl, &options->lworkl, &options->info); +#endif + + if (ido == -1 || ido == 1) { + igraph_real_t *from = workd + options->ipntr[0] - 1; + igraph_real_t *to = workd + options->ipntr[1] - 1; + if (fun(to, from, options->n, extra) != 0) { + IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product", + IGRAPH_ARPACK_PROD); + } + } else { + break; + } + } + + if (options->info == 1) { + igraph_i_arpack_report_no_convergence(options); + } + if (options->info != 0 && options->info != -9999) { + IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dnaupd(options->info)); + } + + options->ierr = 0; +#ifdef HAVE_GFORTRAN + igraphdneupd_(&rvec, all, select, dr, di, v, &options->ldv, + &options->sigma, &options->sigmai, workev, options->bmat, + &options->n, options->which, &options->nev, &options->tol, + resid, &options->ncv, v, &options->ldv, options->iparam, + options->ipntr, workd, workl, &options->lworkl, + &options->ierr, /*howmny_len=*/ 1, /*bmat_len=*/ 1, + /*which_len=*/ 2); +#else + igraphdneupd_(&rvec, all, select, dr, di, v, &options->ldv, + &options->sigma, &options->sigmai, workev, options->bmat, + &options->n, options->which, &options->nev, &options->tol, + resid, &options->ncv, v, &options->ldv, options->iparam, + options->ipntr, workd, workl, &options->lworkl, + &options->ierr); +#endif + + if (options->ierr != 0) { + IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dneupd(options->info)); + } + + /* Save the result */ + + options->noiter = options->iparam[2]; + options->nconv = options->iparam[4]; + options->numop = options->iparam[8]; + options->numopb = options->iparam[9]; + options->numreo = options->iparam[10]; + + if (options->nconv < options->nev) { + IGRAPH_WARNING("Not enough eigenvalues/vectors in ARPACK " + "solver"); + } + + /* ARPACK might modify stuff in 'options' so reset everything that could + * potentially get modified */ + options->ldv = origldv; + options->ncv = origncv; + options->lworkl = origlworkl; + options->which[0] = origwhich[0]; options->which[1] = origwhich[1]; + options->tol = origtol; + options->nev = orignev; + + if (values || vectors) { + IGRAPH_CHECK(igraph_arpack_rnsort(values, vectors, options, + dr, di, v)); + } + + /* Clean up if needed */ + if (free_them) { + IGRAPH_FREE(workev); + IGRAPH_FREE(select); + IGRAPH_FREE(resid); + IGRAPH_FREE(di); + IGRAPH_FREE(dr); + IGRAPH_FREE(workd); + IGRAPH_FREE(workl); + IGRAPH_FREE(v); + IGRAPH_FINALLY_CLEAN(8); + } + return 0; +} + +/** + * \function igraph_arpack_unpack_complex + * \brief Make the result of the non-symmetric ARPACK solver more readable + * + * This function works on the output of \ref igraph_arpack_rnsolve and + * brushes it up a bit: it only keeps \p nev eigenvalues/vectors and + * every eigenvector is stored in two columns of the \p vectors + * matrix. + * + * + * The output of the non-symmetric ARPACK solver is somewhat hard to + * parse, as real eigenvectors occupy only one column in the matrix, + * and the complex conjugate eigenvectors are not stored at all + * (usually). The other problem is that the solver might return more + * eigenvalues than requested. The common use of this function is to + * call it directly after \ref igraph_arpack_rnsolve with its \p + * vectors and \p values argument and \c options->nev as \p nev. + * This will add the vectors for eigenvalues with a negative imaginary + * part and return all vectors as 2 columns, a real and imaginary part. + * \param vectors The eigenvector matrix, as returned by \ref + * igraph_arpack_rnsolve. It will be resized, typically it will be + * larger. + * \param values The eigenvalue matrix, as returned by \ref + * igraph_arpack_rnsolve. It will be resized, typically extra, + * unneeded rows (=eigenvalues) will be removed. + * \param nev The number of eigenvalues/vectors to keep. Can be less + * or equal than the number originally requested from ARPACK. + * \return Error code. + * + * Time complexity: linear in the number of elements in the \p vectors + * matrix. + */ + +int igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, + long int nev) { + + long int nodes = igraph_matrix_nrow(vectors); + long int no_evs = igraph_matrix_nrow(values); + long int i, j; + long int new_vector_pos; + long int vector_pos; + igraph_matrix_t new_vectors; + + /* Error checks */ + if (nev < 0) { + IGRAPH_ERROR("`nev' cannot be negative", IGRAPH_EINVAL); + } + if (nev > no_evs) { + IGRAPH_ERROR("`nev' too large, we don't have that many in `values'", + IGRAPH_EINVAL); + } + + for (i = no_evs -1; i >= nev; i--) { + IGRAPH_CHECK(igraph_matrix_remove_row(values, i)); + } + + IGRAPH_CHECK(igraph_matrix_init(&new_vectors, nodes, nev * 2)); + IGRAPH_FINALLY(igraph_matrix_destroy, &new_vectors); + + new_vector_pos = 0; + vector_pos = 0; + for (i = 0; i < nev && vector_pos < igraph_matrix_ncol(vectors); i++) { + if (MATRIX(*values, i, 1) == 0) { + /* Real eigenvalue */ + for (j = 0; j < nodes; j++) { + MATRIX(new_vectors, j, new_vector_pos) = MATRIX(*vectors, j, vector_pos); + } + new_vector_pos += 2; + vector_pos += 1; + } else { + /* complex eigenvalue */ + for (j = 0; j < nodes; j++) { + MATRIX(new_vectors, j, new_vector_pos) = MATRIX(*vectors, j, vector_pos); + MATRIX(new_vectors, j, new_vector_pos + 1) = MATRIX(*vectors, j, vector_pos + 1); + } + + /* handle the conjugate */ + + /* first check if the conjugate eigenvalue is there */ + i++; + if (i >= nev) { + break; + } + + if (MATRIX(*values, i, 1) != -MATRIX(*values, i-1, 1)) { + IGRAPH_ERROR("Complex eigenvalue not followed by its conjugate.", IGRAPH_EINVAL); + } + + /* then copy and negate */ + for (j = 0; j < nodes; j++) { + MATRIX(new_vectors, j, new_vector_pos + 2) = MATRIX(*vectors, j, vector_pos); + MATRIX(new_vectors, j, new_vector_pos + 3) = -MATRIX(*vectors, j, vector_pos + 1); + } + new_vector_pos += 4; + vector_pos += 2; + } + } + igraph_matrix_destroy(vectors); + IGRAPH_CHECK(igraph_matrix_copy(vectors, &new_vectors)); + igraph_matrix_destroy(&new_vectors); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/linalg/arpack_internal.h b/src/rigraph/core/linalg/arpack_internal.h new file mode 100644 index 0000000..f318371 --- /dev/null +++ b/src/rigraph/core/linalg/arpack_internal.h @@ -0,0 +1,229 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef ARPACK_INTERNAL_H +#define ARPACK_INTERNAL_H + +/* Note: only files calling the arpack routines directly need to + include this header. +*/ + +#include "igraph_types.h" +#include "config.h" + +#ifndef INTERNAL_ARPACK + #define igraphdsaupd_ dsaupd_ + #define igraphdseupd_ dseupd_ + #define igraphdsaup2_ dsaup2_ + #define igraphdstats_ dstats_ + #define igraphdsesrt_ dsesrt_ + #define igraphdsortr_ dsortr_ + #define igraphdsortc_ dsortc_ + #define igraphdgetv0_ dgetv0_ + #define igraphdsaitr_ dsaitr_ + #define igraphdsapps_ dsapps_ + #define igraphdsconv_ dsconv_ + #define igraphdseigt_ dseigt_ + #define igraphdsgets_ dsgets_ + #define igraphdstqrb_ dstqrb_ + #define igraphdmout_ dmout_ + #define igraphivout_ ivout_ + #define igraphsecond_ second_ + #define igraphdvout_ dvout_ + #define igraphdnaitr_ dnaitr_ + #define igraphdnapps_ dnapps_ + #define igraphdnaup2_ dnaup2_ + #define igraphdnaupd_ dnaupd_ + #define igraphdnconv_ dnconv_ + #define igraphdlabad_ dlabad_ + #define igraphdlanhs_ dlanhs_ + #define igraphdsortc_ dsortc_ + #define igraphdneigh_ dneigh_ + #define igraphdngets_ dngets_ + #define igraphdstatn_ dstatn_ + #define igraphdlaqrb_ dlaqrb_ + + #define igraphdsaupd_ dsaupd_ + #define igraphdseupd_ dseupd_ + #define igraphdnaupd_ dnaupd_ + #define igraphdneupd_ dneupd_ +#endif + +#ifndef INTERNAL_LAPACK + #define igraphdlarnv_ dlarnv_ + #define igraphdlascl_ dlascl_ + #define igraphdlartg_ dlartg_ + #define igraphdlaset_ dlaset_ + #define igraphdlae2_ dlae2_ + #define igraphdlaev2_ dlaev2_ + #define igraphdlasr_ dlasr_ + #define igraphdlasrt_ dlasrt_ + #define igraphdgeqr2_ dgeqr2_ + #define igraphdlacpy_ dlacpy_ + #define igraphdorm2r_ dorm2r_ + #define igraphdsteqr_ dsteqr_ + #define igraphdlanst_ dlanst_ + #define igraphdlapy2_ dlapy2_ + #define igraphdlamch_ dlamch_ + #define igraphdlaruv_ dlaruv_ + #define igraphdlarfg_ dlarfg_ + #define igraphdlarf_ dlarf_ + #define igraphdlassq_ dlassq_ + #define igraphdlamc2_ dlamc2_ + #define igraphdlamc1_ dlamc1_ + #define igraphdlamc2_ dlamc2_ + #define igraphdlamc3_ dlamc3_ + #define igraphdlamc4_ dlamc4_ + #define igraphdlamc5_ dlamc5_ + #define igraphdlabad_ dlabad_ + #define igraphdlanhs_ dlanhs_ + #define igraphdtrevc_ dtrevc_ + #define igraphdlanv2_ dlanv2_ + #define igraphdlaln2_ dlaln2_ + #define igraphdladiv_ dladiv_ + #define igraphdtrsen_ dtrsen_ + #define igraphdlahqr_ dlahqr_ + #define igraphdtrsen_ dtrsen_ + #define igraphdlacon_ dlacon_ + #define igraphdtrsyl_ dtrsyl_ + #define igraphdtrexc_ dtrexc_ + #define igraphdlange_ dlange_ + #define igraphdlaexc_ dlaexc_ + #define igraphdlasy2_ dlasy2_ + #define igraphdlarfx_ dlarfx_ +#endif + +#if 0 /* internal f2c functions always used */ + #define igraphd_sign d_sign + #define igraphetime_ etime_ + #define igraphpow_dd pow_dd + #define igraphpow_di pow_di + #define igraphs_cmp s_cmp + #define igraphs_copy s_copy + #define igraphd_lg10_ d_lg10_ + #define igraphi_dnnt_ i_dnnt_ +#endif + +#ifdef HAVE_GFORTRAN + +/* GFortran-specific calling conventions, used when compiling the R interface. + * Derived with "gfortran -fc-prototypes-external", applied on the original + * Fortran sources of these functions. + * + * Caveats: + * + * 1) gfortran prints size_t for the "_len" arguments, but in fact they must be + * long int + * 2) gofrtran maps Fortran LOGICAL types to int_least32_t, but in fact they + * must be void* (anything else doesn't work, not even _Bool*) + * */ + +void igraphdsaupd_(int *ido, char *bmat, int *n, + char *which, int *nev, igraph_real_t *tol, + igraph_real_t *resid, int *ncv, igraph_real_t *v, + int *ldv, int *iparam, int *ipntr, + igraph_real_t *workd, igraph_real_t *workl, + int *lworkl, int *info, + long int bmat_len, long int which_len); + +void igraphdseupd_(void *rvec, char *howmny, void *select, + igraph_real_t *d, igraph_real_t *z, int *ldz, + igraph_real_t *sigma, char *bmat, int *n, + char *which, int *nev, igraph_real_t *tol, + igraph_real_t *resid, int *ncv, igraph_real_t *v, + int *ldv, int *iparam, int *ipntr, + igraph_real_t *workd, igraph_real_t *workl, + int *lworkl, int *info, + long int howmny_len, long int bmat_len, long int which_len); + +void igraphdnaupd_(int *ido, char *bmat, int *n, + char *which, int *nev, igraph_real_t *tol, + igraph_real_t *resid, int *ncv, igraph_real_t *v, + int *ldv, int *iparam, int *ipntr, + igraph_real_t *workd, igraph_real_t *workl, + int *lworkl, int *info, + long int bmat_len, long int which_len); + +void igraphdneupd_(void *rvec, char *howmny, void *select, + igraph_real_t *dr, igraph_real_t *di, + igraph_real_t *z, int *ldz, + igraph_real_t *sigmar, igraph_real_t *sigmai, + igraph_real_t *workev, char *bmat, int *n, + char *which, int *nev, igraph_real_t *tol, + igraph_real_t *resid, int *ncv, igraph_real_t *v, + int *ldv, int *iparam, int *ipntr, + igraph_real_t *workd, igraph_real_t *workl, + int *lworkl, int *info, + long int howmny_len, long int bmat_len, long int which_len); + +void igraphdsortr_(char *which, void *apply, int* n, igraph_real_t *x1, + igraph_real_t *x2, long int which_len); + +void igraphdsortc_(char *which, void *apply, int* n, igraph_real_t *xreal, + igraph_real_t *ximag, igraph_real_t *y, long int which_len); + +#else + +int igraphdsaupd_(int *ido, char *bmat, int *n, + char *which, int *nev, igraph_real_t *tol, + igraph_real_t *resid, int *ncv, igraph_real_t *v, + int *ldv, int *iparam, int *ipntr, + igraph_real_t *workd, igraph_real_t *workl, + int *lworkl, int *info); + +int igraphdseupd_(int *rvec, char *howmny, int *select, + igraph_real_t *d, igraph_real_t *z, int *ldz, + igraph_real_t *sigma, char *bmat, int *n, + char *which, int *nev, igraph_real_t *tol, + igraph_real_t *resid, int *ncv, igraph_real_t *v, + int *ldv, int *iparam, int *ipntr, + igraph_real_t *workd, igraph_real_t *workl, + int *lworkl, int *info); + +int igraphdnaupd_(int *ido, char *bmat, int *n, + char *which, int *nev, igraph_real_t *tol, + igraph_real_t *resid, int *ncv, igraph_real_t *v, + int *ldv, int *iparam, int *ipntr, + igraph_real_t *workd, igraph_real_t *workl, + int *lworkl, int *info); + +int igraphdneupd_(int *rvec, char *howmny, int *select, + igraph_real_t *dr, igraph_real_t *di, + igraph_real_t *z, int *ldz, + igraph_real_t *sigmar, igraph_real_t *sigmai, + igraph_real_t *workev, char *bmat, int *n, + char *which, int *nev, igraph_real_t *tol, + igraph_real_t *resid, int *ncv, igraph_real_t *v, + int *ldv, int *iparam, int *ipntr, + igraph_real_t *workd, igraph_real_t *workl, + int *lworkl, int *info); + +int igraphdsortr_(char *which, int *apply, int* n, igraph_real_t *x1, + igraph_real_t *x2); + +int igraphdsortc_(char *which, int *apply, int* n, igraph_real_t *xreal, + igraph_real_t *ximag, igraph_real_t *y); + +#endif + +#endif /* ARPACK_INTERNAL_H */ diff --git a/src/rigraph/core/linalg/blas.c b/src/rigraph/core/linalg/blas.c new file mode 100644 index 0000000..bc625c5 --- /dev/null +++ b/src/rigraph/core/linalg/blas.c @@ -0,0 +1,157 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_error.h" +#include "igraph_blas.h" + +#include "blas_internal.h" + +/** + * \function igraph_blas_dgemv + * \brief Matrix-vector multiplication using BLAS, vector version. + * + * This function is a somewhat more user-friendly interface to + * the \c dgemv function in BLAS. \c dgemv performs the operation + * y = alpha*A*x + beta*y, where x and y are vectors and A is an + * appropriately sized matrix (symmetric or non-symmetric). + * + * \param transpose whether to transpose the matrix \p A + * \param alpha the constant \p alpha + * \param a the matrix \p A + * \param x the vector \p x + * \param beta the constant \p beta + * \param y the vector \p y (which will be modified in-place) + * + * Time complexity: O(nk) if the matrix is of size n x k + * + * \sa \ref igraph_blas_dgemv_array if you have arrays instead of + * vectors. + * + * \example examples/simple/blas.c + */ +void igraph_blas_dgemv(igraph_bool_t transpose, igraph_real_t alpha, + const igraph_matrix_t* a, const igraph_vector_t* x, + igraph_real_t beta, igraph_vector_t* y) { + char trans = transpose ? 'T' : 'N'; + int m, n; + int inc = 1; + + m = (int) igraph_matrix_nrow(a); + n = (int) igraph_matrix_ncol(a); + + IGRAPH_ASSERT(igraph_vector_size(x) == transpose ? m : n); + IGRAPH_ASSERT(igraph_vector_size(y) == transpose ? n : m); + +#ifdef HAVE_GFORTRAN + igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, + VECTOR(*x), &inc, &beta, VECTOR(*y), &inc, /* trans_len = */ 1); +#else + igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, + VECTOR(*x), &inc, &beta, VECTOR(*y), &inc); +#endif +} + +/** + * \function igraph_blas_dgemv_array + * \brief Matrix-vector multiplication using BLAS, array version. + * + * This function is a somewhat more user-friendly interface to + * the \c dgemv function in BLAS. \c dgemv performs the operation + * y = alpha*A*x + beta*y, where x and y are vectors and A is an + * appropriately sized matrix (symmetric or non-symmetric). + * + * \param transpose whether to transpose the matrix \p A + * \param alpha the constant \p alpha + * \param a the matrix \p A + * \param x the vector \p x as a regular C array + * \param beta the constant \p beta + * \param y the vector \p y as a regular C array + * (which will be modified in-place) + * + * Time complexity: O(nk) if the matrix is of size n x k + * + * \sa \ref igraph_blas_dgemv if you have vectors instead of + * arrays. + */ +void igraph_blas_dgemv_array(igraph_bool_t transpose, igraph_real_t alpha, + const igraph_matrix_t* a, const igraph_real_t* x, + igraph_real_t beta, igraph_real_t* y) { + char trans = transpose ? 'T' : 'N'; + int m, n; + int inc = 1; + + m = (int) igraph_matrix_nrow(a); + n = (int) igraph_matrix_ncol(a); + +#ifdef HAVE_GFORTRAN + igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, + (igraph_real_t*)x, &inc, &beta, y, &inc, /* trans_len = */ 1); +#else + igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, + (igraph_real_t*)x, &inc, &beta, y, &inc); +#endif +} + +/** + * \function igraph_blas_dnrm2 + * \brief Euclidean norm of a vector. + * + * \param v The vector. + * \return Real value, the norm of \p v. + * + * Time complexity: O(n) where n is the length of the vector. + */ +igraph_real_t igraph_blas_dnrm2(const igraph_vector_t *v) { + int n = igraph_vector_size(v); + int one = 1; + return igraphdnrm2_(&n, VECTOR(*v), &one); +} + +/** + * \function igraph_blas_ddot + * \brief Dot product of two vectors. + * + * \param v1 The first vector. + * \param v2 The second vector. + * \param res Pointer to a real, the result will be stored here. + * + * Time complexity: O(n) where n is the length of the vectors. + * + * \example examples/simple/blas.c + */ +int igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2, + igraph_real_t *res) { + + int n = igraph_vector_size(v1); + int one = 1; + + if (igraph_vector_size(v2) != n) { + IGRAPH_ERROR("Dot product of vectors with different dimensions.", + IGRAPH_EINVAL); + } + + *res = igraphddot_(&n, VECTOR(*v1), &one, VECTOR(*v2), &one); + + return 0; +} diff --git a/src/rigraph/core/linalg/blas_internal.h b/src/rigraph/core/linalg/blas_internal.h new file mode 100644 index 0000000..71810b0 --- /dev/null +++ b/src/rigraph/core/linalg/blas_internal.h @@ -0,0 +1,90 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef BLAS_INTERNAL_H +#define BLAS_INTERNAL_H + +/* Note: only files calling the BLAS routines directly need to + include this header. +*/ + +#include "igraph_types.h" +#include "config.h" + +#ifndef INTERNAL_BLAS + #define igraphdaxpy_ daxpy_ + #define igraphdger_ dger_ + #define igraphdcopy_ dcopy_ + #define igraphdscal_ dscal_ + #define igraphdswap_ dswap_ + #define igraphdgemm_ dgemm_ + #define igraphdgemv_ dgemv_ + #define igraphddot_ ddot_ + #define igraphdnrm2_ dnrm2_ + #define igraphlsame_ lsame_ + #define igraphdrot_ drot_ + #define igraphidamax_ idamax_ + #define igraphdtrmm_ dtrmm_ + #define igraphdasum_ dasum_ + #define igraphdtrsm_ dtrsm_ + #define igraphdtrsv_ dtrsv_ + #define igraphdnrm2_ dnrm2_ + #define igraphdsymv_ dsymv_ + #define igraphdsyr2_ dsyr2_ + #define igraphdsyr2k_ dsyr2k_ + #define igraphdtrmv_ dtrmv_ + #define igraphdsyrk_ dsyrk_ +#endif + +#ifdef HAVE_GFORTRAN + +/* GFortran-specific calling conventions, used when compiling the R interface. + * Derived with "gfortran -fc-prototypes-external", applied on the original + * Fortran sources of these functions. */ + +void igraphdgemv_(char *trans, int *m, int *n, igraph_real_t *alpha, + igraph_real_t *a, int *lda, igraph_real_t *x, int *incx, + igraph_real_t *beta, igraph_real_t *y, int *incy, long int trans_len); + +void igraphdgemm_(char *transa, char *transb, int *m, int *n, int *k, + double *alpha, double *a, int *lda, double *b, int *ldb, + double *beta, double *c__, int *ldc, long int transa_len, long int transb_len); + +#else + +int igraphdgemv_(char *trans, int *m, int *n, igraph_real_t *alpha, + igraph_real_t *a, int *lda, igraph_real_t *x, int *incx, + igraph_real_t *beta, igraph_real_t *y, int *incy); + +int igraphdgemm_(char *transa, char *transb, int *m, int *n, int *k, + double *alpha, double *a, int *lda, double *b, int *ldb, + double *beta, double *c__, int *ldc); + +#endif + +double igraphdnrm2_(int *n, double *x, int *incx); + +double igraphddot_(int *n, double *dx, int *incx, double *dy, int *incy); + +#endif diff --git a/src/rigraph/core/linalg/eigen.c b/src/rigraph/core/linalg/eigen.c new file mode 100644 index 0000000..f42dc86 --- /dev/null +++ b/src/rigraph/core/linalg/eigen.c @@ -0,0 +1,1550 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_eigen.h" + +#include "igraph_qsort.h" +#include "igraph_blas.h" +#include "igraph_interface.h" +#include "igraph_adjlist.h" + +#include +#include +#include + +static int igraph_i_eigen_arpackfun_to_mat(igraph_arpack_function_t *fun, + int n, void *extra, + igraph_matrix_t *res) { + + int i; + igraph_vector_t v; + + IGRAPH_CHECK(igraph_matrix_init(res, n, n)); + IGRAPH_FINALLY(igraph_matrix_destroy, res); + IGRAPH_VECTOR_INIT_FINALLY(&v, n); + VECTOR(v)[0] = 1; + IGRAPH_CHECK(fun(/*to=*/ &MATRIX(*res, 0, 0), /*from=*/ VECTOR(v), n, + extra)); + for (i = 1; i < n; i++) { + VECTOR(v)[i - 1] = 0; + VECTOR(v)[i ] = 1; + IGRAPH_CHECK(fun(/*to=*/ &MATRIX(*res, 0, i), /*from=*/ VECTOR(v), n, + extra)); + } + igraph_vector_destroy(&v); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +static int igraph_i_eigen_matrix_symmetric_lapack_lm(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_t *values, + igraph_matrix_t *vectors) { + + igraph_matrix_t vec1, vec2; + igraph_vector_t val1, val2; + int n = (int) igraph_matrix_nrow(A); + int p1 = 0, p2 = which->howmany - 1, pr = 0; + + IGRAPH_VECTOR_INIT_FINALLY(&val1, 0); + IGRAPH_VECTOR_INIT_FINALLY(&val2, 0); + + if (vectors) { + IGRAPH_CHECK(igraph_matrix_init(&vec1, 0, 0)); + IGRAPH_FINALLY(igraph_matrix_destroy, &vec1); + IGRAPH_CHECK(igraph_matrix_init(&vec2, 0, 0)); + IGRAPH_FINALLY(igraph_matrix_destroy, &vec1); + } + + IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, + /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, + /*il=*/ 1, /*iu=*/ which->howmany, + /*abstol=*/ 1e-14, &val1, + vectors ? &vec1 : 0, + /*support=*/ 0)); + + IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, + /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, + /*il=*/ n - which->howmany + 1, /*iu=*/ n, + /*abstol=*/ 1e-14, &val2, + vectors ? &vec2 : 0, + /*support=*/ 0)); + + if (values) { + IGRAPH_CHECK(igraph_vector_resize(values, which->howmany)); + } + if (vectors) { + IGRAPH_CHECK(igraph_matrix_resize(vectors, n, which->howmany)); + } + + while (pr < which->howmany) { + if (p2 < 0 || fabs(VECTOR(val1)[p1]) > fabs(VECTOR(val2)[p2])) { + if (values) { + VECTOR(*values)[pr] = VECTOR(val1)[p1]; + } + if (vectors) { + memcpy(&MATRIX(*vectors, 0, pr), &MATRIX(vec1, 0, p1), + sizeof(igraph_real_t) * (size_t) n); + } + p1++; + pr++; + } else { + if (values) { + VECTOR(*values)[pr] = VECTOR(val2)[p2]; + } + if (vectors) { + memcpy(&MATRIX(*vectors, 0, pr), &MATRIX(vec2, 0, p2), + sizeof(igraph_real_t) * (size_t) n); + } + p2--; + pr++; + } + } + + + if (vectors) { + igraph_matrix_destroy(&vec2); + igraph_matrix_destroy(&vec1); + IGRAPH_FINALLY_CLEAN(2); + } + igraph_vector_destroy(&val2); + igraph_vector_destroy(&val1); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +static int igraph_i_eigen_matrix_symmetric_lapack_sm(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_t *values, + igraph_matrix_t *vectors) { + + igraph_vector_t val; + igraph_matrix_t vec; + int i, w = 0, n = (int) igraph_matrix_nrow(A); + igraph_real_t small; + int p1, p2, pr = 0; + + IGRAPH_VECTOR_INIT_FINALLY(&val, 0); + + if (vectors) { + IGRAPH_MATRIX_INIT_FINALLY(&vec, 0, 0); + } + + IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_ALL, /*vl=*/ 0, + /*vu=*/ 0, /*vestimate=*/ 0, + /*il=*/ 0, /*iu=*/ 0, + /*abstol=*/ 1e-14, &val, + vectors ? &vec : 0, + /*support=*/ 0)); + + /* Look for smallest value */ + small = fabs(VECTOR(val)[0]); + for (i = 1; i < n; i++) { + igraph_real_t v = fabs(VECTOR(val)[i]); + if (v < small) { + small = v; + w = i; + } + } + p1 = w - 1; p2 = w; + + if (values) { + IGRAPH_CHECK(igraph_vector_resize(values, which->howmany)); + } + if (vectors) { + IGRAPH_CHECK(igraph_matrix_resize(vectors, n, which->howmany)); + } + + while (pr < which->howmany) { + if (p2 == n - 1 || fabs(VECTOR(val)[p1]) < fabs(VECTOR(val)[p2])) { + if (values) { + VECTOR(*values)[pr] = VECTOR(val)[p1]; + } + if (vectors) { + memcpy(&MATRIX(*vectors, 0, pr), &MATRIX(vec, 0, p1), + sizeof(igraph_real_t) * (size_t) n); + } + p1--; + pr++; + } else { + if (values) { + VECTOR(*values)[pr] = VECTOR(val)[p2]; + } + if (vectors) { + memcpy(&MATRIX(*vectors, 0, pr), &MATRIX(vec, 0, p2), + sizeof(igraph_real_t) * (size_t) n); + } + p2++; + pr++; + } + } + + if (vectors) { + igraph_matrix_destroy(&vec); + IGRAPH_FINALLY_CLEAN(1); + } + igraph_vector_destroy(&val); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_eigen_matrix_symmetric_lapack_la(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_t *values, + igraph_matrix_t *vectors) { + + /* TODO: ordering? */ + + int n = (int) igraph_matrix_nrow(A); + int il = n - which->howmany + 1; + IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, + /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, + /*il=*/ il, /*iu=*/ n, + /*abstol=*/ 1e-14, values, vectors, + /*support=*/ 0)); + return 0; +} + +static int igraph_i_eigen_matrix_symmetric_lapack_sa(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_t *values, + igraph_matrix_t *vectors) { + + /* TODO: ordering? */ + + IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, + /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, + /*il=*/ 1, /*iu=*/ which->howmany, + /*abstol=*/ 1e-14, values, vectors, + /*support=*/ 0)); + + return 0; +} + +static int igraph_i_eigen_matrix_symmetric_lapack_be(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_t *values, + igraph_matrix_t *vectors) { + + /* TODO: ordering? */ + + igraph_matrix_t vec1, vec2; + igraph_vector_t val1, val2; + int n = (int) igraph_matrix_nrow(A); + int p1 = 0, p2 = which->howmany / 2, pr = 0; + + IGRAPH_VECTOR_INIT_FINALLY(&val1, 0); + IGRAPH_VECTOR_INIT_FINALLY(&val2, 0); + + if (vectors) { + IGRAPH_CHECK(igraph_matrix_init(&vec1, 0, 0)); + IGRAPH_FINALLY(igraph_matrix_destroy, &vec1); + IGRAPH_CHECK(igraph_matrix_init(&vec2, 0, 0)); + IGRAPH_FINALLY(igraph_matrix_destroy, &vec1); + } + + IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, + /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, + /*il=*/ 1, /*iu=*/ (which->howmany) / 2, + /*abstol=*/ 1e-14, &val1, + vectors ? &vec1 : 0, + /*support=*/ 0)); + + IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, + /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, + /*il=*/ n - (which->howmany) / 2, /*iu=*/ n, + /*abstol=*/ 1e-14, &val2, + vectors ? &vec2 : 0, + /*support=*/ 0)); + + if (values) { + IGRAPH_CHECK(igraph_vector_resize(values, which->howmany)); + } + if (vectors) { + IGRAPH_CHECK(igraph_matrix_resize(vectors, n, which->howmany)); + } + + while (pr < which->howmany) { + if (pr % 2) { + if (values) { + VECTOR(*values)[pr] = VECTOR(val1)[p1]; + } + if (vectors) { + memcpy(&MATRIX(*vectors, 0, pr), &MATRIX(vec1, 0, p1), + sizeof(igraph_real_t) * (size_t) n); + } + p1++; + pr++; + } else { + if (values) { + VECTOR(*values)[pr] = VECTOR(val2)[p2]; + } + if (vectors) { + memcpy(&MATRIX(*vectors, 0, pr), &MATRIX(vec2, 0, p2), + sizeof(igraph_real_t) * (size_t) n); + } + p2--; + pr++; + } + } + + if (vectors) { + igraph_matrix_destroy(&vec2); + igraph_matrix_destroy(&vec1); + IGRAPH_FINALLY_CLEAN(2); + } + igraph_vector_destroy(&val2); + igraph_vector_destroy(&val1); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +static int igraph_i_eigen_matrix_symmetric_lapack_all(const igraph_matrix_t *A, + igraph_vector_t *values, + igraph_matrix_t *vectors) { + + IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_ALL, /*vl=*/ 0, + /*vu=*/ 0, /*vestimate=*/ 0, + /*il=*/ 0, /*iu=*/ 0, + /*abstol=*/ 1e-14, values, vectors, + /*support=*/ 0)); + + return 0; +} + +static int igraph_i_eigen_matrix_symmetric_lapack_iv(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_t *values, + igraph_matrix_t *vectors) { + + IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_INTERVAL, + /*vl=*/ which->vl, /*vu=*/ which->vu, + /*vestimate=*/ which->vestimate, + /*il=*/ 0, /*iu=*/ 0, + /*abstol=*/ 1e-14, values, vectors, + /*support=*/ 0)); + + return 0; +} + +static int igraph_i_eigen_matrix_symmetric_lapack_sel(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_t *values, + igraph_matrix_t *vectors) { + + IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT, + /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0, + /*il=*/ which->il, /*iu=*/ which->iu, + /*abstol=*/ 1e-14, values, vectors, + /*support=*/ 0)); + + return 0; +} + +static int igraph_i_eigen_matrix_symmetric_lapack(const igraph_matrix_t *A, + const igraph_sparsemat_t *sA, + igraph_arpack_function_t *fun, + int n, void *extra, + const igraph_eigen_which_t *which, + igraph_vector_t *values, + igraph_matrix_t *vectors) { + + const igraph_matrix_t *myA = A; + igraph_matrix_t mA; + + /* First we need to create a dense square matrix */ + + if (A) { + n = (int) igraph_matrix_nrow(A); + } else if (sA) { + n = (int) igraph_sparsemat_nrow(sA); + IGRAPH_CHECK(igraph_matrix_init(&mA, 0, 0)); + IGRAPH_FINALLY(igraph_matrix_destroy, &mA); + IGRAPH_CHECK(igraph_sparsemat_as_matrix(&mA, sA)); + myA = &mA; + } else if (fun) { + IGRAPH_CHECK(igraph_i_eigen_arpackfun_to_mat(fun, n, extra, &mA)); + IGRAPH_FINALLY(igraph_matrix_destroy, &mA); + myA = &mA; + } + + switch (which->pos) { + case IGRAPH_EIGEN_LM: + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_lm(myA, which, + values, vectors)); + break; + case IGRAPH_EIGEN_SM: + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_sm(myA, which, + values, vectors)); + break; + case IGRAPH_EIGEN_LA: + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_la(myA, which, + values, vectors)); + break; + case IGRAPH_EIGEN_SA: + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_sa(myA, which, + values, vectors)); + break; + case IGRAPH_EIGEN_BE: + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_be(myA, which, + values, vectors)); + break; + case IGRAPH_EIGEN_ALL: + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_all(myA, + values, + vectors)); + break; + case IGRAPH_EIGEN_INTERVAL: + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_iv(myA, which, + values, + vectors)); + break; + case IGRAPH_EIGEN_SELECT: + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_sel(myA, which, + values, + vectors)); + break; + default: + /* This cannot happen */ + break; + } + + if (!A) { + igraph_matrix_destroy(&mA); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +typedef struct igraph_i_eigen_matrix_sym_arpack_data_t { + const igraph_matrix_t *A; + const igraph_sparsemat_t *sA; +} igraph_i_eigen_matrix_sym_arpack_data_t; + +static int igraph_i_eigen_matrix_sym_arpack_cb(igraph_real_t *to, + const igraph_real_t *from, + int n, void *extra) { + + igraph_i_eigen_matrix_sym_arpack_data_t *data = + (igraph_i_eigen_matrix_sym_arpack_data_t *) extra; + + if (data->A) { + igraph_blas_dgemv_array(/*transpose=*/ 0, /*alpha=*/ 1.0, + data->A, from, /*beta=*/ 0.0, to); + } else { /* data->sA */ + igraph_vector_t vto, vfrom; + igraph_vector_view(&vto, to, n); + igraph_vector_view(&vfrom, from, n); + igraph_vector_null(&vto); + igraph_sparsemat_gaxpy(data->sA, &vfrom, &vto); + } + return 0; +} + +static int igraph_i_eigen_matrix_symmetric_arpack_be(const igraph_matrix_t *A, + const igraph_sparsemat_t *sA, + igraph_arpack_function_t *fun, + int n, void *extra, + const igraph_eigen_which_t *which, + igraph_arpack_options_t *options, + igraph_arpack_storage_t *storage, + igraph_vector_t *values, + igraph_matrix_t *vectors) { + + igraph_vector_t tmpvalues, tmpvalues2; + igraph_matrix_t tmpvectors, tmpvectors2; + + int low = (int) floor(which->howmany / 2.0), high = (int) ceil(which->howmany / 2.0); + int l1, l2, w; + + igraph_i_eigen_matrix_sym_arpack_data_t myextra; + myextra.A = A; + myextra.sA = sA; + + if (low + high >= n) { + IGRAPH_ERROR("Requested too many eigenvalues/vectors", IGRAPH_EINVAL); + } + + if (!fun) { + fun = igraph_i_eigen_matrix_sym_arpack_cb; + extra = (void*) &myextra; + } + + IGRAPH_VECTOR_INIT_FINALLY(&tmpvalues, high); + IGRAPH_MATRIX_INIT_FINALLY(&tmpvectors, n, high); + IGRAPH_VECTOR_INIT_FINALLY(&tmpvalues2, low); + IGRAPH_MATRIX_INIT_FINALLY(&tmpvectors2, n, low); + + options->n = n; + options->nev = high; + options->ncv = 2 * options->nev < n ? 2 * options->nev : n; + options->which[0] = 'L'; options->which[1] = 'A'; + + IGRAPH_CHECK(igraph_arpack_rssolve(fun, extra, options, storage, + &tmpvalues, &tmpvectors)); + + options->nev = low; + options->ncv = 2 * options->nev < n ? 2 * options->nev : n; + options->which[0] = 'S'; options->which[1] = 'A'; + + IGRAPH_CHECK(igraph_arpack_rssolve(fun, extra, options, storage, + &tmpvalues2, &tmpvectors2)); + + IGRAPH_CHECK(igraph_vector_resize(values, low + high)); + IGRAPH_CHECK(igraph_matrix_resize(vectors, n, low + high)); + + l1 = 0; l2 = 0; w = 0; + while (w < which->howmany) { + VECTOR(*values)[w] = VECTOR(tmpvalues)[l1]; + memcpy(&MATRIX(*vectors, 0, w), &MATRIX(tmpvectors, 0, l1), + (size_t) n * sizeof(igraph_real_t)); + w++; l1++; + if (w < which->howmany) { + VECTOR(*values)[w] = VECTOR(tmpvalues2)[l2]; + memcpy(&MATRIX(*vectors, 0, w), &MATRIX(tmpvectors2, 0, l2), + (size_t) n * sizeof(igraph_real_t)); + w++; l2++; + } + } + + igraph_matrix_destroy(&tmpvectors2); + igraph_vector_destroy(&tmpvalues2); + igraph_matrix_destroy(&tmpvectors); + igraph_vector_destroy(&tmpvalues); + IGRAPH_FINALLY_CLEAN(4); + + return 0; +} + +static int igraph_i_eigen_matrix_symmetric_arpack(const igraph_matrix_t *A, + const igraph_sparsemat_t *sA, + igraph_arpack_function_t *fun, + int n, void *extra, + const igraph_eigen_which_t *which, + igraph_arpack_options_t *options, + igraph_arpack_storage_t *storage, + igraph_vector_t *values, + igraph_matrix_t *vectors) { + + /* For ARPACK we need a matrix multiplication operation. + This can be done in any format, so everything is fine, + we don't have to convert. */ + + igraph_i_eigen_matrix_sym_arpack_data_t myextra; + + myextra.A = A; + myextra.sA = sA; + + if (!options) { + IGRAPH_ERROR("`options' must be given for ARPACK algorithm", + IGRAPH_EINVAL); + } + + if (which->pos == IGRAPH_EIGEN_BE) { + return igraph_i_eigen_matrix_symmetric_arpack_be(A, sA, fun, n, extra, + which, options, storage, + values, vectors); + } else { + + switch (which->pos) { + case IGRAPH_EIGEN_LM: + options->which[0] = 'L'; options->which[1] = 'M'; + options->nev = which->howmany; + break; + case IGRAPH_EIGEN_SM: + options->which[0] = 'S'; options->which[1] = 'M'; + options->nev = which->howmany; + break; + case IGRAPH_EIGEN_LA: + options->which[0] = 'L'; options->which[1] = 'A'; + options->nev = which->howmany; + break; + case IGRAPH_EIGEN_SA: + options->which[0] = 'S'; options->which[1] = 'A'; + options->nev = which->howmany; + break; + case IGRAPH_EIGEN_ALL: + options->which[0] = 'L'; options->which[1] = 'M'; + options->nev = n; + break; + case IGRAPH_EIGEN_INTERVAL: + IGRAPH_ERROR("Interval of eigenvectors with ARPACK", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + case IGRAPH_EIGEN_SELECT: + IGRAPH_ERROR("Selected eigenvalues with ARPACK", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + default: + /* This cannot happen */ + break; + } + + options->n = n; + options->ncv = 2 * options->nev < n ? 2 * options->nev : n; + + if (!fun) { + fun = igraph_i_eigen_matrix_sym_arpack_cb; + extra = (void*) &myextra; + } + + IGRAPH_CHECK(igraph_arpack_rssolve(fun, extra, options, storage, + values, vectors)); + return 0; + } +} + +/* Get the eigenvalues and the eigenvectors from the compressed + form. Order them according to the ordering criteria. + Comparison functions for the reordering first */ + +typedef int (*igraph_i_eigen_matrix_lapack_cmp_t)(void*, const void*, + const void *); + +typedef struct igraph_i_eml_cmp_t { + const igraph_vector_t *mag, *real, *imag; +} igraph_i_eml_cmp_t; + +/* TODO: these should be defined in some header */ + +#define EPS (DBL_EPSILON*100) +#define LESS(a,b) ((a) < (b)-EPS) +#define MORE(a,b) ((a) > (b)+EPS) +#define ZERO(a) ((a) > -EPS && (a) < EPS) +#define NONZERO(a) ((a) < -EPS || (a) > EPS) + +/* Largest magnitude. Ordering is according to + 1 Larger magnitude + 2 Real eigenvalues before complex ones + 3 Larger real part + 4 Larger imaginary part */ + +static int igraph_i_eigen_matrix_lapack_cmp_lm(void *extra, const void *a, + const void *b) { + igraph_i_eml_cmp_t *myextra = (igraph_i_eml_cmp_t *) extra; + int *aa = (int*) a, *bb = (int*) b; + igraph_real_t a_m = VECTOR(*myextra->mag)[*aa]; + igraph_real_t b_m = VECTOR(*myextra->mag)[*bb]; + + if (LESS(a_m, b_m)) { + return 1; + } else if (MORE(a_m, b_m)) { + return -1; + } else { + igraph_real_t a_r = VECTOR(*myextra->real)[*aa]; + igraph_real_t a_i = VECTOR(*myextra->imag)[*aa]; + igraph_real_t b_r = VECTOR(*myextra->real)[*bb]; + igraph_real_t b_i = VECTOR(*myextra->imag)[*bb]; + if (ZERO(a_i) && NONZERO(b_i)) { + return -1; + } + if (NONZERO(a_i) && ZERO(b_i)) { + return 1; + } + if (MORE(a_r, b_r)) { + return -1; + } + if (LESS(a_r, b_r)) { + return 1; + } + if (MORE(a_i, b_i)) { + return -1; + } + if (LESS(a_i, b_i)) { + return 1; + } + } + return 0; +} + +/* Smallest marginude. Ordering is according to + 1 Magnitude (smaller first) + 2 Complex eigenvalues before real ones + 3 Smaller real part + 4 Smaller imaginary part + This ensures that lm has exactly the opposite order to sm */ + +static int igraph_i_eigen_matrix_lapack_cmp_sm(void *extra, const void *a, + const void *b) { + igraph_i_eml_cmp_t *myextra = (igraph_i_eml_cmp_t *) extra; + int *aa = (int*) a, *bb = (int*) b; + igraph_real_t a_m = VECTOR(*myextra->mag)[*aa]; + igraph_real_t b_m = VECTOR(*myextra->mag)[*bb]; + + if (MORE(a_m, b_m)) { + return 1; + } else if (LESS(a_m, b_m)) { + return -1; + } else { + igraph_real_t a_r = VECTOR(*myextra->real)[*aa]; + igraph_real_t a_i = VECTOR(*myextra->imag)[*aa]; + igraph_real_t b_r = VECTOR(*myextra->real)[*bb]; + igraph_real_t b_i = VECTOR(*myextra->imag)[*bb]; + if (NONZERO(a_i) && ZERO(b_i)) { + return -1; + } + if (ZERO(a_i) && NONZERO(b_i)) { + return 1; + } + if (LESS(a_r, b_r)) { + return -1; + } + if (MORE(a_r, b_r)) { + return 1; + } + if (LESS(a_i, b_i)) { + return -1; + } + if (MORE(a_i, b_i)) { + return 1; + } + } + return 0; +} + +/* Largest real part. Ordering is according to + 1 Larger real part + 2 Real eigenvalues come before complex ones + 3 Larger complex part */ + +static int igraph_i_eigen_matrix_lapack_cmp_lr(void *extra, const void *a, + const void *b) { + + igraph_i_eml_cmp_t *myextra = (igraph_i_eml_cmp_t *) extra; + int *aa = (int*) a, *bb = (int*) b; + igraph_real_t a_r = VECTOR(*myextra->real)[*aa]; + igraph_real_t b_r = VECTOR(*myextra->real)[*bb]; + + if (MORE(a_r, b_r)) { + return -1; + } else if (LESS(a_r, b_r)) { + return 1; + } else { + igraph_real_t a_i = VECTOR(*myextra->imag)[*aa]; + igraph_real_t b_i = VECTOR(*myextra->imag)[*bb]; + if (ZERO(a_i) && NONZERO(b_i)) { + return -1; + } + if (NONZERO(a_i) && ZERO(b_i)) { + return 1; + } + if (MORE(a_i, b_i)) { + return -1; + } + if (LESS(a_i, b_i)) { + return 1; + } + } + + return 0; +} + +/* Largest real part. Ordering is according to + 1 Smaller real part + 2 Complex eigenvalues come before real ones + 3 Smaller complex part + This is opposite to LR +*/ + +static int igraph_i_eigen_matrix_lapack_cmp_sr(void *extra, const void *a, + const void *b) { + + igraph_i_eml_cmp_t *myextra = (igraph_i_eml_cmp_t *) extra; + int *aa = (int*) a, *bb = (int*) b; + igraph_real_t a_r = VECTOR(*myextra->real)[*aa]; + igraph_real_t b_r = VECTOR(*myextra->real)[*bb]; + + if (LESS(a_r, b_r)) { + return -1; + } else if (MORE(a_r, b_r)) { + return 1; + } else { + igraph_real_t a_i = VECTOR(*myextra->imag)[*aa]; + igraph_real_t b_i = VECTOR(*myextra->imag)[*bb]; + if (NONZERO(a_i) && ZERO(b_i)) { + return -1; + } + if (ZERO(a_i) && NONZERO(b_i)) { + return 1; + } + if (LESS(a_i, b_i)) { + return -1; + } + if (MORE(a_i, b_i)) { + return 1; + } + } + + return 0; +} + +/* Order: + 1 Larger imaginary part + 2 Real eigenvalues before complex ones + 3 Larger real part */ + +static int igraph_i_eigen_matrix_lapack_cmp_li(void *extra, const void *a, + const void *b) { + + igraph_i_eml_cmp_t *myextra = (igraph_i_eml_cmp_t *) extra; + int *aa = (int*) a, *bb = (int*) b; + igraph_real_t a_i = VECTOR(*myextra->imag)[*aa]; + igraph_real_t b_i = VECTOR(*myextra->imag)[*bb]; + + if (MORE(a_i, b_i)) { + return -1; + } else if (LESS(a_i, b_i)) { + return 1; + } else { + igraph_real_t a_r = VECTOR(*myextra->real)[*aa]; + igraph_real_t b_r = VECTOR(*myextra->real)[*bb]; + if (ZERO(a_i) && NONZERO(b_i)) { + return -1; + } + if (NONZERO(a_i) && ZERO(b_i)) { + return 1; + } + if (MORE(a_r, b_r)) { + return -1; + } + if (LESS(a_r, b_r)) { + return 1; + } + } + + return 0; +} + +/* Order: + 1 Smaller imaginary part + 2 Complex eigenvalues before real ones + 3 Smaller real part + Order is opposite to LI */ + +static int igraph_i_eigen_matrix_lapack_cmp_si(void *extra, const void *a, + const void *b) { + + igraph_i_eml_cmp_t *myextra = (igraph_i_eml_cmp_t *) extra; + int *aa = (int*) a, *bb = (int*) b; + igraph_real_t a_i = VECTOR(*myextra->imag)[*aa]; + igraph_real_t b_i = VECTOR(*myextra->imag)[*bb]; + + if (LESS(a_i, b_i)) { + return -1; + } else if (MORE(a_i, b_i)) { + return 1; + } else { + igraph_real_t a_r = VECTOR(*myextra->real)[*aa]; + igraph_real_t b_r = VECTOR(*myextra->real)[*bb]; + if (NONZERO(a_i) && ZERO(b_i)) { + return -1; + } + if (ZERO(a_i) && NONZERO(b_i)) { + return 1; + } + if (LESS(a_r, b_r)) { + return -1; + } + if (MORE(a_r, b_r)) { + return 1; + } + } + + return 0; +} + +#undef EPS +#undef LESS +#undef MORE +#undef ZERO +#undef NONZERO + +#define INITMAG() \ + do { \ + int i; \ + IGRAPH_VECTOR_INIT_FINALLY(&mag, nev); \ + hasmag=1; \ + for (i=0; ipos) { + case IGRAPH_EIGEN_LM: + INITMAG(); + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_lm; + howmany = which->howmany; + break; + case IGRAPH_EIGEN_ALL: + INITMAG(); + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_sm; + howmany = nev; + break; + case IGRAPH_EIGEN_SM: + INITMAG(); + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_sm; + howmany = which->howmany; + break; + case IGRAPH_EIGEN_LR: + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_lr; + howmany = which->howmany; + break; + case IGRAPH_EIGEN_SR: + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_sr; + howmany = which->howmany; + break; + case IGRAPH_EIGEN_SELECT: + INITMAG(); + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_sm; + start = which->il - 1; + howmany = which->iu - which->il + 1; + break; + case IGRAPH_EIGEN_LI: + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_li; + howmany = which->howmany; + break; + case IGRAPH_EIGEN_SI: + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_si; + howmany = which->howmany; + break; + case IGRAPH_EIGEN_INTERVAL: + case IGRAPH_EIGEN_BE: + default: + IGRAPH_ERROR("Unimplemented eigenvalue ordering", IGRAPH_UNIMPLEMENTED); + break; + } + + for (i = 0; i < nev; i++) { + VECTOR(idx)[i] = i; + } + + igraph_qsort_r(VECTOR(idx), (size_t) nev, sizeof(VECTOR(idx)[0]), extra, + cmpfunc); + + if (hasmag) { + igraph_vector_destroy(&mag); + IGRAPH_FINALLY_CLEAN(1); + } + + if (values) { + IGRAPH_CHECK(igraph_vector_complex_resize(values, howmany)); + for (i = 0; i < howmany; i++) { + int x = VECTOR(idx)[start + i]; + VECTOR(*values)[i] = igraph_complex(VECTOR(*real)[x], + VECTOR(*imag)[x]); + } + } + + if (vectors) { + int n = (int) igraph_matrix_nrow(compressed); + IGRAPH_CHECK(igraph_matrix_complex_resize(vectors, n, howmany)); + for (i = 0; i < howmany; i++) { + int j, x = VECTOR(idx)[start + i]; + if (VECTOR(*imag)[x] == 0) { + /* real eigenvalue */ + for (j = 0; j < n; j++) { + MATRIX(*vectors, j, i) = igraph_complex(MATRIX(*compressed, j, x), + 0.0); + } + } else { + /* complex eigenvalue */ + int neg = 1, co = 0; + if (VECTOR(*imag)[x] < 0) { + neg = -1; + co = 1; + } + for (j = 0; j < n; j++) { + MATRIX(*vectors, j, i) = + igraph_complex(MATRIX(*compressed, j, x - co), + neg * MATRIX(*compressed, j, x + 1 - co)); + } + } + } + } + + igraph_vector_int_destroy(&idx); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_eigen_matrix_lapack_common(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_complex_t *values, + igraph_matrix_complex_t *vectors) { + + igraph_vector_t valuesreal, valuesimag; + igraph_matrix_t vectorsright, *myvectors = vectors ? &vectorsright : 0; + int n = (int) igraph_matrix_nrow(A); + int info = 1; + + IGRAPH_VECTOR_INIT_FINALLY(&valuesreal, n); + IGRAPH_VECTOR_INIT_FINALLY(&valuesimag, n); + if (vectors) { + IGRAPH_MATRIX_INIT_FINALLY(&vectorsright, n, n); + } + IGRAPH_CHECK(igraph_lapack_dgeev(A, &valuesreal, &valuesimag, + /*vectorsleft=*/ 0, myvectors, &info)); + + IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_reorder(&valuesreal, + &valuesimag, + myvectors, which, values, + vectors)); + + if (vectors) { + igraph_matrix_destroy(&vectorsright); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_destroy(&valuesimag); + igraph_vector_destroy(&valuesreal); + IGRAPH_FINALLY_CLEAN(2); + + return 0; + +} + +static int igraph_i_eigen_matrix_lapack_lm(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_complex_t *values, + igraph_matrix_complex_t *vectors) { + return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors); +} + +static int igraph_i_eigen_matrix_lapack_sm(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_complex_t *values, + igraph_matrix_complex_t *vectors) { + return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors); +} + +static int igraph_i_eigen_matrix_lapack_lr(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_complex_t *values, + igraph_matrix_complex_t *vectors) { + return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors); +} + + +static int igraph_i_eigen_matrix_lapack_sr(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_complex_t *values, + igraph_matrix_complex_t *vectors) { + return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors); +} + +static int igraph_i_eigen_matrix_lapack_li(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_complex_t *values, + igraph_matrix_complex_t *vectors) { + return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors); +} + +static int igraph_i_eigen_matrix_lapack_si(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_complex_t *values, + igraph_matrix_complex_t *vectors) { + return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors); +} + +static int igraph_i_eigen_matrix_lapack_select(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_complex_t *values, + igraph_matrix_complex_t *vectors) { + return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors); +} + +static int igraph_i_eigen_matrix_lapack_all(const igraph_matrix_t *A, + const igraph_eigen_which_t *which, + igraph_vector_complex_t *values, + igraph_matrix_complex_t *vectors) { + return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors); +} + +static int igraph_i_eigen_matrix_lapack(const igraph_matrix_t *A, + const igraph_sparsemat_t *sA, + igraph_arpack_function_t *fun, + int n, void *extra, + const igraph_eigen_which_t *which, + igraph_vector_complex_t *values, + igraph_matrix_complex_t *vectors) { + + const igraph_matrix_t *myA = A; + igraph_matrix_t mA; + + /* We need to create a dense square matrix first */ + + if (A) { + n = (int) igraph_matrix_nrow(A); + } else if (sA) { + n = (int) igraph_sparsemat_nrow(sA); + IGRAPH_CHECK(igraph_matrix_init(&mA, 0, 0)); + IGRAPH_FINALLY(igraph_matrix_destroy, &mA); + IGRAPH_CHECK(igraph_sparsemat_as_matrix(&mA, sA)); + myA = &mA; + } else if (fun) { + IGRAPH_CHECK(igraph_i_eigen_arpackfun_to_mat(fun, n, extra, &mA)); + IGRAPH_FINALLY(igraph_matrix_destroy, &mA); + } + + switch (which->pos) { + case IGRAPH_EIGEN_LM: + IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_lm(myA, which, + values, vectors)); + break; + case IGRAPH_EIGEN_SM: + IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_sm(myA, which, + values, vectors)); + break; + case IGRAPH_EIGEN_LR: + IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_lr(myA, which, + values, vectors)); + break; + case IGRAPH_EIGEN_SR: + IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_sr(myA, which, + values, vectors)); + break; + case IGRAPH_EIGEN_LI: + IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_li(myA, which, + values, vectors)); + break; + case IGRAPH_EIGEN_SI: + IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_si(myA, which, + values, vectors)); + break; + case IGRAPH_EIGEN_SELECT: + IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_select(myA, which, + values, vectors)); + break; + case IGRAPH_EIGEN_ALL: + IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_all(myA, which, + values, + vectors)); + break; + default: + /* This cannot happen */ + break; + } + + if (!A) { + igraph_matrix_destroy(&mA); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +static int igraph_i_eigen_checks(const igraph_matrix_t *A, + const igraph_sparsemat_t *sA, + igraph_arpack_function_t *fun, int n) { + + if ( (A ? 1 : 0) + (sA ? 1 : 0) + (fun ? 1 : 0) != 1) { + IGRAPH_ERROR("Exactly one of 'A', 'sA' and 'fun' must be given", + IGRAPH_EINVAL); + } + + if (A) { + if (n != igraph_matrix_ncol(A) || n != igraph_matrix_nrow(A)) { + IGRAPH_ERROR("Invalid matrix", IGRAPH_NONSQUARE); + } + } else if (sA) { + if (n != igraph_sparsemat_ncol(sA) || n != igraph_sparsemat_nrow(sA)) { + IGRAPH_ERROR("Invalid matrix", IGRAPH_NONSQUARE); + } + } + + return 0; +} + +/** + * \function igraph_eigen_matrix_symmetric + * + * \example examples/simple/igraph_eigen_matrix_symmetric.c + */ + +int igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, + const igraph_sparsemat_t *sA, + igraph_arpack_function_t *fun, int n, + void *extra, + igraph_eigen_algorithm_t algorithm, + const igraph_eigen_which_t *which, + igraph_arpack_options_t *options, + igraph_arpack_storage_t *storage, + igraph_vector_t *values, + igraph_matrix_t *vectors) { + + IGRAPH_CHECK(igraph_i_eigen_checks(A, sA, fun, n)); + + if (which->pos != IGRAPH_EIGEN_LM && + which->pos != IGRAPH_EIGEN_SM && + which->pos != IGRAPH_EIGEN_LA && + which->pos != IGRAPH_EIGEN_SA && + which->pos != IGRAPH_EIGEN_BE && + which->pos != IGRAPH_EIGEN_ALL && + which->pos != IGRAPH_EIGEN_INTERVAL && + which->pos != IGRAPH_EIGEN_SELECT) { + IGRAPH_ERROR("Invalid 'pos' position in 'which'", IGRAPH_EINVAL); + } + + switch (algorithm) { + case IGRAPH_EIGEN_AUTO: + if (which->howmany == n || n < 100) { + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack(A, sA, fun, n, + extra, which, + values, vectors)); + } else { + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_arpack(A, sA, fun, n, + extra, which, + options, storage, + values, vectors)); + } + break; + case IGRAPH_EIGEN_LAPACK: + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack(A, sA, fun, n, extra, + which, values, + vectors)); + break; + case IGRAPH_EIGEN_ARPACK: + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_arpack(A, sA, fun, n, extra, + which, options, + storage, + values, vectors)); + break; + default: + IGRAPH_ERROR("Unknown 'algorithm'", IGRAPH_EINVAL); + } + + return 0; +} + +/** + * \function igraph_eigen_matrix + * + */ + +int igraph_eigen_matrix(const igraph_matrix_t *A, + const igraph_sparsemat_t *sA, + igraph_arpack_function_t *fun, int n, + void *extra, + igraph_eigen_algorithm_t algorithm, + const igraph_eigen_which_t *which, + igraph_arpack_options_t *options, + igraph_arpack_storage_t *storage, + igraph_vector_complex_t *values, + igraph_matrix_complex_t *vectors) { + + IGRAPH_UNUSED(options); + IGRAPH_UNUSED(storage); + + IGRAPH_CHECK(igraph_i_eigen_checks(A, sA, fun, n)); + + if (which->pos != IGRAPH_EIGEN_LM && + which->pos != IGRAPH_EIGEN_SM && + which->pos != IGRAPH_EIGEN_LR && + which->pos != IGRAPH_EIGEN_SR && + which->pos != IGRAPH_EIGEN_LI && + which->pos != IGRAPH_EIGEN_SI && + which->pos != IGRAPH_EIGEN_SELECT && + which->pos != IGRAPH_EIGEN_ALL) { + IGRAPH_ERROR("Invalid 'pos' position in 'which'", IGRAPH_EINVAL); + } + + switch (algorithm) { + case IGRAPH_EIGEN_AUTO: + IGRAPH_ERROR("'AUTO' algorithm not implemented yet", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + case IGRAPH_EIGEN_LAPACK: + IGRAPH_CHECK(igraph_i_eigen_matrix_lapack(A, sA, fun, n, extra, which, + values, vectors)); + /* TODO */ + break; + case IGRAPH_EIGEN_ARPACK: + IGRAPH_ERROR("'ARPACK' algorithm not implemented yet", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + case IGRAPH_EIGEN_COMP_AUTO: + IGRAPH_ERROR("'COMP_AUTO' algorithm not implemented yet", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + case IGRAPH_EIGEN_COMP_LAPACK: + IGRAPH_ERROR("'COMP_LAPACK' algorithm not implemented yet", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + case IGRAPH_EIGEN_COMP_ARPACK: + IGRAPH_ERROR("'COMP_ARPACK' algorithm not implemented yet", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + default: + IGRAPH_ERROR("Unknown `algorithm'", IGRAPH_EINVAL); + } + + return 0; +} + +static int igraph_i_eigen_adjacency_arpack_sym_cb(igraph_real_t *to, + const igraph_real_t *from, + int n, void *extra) { + igraph_adjlist_t *adjlist = (igraph_adjlist_t *) extra; + igraph_vector_int_t *neis; + int i, j, nlen; + + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(adjlist, i); + nlen = igraph_vector_int_size(neis); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + int nei = VECTOR(*neis)[j]; + to[i] += from[nei]; + } + } + + return 0; +} + +static int igraph_i_eigen_adjacency_arpack(const igraph_t *graph, + const igraph_eigen_which_t *which, + igraph_arpack_options_t *options, + igraph_arpack_storage_t* storage, + igraph_vector_t *values, + igraph_matrix_t *vectors, + igraph_vector_complex_t *cmplxvalues, + igraph_matrix_complex_t *cmplxvectors) { + + IGRAPH_UNUSED(cmplxvalues); + IGRAPH_UNUSED(cmplxvectors); + + igraph_adjlist_t adjlist; + void *extra = (void*) &adjlist; + int n = igraph_vcount(graph); + + if (!options) { + IGRAPH_ERROR("`options' must be given for ARPACK algorithm", + IGRAPH_EINVAL); + } + + if (igraph_is_directed(graph)) { + IGRAPH_ERROR("ARPACK adjacency eigensolver not implemented for " + "directed graphs", IGRAPH_UNIMPLEMENTED); + } + if (which->pos == IGRAPH_EIGEN_INTERVAL) { + IGRAPH_ERROR("ARPACK adjacency eigensolver does not implement " + "`INTERNAL' eigenvalues", IGRAPH_UNIMPLEMENTED); + } + if (which->pos == IGRAPH_EIGEN_SELECT) { + IGRAPH_ERROR("ARPACK adjacency eigensolver does not implement " + "`SELECT' eigenvalues", IGRAPH_UNIMPLEMENTED); + } + if (which->pos == IGRAPH_EIGEN_ALL) { + IGRAPH_ERROR("ARPACK adjacency eigensolver does not implement " + "`ALL' eigenvalues", IGRAPH_UNIMPLEMENTED); + } + + switch (which->pos) { + case IGRAPH_EIGEN_LM: + options->which[0] = 'L'; options->which[1] = 'M'; + options->nev = which->howmany; + break; + case IGRAPH_EIGEN_SM: + options->which[0] = 'S'; options->which[1] = 'M'; + options->nev = which->howmany; + break; + case IGRAPH_EIGEN_LA: + options->which[0] = 'L'; options->which[1] = 'A'; + options->nev = which->howmany; + break; + case IGRAPH_EIGEN_SA: + options->which[0] = 'S'; options->which[1] = 'A'; + options->nev = which->howmany; + break; + case IGRAPH_EIGEN_ALL: + options->which[0] = 'L'; options->which[1] = 'M'; + options->nev = n; + break; + case IGRAPH_EIGEN_BE: + IGRAPH_ERROR("Eigenvectors from both ends with ARPACK", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + case IGRAPH_EIGEN_INTERVAL: + IGRAPH_ERROR("Interval of eigenvectors with ARPACK", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + case IGRAPH_EIGEN_SELECT: + IGRAPH_ERROR("Selected eigenvalues with ARPACK", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + default: + /* This cannot happen */ + break; + } + + options->n = n; + options->ncv = 2 * options->nev < n ? 2 * options->nev : n; + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_eigen_adjacency_arpack_sym_cb, + extra, options, storage, values, vectors)); + + igraph_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_eigen_adjacency + * + */ + +int igraph_eigen_adjacency(const igraph_t *graph, + igraph_eigen_algorithm_t algorithm, + const igraph_eigen_which_t *which, + igraph_arpack_options_t *options, + igraph_arpack_storage_t *storage, + igraph_vector_t *values, + igraph_matrix_t *vectors, + igraph_vector_complex_t *cmplxvalues, + igraph_matrix_complex_t *cmplxvectors) { + + if (which->pos != IGRAPH_EIGEN_LM && + which->pos != IGRAPH_EIGEN_SM && + which->pos != IGRAPH_EIGEN_LA && + which->pos != IGRAPH_EIGEN_SA && + which->pos != IGRAPH_EIGEN_BE && + which->pos != IGRAPH_EIGEN_SELECT && + which->pos != IGRAPH_EIGEN_INTERVAL && + which->pos != IGRAPH_EIGEN_ALL) { + IGRAPH_ERROR("Invalid 'pos' position in 'which'", IGRAPH_EINVAL); + } + + switch (algorithm) { + case IGRAPH_EIGEN_AUTO: + IGRAPH_ERROR("'AUTO' algorithm not implemented yet", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + case IGRAPH_EIGEN_LAPACK: + IGRAPH_ERROR("'LAPACK' algorithm not implemented yet", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + case IGRAPH_EIGEN_ARPACK: + IGRAPH_CHECK(igraph_i_eigen_adjacency_arpack(graph, which, options, + storage, values, vectors, + cmplxvalues, + cmplxvectors)); + break; + case IGRAPH_EIGEN_COMP_AUTO: + IGRAPH_ERROR("'COMP_AUTO' algorithm not implemented yet", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + case IGRAPH_EIGEN_COMP_LAPACK: + IGRAPH_ERROR("'COMP_LAPACK' algorithm not implemented yet", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + case IGRAPH_EIGEN_COMP_ARPACK: + IGRAPH_ERROR("'COMP_ARPACK' algorithm not implemented yet", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + default: + IGRAPH_ERROR("Unknown `algorithm'", IGRAPH_EINVAL); + } + + + return 0; +} + +/** + * \function igraph_eigen_laplacian + * + */ + +int igraph_eigen_laplacian(const igraph_t *graph, + igraph_eigen_algorithm_t algorithm, + const igraph_eigen_which_t *which, + igraph_arpack_options_t *options, + igraph_arpack_storage_t *storage, + igraph_vector_t *values, + igraph_matrix_t *vectors, + igraph_vector_complex_t *cmplxvalues, + igraph_matrix_complex_t *cmplxvectors) { + + IGRAPH_UNUSED(graph); + IGRAPH_UNUSED(algorithm); + IGRAPH_UNUSED(which); + IGRAPH_UNUSED(options); + IGRAPH_UNUSED(storage); + IGRAPH_UNUSED(values); + IGRAPH_UNUSED(vectors); + IGRAPH_UNUSED(cmplxvalues); + IGRAPH_UNUSED(cmplxvectors); + + /* TODO */ + + IGRAPH_ERROR("'igraph_eigen_laplacian'", IGRAPH_UNIMPLEMENTED); +} diff --git a/src/rigraph/core/linalg/lapack.c b/src/rigraph/core/linalg/lapack.c new file mode 100644 index 0000000..7d0890d --- /dev/null +++ b/src/rigraph/core/linalg/lapack.c @@ -0,0 +1,954 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_lapack.h" + +#include "lapack_internal.h" + +/** + * \function igraph_lapack_dgetrf + * \brief LU factorization of a general M-by-N matrix. + * + * The factorization has the form + * A = P * L * U + * where P is a permutation matrix, L is lower triangular with unit + * diagonal elements (lower trapezoidal if m > n), and U is upper + * triangular (upper trapezoidal if m < n). + * \param a The input/output matrix. On entry, the M-by-N matrix to be + * factored. On exit, the factors L and U from the factorization + * A = P * L * U; the unit diagonal elements of L are not + * stored. + * \param ipiv An integer vector, the pivot indices are stored here, + * unless it is a null pointer. Row \c i of the matrix was + * interchanged with row ipiv[i]. + * \param info LAPACK error code. Zero on successful exit. If its value is + * a positive number i, it indicates that U(i,i) is exactly zero. + * The factorization has been + * completed, but the factor U is exactly singular, and division + * by zero will occur if it is used to solve a system of + * equations. If LAPACK returns an error, i.e. a negative info + * value, then an igraph error is generated as well. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, + int *info) { + int m = (int) igraph_matrix_nrow(a); + int n = (int) igraph_matrix_ncol(a); + int lda = m > 0 ? m : 1; + igraph_vector_int_t *myipiv = ipiv, vipiv; + + if (!ipiv) { + IGRAPH_CHECK(igraph_vector_int_init(&vipiv, m < n ? m : n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &vipiv); + myipiv = &vipiv; + } else { + IGRAPH_CHECK(igraph_vector_int_resize(ipiv, m < n ? m : n)); + } + + igraphdgetrf_(&m, &n, VECTOR(a->data), &lda, VECTOR(*myipiv), info); + + if (*info > 0) { + IGRAPH_WARNING("LU: factor is exactly singular."); + } else if (*info < 0) { + switch (*info) { + case -1: + IGRAPH_ERROR("Invalid number of rows.", IGRAPH_ELAPACK); + break; + case -2: + IGRAPH_ERROR("Invalid number of columns.", IGRAPH_ELAPACK); + break; + case -3: + IGRAPH_ERROR("Invalid input matrix.", IGRAPH_ELAPACK); + break; + case -4: + IGRAPH_ERROR("Invalid LDA parameter.", IGRAPH_ELAPACK); + break; + case -5: + IGRAPH_ERROR("Invalid pivot vector.", IGRAPH_ELAPACK); + break; + case -6: + IGRAPH_ERROR("Invalid info argument.", IGRAPH_ELAPACK); + break; + default: + IGRAPH_ERROR("Unknown LAPACK error.", IGRAPH_ELAPACK); + break; + } + } + + if (!ipiv) { + igraph_vector_int_destroy(&vipiv); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_lapack_dgetrs + * \brief Solve general system of linear equations using LU factorization. + * + * This function calls LAPACK to solve a system of linear equations + * A * X = B or A' * X = B + * with a general N-by-N matrix A using the LU factorization + * computed by \ref igraph_lapack_dgetrf. + * \param transpose Logical scalar, whether to transpose the input + * matrix. + * \param a A matrix containing the L and U factors from the + * factorization A = P*L*U. L is expected to be unitriangular, + * diagonal entries are those of U. If A is singular, no warning or + * error wil be given and random output will be returned. + * \param ipiv An integer vector, the pivot indices from \ref + * igraph_lapack_dgetrf() must be given here. Row \c i of A was + * interchanged with row ipiv[i]. + * \param b The right hand side matrix must be given here. The solution + will also be placed here. + * \return Error code. + * + * Time complexity: TODO. + */ + +int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, + const igraph_vector_int_t *ipiv, igraph_matrix_t *b) { + char trans = transpose ? 'T' : 'N'; + int n = (int) igraph_matrix_nrow(a); + int nrhs = (int) igraph_matrix_ncol(b); + int lda = n > 0 ? n : 1; + int ldb = n > 0 ? n : 1; + int info; + + if (n != igraph_matrix_ncol(a)) { + IGRAPH_ERROR("Cannot LU solve matrix.", IGRAPH_NONSQUARE); + } + if (n != igraph_matrix_nrow(b)) { + IGRAPH_ERROR("Cannot LU solve matrix, RHS of wrong size.", IGRAPH_EINVAL); + } + if (igraph_vector_int_size(ipiv) > 0) { + igraph_integer_t min, max; + igraph_vector_int_minmax(ipiv, &min, &max); + if (max > n || min < 1) { + IGRAPH_ERROR("Pivot index out of range.", IGRAPH_EINVAL); + } + } + if (igraph_vector_int_size(ipiv) != n) { + IGRAPH_ERROR("Pivot vector length must match number of matrix rows.", IGRAPH_EINVAL); + } + igraphdgetrs_(&trans, &n, &nrhs, VECTOR(a->data), &lda, VECTOR(*ipiv), + VECTOR(b->data), &ldb, &info); + + if (info < 0) { + switch (info) { + case -1: + IGRAPH_ERROR("Invalid transpose argument.", IGRAPH_ELAPACK); + break; + case -2: + IGRAPH_ERROR("Invalid number of rows/columns.", IGRAPH_ELAPACK); + break; + case -3: + IGRAPH_ERROR("Invalid number of RHS vectors.", IGRAPH_ELAPACK); + break; + case -4: + IGRAPH_ERROR("Invalid LU matrix.", IGRAPH_ELAPACK); + break; + case -5: + IGRAPH_ERROR("Invalid LDA parameter.", IGRAPH_ELAPACK); + break; + case -6: + IGRAPH_ERROR("Invalid pivot vector.", IGRAPH_ELAPACK); + break; + case -7: + IGRAPH_ERROR("Invalid RHS matrix.", IGRAPH_ELAPACK); + break; + case -8: + IGRAPH_ERROR("Invalid LDB parameter.", IGRAPH_ELAPACK); + break; + case -9: + IGRAPH_ERROR("Invalid info argument.", IGRAPH_ELAPACK); + break; + default: + IGRAPH_ERROR("Unknown LAPACK error.", IGRAPH_ELAPACK); + break; + } + } + + return 0; +} + +/** + * \function igraph_lapack_dgesv + * Solve system of linear equations with LU factorization + * + * This function computes the solution to a real system of linear + * equations A * X = B, where A is an N-by-N matrix and X and B are + * N-by-NRHS matrices. + * + * The LU decomposition with partial pivoting and row + * interchanges is used to factor A as + * A = P * L * U, + * where P is a permutation matrix, L is unit lower triangular, and U is + * upper triangular. The factored form of A is then used to solve the + * system of equations A * X = B. + * \param a Matrix. On entry the N-by-N coefficient matrix, on exit, + * the factors L and U from the factorization A=P*L*U; the unit + * diagonal elements of L are not stored. + * \param ipiv An integer vector or a null pointer. If not a null + * pointer, then the pivot indices that define the permutation + * matrix P, are stored here. Row i of the matrix was + * interchanged with row IPIV(i). + * \param b Matrix, on entry the right hand side matrix should be + * stored here. On exit, if there was no error, and the info + * argument is zero, then it contains the solution matrix X. + * \param info The LAPACK info code. If it is positive, then + * U(info,info) is exactly zero. In this case the factorization + * has been completed, but the factor U is exactly + * singular, so the solution could not be computed. + * \return Error code. + * + * Time complexity: TODO. + * + * \example examples/simple/igraph_lapack_dgesv.c + */ + +int igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, + igraph_matrix_t *b, int *info) { + + int n = (int) igraph_matrix_nrow(a); + int nrhs = (int) igraph_matrix_ncol(b); + int lda = n > 0 ? n : 1; + int ldb = n > 0 ? n : 1; + igraph_vector_int_t *myipiv = ipiv, vipiv; + + if (n != igraph_matrix_ncol(a)) { + IGRAPH_ERROR("Cannot LU solve matrix.", IGRAPH_NONSQUARE); + } + if (n != igraph_matrix_nrow(b)) { + IGRAPH_ERROR("Cannot LU solve matrix, RHS of wrong size.", IGRAPH_EINVAL); + } + + if (!ipiv) { + IGRAPH_CHECK(igraph_vector_int_init(&vipiv, n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &vipiv); + myipiv = &vipiv; + } + + igraphdgesv_(&n, &nrhs, VECTOR(a->data), &lda, VECTOR(*myipiv), + VECTOR(b->data), &ldb, info); + + if (*info > 0) { + IGRAPH_WARNING("LU: factor is exactly singular."); + } else if (*info < 0) { + switch (*info) { + case -1: + IGRAPH_ERROR("Invalid number of rows/column.", IGRAPH_ELAPACK); + break; + case -2: + IGRAPH_ERROR("Invalid number of RHS vectors.", IGRAPH_ELAPACK); + break; + case -3: + IGRAPH_ERROR("Invalid input matrix.", IGRAPH_ELAPACK); + break; + case -4: + IGRAPH_ERROR("Invalid LDA parameter.", IGRAPH_ELAPACK); + break; + case -5: + IGRAPH_ERROR("Invalid pivot vector.", IGRAPH_ELAPACK); + break; + case -6: + IGRAPH_ERROR("Invalid RHS matrix.", IGRAPH_ELAPACK); + break; + case -7: + IGRAPH_ERROR("Invalid LDB parameter.", IGRAPH_ELAPACK); + break; + case -8: + IGRAPH_ERROR("Invalid info argument.", IGRAPH_ELAPACK); + break; + default: + IGRAPH_ERROR("Unknown LAPACK error.", IGRAPH_ELAPACK); + break; + } + } + + if (!ipiv) { + igraph_vector_int_destroy(&vipiv); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_lapack_dsyevr + * Selected eigenvalues and optionally eigenvectors of a symmetric matrix + * + * Calls the DSYEVR LAPACK function to compute selected eigenvalues + * and, optionally, eigenvectors of a real symmetric matrix A. + * Eigenvalues and eigenvectors can be selected by specifying either + * a range of values or a range of indices for the desired eigenvalues. + * + * See more in the LAPACK documentation. + * \param A Matrix, on entry it contains the symmetric input + * matrix. Only the leading N-by-N upper triangular part is + * used for the computation. + * \param which Constant that gives which eigenvalues (and possibly + * the corresponding eigenvectors) to calculate. Possible + * values are \c IGRAPH_LAPACK_DSYEV_ALL, all eigenvalues; + * \c IGRAPH_LAPACK_DSYEV_INTERVAL, all eigenvalues in the + * half-open interval (vl,vu]; + * \c IGRAPH_LAPACK_DSYEV_SELECT, the il-th through iu-th + * eigenvalues. + * \param vl If \p which is \c IGRAPH_LAPACK_DSYEV_INTERVAL, then + * this is the lower bound of the interval to be searched for + * eigenvalues. See also the \p vestimate argument. + * \param vu If \p which is \c IGRAPH_LAPACK_DSYEV_INTERVAL, then + * this is the upper bound of the interval to be searched for + * eigenvalues. See also the \p vestimate argument. + * \param vestimate An upper bound for the number of eigenvalues in + * the (vl,vu] interval, if \p which is \c + * IGRAPH_LAPACK_DSYEV_INTERVAL. Memory is allocated only for + * the given number of eigenvalues (and eigenvectors), so this + * upper bound must be correct. + * \param il The index of the smallest eigenvalue to return, if \p + * which is \c IGRAPH_LAPACK_DSYEV_SELECT. + * \param iu The index of the largets eigenvalue to return, if \p + * which is \c IGRAPH_LAPACK_DSYEV_SELECT. + * \param abstol The absolute error tolerance for the eigevalues. An + * approximate eigenvalue is accepted as converged when it is + * determined to lie in an interval [a,b] of width less than or + * equal to abstol + EPS * max(|a|,|b|), where EPS is the + * machine precision. + * \param values An initialized vector, the eigenvalues are stored + * here, unless it is a null pointer. It will be resized as + * needed. + * \param vectors An initialized matrix, the eigenvectors are stored + * in its columns, unless it is a null pointer. It will be + * resized as needed. + * \param support An integer vector. If not a null pointer, then it + * will be resized to (2*max(1,M)) (M is a the total number of + * eigenvalues found). Then the support of the eigenvectors in + * \p vectors is stored here, i.e., the indices + * indicating the nonzero elements in \p vectors. + * The i-th eigenvector is nonzero only in elements + * support(2*i-1) through support(2*i). + * \return Error code. + * + * Time complexity: TODO. + * + * \example examples/simple/igraph_lapack_dsyevr.c + */ + +int igraph_lapack_dsyevr(const igraph_matrix_t *A, + igraph_lapack_dsyev_which_t which, + igraph_real_t vl, igraph_real_t vu, int vestimate, + int il, int iu, igraph_real_t abstol, + igraph_vector_t *values, igraph_matrix_t *vectors, + igraph_vector_int_t *support) { + + igraph_matrix_t Acopy; + char jobz = vectors ? 'V' : 'N', range, uplo = 'U'; + int n = (int) igraph_matrix_nrow(A), lda = n, ldz = n; + int m, info; + igraph_vector_t *myvalues = values, vvalues; + igraph_vector_int_t *mysupport = support, vsupport; + igraph_vector_t work; + igraph_vector_int_t iwork; + int lwork = -1, liwork = -1; + + if (n != igraph_matrix_ncol(A)) { + IGRAPH_ERROR("Cannot find eigenvalues/vectors.", IGRAPH_NONSQUARE); + } + if (which == IGRAPH_LAPACK_DSYEV_INTERVAL && + (vestimate < 1 || vestimate > n)) { + IGRAPH_ERROR("Estimated (upper bound) number of eigenvalues must be " + "between 1 and n.", IGRAPH_EINVAL); + } + if (which == IGRAPH_LAPACK_DSYEV_SELECT && iu - il < 0) { + IGRAPH_ERROR("Invalid 'il' and/or 'iu' values.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); + IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); + + IGRAPH_VECTOR_INIT_FINALLY(&work, 1); + IGRAPH_CHECK(igraph_vector_int_init(&iwork, 1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &iwork); + + if (!values) { + IGRAPH_VECTOR_INIT_FINALLY(&vvalues, 0); + myvalues = &vvalues; + } + if (!support) { + IGRAPH_CHECK(igraph_vector_int_init(&vsupport, 0)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &vsupport); + mysupport = &vsupport; + } + + IGRAPH_CHECK(igraph_vector_resize(myvalues, n)); + + switch (which) { + case IGRAPH_LAPACK_DSYEV_ALL: + range = 'A'; + IGRAPH_CHECK(igraph_vector_int_resize(mysupport, 2 * n)); + if (vectors) { + IGRAPH_CHECK(igraph_matrix_resize(vectors, n, n)); + } + break; + case IGRAPH_LAPACK_DSYEV_INTERVAL: + range = 'V'; + IGRAPH_CHECK(igraph_vector_int_resize(mysupport, 2 * vestimate)); + if (vectors) { + IGRAPH_CHECK(igraph_matrix_resize(vectors, n, vestimate)); + } + break; + case IGRAPH_LAPACK_DSYEV_SELECT: + range = 'I'; + IGRAPH_CHECK(igraph_vector_int_resize(mysupport, 2 * (iu - il + 1))); + if (vectors) { + IGRAPH_CHECK(igraph_matrix_resize(vectors, n, iu - il + 1)); + } + break; + } + + igraphdsyevr_(&jobz, &range, &uplo, &n, &MATRIX(Acopy, 0, 0), &lda, + &vl, &vu, &il, &iu, &abstol, &m, VECTOR(*myvalues), + vectors ? &MATRIX(*vectors, 0, 0) : 0, &ldz, VECTOR(*mysupport), + VECTOR(work), &lwork, VECTOR(iwork), &liwork, &info); + + if (info != 0) { + IGRAPH_ERROR("Invalid argument to dsyevr in workspace query.", IGRAPH_EINVAL); + } + + lwork = (int) VECTOR(work)[0]; + liwork = VECTOR(iwork)[0]; + IGRAPH_CHECK(igraph_vector_resize(&work, lwork)); + IGRAPH_CHECK(igraph_vector_int_resize(&iwork, liwork)); + + igraphdsyevr_(&jobz, &range, &uplo, &n, &MATRIX(Acopy, 0, 0), &lda, + &vl, &vu, &il, &iu, &abstol, &m, VECTOR(*myvalues), + vectors ? &MATRIX(*vectors, 0, 0) : 0, &ldz, VECTOR(*mysupport), + VECTOR(work), &lwork, VECTOR(iwork), &liwork, &info); + + if (info != 0) { + IGRAPH_ERROR("Invalid argument to dsyevr in calculation.", IGRAPH_EINVAL); + } + + if (values) { + IGRAPH_CHECK(igraph_vector_resize(values, m)); + } + if (vectors) { + IGRAPH_CHECK(igraph_matrix_resize(vectors, n, m)); + } + if (support) { + IGRAPH_CHECK(igraph_vector_int_resize(support, m)); + } + + if (!support) { + igraph_vector_int_destroy(&vsupport); + IGRAPH_FINALLY_CLEAN(1); + } + if (!values) { + igraph_vector_destroy(&vvalues); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_destroy(&iwork); + igraph_vector_destroy(&work); + igraph_matrix_destroy(&Acopy); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \function igraph_lapack_dgeev + * Eigenvalues and optionally eigenvectors of a non-symmetric matrix + * + * This function calls LAPACK to compute, for an N-by-N real + * nonsymmetric matrix A, the eigenvalues and, optionally, the left + * and/or right eigenvectors. + * + * + * The right eigenvector v(j) of A satisfies + * A * v(j) = lambda(j) * v(j) + * where lambda(j) is its eigenvalue. + * The left eigenvector u(j) of A satisfies + * u(j)**H * A = lambda(j) * u(j)**H + * where u(j)**H denotes the conjugate transpose of u(j). + * + * + * The computed eigenvectors are normalized to have Euclidean norm + * equal to 1 and largest component real. + * + * \param A matrix. On entry it contains the N-by-N input matrix. + * \param valuesreal Pointer to an initialized vector, or a null + * pointer. If not a null pointer, then the real parts of the + * eigenvalues are stored here. The vector will be resized as + * needed. + * \param valuesimag Pointer to an initialized vector, or a null + * pointer. If not a null pointer, then the imaginary parts of + * the eigenvalues are stored here. The vector will be resized + * as needed. + * \param vectorsleft Pointer to an initialized matrix, or a null + * pointer. If not a null pointer, then the left eigenvectors + * are stored in the columns of the matrix. The matrix will be + * resized as needed. + * \param vectorsright Pointer to an initialized matrix, or a null + * pointer. If not a null pointer, then the right eigenvectors + * are stored in the columns of the matrix. The matrix will be + * resized as needed. + * \param info This argument is used for two purposes. As an input + * argument it gives whether an igraph error should be + * generated if the QR algorithm fails to compute all + * eigenvalues. If \p info is non-zero, then an error is + * generated, otherwise only a warning is given. + * On exit it contains the LAPACK error code. + * Zero means successful exit. + * A negative values means that some of the arguments had an + * illegal value, this always triggers an igraph error. An i + * positive value means that the QR algorithm failed to + * compute all the eigenvalues, and no eigenvectors have been + * computed; element i+1:N of \p valuesreal and \p valuesimag + * contain eigenvalues which have converged. This case only + * generates an igraph error, if \p info was non-zero on entry. + * \return Error code. + * + * Time complexity: TODO. + * + * \example examples/simple/igraph_lapack_dgeev.c + */ + +int igraph_lapack_dgeev(const igraph_matrix_t *A, + igraph_vector_t *valuesreal, + igraph_vector_t *valuesimag, + igraph_matrix_t *vectorsleft, + igraph_matrix_t *vectorsright, + int *info) { + + char jobvl = vectorsleft ? 'V' : 'N'; + char jobvr = vectorsright ? 'V' : 'N'; + int n = (int) igraph_matrix_nrow(A); + int lda = n, ldvl = n, ldvr = n, lwork = -1; + igraph_vector_t work; + igraph_vector_t *myreal = valuesreal, *myimag = valuesimag, vreal, vimag; + igraph_matrix_t Acopy; + int error = *info; + + if (igraph_matrix_ncol(A) != n) { + IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev).", IGRAPH_NONSQUARE); + } + + IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); + IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); + + IGRAPH_VECTOR_INIT_FINALLY(&work, 1); + + if (!valuesreal) { + IGRAPH_VECTOR_INIT_FINALLY(&vreal, n); + myreal = &vreal; + } else { + IGRAPH_CHECK(igraph_vector_resize(myreal, n)); + } + if (!valuesimag) { + IGRAPH_VECTOR_INIT_FINALLY(&vimag, n); + myimag = &vimag; + } else { + IGRAPH_CHECK(igraph_vector_resize(myimag, n)); + } + if (vectorsleft) { + IGRAPH_CHECK(igraph_matrix_resize(vectorsleft, n, n)); + } + if (vectorsright) { + IGRAPH_CHECK(igraph_matrix_resize(vectorsright, n, n)); + } + + igraphdgeev_(&jobvl, &jobvr, &n, &MATRIX(Acopy, 0, 0), &lda, + VECTOR(*myreal), VECTOR(*myimag), + vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : 0, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : 0, &ldvr, + VECTOR(work), &lwork, info); + + lwork = (int) VECTOR(work)[0]; + IGRAPH_CHECK(igraph_vector_resize(&work, lwork)); + + igraphdgeev_(&jobvl, &jobvr, &n, &MATRIX(Acopy, 0, 0), &lda, + VECTOR(*myreal), VECTOR(*myimag), + vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : 0, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : 0, &ldvr, + VECTOR(work), &lwork, info); + + if (*info < 0) { + IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev).", IGRAPH_ELAPACK); + } else if (*info > 0) { + if (error) { + IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev).", IGRAPH_ELAPACK); + } else { + IGRAPH_WARNING("Cannot calculate eigenvalues (dgeev)."); + } + } + + if (!valuesimag) { + igraph_vector_destroy(&vimag); + IGRAPH_FINALLY_CLEAN(1); + } + if (!valuesreal) { + igraph_vector_destroy(&vreal); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_destroy(&work); + igraph_matrix_destroy(&Acopy); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \function igraph_lapack_dgeevx + * Eigenvalues/vectors of nonsymmetric matrices, expert mode + * + * This function calculates the eigenvalues and optionally the left + * and/or right eigenvectors of a nonsymmetric N-by-N real matrix. + * + * + * Optionally also, it computes a balancing transformation to improve + * the conditioning of the eigenvalues and eigenvectors (\p ilo, \p ihi, + * \p scale, and \p abnrm), reciprocal condition numbers for the + * eigenvalues (\p rconde), and reciprocal condition numbers for the + * right eigenvectors (\p rcondv). + * + * + * The right eigenvector v(j) of A satisfies + * A * v(j) = lambda(j) * v(j) + * where lambda(j) is its eigenvalue. + * The left eigenvector u(j) of A satisfies + * u(j)^H * A = lambda(j) * u(j)^H + * where u(j)^H denotes the conjugate transpose of u(j). + * + * + * The computed eigenvectors are normalized to have Euclidean norm + * equal to 1 and largest component real. + * + * + * Balancing a matrix means permuting the rows and columns to make it + * more nearly upper triangular, and applying a diagonal similarity + * transformation D * A * D^(-1), where D is a diagonal matrix, to + * make its rows and columns closer in norm and the condition numbers + * of its eigenvalues and eigenvectors smaller. The computed + * reciprocal condition numbers correspond to the balanced matrix. + * Permuting rows and columns will not change the condition numbers + * (in exact arithmetic) but diagonal scaling will. For further + * explanation of balancing, see section 4.10.2 of the LAPACK + * Users' Guide. + * + * \param balance Scalar that indicated, whether the input matrix + * should be balanced. Possible values: + * \clist + * \cli IGRAPH_LAPACK_DGEEVX_BALANCE_NONE + * no not diagonally scale or permute. + * \cli IGRAPH_LAPACK_DGEEVX_BALANCE_PERM + * perform permutations to make the matrix more nearly upper + * triangular. Do not diagonally scale. + * \cli IGRAPH_LAPACK_DGEEVX_BALANCE_SCALE + * diagonally scale the matrix, i.e. replace A by + * D*A*D^(-1), where D is a diagonal matrix, chosen to make + * the rows and columns of A more equal in norm. Do not + * permute. + * \cli IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH + * both diagonally scale and permute A. + * \endclist + * \param A The input matrix, must be square. + * \param valuesreal An initialized vector, or a NULL pointer. If not + * a NULL pointer, then the real parts of the eigenvalues are stored + * here. The vector will be resized, as needed. + * \param valuesimag An initialized vector, or a NULL pointer. If not + * a NULL pointer, then the imaginary parts of the eigenvalues are stored + * here. The vector will be resized, as needed. + * \param vectorsleft An initialized matrix or a NULL pointer. If not + * a null pointer, then the left eigenvectors are stored here. The + * order corresponds to the eigenvalues and the eigenvectors are + * stored in a compressed form. If the j-th eigenvalue is real then + * column j contains the corresponding eigenvector. If the j-th and + * (j+1)-th eigenvalues form a complex conjugate pair, then the j-th + * and (j+1)-th columns contain their corresponding eigenvectors. + * \param vectorsright An initialized matrix or a NULL pointer. If not + * a null pointer, then the right eigenvectors are stored here. The + * format is the same, as for the \p vectorsleft argument. + * \param ilo + * \param ihi \p ilo and \p ihi are integer values determined when A was + * balanced. The balanced A(i,j) = 0 if I>J and + * J=1,...,ilo-1 or I=ihi+1,...,N. + * \param scale Pointer to an initialized vector or a NULL pointer. If + * not a NULL pointer, then details of the permutations and scaling + * factors applied when balancing \p A, are stored here. + * If P(j) is the index of the row and column + * interchanged with row and column j, and D(j) is the scaling + * factor applied to row and column j, then + * \clist + * \cli scale(J) = P(J), for J = 1,...,ilo-1 + * \cli scale(J) = D(J), for J = ilo,...,ihi + * \cli scale(J) = P(J) for J = ihi+1,...,N. + * \endclist + * The order in which the interchanges are made is N to \p ihi+1, + * then 1 to \p ilo-1. + * \param abnrm Pointer to a real variable, the one-norm of the + * balanced matrix is stored here. (The one-norm is the maximum of + * the sum of absolute values of elements in any column.) + * \param rconde An initialized vector or a NULL pointer. If not a + * null pointer, then the reciprocal condition numbers of the + * eigenvalues are stored here. + * \param rcondv An initialized vector or a NULL pointer. If not a + * null pointer, then the reciprocal condition numbers of the right + * eigenvectors are stored here. + * \param info This argument is used for two purposes. As an input + * argument it gives whether an igraph error should be + * generated if the QR algorithm fails to compute all + * eigenvalues. If \p info is non-zero, then an error is + * generated, otherwise only a warning is given. + * On exit it contains the LAPACK error code. + * Zero means successful exit. + * A negative values means that some of the arguments had an + * illegal value, this always triggers an igraph error. An i + * positive value means that the QR algorithm failed to + * compute all the eigenvalues, and no eigenvectors have been + * computed; element i+1:N of \p valuesreal and \p valuesimag + * contain eigenvalues which have converged. This case only + * generated an igraph error, if \p info was non-zero on entry. + * \return Error code. + * + * Time complexity: TODO + * + * \example examples/simple/igraph_lapack_dgeevx.c + */ + +int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, + const igraph_matrix_t *A, + igraph_vector_t *valuesreal, + igraph_vector_t *valuesimag, + igraph_matrix_t *vectorsleft, + igraph_matrix_t *vectorsright, + int *ilo, int *ihi, igraph_vector_t *scale, + igraph_real_t *abnrm, + igraph_vector_t *rconde, + igraph_vector_t *rcondv, + int *info) { + + char balanc; + char jobvl = vectorsleft ? 'V' : 'N'; + char jobvr = vectorsright ? 'V' : 'N'; + char sense; + int n = (int) igraph_matrix_nrow(A); + int lda = n, ldvl = n, ldvr = n, lwork = -1; + igraph_vector_t work; + igraph_vector_int_t iwork; + igraph_matrix_t Acopy; + int error = *info; + igraph_vector_t *myreal = valuesreal, *myimag = valuesimag, vreal, vimag; + igraph_vector_t *myscale = scale, vscale; + + if (igraph_matrix_ncol(A) != n) { + IGRAPH_ERROR("Cannot calculate eigenvalues (dgeevx).", IGRAPH_NONSQUARE); + } + + switch (balance) { + case IGRAPH_LAPACK_DGEEVX_BALANCE_NONE: + balanc = 'N'; + break; + case IGRAPH_LAPACK_DGEEVX_BALANCE_PERM: + balanc = 'P'; + break; + case IGRAPH_LAPACK_DGEEVX_BALANCE_SCALE: + balanc = 'S'; + break; + case IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH: + balanc = 'B'; + break; + default: + IGRAPH_ERROR("Invalid 'balance' argument.", IGRAPH_EINVAL); + break; + } + + if (!rconde && !rcondv) { + sense = 'N'; + } else if (rconde && !rcondv) { + sense = 'E'; + } else if (!rconde && rcondv) { + sense = 'V'; + } else { + sense = 'B'; + } + + IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); + IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); + + IGRAPH_VECTOR_INIT_FINALLY(&work, 1); + IGRAPH_CHECK(igraph_vector_int_init(&iwork, n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &iwork); + + if (!valuesreal) { + IGRAPH_VECTOR_INIT_FINALLY(&vreal, n); + myreal = &vreal; + } else { + IGRAPH_CHECK(igraph_vector_resize(myreal, n)); + } + if (!valuesimag) { + IGRAPH_VECTOR_INIT_FINALLY(&vimag, n); + myimag = &vimag; + } else { + IGRAPH_CHECK(igraph_vector_resize(myimag, n)); + } + if (!scale) { + IGRAPH_VECTOR_INIT_FINALLY(&vscale, n); + myscale = &vscale; + } else { + IGRAPH_CHECK(igraph_vector_resize(scale, n)); + } + if (vectorsleft) { + IGRAPH_CHECK(igraph_matrix_resize(vectorsleft, n, n)); + } + if (vectorsright) { + IGRAPH_CHECK(igraph_matrix_resize(vectorsright, n, n)); + } + + igraphdgeevx_(&balanc, &jobvl, &jobvr, &sense, &n, &MATRIX(Acopy, 0, 0), + &lda, VECTOR(*myreal), VECTOR(*myimag), + vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : 0, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : 0, &ldvr, + ilo, ihi, VECTOR(*myscale), abnrm, + rconde ? VECTOR(*rconde) : 0, + rcondv ? VECTOR(*rcondv) : 0, + VECTOR(work), &lwork, VECTOR(iwork), info); + + lwork = (int) VECTOR(work)[0]; + IGRAPH_CHECK(igraph_vector_resize(&work, lwork)); + + igraphdgeevx_(&balanc, &jobvl, &jobvr, &sense, &n, &MATRIX(Acopy, 0, 0), + &lda, VECTOR(*myreal), VECTOR(*myimag), + vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : 0, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : 0, &ldvr, + ilo, ihi, VECTOR(*myscale), abnrm, + rconde ? VECTOR(*rconde) : 0, + rcondv ? VECTOR(*rcondv) : 0, + VECTOR(work), &lwork, VECTOR(iwork), info); + + if (*info < 0) { + IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev).", IGRAPH_ELAPACK); + } else if (*info > 0) { + if (error) { + IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev).", IGRAPH_ELAPACK); + } else { + IGRAPH_WARNING("Cannot calculate eigenvalues (dgeev)."); + } + } + + if (!scale) { + igraph_vector_destroy(&vscale); + IGRAPH_FINALLY_CLEAN(1); + } + + if (!valuesimag) { + igraph_vector_destroy(&vimag); + IGRAPH_FINALLY_CLEAN(1); + } + + if (!valuesreal) { + igraph_vector_destroy(&vreal); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_destroy(&iwork); + igraph_vector_destroy(&work); + igraph_matrix_destroy(&Acopy); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +int igraph_lapack_dgehrd(const igraph_matrix_t *A, + int ilo, int ihi, + igraph_matrix_t *result) { + + int n = (int) igraph_matrix_nrow(A); + int lda = n; + int lwork = -1; + igraph_vector_t work; + igraph_real_t optwork; + igraph_vector_t tau; + igraph_matrix_t Acopy; + int info = 0; + int i; + + if (igraph_matrix_ncol(A) != n) { + IGRAPH_ERROR("Hessenberg reduction failed.", IGRAPH_NONSQUARE); + } + + if (ilo < 1 || ihi > n || ilo > ihi) { + IGRAPH_ERROR("Invalid `ilo' and/or `ihi'.", IGRAPH_EINVAL); + } + + if (n <= 1) { + IGRAPH_CHECK(igraph_matrix_update(result, A)); + return 0; + } + + IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); + IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); + IGRAPH_VECTOR_INIT_FINALLY(&tau, n - 1); + + igraphdgehrd_(&n, &ilo, &ihi, &MATRIX(Acopy, 0, 0), &lda, VECTOR(tau), + &optwork, &lwork, &info); + + if (info != 0) { + IGRAPH_ERROR("Internal Hessenberg transformation error.", + IGRAPH_EINTERNAL); + } + + lwork = (int) optwork; + IGRAPH_VECTOR_INIT_FINALLY(&work, lwork); + + igraphdgehrd_(&n, &ilo, &ihi, &MATRIX(Acopy, 0, 0), &lda, VECTOR(tau), + VECTOR(work), &lwork, &info); + + if (info != 0) { + IGRAPH_ERROR("Internal Hessenberg transformation error.", + IGRAPH_EINTERNAL); + } + + igraph_vector_destroy(&work); + igraph_vector_destroy(&tau); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_matrix_update(result, &Acopy)); + + igraph_matrix_destroy(&Acopy); + IGRAPH_FINALLY_CLEAN(1); + + for (i = 0; i < n - 2; i++) { + int j; + for (j = i + 2; j < n; j++) { + MATRIX(*result, j, i) = 0.0; + } + } + + return 0; +} diff --git a/src/rigraph/core/linalg/lapack_internal.h b/src/rigraph/core/linalg/lapack_internal.h new file mode 100644 index 0000000..0ab117e --- /dev/null +++ b/src/rigraph/core/linalg/lapack_internal.h @@ -0,0 +1,183 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef LAPACK_INTERNAL_H +#define LAPACK_INTERNAL_H + +/* Note: only files calling the LAPACK routines directly need to + include this header. +*/ + +#include "igraph_types.h" +#include "config.h" + +#ifndef INTERNAL_LAPACK + #define igraphdgeevx_ dgeevx_ + #define igraphdgeev_ dgeev_ + #define igraphdgebak_ dgebak_ + #define igraphxerbla_ xerbla_ + #define igraphdgebal_ dgebal_ + #define igraphdisnan_ disnan_ + #define igraphdlaisnan_ dlaisnan_ + #define igraphdgehrd_ dgehrd_ + #define igraphdgehd2_ dgehd2_ + #define igraphdlarf_ dlarf_ + #define igraphiladlc_ iladlc_ + #define igraphiladlr_ iladlr_ + #define igraphdlarfg_ dlarfg_ + #define igraphdlapy2_ dlapy2_ + #define igraphdlahr2_ dlahr2_ + #define igraphdlacpy_ dlacpy_ + #define igraphdlarfb_ dlarfb_ + #define igraphilaenv_ ilaenv_ + #define igraphieeeck_ ieeeck_ + #define igraphiparmq_ iparmq_ + #define igraphdhseqr_ dhseqr_ + #define igraphdlahqr_ dlahqr_ + #define igraphdlabad_ dlabad_ + #define igraphdlanv2_ dlanv2_ + #define igraphdlaqr0_ dlaqr0_ + #define igraphdlaqr3_ dlaqr3_ + #define igraphdlaqr4_ dlaqr4_ + #define igraphdlaqr2_ dlaqr2_ + #define igraphdlaset_ dlaset_ + #define igraphdormhr_ dormhr_ + #define igraphdormqr_ dormqr_ + #define igraphdlarft_ dlarft_ + #define igraphdorm2r_ dorm2r_ + #define igraphdtrexc_ dtrexc_ + #define igraphdlaexc_ dlaexc_ + #define igraphdlange_ dlange_ + #define igraphdlassq_ dlassq_ + #define igraphdlarfx_ dlarfx_ + #define igraphdlartg_ dlartg_ + #define igraphdlasy2_ dlasy2_ + #define igraphdlaqr5_ dlaqr5_ + #define igraphdlaqr1_ dlaqr1_ + #define igraphdlascl_ dlascl_ + #define igraphdorghr_ dorghr_ + #define igraphdorgqr_ dorgqr_ + #define igraphdorg2r_ dorg2r_ + #define igraphdtrevc_ dtrevc_ + #define igraphdlaln2_ dlaln2_ + #define igraphdladiv_ dladiv_ + #define igraphdsyevr_ dsyevr_ + #define igraphdsyrk_ dsyrk_ + #define igraphdlansy_ dlansy_ + #define igraphdormtr_ dormtr_ + #define igraphdormql_ dormql_ + #define igraphdorm2l_ dorm2l_ + #define igraphdstebz_ dstebz_ + #define igraphdlaebz_ dlaebz_ + #define igraphdstein_ dstein_ + #define igraphdlagtf_ dlagtf_ + #define igraphdlagts_ dlagts_ + #define igraphdlarnv_ dlarnv_ + #define igraphdlaruv_ dlaruv_ + #define igraphdstemr_ dstemr_ + #define igraphdlae2_ dlae2_ + #define igraphdlaev2_ dlaev2_ + #define igraphdlanst_ dlanst_ + #define igraphdlarrc_ dlarrc_ + #define igraphdlarre_ dlarre_ + #define igraphdlarra_ dlarra_ + #define igraphdlarrb_ dlarrb_ + #define igraphdlaneg_ dlaneg_ + #define igraphdlarrd_ dlarrd_ + #define igraphdlarrk_ dlarrk_ + #define igraphdlasq2_ dlasq2_ + #define igraphdlasq3_ dlasq3_ + #define igraphdlasq4_ dlasq4_ + #define igraphdlasq5_ dlasq5_ + #define igraphdlasq6_ dlasq6_ + #define igraphdlasrt_ dlasrt_ + #define igraphdlarrj_ dlarrj_ + #define igraphdlarrr_ dlarrr_ + #define igraphdlarrv_ dlarrv_ + #define igraphdlar1v_ dlar1v_ + #define igraphdlarrf_ dlarrf_ + #define igraphdpotrf_ dpotrf_ + #define igraphdsterf_ dsterf_ + #define igraphdsytrd_ dsytrd_ + #define igraphdlatrd_ dlatrd_ + #define igraphdsytd2_ dsytd2_ + #define igraphdlanhs_ dlanhs_ + #define igraphdgeqr2_ dgeqr2_ + #define igraphdtrsen_ dtrsen_ + #define igraphdlacn2_ dlacn2_ + #define igraphdtrsyl_ dtrsyl_ + #define igraphdlasr_ dlasr_ + #define igraphdsteqr_ dsteqr_ + #define igraphdgesv_ dgesv_ + #define igraphdgetrf_ dgetrf_ + #define igraphdgetf2_ dgetf2_ + #define igraphdlaswp_ dlaswp_ + #define igraphdgetrs_ dgetrs_ + #define igraphlen_trim_ len_trim_ + #define igraph_dlamc1_ dlamc1_ + #define igraph_dlamc2_ dlamc2_ + #define igraph_dlamc3_ dlamc3_ + #define igraph_dlamc4_ dlamc4_ + #define igraph_dlamc5_ dlamc5_ +#endif + +int igraphdgetrf_(int *m, int *n, igraph_real_t *a, int *lda, int *ipiv, + int *info); +int igraphdgetrs_(char *trans, int *n, int *nrhs, igraph_real_t *a, + int *lda, int *ipiv, igraph_real_t *b, int *ldb, + int *info); +int igraphdgesv_(int *n, int *nrhs, igraph_real_t *a, int *lda, + int *ipiv, igraph_real_t *b, int *ldb, int *info); + +igraph_real_t igraphdlapy2_(igraph_real_t *x, igraph_real_t *y); + +int igraphdsyevr_(char *jobz, char *range, char *uplo, int *n, + igraph_real_t *a, int *lda, igraph_real_t *vl, + igraph_real_t *vu, int * il, int *iu, + igraph_real_t *abstol, int *m, igraph_real_t *w, + igraph_real_t *z, int *ldz, int *isuppz, + igraph_real_t *work, int *lwork, int *iwork, + int *liwork, int *info); + +int igraphdgeev_(char *jobvl, char *jobvr, int *n, igraph_real_t *a, + int *lda, igraph_real_t *wr, igraph_real_t *wi, + igraph_real_t *vl, int *ldvl, igraph_real_t *vr, int *ldvr, + igraph_real_t *work, int *lwork, int *info); + +int igraphdgeevx_(char *balanc, char *jobvl, char *jobvr, char *sense, + int *n, igraph_real_t *a, int *lda, igraph_real_t *wr, + igraph_real_t *wi, igraph_real_t *vl, int *ldvl, + igraph_real_t *vr, int *ldvr, int *ilo, int *ihi, + igraph_real_t *scale, igraph_real_t *abnrm, + igraph_real_t *rconde, igraph_real_t *rcondv, + igraph_real_t *work, int *lwork, int *iwork, int *info); + +int igraphdgehrd_(int *n, int *ilo, int *ihi, igraph_real_t *A, int *lda, + igraph_real_t *tau, igraph_real_t *work, int *lwork, + int *info); + +igraph_real_t igraphddot_(int *n, igraph_real_t *dx, int *incx, + igraph_real_t *dy, int *incy); + +#endif diff --git a/src/rigraph/core/math/bfgs.c b/src/rigraph/core/math/bfgs.c new file mode 100644 index 0000000..09631db --- /dev/null +++ b/src/rigraph/core/math/bfgs.c @@ -0,0 +1,221 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_nongraph.h" +#include "../core/interruption.h" +#include "igraph_statusbar.h" + +#include + +/* This is from GNU R's optim.c, slightly adapted to igraph */ + +#define stepredn 0.2 +#define acctol 0.0001 +#define reltest 10.0 +#define FALSE 0 +#define TRUE 1 + +/* BFGS variable-metric method, based on Pascal code +in J.C. Nash, `Compact Numerical Methods for Computers', 2nd edition, +converted by p2c then re-crafted by B.D. Ripley */ + +int +igraph_bfgs(igraph_vector_t *b, igraph_real_t *Fmin, + igraph_scalar_function_t fminfn, igraph_vector_function_t fmingr, + int maxit, int trace, + igraph_real_t abstol, igraph_real_t reltol, int nREPORT, void *ex, + igraph_integer_t *fncount, igraph_integer_t *grcount) { + int n = (int) igraph_vector_size(b); + igraph_bool_t accpoint, enough; + igraph_vector_t g, t, X, c; + igraph_matrix_t B; /* Lmatrix really */ + int count, funcount, gradcount; + igraph_real_t f, gradproj; + int i, j, ilast, iter = 0; + igraph_real_t s, steplength; + igraph_real_t D1, D2; + + if (maxit <= 0) { + *Fmin = fminfn(b, 0, ex); + *fncount = 1; + *grcount = 0; + return 0; + } + + if (nREPORT <= 0) { + IGRAPH_ERROR("REPORT must be > 0 (method = \"BFGS\")", IGRAPH_EINVAL); + } + IGRAPH_VECTOR_INIT_FINALLY(&g, n); + IGRAPH_VECTOR_INIT_FINALLY(&t, n); + IGRAPH_VECTOR_INIT_FINALLY(&X, n); + IGRAPH_VECTOR_INIT_FINALLY(&c, n); + IGRAPH_MATRIX_INIT_FINALLY(&B, n, n); + f = fminfn(b, 0, ex); + if (!IGRAPH_FINITE(f)) { + IGRAPH_ERROR("initial value in 'BFGS' is not finite", IGRAPH_DIVERGED); + } + if (trace) { + igraph_statusf("initial value %f ", 0, f); + } + *Fmin = f; + funcount = gradcount = 1; + fmingr(b, 0, &g, ex); + iter++; + ilast = gradcount; + + do { + + IGRAPH_ALLOW_INTERRUPTION(); + + if (ilast == gradcount) { + for (i = 0; i < n; i++) { + for (j = 0; j < i; j++) { + MATRIX(B, i, j) = 0.0; + } + MATRIX(B, i, i) = 1.0; + } + } + for (i = 0; i < n; i++) { + VECTOR(X)[i] = VECTOR(*b)[i]; + VECTOR(c)[i] = VECTOR(g)[i]; + } + gradproj = 0.0; + for (i = 0; i < n; i++) { + s = 0.0; + for (j = 0; j <= i; j++) { + s -= MATRIX(B, i, j) * VECTOR(g)[j]; + } + for (j = i + 1; j < n; j++) { + s -= MATRIX(B, j, i) * VECTOR(g)[j]; + } + VECTOR(t)[i] = s; + gradproj += s * VECTOR(g)[i]; + } + + if (gradproj < 0.0) { /* search direction is downhill */ + steplength = 1.0; + accpoint = FALSE; + do { + count = 0; + for (i = 0; i < n; i++) { + VECTOR(*b)[i] = VECTOR(X)[i] + steplength * VECTOR(t)[i]; + if (reltest + VECTOR(X)[i] == reltest + VECTOR(*b)[i]) { /* no change */ + count++; + } + } + if (count < n) { + f = fminfn(b, 0, ex); + funcount++; + accpoint = IGRAPH_FINITE(f) && + (f <= *Fmin + gradproj * steplength * acctol); + if (!accpoint) { + steplength *= stepredn; + } + } + } while (!(count == n || accpoint)); + enough = (f > abstol) && + fabs(f - *Fmin) > reltol * (fabs(*Fmin) + reltol); + /* stop if value if small or if relative change is low */ + if (!enough) { + count = n; + *Fmin = f; + } + if (count < n) {/* making progress */ + *Fmin = f; + fmingr(b, 0, &g, ex); + gradcount++; + iter++; + D1 = 0.0; + for (i = 0; i < n; i++) { + VECTOR(t)[i] = steplength * VECTOR(t)[i]; + VECTOR(c)[i] = VECTOR(g)[i] - VECTOR(c)[i]; + D1 += VECTOR(t)[i] * VECTOR(c)[i]; + } + if (D1 > 0) { + D2 = 0.0; + for (i = 0; i < n; i++) { + s = 0.0; + for (j = 0; j <= i; j++) { + s += MATRIX(B, i, j) * VECTOR(c)[j]; + } + for (j = i + 1; j < n; j++) { + s += MATRIX(B, j, i) * VECTOR(c)[j]; + } + VECTOR(X)[i] = s; + D2 += s * VECTOR(c)[i]; + } + D2 = 1.0 + D2 / D1; + for (i = 0; i < n; i++) { + for (j = 0; j <= i; j++) + MATRIX(B, i, j) += (D2 * VECTOR(t)[i] * VECTOR(t)[j] + - VECTOR(X)[i] * VECTOR(t)[j] + - VECTOR(t)[i] * VECTOR(X)[j]) / D1; + } + } else { /* D1 < 0 */ + ilast = gradcount; + } + } else { /* no progress */ + if (ilast < gradcount) { + count = 0; + ilast = gradcount; + } + } + } else { /* uphill search */ + count = 0; + if (ilast == gradcount) { + count = n; + } else { + ilast = gradcount; + } + /* Resets unless has just been reset */ + } + if (trace && (iter % nREPORT == 0)) { + igraph_statusf("iter%4d value %f", 0, iter, f); + } + if (iter >= maxit) { + break; + } + if (gradcount - ilast > 2 * n) { + ilast = gradcount; /* periodic restart */ + } + } while (count != n || ilast != gradcount); + if (trace) { + igraph_statusf("final value %f ", 0, *Fmin); + if (iter < maxit) { + igraph_status("converged", 0); + } else { + igraph_statusf("stopped after %i iterations", 0, iter); + } + } + *fncount = funcount; + *grcount = gradcount; + + igraph_matrix_destroy(&B); + igraph_vector_destroy(&c); + igraph_vector_destroy(&X); + igraph_vector_destroy(&t); + igraph_vector_destroy(&g); + IGRAPH_FINALLY_CLEAN(5); + + return (iter < maxit) ? 0 : IGRAPH_DIVERGED; +} diff --git a/src/rigraph/core/math/complex.c b/src/rigraph/core/math/complex.c new file mode 100644 index 0000000..b397da9 --- /dev/null +++ b/src/rigraph/core/math/complex.c @@ -0,0 +1,391 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_complex.h" +#include "../core/math.h" +#include + +/** + * \example igraph_complex.c + */ + +igraph_complex_t igraph_complex(igraph_real_t x, igraph_real_t y) { + igraph_complex_t res; + IGRAPH_REAL(res) = x; + IGRAPH_IMAG(res) = y; + return res; +} + +igraph_complex_t igraph_complex_polar(igraph_real_t r, igraph_real_t theta) { + igraph_complex_t res; + IGRAPH_REAL(res) = r * cos(theta); + IGRAPH_IMAG(res) = r * sin(theta); + return res; +} + +igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, + igraph_complex_t z2, + igraph_real_t tol) { + if (fabs(IGRAPH_REAL(z1) - IGRAPH_REAL(z2)) > tol || + fabs(IGRAPH_IMAG(z1) - IGRAPH_IMAG(z2)) > tol) { + return 0; + } + return 1; +} + +igraph_real_t igraph_complex_mod(igraph_complex_t z) { + igraph_real_t x = IGRAPH_REAL(z); + igraph_real_t y = IGRAPH_IMAG(z); + return hypot(x, y); +} + +igraph_real_t igraph_complex_arg(igraph_complex_t z) { + igraph_real_t x = IGRAPH_REAL(z); + igraph_real_t y = IGRAPH_IMAG(z); + if (x == 0.0 && y == 0.0) { + return 0.0; + } + return atan2(y, x); +} + +igraph_complex_t igraph_complex_add(igraph_complex_t z1, + igraph_complex_t z2) { + igraph_complex_t res; + IGRAPH_REAL(res) = IGRAPH_REAL(z1) + IGRAPH_REAL(z2); + IGRAPH_IMAG(res) = IGRAPH_IMAG(z1) + IGRAPH_IMAG(z2); + return res; +} + +igraph_complex_t igraph_complex_sub(igraph_complex_t z1, + igraph_complex_t z2) { + igraph_complex_t res; + IGRAPH_REAL(res) = IGRAPH_REAL(z1) - IGRAPH_REAL(z2); + IGRAPH_IMAG(res) = IGRAPH_IMAG(z1) - IGRAPH_IMAG(z2); + return res; +} + +igraph_complex_t igraph_complex_mul(igraph_complex_t z1, + igraph_complex_t z2) { + igraph_complex_t res; + IGRAPH_REAL(res) = IGRAPH_REAL(z1) * IGRAPH_REAL(z2) - + IGRAPH_IMAG(z1) * IGRAPH_IMAG(z2); + IGRAPH_IMAG(res) = IGRAPH_REAL(z1) * IGRAPH_IMAG(z2) + + IGRAPH_IMAG(z1) * IGRAPH_REAL(z2); + return res; +} + +igraph_complex_t igraph_complex_div(igraph_complex_t z1, + igraph_complex_t z2) { + igraph_complex_t res; + igraph_real_t z1r = IGRAPH_REAL(z1), z1i = IGRAPH_IMAG(z1); + igraph_real_t z2r = IGRAPH_REAL(z2), z2i = IGRAPH_IMAG(z2); + igraph_real_t s = 1.0 / igraph_complex_abs(z2); + igraph_real_t sz2r = s * z2r; + igraph_real_t sz2i = s * z2i; + IGRAPH_REAL(res) = (z1r * sz2r + z1i * sz2i) * s; + IGRAPH_IMAG(res) = (z1i * sz2r - z1r * sz2i) * s; + return res; +} + +igraph_complex_t igraph_complex_add_real(igraph_complex_t z, + igraph_real_t x) { + igraph_complex_t res; + IGRAPH_REAL(res) = IGRAPH_REAL(z) + x; + IGRAPH_IMAG(res) = IGRAPH_IMAG(z); + return res; +} + +igraph_complex_t igraph_complex_add_imag(igraph_complex_t z, + igraph_real_t y) { + igraph_complex_t res; + IGRAPH_REAL(res) = IGRAPH_REAL(z); + IGRAPH_IMAG(res) = IGRAPH_IMAG(z) + y; + return res; +} + +igraph_complex_t igraph_complex_sub_real(igraph_complex_t z, + igraph_real_t x) { + igraph_complex_t res; + IGRAPH_REAL(res) = IGRAPH_REAL(z) - x; + IGRAPH_IMAG(res) = IGRAPH_IMAG(z); + return res; +} + +igraph_complex_t igraph_complex_sub_imag(igraph_complex_t z, + igraph_real_t y) { + igraph_complex_t res; + IGRAPH_REAL(res) = IGRAPH_REAL(z); + IGRAPH_IMAG(res) = IGRAPH_IMAG(z) - y; + return res; +} + +igraph_complex_t igraph_complex_mul_real(igraph_complex_t z, + igraph_real_t x) { + igraph_complex_t res; + IGRAPH_REAL(res) = IGRAPH_REAL(z) * x; + IGRAPH_IMAG(res) = IGRAPH_IMAG(z) * x; + return res; +} + +igraph_complex_t igraph_complex_mul_imag(igraph_complex_t z, + igraph_real_t y) { + igraph_complex_t res; + IGRAPH_REAL(res) = - IGRAPH_IMAG(z) * y; + IGRAPH_IMAG(res) = IGRAPH_REAL(z) * y; + return res; +} + +igraph_complex_t igraph_complex_div_real(igraph_complex_t z, + igraph_real_t x) { + igraph_complex_t res; + IGRAPH_REAL(res) = IGRAPH_REAL(z) / x; + IGRAPH_IMAG(res) = IGRAPH_IMAG(z) / x; + return res; +} + +igraph_complex_t igraph_complex_div_imag(igraph_complex_t z, + igraph_real_t y) { + igraph_complex_t res; + IGRAPH_REAL(res) = IGRAPH_IMAG(z) / y; + IGRAPH_IMAG(res) = - IGRAPH_REAL(z) / y; + return res; +} + +igraph_complex_t igraph_complex_conj(igraph_complex_t z) { + igraph_complex_t res; + IGRAPH_REAL(res) = IGRAPH_REAL(z); + IGRAPH_IMAG(res) = - IGRAPH_IMAG(z); + return res; +} + +igraph_complex_t igraph_complex_neg(igraph_complex_t z) { + igraph_complex_t res; + IGRAPH_REAL(res) = - IGRAPH_REAL(z); + IGRAPH_IMAG(res) = - IGRAPH_IMAG(z); + return res; +} + +igraph_complex_t igraph_complex_inv(igraph_complex_t z) { + igraph_complex_t res; + igraph_real_t s = 1.0 / igraph_complex_abs(z); + IGRAPH_REAL(res) = (IGRAPH_REAL(z) * s) * s; + IGRAPH_IMAG(res) = - (IGRAPH_IMAG(z) * s) * s; + return res; +} + +igraph_real_t igraph_complex_abs(igraph_complex_t z) { + return hypot(IGRAPH_REAL(z), IGRAPH_IMAG(z)); +} + +igraph_real_t igraph_complex_logabs(igraph_complex_t z) { + igraph_real_t xabs = fabs(IGRAPH_REAL(z)); + igraph_real_t yabs = fabs(IGRAPH_IMAG(z)); + igraph_real_t max, u; + if (xabs >= yabs) { + max = xabs; + u = yabs / xabs; + } else { + max = yabs; + u = xabs / yabs; + } + return log (max) + 0.5 * log1p (u * u); +} + +igraph_complex_t igraph_complex_sqrt(igraph_complex_t z) { + igraph_complex_t res; + + if (IGRAPH_REAL(z) == 0.0 && IGRAPH_IMAG(z) == 0.0) { + IGRAPH_REAL(res) = IGRAPH_IMAG(res) = 0.0; + } else { + igraph_real_t x = fabs (IGRAPH_REAL(z)); + igraph_real_t y = fabs (IGRAPH_IMAG(z)); + igraph_real_t w; + if (x >= y) { + igraph_real_t t = y / x; + w = sqrt (x) * sqrt (0.5 * (1.0 + sqrt (1.0 + t * t))); + } else { + igraph_real_t t = x / y; + w = sqrt (y) * sqrt (0.5 * (t + sqrt (1.0 + t * t))); + } + + if (IGRAPH_REAL(z) >= 0.0) { + igraph_real_t ai = IGRAPH_IMAG(z); + IGRAPH_REAL(res) = w; + IGRAPH_IMAG(res) = ai / (2.0 * w); + } else { + igraph_real_t ai = IGRAPH_IMAG(z); + igraph_real_t vi = (ai >= 0) ? w : -w; + IGRAPH_REAL(res) = ai / (2.0 * vi); + IGRAPH_IMAG(res) = vi; + } + } + + return res; +} + +igraph_complex_t igraph_complex_sqrt_real(igraph_real_t x) { + igraph_complex_t res; + if (x >= 0) { + IGRAPH_REAL(res) = sqrt(x); + IGRAPH_IMAG(res) = 0.0; + } else { + IGRAPH_REAL(res) = 0.0; + IGRAPH_IMAG(res) = sqrt(-x); + } + return res; +} + +igraph_complex_t igraph_complex_exp(igraph_complex_t z) { + igraph_real_t rho = exp(IGRAPH_REAL(z)); + igraph_real_t theta = IGRAPH_IMAG(z); + igraph_complex_t res; + IGRAPH_REAL(res) = rho * cos(theta); + IGRAPH_IMAG(res) = rho * sin(theta); + return res; +} + +igraph_complex_t igraph_complex_pow(igraph_complex_t z1, + igraph_complex_t z2) { + igraph_complex_t res; + + if (IGRAPH_REAL(z1) == 0 && IGRAPH_IMAG(z1) == 0.0) { + if (IGRAPH_REAL(z2) == 0 && IGRAPH_IMAG(z2) == 0.0) { + IGRAPH_REAL(res) = 1.0; + IGRAPH_IMAG(res) = 0.0; + } else { + IGRAPH_REAL(res) = IGRAPH_IMAG(res) = 0.0; + } + } else if (IGRAPH_REAL(z2) == 1.0 && IGRAPH_IMAG(z2) == 0.0) { + IGRAPH_REAL(res) = IGRAPH_REAL(z1); + IGRAPH_IMAG(res) = IGRAPH_IMAG(z1); + } else if (IGRAPH_REAL(z2) == -1.0 && IGRAPH_IMAG(z2) == 0.0) { + res = igraph_complex_inv(z1); + } else { + igraph_real_t logr = igraph_complex_logabs (z1); + igraph_real_t theta = igraph_complex_arg (z1); + igraph_real_t z2r = IGRAPH_REAL(z2), z2i = IGRAPH_IMAG(z2); + igraph_real_t rho = exp (logr * z2r - z2i * theta); + igraph_real_t beta = theta * z2r + z2i * logr; + IGRAPH_REAL(res) = rho * cos(beta); + IGRAPH_IMAG(res) = rho * sin(beta); + } + + return res; +} + +igraph_complex_t igraph_complex_pow_real(igraph_complex_t z, + igraph_real_t x) { + igraph_complex_t res; + if (IGRAPH_REAL(z) == 0.0 && IGRAPH_IMAG(z) == 0.0) { + if (x == 0) { + IGRAPH_REAL(res) = 1.0; + IGRAPH_IMAG(res) = 0.0; + } else { + IGRAPH_REAL(res) = IGRAPH_IMAG(res) = 0.0; + } + } else { + igraph_real_t logr = igraph_complex_logabs(z); + igraph_real_t theta = igraph_complex_arg(z); + igraph_real_t rho = exp (logr * x); + igraph_real_t beta = theta * x; + IGRAPH_REAL(res) = rho * cos(beta); + IGRAPH_IMAG(res) = rho * sin(beta); + } + return res; +} + +igraph_complex_t igraph_complex_log(igraph_complex_t z) { + igraph_complex_t res; + IGRAPH_REAL(res) = igraph_complex_logabs(z); + IGRAPH_IMAG(res) = igraph_complex_arg(z); + return res; +} + +igraph_complex_t igraph_complex_log10(igraph_complex_t z) { + return igraph_complex_mul_real(igraph_complex_log(z), 1 / log(10.0)); +} + +igraph_complex_t igraph_complex_log_b(igraph_complex_t z, + igraph_complex_t b) { + return igraph_complex_div (igraph_complex_log(z), igraph_complex_log(b)); +} + +igraph_complex_t igraph_complex_sin(igraph_complex_t z) { + igraph_real_t zr = IGRAPH_REAL(z); + igraph_real_t zi = IGRAPH_IMAG(z); + igraph_complex_t res; + if (zi == 0.0) { + IGRAPH_REAL(res) = sin(zr); + IGRAPH_IMAG(res) = 0.0; + } else { + IGRAPH_REAL(res) = sin(zr) * cosh(zi); + IGRAPH_IMAG(res) = cos(zr) * sinh(zi); + } + return res; +} + +igraph_complex_t igraph_complex_cos(igraph_complex_t z) { + igraph_real_t zr = IGRAPH_REAL(z); + igraph_real_t zi = IGRAPH_IMAG(z); + igraph_complex_t res; + if (zi == 0.0) { + IGRAPH_REAL(res) = cos(zr); + IGRAPH_IMAG(res) = 0.0; + } else { + IGRAPH_REAL(res) = cos(zr) * cosh(zi); + IGRAPH_IMAG(res) = sin(zr) * sinh(-zi); + } + return res; +} + +igraph_complex_t igraph_complex_tan(igraph_complex_t z) { + igraph_real_t zr = IGRAPH_REAL(z); + igraph_real_t zi = IGRAPH_IMAG(z); + igraph_complex_t res; + if (fabs (zi) < 1) { + igraph_real_t D = pow (cos (zr), 2.0) + pow (sinh (zi), 2.0); + IGRAPH_REAL(res) = 0.5 * sin (2 * zr) / D; + IGRAPH_IMAG(res) = 0.5 * sinh (2 * zi) / D; + } else { + igraph_real_t u = exp (-zi); + igraph_real_t C = 2 * u / (1 - pow (u, 2.0)); + igraph_real_t D = 1 + pow (cos (zr), 2.0) * pow (C, 2.0); + igraph_real_t S = pow (C, 2.0); + igraph_real_t T = 1.0 / tanh (zi); + IGRAPH_REAL(res) = 0.5 * sin (2 * zr) * S / D; + IGRAPH_IMAG(res) = T / D; + } + return res; +} + +igraph_complex_t igraph_complex_sec(igraph_complex_t z) { + return igraph_complex_inv(igraph_complex_cos(z)); +} + +igraph_complex_t igraph_complex_csc(igraph_complex_t z) { + return igraph_complex_inv(igraph_complex_sin(z)); +} + +igraph_complex_t igraph_complex_cot(igraph_complex_t z) { + return igraph_complex_inv(igraph_complex_tan(z)); +} diff --git a/src/rigraph/core/math/utils.c b/src/rigraph/core/math/utils.c new file mode 100644 index 0000000..2909960 --- /dev/null +++ b/src/rigraph/core/math/utils.c @@ -0,0 +1,339 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_types.h" + +#include "../core/math.h" + +#include "config.h" + +#include +#include + +#ifdef _MSC_VER +# ifndef isinf +# define isinf(x) (!_finite(x) && !_isnan(x)) +# endif +#endif + +int igraph_finite(double x) { +#if HAVE_ISFINITE + return isfinite(x); +#elif HAVE_FINITE + return finite(x); +#else + return (!isnan(x) & (x != IGRAPH_POSINFINITY) & (x != IGRAPH_NEGINFINITY)); +#endif +} + +double igraph_log2(const double a) { + return log(a) / log(2.0); +} + +int igraph_chebyshev_init(const double *dos, int nos, double eta) { + int i, ii; + double err; + + if (nos < 1) { + return 0; + } + + err = 0.0; + i = 0; /* just to avoid compiler warnings */ + for (ii = 1; ii <= nos; ii++) { + i = nos - ii; + err += fabs(dos[i]); + if (err > eta) { + return i; + } + } + return i; +} + +double igraph_chebyshev_eval(double x, const double *a, const int n) { + double b0, b1, b2, twox; + int i; + + if (n < 1 || n > 1000) { + IGRAPH_WARNING("chebyshev_eval: argument out of domain"); + return IGRAPH_NAN; + } + + if (x < -1.1 || x > 1.1) { + IGRAPH_WARNING("chebyshev_eval: argument out of domain"); + return IGRAPH_NAN; + } + + twox = x * 2; + b2 = b1 = 0; + b0 = 0; + for (i = 1; i <= n; i++) { + b2 = b1; + b1 = b0; + b0 = twox * b1 - b2 + a[n - i]; + } + return (b0 - b2) * 0.5; +} + +double igraph_log1p(double x) { + /* series for log1p on the interval -.375 to .375 + * with weighted error 6.35e-32 + * log weighted error 31.20 + * significant figures required 30.93 + * decimal places required 32.01 + */ + static const double alnrcs[43] = { + +.10378693562743769800686267719098e+1, + -.13364301504908918098766041553133e+0, + +.19408249135520563357926199374750e-1, + -.30107551127535777690376537776592e-2, + +.48694614797154850090456366509137e-3, + -.81054881893175356066809943008622e-4, + +.13778847799559524782938251496059e-4, + -.23802210894358970251369992914935e-5, + +.41640416213865183476391859901989e-6, + -.73595828378075994984266837031998e-7, + +.13117611876241674949152294345011e-7, + -.23546709317742425136696092330175e-8, + +.42522773276034997775638052962567e-9, + -.77190894134840796826108107493300e-10, + +.14075746481359069909215356472191e-10, + -.25769072058024680627537078627584e-11, + +.47342406666294421849154395005938e-12, + -.87249012674742641745301263292675e-13, + +.16124614902740551465739833119115e-13, + -.29875652015665773006710792416815e-14, + +.55480701209082887983041321697279e-15, + -.10324619158271569595141333961932e-15, + +.19250239203049851177878503244868e-16, + -.35955073465265150011189707844266e-17, + +.67264542537876857892194574226773e-18, + -.12602624168735219252082425637546e-18, + +.23644884408606210044916158955519e-19, + -.44419377050807936898878389179733e-20, + +.83546594464034259016241293994666e-21, + -.15731559416479562574899253521066e-21, + +.29653128740247422686154369706666e-22, + -.55949583481815947292156013226666e-23, + +.10566354268835681048187284138666e-23, + -.19972483680670204548314999466666e-24, + +.37782977818839361421049855999999e-25, + -.71531586889081740345038165333333e-26, + +.13552488463674213646502024533333e-26, + -.25694673048487567430079829333333e-27, + +.48747756066216949076459519999999e-28, + -.92542112530849715321132373333333e-29, + +.17578597841760239233269760000000e-29, + -.33410026677731010351377066666666e-30, + +.63533936180236187354180266666666e-31, + }; + + static int nlnrel = 0; + static double xmin = 0.0; + + if (xmin == 0.0) { + xmin = -1 + sqrt(DBL_EPSILON); /*was sqrt(d1mach(4)); */ + } + if (nlnrel == 0) { /* initialize chebychev coefficients */ + nlnrel = igraph_chebyshev_init(alnrcs, 43, DBL_EPSILON / 20); /*was .1*d1mach(3)*/ + } + + if (x == 0.) { + return 0.; /* speed */ + } + if (x == -1) { + return (IGRAPH_NEGINFINITY); + } + if (x < -1) { + return (IGRAPH_NAN); + } + + if (fabs(x) <= .375) { + /* Improve on speed (only); + again give result accurate to IEEE double precision: */ + if (fabs(x) < .5 * DBL_EPSILON) { + return x; + } + + if ( (0 < x && x < 1e-8) || (-1e-9 < x && x < 0)) { + return x * (1 - .5 * x); + } + /* else */ + return x * (1 - x * igraph_chebyshev_eval(x / .375, alnrcs, nlnrel)); + } + /* else */ + /* if (x < xmin) { */ + /* /\* answer less than half precision because x too near -1 *\/ */ + /* ML_ERROR(ME_PRECISION, "log1p"); */ + /* } */ + return log(1 + x); +} + +double igraph_fmin(double a, double b) { + if (b < a) { + return b; + } else { + return a; + } +} + +double igraph_i_round(double X) { + + /* NaN */ + if (X != X) { + return X; + } + + if (X < 0.0) { + return floor(X); + } + + return ceil(X); +} + +#ifdef _MSC_VER +/** + * Internal function, replacement for snprintf + * Used only in case of the Microsoft Visual C compiler which does not + * provide a proper sprintf implementation. + * + * This implementation differs from the standard in the value returned + * when the number of characters needed by the output, excluding the + * terminating '\0' is larger than count + */ +int igraph_i_snprintf(char *buffer, size_t count, const char *format, ...) { + int n; + va_list args; + if (count > 0) { + va_start(args, format); + n = _vsnprintf(buffer, count, format, args); + buffer[count - 1] = 0; + va_end(args); + } else { + n = 0; + } + return n; +} + +#endif + +int igraph_is_nan(double x) { + return isnan(x); +} + +int igraph_is_inf(double x) { + return isinf(x) != 0; +} + +int igraph_is_posinf(double x) { + return isinf(x) && x > 0; +} + +int igraph_is_neginf(double x) { + return isinf(x) && x < 0; +} + +/** + * \function igraph_almost_equals + * Compare two double-precision floats with a tolerance + * + * Determines whether two double-precision floats are "almost equal" + * to each other with a given level of tolerance on the relative error. + * + * \param a the first float + * \param b the second float + * \param eps the level of tolerance on the relative error. The relative + * error is defined as \c "abs(a-b) / (abs(a) + abs(b))". The + * two numbers are considered equal if this is less than \c eps. + * + * \return nonzero if the two floats are nearly equal to each other within + * the given level of tolerance, zero otherwise + */ +int igraph_almost_equals(double a, double b, double eps) { + return igraph_cmp_epsilon(a, b, eps) == 0 ? 1 : 0; +} + +/* Use value-safe floating point math for igraph_cmp_epsilon() with + * the Intel compiler. + * + * The Intel compiler rewrites arithmetic expressions for faster + * evaluation by default. In the below function, it will evaluate + * (eps * fabs(a) + eps * fabs(b)) as eps*(fabs(a) + fabs(b)). + * However, this code path is taken precisely when fabs(a) + fabs(b) + * overflows, thus this rearrangement of the expression causes + * the function to return incorrect results, and some test failures. + * To avoid this, we switch the Intel compiler to "precise" mode. + */ +#ifdef __INTEL_COMPILER +#pragma float_control(push) +#pragma float_control (precise, on) +#endif + +/** + * \function igraph_cmp_epsilon + * Compare two double-precision floats with a tolerance + * + * Determines whether two double-precision floats are "almost equal" + * to each other with a given level of tolerance on the relative error. + * + * \param a the first float + * \param b the second float + * \param eps the level of tolerance on the relative error. The relative + * error is defined as \c "abs(a-b) / (abs(a) + abs(b))". The + * two numbers are considered equal if this is less than \c eps. + * + * \return zero if the two floats are nearly equal to each other within + * the given level of tolerance, positive number if the first float is + * larger, negative number if the second float is larger + */ +int igraph_cmp_epsilon(double a, double b, double eps) { + double diff; + double abs_diff; + double sum; + + if (a == b) { + /* shortcut, handles infinities */ + return 0; + } + + diff = a - b; + abs_diff = fabs(diff); + sum = fabs(a) + fabs(b); + + if (a == 0 || b == 0 || sum < DBL_MIN) { + /* a or b is zero or both are extremely close to it; relative + * error is less meaningful here so just compare it with + * epsilon */ + return abs_diff < (eps * DBL_MIN) ? 0 : (diff < 0 ? -1 : 1); + } else if (!isfinite(sum)) { + /* addition overflow, so presumably |a| and |b| are both large; use a + * different formulation */ + return (abs_diff < (eps * fabs(a) + eps * fabs(b))) ? 0 : (diff < 0 ? -1 : 1); + } else { + return (abs_diff / sum < eps) ? 0 : (diff < 0 ? -1 : 1); + } +} + +#ifdef __INTEL_COMPILER +#pragma float_control(pop) +#endif diff --git a/src/rigraph/core/misc/bipartite.c b/src/rigraph/core/misc/bipartite.c new file mode 100644 index 0000000..49158b3 --- /dev/null +++ b/src/rigraph/core/misc/bipartite.c @@ -0,0 +1,1148 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2008-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_bipartite.h" + +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_random.h" +#include "igraph_nongraph.h" + +#include "../graph/attributes.h" + +/** + * \section about_bipartite Bipartite networks in igraph + * + * + * A bipartite network contains two kinds of vertices and connections + * are only possible between two vertices of different kinds. There are + * many natural examples, e.g. movies and actors as vertices and a + * movie is connected to all participating actors, etc. + * + * + * igraph does not have direct support for bipartite networks, at + * least not at the C language level. In other words the igraph_t + * structure does not contain information about the vertex types. + * The C functions for bipartite networks usually have an additional + * input argument to graph, called \c types, a boolean vector giving + * the vertex types. + * + * + * Most functions creating bipartite networks are able to create this + * extra vector, you just need to supply an initialized boolean vector + * to them. + */ + +/** + * \function igraph_bipartite_projection_size + * \brief Calculate the number of vertices and edges in the bipartite projections. + * + * This function calculates the number of vertices and edges in the + * two projections of a bipartite network. This is useful if you have + * a big bipartite network and you want to estimate the amount of + * memory you would need to calculate the projections themselves. + * + * \param graph The input graph. + * \param types Boolean vector giving the vertex types of the graph. + * \param vcount1 Pointer to an \c igraph_integer_t, the number of + * vertices in the first projection is stored here. + * \param ecount1 Pointer to an \c igraph_integer_t, the number of + * edges in the first projection is stored here. + * \param vcount2 Pointer to an \c igraph_integer_t, the number of + * vertices in the second projection is stored here. + * \param ecount2 Pointer to an \c igraph_integer_t, the number of + * edges in the second projection is stored here. + * \return Error code. + * + * \sa \ref igraph_bipartite_projection() to calculate the actual + * projection. + * + * Time complexity: O(|V|*d^2+|E|), |V| is the number of vertices, |E| + * is the number of edges, d is the average (total) degree of the + * graphs. + * + * \example examples/simple/igraph_bipartite_projection.c + */ + +int igraph_bipartite_projection_size(const igraph_t *graph, + const igraph_vector_bool_t *types, + igraph_integer_t *vcount1, + igraph_integer_t *ecount1, + igraph_integer_t *vcount2, + igraph_integer_t *ecount2) { + + long int no_of_nodes = igraph_vcount(graph); + long int vc1 = 0, ec1 = 0, vc2 = 0, ec2 = 0; + igraph_adjlist_t adjlist; + igraph_vector_long_t added; + long int i; + + IGRAPH_CHECK(igraph_vector_long_init(&added, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &added); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + for (i = 0; i < no_of_nodes; i++) { + igraph_vector_int_t *neis1; + long int neilen1, j; + long int *ecptr; + if (VECTOR(*types)[i]) { + vc2++; + ecptr = &ec2; + } else { + vc1++; + ecptr = &ec1; + } + neis1 = igraph_adjlist_get(&adjlist, i); + neilen1 = igraph_vector_int_size(neis1); + for (j = 0; j < neilen1; j++) { + long int k, neilen2, nei = (long int) VECTOR(*neis1)[j]; + igraph_vector_int_t *neis2 = igraph_adjlist_get(&adjlist, nei); + if (IGRAPH_UNLIKELY(VECTOR(*types)[i] == VECTOR(*types)[nei])) { + IGRAPH_ERROR("Non-bipartite edge found in bipartite projection", + IGRAPH_EINVAL); + } + neilen2 = igraph_vector_int_size(neis2); + for (k = 0; k < neilen2; k++) { + long int nei2 = (long int) VECTOR(*neis2)[k]; + if (nei2 <= i) { + continue; + } + if (VECTOR(added)[nei2] == i + 1) { + continue; + } + VECTOR(added)[nei2] = i + 1; + (*ecptr)++; + } + } + } + + *vcount1 = (igraph_integer_t) vc1; + *ecount1 = (igraph_integer_t) ec1; + *vcount2 = (igraph_integer_t) vc2; + *ecount2 = (igraph_integer_t) ec2; + + igraph_adjlist_destroy(&adjlist); + igraph_vector_long_destroy(&added); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +static int igraph_i_bipartite_projection(const igraph_t *graph, + const igraph_vector_bool_t *types, + igraph_t *proj, + int which, + igraph_vector_t *multiplicity) { + + long int no_of_nodes = igraph_vcount(graph); + long int i, j, k; + igraph_integer_t remaining_nodes = 0; + igraph_vector_t vertex_perm, vertex_index; + igraph_vector_t edges; + igraph_adjlist_t adjlist; + igraph_vector_int_t *neis1, *neis2; + long int neilen1, neilen2; + igraph_vector_long_t added; + igraph_vector_t mult; + + if (which < 0) { + return 0; + } + + IGRAPH_VECTOR_INIT_FINALLY(&vertex_perm, 0); + IGRAPH_CHECK(igraph_vector_reserve(&vertex_perm, no_of_nodes)); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vertex_index, no_of_nodes); + IGRAPH_CHECK(igraph_vector_long_init(&added, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &added); + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + /* we won't need the 'mult' vector if 'multiplicity' is NULL, but MSVC will + * throw warnings in the compiler output if we initialize it conditionally */ + IGRAPH_VECTOR_INIT_FINALLY(&mult, multiplicity ? no_of_nodes : 1); + if (multiplicity) { + igraph_vector_clear(multiplicity); + } + + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*types)[i] == which) { + VECTOR(vertex_index)[i] = ++remaining_nodes; + igraph_vector_push_back(&vertex_perm, i); + } + } + + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*types)[i] == which) { + long int new_i = (long int) VECTOR(vertex_index)[i] - 1; + long int iedges = 0; + neis1 = igraph_adjlist_get(&adjlist, i); + neilen1 = igraph_vector_int_size(neis1); + for (j = 0; j < neilen1; j++) { + long int nei = (long int) VECTOR(*neis1)[j]; + if (IGRAPH_UNLIKELY(VECTOR(*types)[i] == VECTOR(*types)[nei])) { + IGRAPH_ERROR("Non-bipartite edge found in bipartite projection", + IGRAPH_EINVAL); + } + neis2 = igraph_adjlist_get(&adjlist, nei); + neilen2 = igraph_vector_int_size(neis2); + for (k = 0; k < neilen2; k++) { + long int nei2 = (long int) VECTOR(*neis2)[k], new_nei2; + if (nei2 <= i) { + continue; + } + if (VECTOR(added)[nei2] == i + 1) { + if (multiplicity) { + VECTOR(mult)[nei2] += 1; + } + continue; + } + VECTOR(added)[nei2] = i + 1; + if (multiplicity) { + VECTOR(mult)[nei2] = 1; + } + iedges++; + + IGRAPH_CHECK(igraph_vector_push_back(&edges, new_i)); + if (multiplicity) { + /* If we need the multiplicity as well, then we put in the + old vertex ids here and rewrite it later */ + IGRAPH_CHECK(igraph_vector_push_back(&edges, nei2)); + } else { + new_nei2 = (long int) VECTOR(vertex_index)[nei2] - 1; + IGRAPH_CHECK(igraph_vector_push_back(&edges, new_nei2)); + } + } + } + if (multiplicity) { + /* OK, we need to go through all the edges added for vertex new_i + and check their multiplicity */ + long int now = igraph_vector_size(&edges); + long int from = now - iedges * 2; + for (j = from; j < now; j += 2) { + long int nei2 = (long int) VECTOR(edges)[j + 1]; + long int new_nei2 = (long int) VECTOR(vertex_index)[nei2] - 1; + long int m = (long int) VECTOR(mult)[nei2]; + VECTOR(edges)[j + 1] = new_nei2; + IGRAPH_CHECK(igraph_vector_push_back(multiplicity, m)); + } + } + } /* if VECTOR(*type)[i] == which */ + } + + igraph_vector_destroy(&mult); + igraph_adjlist_destroy(&adjlist); + igraph_vector_long_destroy(&added); + igraph_vector_destroy(&vertex_index); + IGRAPH_FINALLY_CLEAN(4); + + IGRAPH_CHECK(igraph_create(proj, &edges, remaining_nodes, + /*directed=*/ 0)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, proj); + + IGRAPH_I_ATTRIBUTE_DESTROY(proj); + IGRAPH_I_ATTRIBUTE_COPY(proj, graph, 1, 0, 0); + IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, proj, &vertex_perm)); + igraph_vector_destroy(&vertex_perm); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \function igraph_bipartite_projection + * \brief Create one or both projections of a bipartite (two-mode) network. + * + * Creates one or both projections of a bipartite graph. + * + * \param graph The bipartite input graph. Directedness of the edges + * is ignored. + * \param types Boolean vector giving the vertex types of the graph. + * \param proj1 Pointer to an uninitialized graph object, the first + * projection will be created here. It a null pointer, then it is + * ignored, see also the \p probe1 argument. + * \param proj2 Pointer to an uninitialized graph object, the second + * projection is created here, if it is not a null pointer. See also + * the \p probe1 argument. + * \param multiplicity1 Pointer to a vector, or a null pointer. If not + * the latter, then the multiplicity of the edges is stored + * here. E.g. if there is an A-C-B and also an A-D-B triple in the + * bipartite graph (but no more X, such that A-X-B is also in the + * graph), then the multiplicity of the A-B edge in the projection + * will be 2. + * \param multiplicity2 The same as \c multiplicity1, but for the + * other projection. + * \param probe1 This argument can be used to specify the order of the + * projections in the resulting list. When it is non-negative, then + * it is considered as a vertex ID and the projection containing + * this vertex will be the first one in the result. Setting this + * argument to a non-negative value implies that \c proj1 must be + * a non-null pointer. If you don't care about the ordering of the + * projections, pass -1 here. + * \return Error code. + * + * \sa \ref igraph_bipartite_projection_size() to calculate the number + * of vertices and edges in the projections, without creating the + * projection graphs themselves. + * + * Time complexity: O(|V|*d^2+|E|), |V| is the number of vertices, |E| + * is the number of edges, d is the average (total) degree of the + * graphs. + * + * \example examples/simple/igraph_bipartite_projection.c + */ + +int igraph_bipartite_projection(const igraph_t *graph, + const igraph_vector_bool_t *types, + igraph_t *proj1, + igraph_t *proj2, + igraph_vector_t *multiplicity1, + igraph_vector_t *multiplicity2, + igraph_integer_t probe1) { + + long int no_of_nodes = igraph_vcount(graph); + + /* t1 is -1 if proj1 is omitted, it is 0 if it belongs to type zero, + it is 1 if it belongs to type one. The same for t2 */ + int t1, t2; + + if (igraph_vector_bool_size(types) != no_of_nodes) { + IGRAPH_ERROR("Invalid bipartite type vector size", IGRAPH_EINVAL); + } + + if (probe1 >= no_of_nodes) { + IGRAPH_ERROR("No such vertex to probe", IGRAPH_EINVAL); + } + + if (probe1 >= 0 && !proj1) { + IGRAPH_ERROR("`probe1' given, but `proj1' is a null pointer", IGRAPH_EINVAL); + } + + if (probe1 >= 0) { + t1 = VECTOR(*types)[(long int)probe1]; + if (proj2) { + t2 = 1 - t1; + } else { + t2 = -1; + } + } else { + t1 = proj1 ? 0 : -1; + t2 = proj2 ? 1 : -1; + } + + IGRAPH_CHECK(igraph_i_bipartite_projection(graph, types, proj1, t1, multiplicity1)); + IGRAPH_FINALLY(igraph_destroy, proj1); + IGRAPH_CHECK(igraph_i_bipartite_projection(graph, types, proj2, t2, multiplicity2)); + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + + +/** + * \function igraph_full_bipartite + * \brief Create a full bipartite network. + * + * A bipartite network contains two kinds of vertices and connections + * are only possible between two vertices of different kind. There are + * many natural examples, e.g. movies and actors as vertices and a + * movie is connected to all participating actors, etc. + * + * + * igraph does not have direct support for bipartite networks, at + * least not at the C language level. In other words the igraph_t + * structure does not contain information about the vertex types. + * The C functions for bipartite networks usually have an additional + * input argument to graph, called \c types, a boolean vector giving + * the vertex types. + * + * + * Most functions creating bipartite networks are able to create this + * extra vector, you just need to supply an initialized boolean vector + * to them. + * + * \param graph Pointer to an igraph_t object, the graph will be + * created here. + * \param types Pointer to a boolean vector. If not a null pointer, + * then the vertex types will be stored here. + * \param n1 Integer, the number of vertices of the first kind. + * \param n2 Integer, the number of vertices of the second kind. + * \param directed Boolean, whether to create a directed graph. + * \param mode A constant that gives the type of connections for + * directed graphs. If \c IGRAPH_OUT, then edges point from vertices + * of the first kind to vertices of the second kind; if \c + * IGRAPH_IN, then the opposite direction is realized; if \c + * IGRAPH_ALL, then mutual edges will be created. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + * + * \sa \ref igraph_full() for non-bipartite full graphs. + */ + +int igraph_full_bipartite(igraph_t *graph, + igraph_vector_bool_t *types, + igraph_integer_t n1, igraph_integer_t n2, + igraph_bool_t directed, + igraph_neimode_t mode) { + + igraph_integer_t nn1 = n1, nn2 = n2; + igraph_integer_t no_of_nodes = nn1 + nn2; + igraph_vector_t edges; + long int no_of_edges; + long int ptr = 0; + long int i, j; + + if (!directed) { + no_of_edges = nn1 * nn2; + } else if (mode == IGRAPH_OUT || mode == IGRAPH_IN) { + no_of_edges = nn1 * nn2; + } else { /* mode==IGRAPH_ALL */ + no_of_edges = nn1 * nn2 * 2; + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + + if (!directed || mode == IGRAPH_OUT) { + + for (i = 0; i < nn1; i++) { + for (j = 0; j < nn2; j++) { + VECTOR(edges)[ptr++] = i; + VECTOR(edges)[ptr++] = nn1 + j; + } + } + + } else if (mode == IGRAPH_IN) { + + for (i = 0; i < nn1; i++) { + for (j = 0; j < nn2; j++) { + VECTOR(edges)[ptr++] = nn1 + j; + VECTOR(edges)[ptr++] = i; + } + } + + } else { + + for (i = 0; i < nn1; i++) { + for (j = 0; j < nn2; j++) { + VECTOR(edges)[ptr++] = i; + VECTOR(edges)[ptr++] = nn1 + j; + VECTOR(edges)[ptr++] = nn1 + j; + VECTOR(edges)[ptr++] = i; + } + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, graph); + + if (types) { + IGRAPH_CHECK(igraph_vector_bool_resize(types, no_of_nodes)); + igraph_vector_bool_null(types); + for (i = nn1; i < no_of_nodes; i++) { + VECTOR(*types)[i] = 1; + } + } + + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_create_bipartite + * \brief Create a bipartite graph. + * + * This is a simple wrapper function to create a bipartite graph. It + * does a little more than \ref igraph_create(), e.g. it checks that + * the graph is indeed bipartite with respect to the given \p types + * vector. If there is an edge connecting two vertices of the same + * kind, then an error is reported. + * + * \param graph Pointer to an uninitialized graph object, the result is + * created here. + * \param types Boolean vector giving the vertex types. The length of + * the vector defines the number of vertices in the graph. + * \param edges Vector giving the edges of the graph. The highest + * vertex id in this vector must be smaller than the length of the + * \p types vector. + * \param directed Boolean scalar, whether to create a directed + * graph. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + * + * \example examples/simple/igraph_bipartite_create.c + */ + +int igraph_create_bipartite(igraph_t *graph, const igraph_vector_bool_t *types, + const igraph_vector_t *edges, + igraph_bool_t directed) { + + igraph_integer_t no_of_nodes = + (igraph_integer_t) igraph_vector_bool_size(types); + long int no_of_edges = igraph_vector_size(edges); + igraph_real_t min_edge = 0, max_edge = 0; + long int i; + + if (no_of_edges % 2 != 0) { + IGRAPH_ERROR("Invalid (odd) edges vector", IGRAPH_EINVEVECTOR); + } + no_of_edges /= 2; + + if (no_of_edges != 0) { + igraph_vector_minmax(edges, &min_edge, &max_edge); + } + if (min_edge < 0 || max_edge >= no_of_nodes) { + IGRAPH_ERROR("Invalid (negative or too large) vertex id", IGRAPH_EINVVID); + } + + /* Check bipartiteness */ + for (i = 0; i < no_of_edges * 2; i += 2) { + long int from = (long int) VECTOR(*edges)[i]; + long int to = (long int) VECTOR(*edges)[i + 1]; + long int t1 = VECTOR(*types)[from]; + long int t2 = VECTOR(*types)[to]; + if ( (t1 && t2) || (!t1 && !t2) ) { + IGRAPH_ERROR("Invalid edges, not a bipartite graph", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_empty(graph, no_of_nodes, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + IGRAPH_CHECK(igraph_add_edges(graph, edges, 0)); + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \function igraph_incidence + * \brief Creates a bipartite graph from an incidence matrix. + * + * A bipartite (or two-mode) graph contains two types of vertices and + * edges always connect vertices of different types. An incidence + * matrix is an nxm matrix, n and m are the number of vertices of the + * two types, respectively. Nonzero elements in the matrix denote + * edges between the two corresponding vertices. + * + * + * Note that this function can operate in two modes, depending on the + * \p multiple argument. If it is FALSE (i.e. 0), then a single edge is + * created for every non-zero element in the incidence matrix. If \p + * multiple is TRUE (i.e. 1), then the matrix elements are rounded up + * to the closest non-negative integer to get the number of edges to + * create between a pair of vertices. + * + * + * This function does not create multiple edges if \p multiple is + * \c FALSE, but might create some if it is \c TRUE. + * + * \param graph Pointer to an uninitialized graph object. + * \param types Pointer to an initialized boolean vector, or a null + * pointer. If not a null pointer, then the vertex types are stored + * here. It is resized as needed. + * \param incidence The incidence matrix. + * \param directed Gives whether to create an undirected or a directed + * graph. + * \param mode Specifies the direction of the edges in a directed + * graph. If \c IGRAPH_OUT, then edges point from vertices + * of the first kind (corresponding to rows) to vertices of the + * second kind (corresponding to columns); if \c + * IGRAPH_IN, then the opposite direction is realized; if \c + * IGRAPH_ALL, then mutual edges will be created. + * \param multiple How to interpret the incidence matrix elements. See + * details below. + * \return Error code. + * + * Time complexity: O(n*m), the size of the incidence matrix. + */ + +int igraph_incidence(igraph_t *graph, igraph_vector_bool_t *types, + const igraph_matrix_t *incidence, + igraph_bool_t directed, + igraph_neimode_t mode, igraph_bool_t multiple) { + + igraph_integer_t n1 = (igraph_integer_t) igraph_matrix_nrow(incidence); + igraph_integer_t n2 = (igraph_integer_t) igraph_matrix_ncol(incidence); + igraph_integer_t no_of_nodes = n1 + n2; + igraph_vector_t edges; + long int i, j, k; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + if (multiple) { + + for (i = 0; i < n1; i++) { + for (j = 0; j < n2; j++) { + long int elem = (long int) MATRIX(*incidence, i, j); + long int from, to; + + if (!elem) { + continue; + } + + if (mode == IGRAPH_IN) { + from = n1 + j; + to = i; + } else { + from = i; + to = n1 + j; + } + + if (mode != IGRAPH_ALL || !directed) { + for (k = 0; k < elem; k++) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + } + } else { + for (k = 0; k < elem; k++) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, from)); + } + } + } + } + + } else { + + for (i = 0; i < n1; i++) { + for (j = 0; j < n2; j++) { + long int from, to; + + if (MATRIX(*incidence, i, j) != 0) { + if (mode == IGRAPH_IN) { + from = n1 + j; + to = i; + } else { + from = i; + to = n1 + j; + } + if (mode != IGRAPH_ALL || !directed) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + } else { + IGRAPH_CHECK(igraph_vector_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, from)); + } + } + } + } + + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, graph); + + if (types) { + IGRAPH_CHECK(igraph_vector_bool_resize(types, no_of_nodes)); + igraph_vector_bool_null(types); + for (i = n1; i < no_of_nodes; i++) { + VECTOR(*types)[i] = 1; + } + } + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \function igraph_get_incidence + * \brief Convert a bipartite graph into an incidence matrix. + * + * \param graph The input graph, edge directions are ignored. + * \param types Boolean vector containing the vertex types. All vertices + * in one part of the graph should have type 0, the others type 1. + * \param res Pointer to an initialized matrix, the result is stored + * here. An element of the matrix gives the number of edges + * (irrespectively of their direction) between the two corresponding + * vertices. The rows will correspond to vertices with type 0, + * the columns correspond to vertices with type 1. + * \param row_ids Pointer to an initialized vector or a null + * pointer. If not a null pointer, then the vertex ids (in the + * graph) corresponding to the rows of the result matrix are stored + * here. + * \param col_ids Pointer to an initialized vector or a null + * pointer. If not a null pointer, then the vertex ids corresponding + * to the columns of the result matrix are stored here. + * \return Error code. + * + * Time complexity: O(n*m), n and m are number of vertices of the two + * different kind. + * + * \sa \ref igraph_incidence() for the opposite operation. + */ + +int igraph_get_incidence(const igraph_t *graph, + const igraph_vector_bool_t *types, + igraph_matrix_t *res, + igraph_vector_t *row_ids, + igraph_vector_t *col_ids) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int n1 = 0, n2 = 0, i; + igraph_vector_t perm; + long int p1, p2; + long int ignored_edges = 0; + + if (igraph_vector_bool_size(types) != no_of_nodes) { + IGRAPH_ERRORF("Vertex type vector size (%ld) not equal to number of vertices (%ld).", + IGRAPH_EINVAL, igraph_vector_bool_size(types), no_of_nodes); + } + + for (i = 0; i < no_of_nodes; i++) { + n1 += VECTOR(*types)[i] == 0 ? 1 : 0; + } + n2 = no_of_nodes - n1; + + IGRAPH_VECTOR_INIT_FINALLY(&perm, no_of_nodes); + + for (i = 0, p1 = 0, p2 = n1; i < no_of_nodes; i++) { + VECTOR(perm)[i] = VECTOR(*types)[i] ? p2++ : p1++; + } + + IGRAPH_CHECK(igraph_matrix_resize(res, n1, n2)); + igraph_matrix_null(res); + for (i = 0; i < no_of_edges; i++) { + long int from = IGRAPH_FROM(graph, i); + long int to = IGRAPH_TO(graph, i); + long int from2 = (long int) VECTOR(perm)[from]; + long int to2 = (long int) VECTOR(perm)[to]; + if (VECTOR(*types)[from] == VECTOR(*types)[to]) { + ignored_edges++; + } else if (! VECTOR(*types)[from]) { + MATRIX(*res, from2, to2 - n1) += 1; + } else { + MATRIX(*res, to2, from2 - n1) += 1; + } + } + if (ignored_edges) { + IGRAPH_WARNINGF("%ld edges running within partitions were ignored.", ignored_edges); + } + + if (row_ids) { + IGRAPH_CHECK(igraph_vector_resize(row_ids, n1)); + } + if (col_ids) { + IGRAPH_CHECK(igraph_vector_resize(col_ids, n2)); + } + if (row_ids || col_ids) { + for (i = 0; i < no_of_nodes; i++) { + if (! VECTOR(*types)[i]) { + if (row_ids) { + long int i2 = (long int) VECTOR(perm)[i]; + VECTOR(*row_ids)[i2] = i; + } + } else { + if (col_ids) { + long int i2 = (long int) VECTOR(perm)[i]; + VECTOR(*col_ids)[i2 - n1] = i; + } + } + } + } + + igraph_vector_destroy(&perm); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_is_bipartite + * \brief Check whether a graph is bipartite. + * + * This function checks whether a graph is bipartite. It tries + * to find a mapping that gives a possible division of the vertices into two + * classes, such that no two vertices of the same class are connected by an + * edge. + * + * + * The existence of such a mapping is equivalent of having no circuits of + * odd length in the graph. A graph with loop edges cannot be bipartite. + * + * + * Note that the mapping is not necessarily unique, e.g. if the graph has + * at least two components, then the vertices in the separate components + * can be mapped independently. + * + * \param graph The input graph. + * \param res Pointer to a boolean, the result is stored here. + * \param types Pointer to an initialized boolean vector, or a null + * pointer. If not a null pointer and a mapping was found, then it + * is stored here. If not a null pointer, but no mapping was found, + * the contents of this vector is invalid. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + */ + +int igraph_is_bipartite(const igraph_t *graph, + igraph_bool_t *res, + igraph_vector_bool_t *types) { + + /* We basically do a breadth first search and label the + vertices along the way. We stop as soon as we can find a + contradiction. + + In the 'seen' vector 0 means 'not seen yet', 1 means type 1, + 2 means type 2. + */ + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_char_t seen; + igraph_dqueue_t Q; + igraph_vector_t neis; + igraph_bool_t bi = 1; + long int i; + + IGRAPH_CHECK(igraph_vector_char_init(&seen, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_char_destroy, &seen); + IGRAPH_DQUEUE_INIT_FINALLY(&Q, 100); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + for (i = 0; bi && i < no_of_nodes; i++) { + + if (VECTOR(seen)[i]) { + continue; + } + + IGRAPH_CHECK(igraph_dqueue_push(&Q, i)); + VECTOR(seen)[i] = 1; + + while (bi && !igraph_dqueue_empty(&Q)) { + long int n, j; + igraph_integer_t actnode = (igraph_integer_t) igraph_dqueue_pop(&Q); + char acttype = VECTOR(seen)[actnode]; + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, IGRAPH_ALL)); + n = igraph_vector_size(&neis); + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(neis)[j]; + if (VECTOR(seen)[nei]) { + long int neitype = VECTOR(seen)[nei]; + if (neitype == acttype) { + bi = 0; + break; + } + } else { + VECTOR(seen)[nei] = 3 - acttype; + IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); + } + } + } + } + + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&Q); + IGRAPH_FINALLY_CLEAN(2); + + if (res) { + *res = bi; + } + + if (types && bi) { + IGRAPH_CHECK(igraph_vector_bool_resize(types, no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*types)[i] = VECTOR(seen)[i] - 1; + } + } + + igraph_vector_char_destroy(&seen); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +int igraph_bipartite_game_gnp(igraph_t *graph, igraph_vector_bool_t *types, + igraph_integer_t n1, igraph_integer_t n2, + igraph_real_t p, igraph_bool_t directed, + igraph_neimode_t mode) { + + int retval = 0; + igraph_vector_t edges, s; + int i; + + if (p < 0.0 || p > 1.0) { + IGRAPH_ERROR("Invalid connection probability", IGRAPH_EINVAL); + } + + if (types) { + IGRAPH_CHECK(igraph_vector_bool_resize(types, n1 + n2)); + igraph_vector_bool_null(types); + for (i = n1; i < n1 + n2; i++) { + VECTOR(*types)[i] = 1; + } + } + + if (p == 0 || n1 * n2 < 1) { + IGRAPH_CHECK(retval = igraph_empty(graph, n1 + n2, directed)); + } else if (p == 1.0) { + IGRAPH_CHECK(retval = igraph_full_bipartite(graph, types, n1, n2, directed, + mode)); + } else { + + long int to, from, slen; + double maxedges, last; + if (!directed || mode != IGRAPH_ALL) { + maxedges = (double) n1 * (double) n2; + } else { + maxedges = 2.0 * (double) n1 * (double) n2; + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + IGRAPH_CHECK(igraph_vector_reserve(&s, (long) (maxedges * p * 1.1))); + + RNG_BEGIN(); + + last = RNG_GEOM(p); + while (last < maxedges) { + IGRAPH_CHECK(igraph_vector_push_back(&s, last)); + last += RNG_GEOM(p); + last += 1; + } + + RNG_END(); + + slen = igraph_vector_size(&s); + IGRAPH_CHECK(igraph_vector_reserve(&edges, slen * 2)); + + for (i = 0; i < slen; i++) { + if (!directed || mode != IGRAPH_ALL) { + to = (long) floor(VECTOR(s)[i] / n1); + from = (long) (VECTOR(s)[i] - ((igraph_real_t) to) * n1); + to += n1; + } else { + long int n1n2 = n1 * n2; + if (VECTOR(s)[i] < n1n2) { + to = (long) floor(VECTOR(s)[i] / n1); + from = (long) (VECTOR(s)[i] - ((igraph_real_t) to) * n1); + to += n1; + } else { + to = (long) floor( (VECTOR(s)[i] - n1n2) / n2); + from = (long) (VECTOR(s)[i] - n1n2 - ((igraph_real_t) to) * n2); + from += n1; + } + } + + if (mode != IGRAPH_IN) { + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } else { + igraph_vector_push_back(&edges, to); + igraph_vector_push_back(&edges, from); + } + } + + igraph_vector_destroy(&s); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(retval = igraph_create(graph, &edges, n1 + n2, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + } + + return retval; +} + +int igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t *types, + igraph_integer_t n1, igraph_integer_t n2, + igraph_integer_t m, igraph_bool_t directed, + igraph_neimode_t mode) { + igraph_vector_t edges; + igraph_vector_t s; + int retval = 0; + + if (n1 < 0 || n2 < 0) { + IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); + } + if (m < 0) { + IGRAPH_ERROR("Invalid number of edges", IGRAPH_EINVAL); + } + + if (types) { + long int i; + IGRAPH_CHECK(igraph_vector_bool_resize(types, n1 + n2)); + igraph_vector_bool_null(types); + for (i = n1; i < n1 + n2; i++) { + VECTOR(*types)[i] = 1; + } + } + + if (m == 0 || n1 * n2 == 0) { + if (m > 0) { + IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL); + } + IGRAPH_CHECK(retval = igraph_empty(graph, n1 + n2, directed)); + } else { + + + long int i; + double maxedges; + if (!directed || mode != IGRAPH_ALL) { + maxedges = (double) n1 * (double) n2; + } else { + maxedges = 2.0 * (double) n1 * (double) n2; + } + + if (m > maxedges) { + IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL); + } + + if (maxedges == m) { + IGRAPH_CHECK(retval = igraph_full_bipartite(graph, types, n1, n2, + directed, mode)); + } else { + + long int to, from; + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + IGRAPH_CHECK(igraph_random_sample(&s, 0, maxedges - 1, m)); + IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&s) * 2)); + + for (i = 0; i < m; i++) { + if (!directed || mode != IGRAPH_ALL) { + to = (long) floor(VECTOR(s)[i] / n1); + from = (long) (VECTOR(s)[i] - ((igraph_real_t) to) * n1); + to += n1; + } else { + long int n1n2 = n1 * n2; + if (VECTOR(s)[i] < n1n2) { + to = (long) floor(VECTOR(s)[i] / n1); + from = (long) (VECTOR(s)[i] - ((igraph_real_t) to) * n1); + to += n1; + } else { + to = (long) floor( (VECTOR(s)[i] - n1n2) / n2); + from = (long) (VECTOR(s)[i] - n1n2 - ((igraph_real_t) to) * n2); + from += n1; + } + } + + if (mode != IGRAPH_IN) { + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } else { + igraph_vector_push_back(&edges, to); + igraph_vector_push_back(&edges, from); + } + } + + igraph_vector_destroy(&s); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(retval = igraph_create(graph, &edges, n1 + n2, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + } + } + + return retval; +} + +/** + * \function igraph_bipartite_game + * \brief Generate a bipartite random graph (similar to Erdős-Rényi). + * + * Similarly to unipartite (one-mode) networks, we can define the + * G(n,p), and G(n,m) graph classes for bipartite graphs, via their + * generating process. In G(n,p) every possible edge between top and + * bottom vertices is realized with probablity p, independently of the + * rest of the edges. In G(n,m), we uniformly choose m edges to + * realize. + * + * \param graph Pointer to an uninitialized igraph graph, the result + * is stored here. + * \param types Pointer to an initialized boolean vector, or a null + * pointer. If not a null pointer, then the vertex types are stored + * here. Bottom vertices come first, n1 of them, then n2 top + * vertices. + * \param type The type of the random graph, possible values: + * \clist + * \cli IGRAPH_ERDOS_RENYI_GNM + * G(n,m) graph, + * m edges are + * selected uniformly randomly in a graph with + * n vertices. + * \cli IGRAPH_ERDOS_RENYI_GNP + * G(n,p) graph, + * every possible edge is included in the graph with + * probability p. + * \endclist + * \param n1 The number of bottom vertices. + * \param n2 The number of top verices. + * \param p The connection probability for G(n,p) graphs. It is + * ignored for G(n,m) graphs. + * \param m The number of edges for G(n,m) graphs. It is ignored for + * G(n,p) graphs. + * \param directed Boolean, whether to generate a directed graph. See + * also the \p mode argument. + * \param mode Specifies how to direct the edges in directed + * graphs. If it is \c IGRAPH_OUT, then directed edges point from + * bottom vertices to top vertices. If it is \c IGRAPH_IN, edges + * point from top vertices to bottom vertices. \c IGRAPH_OUT and + * \c IGRAPH_IN do not generate mutual edges. If this argument is + * \c IGRAPH_ALL, then each edge direction is considered + * independently and mutual edges might be generated. This + * argument is ignored for undirected graphs. + * \return Error code. + * + * \sa \ref igraph_erdos_renyi_game. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + */ + +int igraph_bipartite_game(igraph_t *graph, igraph_vector_bool_t *types, + igraph_erdos_renyi_t type, + igraph_integer_t n1, igraph_integer_t n2, + igraph_real_t p, igraph_integer_t m, + igraph_bool_t directed, igraph_neimode_t mode) { + + if (n1 < 0 || n2 < 0) { + IGRAPH_ERROR("Invalid number of vertices for bipartite game.", IGRAPH_EINVAL); + } + + if (type == IGRAPH_ERDOS_RENYI_GNP) { + return igraph_bipartite_game_gnp(graph, types, n1, n2, p, directed, mode); + } else if (type == IGRAPH_ERDOS_RENYI_GNM) { + return igraph_bipartite_game_gnm(graph, types, n1, n2, m, directed, mode); + } else { + IGRAPH_ERROR("Invalid bipartite game type.", IGRAPH_EINVAL); + } +} diff --git a/src/rigraph/core/misc/chordality.c b/src/rigraph/core/misc/chordality.c new file mode 100644 index 0000000..fe57ff1 --- /dev/null +++ b/src/rigraph/core/misc/chordality.c @@ -0,0 +1,487 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2008-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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 . +*/ + +#include "igraph_structural.h" +#include "igraph_error.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" + +/** + * \function igraph_maximum_cardinality_search + * \brief Maximum cardinality search. + * + * This function implements the maximum cardinality search algorithm. + * It computes a rank \p alpha for each vertex, such that visiting + * vertices in decreasing rank order corresponds to always choosing + * the vertex with the most already visited neighbors as the next one + * to visit. + * + * + * Maximum cardinality search is useful in deciding the chordality + * of a graph. A graph is chordal if and only if any two neighbors + * of a vertex which are higher in rank than it are connected to + * each other. + * + * + * References: + * + * + * Robert E Tarjan and Mihalis Yannakakis: Simple linear-time + * algorithms to test chordality of graphs, test acyclicity of + * hypergraphs, and selectively reduce acyclic hypergraphs. + * SIAM Journal of Computation 13, 566--579, 1984. + * https://doi.org/10.1137/0213035 + * + * \param graph The input graph. Edge directions will be ignored. + * \param alpha Pointer to an initialized vector, the result is stored here. + * It will be resized, as needed. Upon return it contains + * the rank of the each vertex in the range 0 to n - 1, + * where \c n is the number of vertices. + * \param alpham1 Pointer to an initialized vector or a \c NULL + * pointer. If not \c NULL, then the inverse of \p alpha is stored + * here. In other words, the elements of \p alpham1 are vertex IDs + * in reverse maximum cardinality search order. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in terms of the number of + * vertices and edges. + * + * \sa \ref igraph_is_chordal(). + */ + +int igraph_maximum_cardinality_search(const igraph_t *graph, + igraph_vector_t *alpha, + igraph_vector_t *alpham1) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_long_t size; + igraph_vector_long_t head, next, prev; /* doubly linked list with head */ + long int i; + igraph_adjlist_t adjlist; + + /***************/ + /* local j, v; */ + /***************/ + + long int j, v; + + if (no_of_nodes == 0) { + igraph_vector_clear(alpha); + if (alpham1) { + igraph_vector_clear(alpham1); + } + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_vector_long_init(&size, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &size); + IGRAPH_CHECK(igraph_vector_long_init(&head, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &head); + IGRAPH_CHECK(igraph_vector_long_init(&next, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &next); + IGRAPH_CHECK(igraph_vector_long_init(&prev, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &prev); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + IGRAPH_CHECK(igraph_vector_resize(alpha, no_of_nodes)); + if (alpham1) { + IGRAPH_CHECK(igraph_vector_resize(alpham1, no_of_nodes)); + } + + /***********************************************/ + /* for i in [0,n-1] -> set(i) := emptyset rof; */ + /***********************************************/ + + /* nothing to do, 'head' contains all zeros */ + + /*********************************************************/ + /* for v in vertices -> size(v):=0; add v to set(0) rof; */ + /*********************************************************/ + + VECTOR(head)[0] = 1; + for (v = 0; v < no_of_nodes; v++) { + VECTOR(next)[v] = v + 2; + VECTOR(prev)[v] = v; + } + VECTOR(next)[no_of_nodes - 1] = 0; + /* size is already all zero */ + + /***************/ + /* i:=n; j:=0; */ + /***************/ + + i = no_of_nodes; j = 0; + + /**************/ + /* do i>=1 -> */ + /**************/ + + while (i >= 1) { + long int x, k, len; + igraph_vector_int_t *neis; + + /********************************/ + /* v := delete any from set(j) */ + /********************************/ + + v = VECTOR(head)[j] - 1; + x = VECTOR(next)[v]; + VECTOR(head)[j] = x; + if (x != 0) { + VECTOR(prev)[x - 1] = 0; + } + + /*************************************************/ + /* alpha(v) := i; alpham1(i) := v; size(v) := -1 */ + /*************************************************/ + + VECTOR(*alpha)[v] = i - 1; + if (alpham1) { + VECTOR(*alpham1)[i - 1] = v; + } + VECTOR(size)[v] = -1; + + /********************************************/ + /* for {v,w} in E such that size(w) >= 0 -> */ + /********************************************/ + + neis = igraph_adjlist_get(&adjlist, v); + len = igraph_vector_int_size(neis); + for (k = 0; k < len; k++) { + long int w = (long int) VECTOR(*neis)[k]; + long int ws = VECTOR(size)[w]; + if (ws >= 0) { + + /******************************/ + /* delete w from set(size(w)) */ + /******************************/ + + long int nw = VECTOR(next)[w]; + long int pw = VECTOR(prev)[w]; + if (nw != 0) { + VECTOR(prev)[nw - 1] = pw; + } + if (pw != 0) { + VECTOR(next)[pw - 1] = nw; + } else { + VECTOR(head)[ws] = nw; + } + + /******************************/ + /* size(w) := size(w)+1 */ + /******************************/ + + VECTOR(size)[w] += 1; + + /******************************/ + /* add w to set(size(w)) */ + /******************************/ + + ws = VECTOR(size)[w]; + nw = VECTOR(head)[ws]; + VECTOR(next)[w] = nw; + VECTOR(prev)[w] = 0; + if (nw != 0) { + VECTOR(prev)[nw - 1] = w + 1; + } + VECTOR(head)[ws] = w + 1; + + } + } + + /***********************/ + /* i := i-1; j := j+1; */ + /***********************/ + + i -= 1; + j += 1; + + /*********************************************/ + /* do j>=0 and set(j)=emptyset -> j:=j-1; od */ + /*********************************************/ + + if (j < no_of_nodes) { + while (j >= 0 && VECTOR(head)[j] == 0) { + j--; + } + } + } + + igraph_adjlist_destroy(&adjlist); + igraph_vector_long_destroy(&prev); + igraph_vector_long_destroy(&next); + igraph_vector_long_destroy(&head); + igraph_vector_long_destroy(&size); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_is_chordal + * \brief Decides whether a graph is chordal. + * + * A graph is chordal if each of its cycles of four or more nodes + * has a chord, i.e. an edge joining two nodes that are not + * adjacent in the cycle. An equivalent definition is that any + * chordless cycles have at most three nodes. + * + * If either \p alpha or \p alpham1 is given, then the other is + * calculated by taking simply the inverse. If neither are given, + * then \ref igraph_maximum_cardinality_search() is called to calculate + * them. + * + * \param graph The input graph. Edge directions will be ignored. + * \param alpha Either an alpha vector coming from + * \ref igraph_maximum_cardinality_search() (on the same graph), or a + * \c NULL pointer. + * \param alpham1 Either an inverse alpha vector coming from \ref + * igraph_maximum_cardinality_search() (on the same graph) or a \c NULL + * pointer. + * \param chordal Pointer to a boolean. If not NULL the result is stored here. + * \param fill_in Pointer to an initialized vector, or a \c NULL + * pointer. If not a \c NULL pointer, then the fill-in, also called the + * chordal completion of the graph is stored here. + * The chordal completion is a set of edges that are needed to + * make the graph chordal. The vector is resized as needed. + * Note that the chordal completion returned by this function may not + * be minimal, i.e. some of the returned fill-in edges may not be needed + * to make the graph chordal. + * \param newgraph Pointer to an uninitialized graph, or a \c NULL + * pointer. If not a null pointer, then a new triangulated graph is + * created here. This essentially means adding the fill-in edges to + * the original graph. + * \return Error code. + * + * Time complexity: O(n). + * + * \sa \ref igraph_maximum_cardinality_search(). + */ + +int igraph_is_chordal(const igraph_t *graph, + const igraph_vector_t *alpha, + const igraph_vector_t *alpham1, + igraph_bool_t *chordal, + igraph_vector_t *fill_in, + igraph_t *newgraph) { + + long int no_of_nodes = igraph_vcount(graph); + const igraph_vector_t *my_alpha = alpha, *my_alpham1 = alpham1; + igraph_vector_t v_alpha, v_alpham1; + igraph_vector_long_t f, index; + long int i; + igraph_adjlist_t adjlist; + igraph_vector_long_t mark; + igraph_bool_t calc_edges = fill_in || newgraph; + igraph_vector_t *my_fill_in = fill_in, v_fill_in; + + /*****************/ + /* local v, w, x */ + /*****************/ + + long int v, w, x; + + if (alpha && (igraph_vector_size(alpha) != no_of_nodes)) { + IGRAPH_ERRORF("Alpha vector size (%ld) not equal to number of nodes (%ld).", + IGRAPH_EINVAL, igraph_vector_size(alpha), no_of_nodes); + } + + if (alpham1 && (igraph_vector_size(alpham1) != no_of_nodes)) { + IGRAPH_ERRORF("Inverse alpha vector size (%ld) not equal to number of nodes (%ld).", + IGRAPH_EINVAL, igraph_vector_size(alpham1), no_of_nodes); + } + + if (!chordal && !calc_edges) { + /* Nothing to calculate */ + return IGRAPH_SUCCESS; + } + + if (!alpha && !alpham1) { + IGRAPH_VECTOR_INIT_FINALLY(&v_alpha, no_of_nodes); + my_alpha = &v_alpha; + IGRAPH_VECTOR_INIT_FINALLY(&v_alpham1, no_of_nodes); + my_alpham1 = &v_alpham1; + IGRAPH_CHECK(igraph_maximum_cardinality_search(graph, + (igraph_vector_t*) my_alpha, + (igraph_vector_t*) my_alpham1)); + } else if (alpha && !alpham1) { + long int v; + IGRAPH_VECTOR_INIT_FINALLY(&v_alpham1, no_of_nodes); + my_alpham1 = &v_alpham1; + for (v = 0; v < no_of_nodes; v++) { + long int i = (long int) VECTOR(*my_alpha)[v]; + VECTOR(*my_alpham1)[i] = v; + } + } else if (!alpha && alpham1) { + long int i; + IGRAPH_VECTOR_INIT_FINALLY(&v_alpha, no_of_nodes); + my_alpha = &v_alpha; + for (i = 0; i < no_of_nodes; i++) { + long int v = (long int) VECTOR(*my_alpham1)[i]; + VECTOR(*my_alpha)[v] = i; + } + } + + if (!fill_in && newgraph) { + IGRAPH_VECTOR_INIT_FINALLY(&v_fill_in, 0); + my_fill_in = &v_fill_in; + } + + IGRAPH_CHECK(igraph_vector_long_init(&f, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &f); + IGRAPH_CHECK(igraph_vector_long_init(&index, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &index); + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + IGRAPH_CHECK(igraph_vector_long_init(&mark, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &mark); + if (my_fill_in) { + igraph_vector_clear(my_fill_in); + } + + if (chordal) { + *chordal = 1; + } + + /*********************/ + /* for i in [1,n] -> */ + /*********************/ + + for (i = 0; i < no_of_nodes; i++) { + igraph_vector_int_t *neis; + long int j, len; + + /**********************************************/ + /* w := alpham1(i); f(w) := w; index(w) := i; */ + /**********************************************/ + + w = (long int) VECTOR(*my_alpham1)[i]; + VECTOR(f)[w] = w; + VECTOR(index)[w] = i; + + /******************************************/ + /* for {v,w} in E such that alpha(v) */ + /******************************************/ + + neis = igraph_adjlist_get(&adjlist, w); + len = igraph_vector_int_size(neis); + for (j = 0; j < len; j++) { + v = (long int) VECTOR(*neis)[j]; + VECTOR(mark)[v] = w + 1; + } + + for (j = 0; j < len; j++) { + v = (long int) VECTOR(*neis)[j]; + if (VECTOR(*my_alpha)[v] >= i) { + continue; + } + + /**********/ + /* x := v */ + /**********/ + + x = v; + + /********************/ + /* do index(x) */ + /********************/ + + while (VECTOR(index)[x] < i) { + + /******************/ + /* index(x) := i; */ + /******************/ + + VECTOR(index)[x] = i; + + /**********************************/ + /* add {x,w} to E union F(alpha); */ + /**********************************/ + + if (VECTOR(mark)[x] != w + 1) { + + if (chordal) { + *chordal = 0; + } + + if (my_fill_in) { + IGRAPH_CHECK(igraph_vector_push_back(my_fill_in, x)); + IGRAPH_CHECK(igraph_vector_push_back(my_fill_in, w)); + } + + if (!calc_edges) { + /* make sure that we exit from all loops */ + i = no_of_nodes; + j = len; + break; + } + } + + /*************/ + /* x := f(x) */ + /*************/ + + x = VECTOR(f)[x]; + + } /* while (VECTOR(index)[x] < i) */ + + /*****************************/ + /* if (f(x)=x -> f(x):=w; fi */ + /*****************************/ + + if (VECTOR(f)[x] == x) { + VECTOR(f)[x] = w; + } + } + } + + igraph_vector_long_destroy(&mark); + igraph_adjlist_destroy(&adjlist); + igraph_vector_long_destroy(&index); + igraph_vector_long_destroy(&f); + IGRAPH_FINALLY_CLEAN(4); + + if (newgraph) { + IGRAPH_CHECK(igraph_copy(newgraph, graph)); + IGRAPH_FINALLY(igraph_destroy, newgraph); + IGRAPH_CHECK(igraph_add_edges(newgraph, my_fill_in, 0)); + IGRAPH_FINALLY_CLEAN(1); + } + + if (!fill_in && newgraph) { + igraph_vector_destroy(&v_fill_in); + IGRAPH_FINALLY_CLEAN(1); + } + + if (!alpha && !alpham1) { + igraph_vector_destroy(&v_alpham1); + igraph_vector_destroy(&v_alpha); + IGRAPH_FINALLY_CLEAN(2); + } else if (alpha && !alpham1) { + igraph_vector_destroy(&v_alpham1); + IGRAPH_FINALLY_CLEAN(1); + } else if (!alpha && alpham1) { + igraph_vector_destroy(&v_alpha); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/misc/cocitation.c b/src/rigraph/core/misc/cocitation.c new file mode 100644 index 0000000..4b59420 --- /dev/null +++ b/src/rigraph/core/misc/cocitation.c @@ -0,0 +1,779 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph R package. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_cocitation.h" +#include "igraph_memory.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" + +#include "../core/interruption.h" + +#include + +int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, + igraph_vs_t vids, igraph_neimode_t mode, + igraph_vector_t *weights); + +/** + * \ingroup structural + * \function igraph_cocitation + * \brief Cocitation coupling. + * + * + * Two vertices are cocited if there is another vertex citing both of + * them. \ref igraph_cocitation() simply counts how many times two vertices are + * cocited. + * The cocitation score for each given vertex and all other vertices + * in the graph will be calculated. + * \param graph The graph object to analyze. + * \param res Pointer to a matrix, the result of the calculation will + * be stored here. The number of its rows is the same as the + * number of vertex ids in \p vids, the number of + * columns is the number of vertices in the graph. + * \param vids The vertex ids of the vertices for which the + * calculation will be done. + * \return Error code: + * \c IGRAPH_EINVVID: invalid vertex id. + * + * Time complexity: O(|V|d^2), |V| is + * the number of vertices in the graph, + * d is the (maximum) degree of + * the vertices in the graph. + * + * \sa \ref igraph_bibcoupling() + * + * \example examples/simple/igraph_cocitation.c + */ + +int igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t vids) { + return igraph_cocitation_real(graph, res, vids, IGRAPH_OUT, 0); +} + +/** + * \ingroup structural + * \function igraph_bibcoupling + * \brief Bibliographic coupling. + * + * + * The bibliographic coupling of two vertices is the number + * of other vertices they both cite, \ref igraph_bibcoupling() calculates + * this. + * The bibliographic coupling score for each given vertex and all + * other vertices in the graph will be calculated. + * \param graph The graph object to analyze. + * \param res Pointer to a matrix, the result of the calculation will + * be stored here. The number of its rows is the same as the + * number of vertex ids in \p vids, the number of + * columns is the number of vertices in the graph. + * \param vids The vertex ids of the vertices for which the + * calculation will be done. + * \return Error code: + * \c IGRAPH_EINVVID: invalid vertex id. + * + * Time complexity: O(|V|d^2), + * |V| is the number of vertices in + * the graph, d is the (maximum) + * degree of the vertices in the graph. + * + * \sa \ref igraph_cocitation() + * + * \example examples/simple/igraph_cocitation.c + */ + +int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t vids) { + return igraph_cocitation_real(graph, res, vids, IGRAPH_IN, 0); +} + +/** + * \ingroup structural + * \function igraph_similarity_inverse_log_weighted + * \brief Vertex similarity based on the inverse logarithm of vertex degrees. + * + * + * The inverse log-weighted similarity of two vertices is the number of + * their common neighbors, weighted by the inverse logarithm of their degrees. + * It is based on the assumption that two vertices should be considered + * more similar if they share a low-degree common neighbor, since high-degree + * common neighbors are more likely to appear even by pure chance. + * + * + * Isolated vertices will have zero similarity to any other vertex. + * Self-similarities are not calculated. + * + * + * See the following paper for more details: Lada A. Adamic and Eytan Adar: + * Friends and neighbors on the Web. Social Networks, 25(3):211-230, 2003. + * + * \param graph The graph object to analyze. + * \param res Pointer to a matrix, the result of the calculation will + * be stored here. The number of its rows is the same as the + * number of vertex ids in \p vids, the number of + * columns is the number of vertices in the graph. + * \param vids The vertex ids of the vertices for which the + * calculation will be done. + * \param mode The type of neighbors to be used for the calculation in + * directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the outgoing edges will be considered for each node. Nodes + * will be weighted according to their in-degree. + * \cli IGRAPH_IN + * the incoming edges will be considered for each node. Nodes + * will be weighted according to their out-degree. + * \cli IGRAPH_ALL + * the directed graph is considered as an undirected one for the + * computation. Every node is weighted according to its undirected + * degree. + * \endclist + * \return Error code: + * \c IGRAPH_EINVVID: invalid vertex id. + * + * Time complexity: O(|V|d^2), + * |V| is the number of vertices in + * the graph, d is the (maximum) + * degree of the vertices in the graph. + * + * \example examples/simple/igraph_similarity.c + */ + +int igraph_similarity_inverse_log_weighted(const igraph_t *graph, + igraph_matrix_t *res, const igraph_vs_t vids, igraph_neimode_t mode) { + igraph_vector_t weights; + igraph_neimode_t mode0; + long int i, no_of_nodes; + + switch (mode) { + case IGRAPH_OUT: mode0 = IGRAPH_IN; break; + case IGRAPH_IN: mode0 = IGRAPH_OUT; break; + default: mode0 = IGRAPH_ALL; + } + + no_of_nodes = igraph_vcount(graph); + + IGRAPH_VECTOR_INIT_FINALLY(&weights, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, &weights, igraph_vss_all(), mode0, 1)); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(weights)[i] > 1) { + VECTOR(weights)[i] = 1.0 / log(VECTOR(weights)[i]); + } + } + + IGRAPH_CHECK(igraph_cocitation_real(graph, res, vids, mode0, &weights)); + igraph_vector_destroy(&weights); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, + igraph_vs_t vids, + igraph_neimode_t mode, + igraph_vector_t *weights) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_vids; + long int from, i, j, k, l, u, v; + igraph_vector_t neis = IGRAPH_VECTOR_NULL; + igraph_vector_t vid_reverse_index; + igraph_vit_t vit; + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + no_of_vids = IGRAPH_VIT_SIZE(vit); + + /* Create a mapping from vertex IDs to the row of the matrix where + * the result for this vertex will appear */ + IGRAPH_VECTOR_INIT_FINALLY(&vid_reverse_index, no_of_nodes); + igraph_vector_fill(&vid_reverse_index, -1); + for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + v = IGRAPH_VIT_GET(vit); + if (v < 0 || v >= no_of_nodes) { + IGRAPH_ERROR("invalid vertex ID in vertex selector", IGRAPH_EINVAL); + } + VECTOR(vid_reverse_index)[v] = i; + } + + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_vids, no_of_nodes)); + igraph_matrix_null(res); + + /* The result */ + + for (from = 0; from < no_of_nodes; from++) { + igraph_real_t weight = 1; + + IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, + (igraph_integer_t) from, mode)); + if (weights) { + weight = VECTOR(*weights)[from]; + } + + for (i = 0; i < igraph_vector_size(&neis) - 1; i++) { + u = (long int) VECTOR(neis)[i]; + k = (long int) VECTOR(vid_reverse_index)[u]; + for (j = i + 1; j < igraph_vector_size(&neis); j++) { + v = (long int) VECTOR(neis)[j]; + l = (long int) VECTOR(vid_reverse_index)[v]; + if (k != -1) { + MATRIX(*res, k, v) += weight; + } + if (l != -1) { + MATRIX(*res, l, u) += weight; + } + } + } + } + + /* Clean up */ + igraph_vector_destroy(&neis); + igraph_vector_destroy(&vid_reverse_index); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + + +static int igraph_i_neisets_intersect(const igraph_vector_int_t *v1, + const igraph_vector_int_t *v2, long int *len_union, + long int *len_intersection) { + /* ASSERT: v1 and v2 are sorted */ + long int i, j, i0, jj0; + i0 = igraph_vector_int_size(v1); jj0 = igraph_vector_int_size(v2); + *len_union = i0 + jj0; *len_intersection = 0; + i = 0; j = 0; + while (i < i0 && j < jj0) { + if (VECTOR(*v1)[i] == VECTOR(*v2)[j]) { + (*len_intersection)++; (*len_union)--; + i++; j++; + } else if (VECTOR(*v1)[i] < VECTOR(*v2)[j]) { + i++; + } else { + j++; + } + } + return 0; +} + +/** + * \ingroup structural + * \function igraph_similarity_jaccard + * \brief Jaccard similarity coefficient for the given vertices. + * + * + * The Jaccard similarity coefficient of two vertices is the number of common + * neighbors divided by the number of vertices that are neighbors of at + * least one of the two vertices being considered. This function calculates + * the pairwise Jaccard similarities for some (or all) of the vertices. + * + * \param graph The graph object to analyze + * \param res Pointer to a matrix, the result of the calculation will + * be stored here. The number of its rows and columns is the same + * as the number of vertex ids in \p vids. + * \param vids The vertex ids of the vertices for which the + * calculation will be done. + * \param mode The type of neighbors to be used for the calculation in + * directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the outgoing edges will be considered for each node. + * \cli IGRAPH_IN + * the incoming edges will be considered for each node. + * \cli IGRAPH_ALL + * the directed graph is considered as an undirected one for the + * computation. + * \endclist + * \param loops Whether to include the vertices themselves in the neighbor + * sets. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * invalid vertex id passed. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(|V|^2 d), + * |V| is the number of vertices in the vertex iterator given, d is the + * (maximum) degree of the vertices in the graph. + * + * \sa \ref igraph_similarity_dice(), a measure very similar to the Jaccard + * coefficient + * + * \example examples/simple/igraph_similarity.c + */ +int igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops) { + igraph_lazy_adjlist_t al; + igraph_vit_t vit, vit2; + long int i, j, k; + long int len_union, len_intersection; + igraph_vector_int_t *v1, *v2; + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit2)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit2); + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &al, mode, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &al); + + IGRAPH_CHECK(igraph_matrix_resize(res, IGRAPH_VIT_SIZE(vit), IGRAPH_VIT_SIZE(vit))); + + if (loops) { + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + i = IGRAPH_VIT_GET(vit); + v1 = igraph_lazy_adjlist_get(&al, (igraph_integer_t) i); + if (!igraph_vector_int_binsearch(v1, i, &k)) { + igraph_vector_int_insert(v1, k, i); + } + } + } + + for (IGRAPH_VIT_RESET(vit), i = 0; + !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + MATRIX(*res, i, i) = 1.0; + for (IGRAPH_VIT_RESET(vit2), j = 0; + !IGRAPH_VIT_END(vit2); IGRAPH_VIT_NEXT(vit2), j++) { + if (j <= i) { + continue; + } + v1 = igraph_lazy_adjlist_get(&al, IGRAPH_VIT_GET(vit)); + v2 = igraph_lazy_adjlist_get(&al, IGRAPH_VIT_GET(vit2)); + igraph_i_neisets_intersect(v1, v2, &len_union, &len_intersection); + if (len_union > 0) { + MATRIX(*res, i, j) = ((igraph_real_t)len_intersection) / len_union; + } else { + MATRIX(*res, i, j) = 0.0; + } + MATRIX(*res, j, i) = MATRIX(*res, i, j); + } + } + + igraph_lazy_adjlist_destroy(&al); + igraph_vit_destroy(&vit); + igraph_vit_destroy(&vit2); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \ingroup structural + * \function igraph_similarity_jaccard_pairs + * \brief Jaccard similarity coefficient for given vertex pairs. + * + * + * The Jaccard similarity coefficient of two vertices is the number of common + * neighbors divided by the number of vertices that are neighbors of at + * least one of the two vertices being considered. This function calculates + * the pairwise Jaccard similarities for a list of vertex pairs. + * + * \param graph The graph object to analyze + * \param res Pointer to a vector, the result of the calculation will + * be stored here. The number of elements is the same as the number + * of pairs in \p pairs. + * \param pairs A vector that contains the pairs for which the similarity + * will be calculated. Each pair is defined by two consecutive elements, + * i.e. the first and second element of the vector specifies the first + * pair, the third and fourth element specifies the second pair and so on. + * \param mode The type of neighbors to be used for the calculation in + * directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the outgoing edges will be considered for each node. + * \cli IGRAPH_IN + * the incoming edges will be considered for each node. + * \cli IGRAPH_ALL + * the directed graph is considered as an undirected one for the + * computation. + * \endclist + * \param loops Whether to include the vertices themselves in the neighbor + * sets. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * invalid vertex id passed. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(nd), n is the number of pairs in the given vector, d is + * the (maximum) degree of the vertices in the graph. + * + * \sa \ref igraph_similarity_jaccard() to calculate the Jaccard similarity + * between all pairs of a vertex set, or \ref igraph_similarity_dice() and + * \ref igraph_similarity_dice_pairs() for a measure very similar to the + * Jaccard coefficient + * + * \example examples/simple/igraph_similarity.c + */ +int igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_t *pairs, igraph_neimode_t mode, igraph_bool_t loops) { + igraph_lazy_adjlist_t al; + long int i, j, k, u, v; + long int len_union, len_intersection; + igraph_vector_int_t *v1, *v2; + igraph_bool_t *seen; + + k = igraph_vector_size(pairs); + if (k % 2 != 0) { + IGRAPH_ERROR("number of elements in `pairs' must be even", IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_vector_resize(res, k / 2)); + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &al, mode, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &al); + + if (loops) { + /* Add the loop edges */ + i = igraph_vcount(graph); + seen = IGRAPH_CALLOC(i, igraph_bool_t); + if (seen == 0) { + IGRAPH_ERROR("cannot calculate Jaccard similarity", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, seen); + + for (i = 0; i < k; i++) { + j = (long int) VECTOR(*pairs)[i]; + if (seen[j]) { + continue; + } + seen[j] = 1; + v1 = igraph_lazy_adjlist_get(&al, (igraph_integer_t) j); + if (!igraph_vector_int_binsearch(v1, j, &u)) { + igraph_vector_int_insert(v1, u, j); + } + } + + IGRAPH_FREE(seen); + IGRAPH_FINALLY_CLEAN(1); + } + + for (i = 0, j = 0; i < k; i += 2, j++) { + u = (long int) VECTOR(*pairs)[i]; + v = (long int) VECTOR(*pairs)[i + 1]; + + if (u == v) { + VECTOR(*res)[j] = 1.0; + continue; + } + + v1 = igraph_lazy_adjlist_get(&al, (igraph_integer_t) u); + v2 = igraph_lazy_adjlist_get(&al, (igraph_integer_t) v); + igraph_i_neisets_intersect(v1, v2, &len_union, &len_intersection); + if (len_union > 0) { + VECTOR(*res)[j] = ((igraph_real_t)len_intersection) / len_union; + } else { + VECTOR(*res)[j] = 0.0; + } + } + + igraph_lazy_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \ingroup structural + * \function igraph_similarity_jaccard_es + * \brief Jaccard similarity coefficient for a given edge selector. + * + * + * The Jaccard similarity coefficient of two vertices is the number of common + * neighbors divided by the number of vertices that are neighbors of at + * least one of the two vertices being considered. This function calculates + * the pairwise Jaccard similarities for the endpoints of edges in a given edge + * selector. + * + * \param graph The graph object to analyze + * \param res Pointer to a vector, the result of the calculation will + * be stored here. The number of elements is the same as the number + * of edges in \p es. + * \param es An edge selector that specifies the edges to be included in the + * result. + * \param mode The type of neighbors to be used for the calculation in + * directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the outgoing edges will be considered for each node. + * \cli IGRAPH_IN + * the incoming edges will be considered for each node. + * \cli IGRAPH_ALL + * the directed graph is considered as an undirected one for the + * computation. + * \endclist + * \param loops Whether to include the vertices themselves in the neighbor + * sets. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * invalid vertex id passed. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(nd), n is the number of edges in the edge selector, d is + * the (maximum) degree of the vertices in the graph. + * + * \sa \ref igraph_similarity_jaccard() and \ref igraph_similarity_jaccard_pairs() + * to calculate the Jaccard similarity between all pairs of a vertex set or + * some selected vertex pairs, or \ref igraph_similarity_dice(), + * \ref igraph_similarity_dice_pairs() and \ref igraph_similarity_dice_es() for a + * measure very similar to the Jaccard coefficient + * + * \example examples/simple/igraph_similarity.c + */ +int igraph_similarity_jaccard_es(const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t es, igraph_neimode_t mode, igraph_bool_t loops) { + igraph_vector_t v; + igraph_eit_t eit; + + IGRAPH_VECTOR_INIT_FINALLY(&v, 0); + + IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + while (!IGRAPH_EIT_END(eit)) { + long int eid = IGRAPH_EIT_GET(eit); + igraph_vector_push_back(&v, IGRAPH_FROM(graph, eid)); + igraph_vector_push_back(&v, IGRAPH_TO(graph, eid)); + IGRAPH_EIT_NEXT(eit); + } + + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_similarity_jaccard_pairs(graph, res, &v, mode, loops)); + igraph_vector_destroy(&v); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_similarity_dice + * \brief Dice similarity coefficient. + * + * + * The Dice similarity coefficient of two vertices is twice the number of common + * neighbors divided by the sum of the degrees of the vertices. This function + * calculates the pairwise Dice similarities for some (or all) of the vertices. + * + * \param graph The graph object to analyze + * \param res Pointer to a matrix, the result of the calculation will + * be stored here. The number of its rows and columns is the same + * as the number of vertex ids in \p vids. + * \param vids The vertex ids of the vertices for which the + * calculation will be done. + * \param mode The type of neighbors to be used for the calculation in + * directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the outgoing edges will be considered for each node. + * \cli IGRAPH_IN + * the incoming edges will be considered for each node. + * \cli IGRAPH_ALL + * the directed graph is considered as an undirected one for the + * computation. + * \endclist + * \param loops Whether to include the vertices themselves as their own + * neighbors. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * invalid vertex id passed. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(|V|^2 d), + * |V| is the number of vertices in the vertex iterator given, d is the + * (maximum) degree of the vertices in the graph. + * + * \sa \ref igraph_similarity_jaccard(), a measure very similar to the Dice + * coefficient + * + * \example examples/simple/igraph_similarity.c + */ +int igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops) { + long int i, j, nr, nc; + + IGRAPH_CHECK(igraph_similarity_jaccard(graph, res, vids, mode, loops)); + + nr = igraph_matrix_nrow(res); + nc = igraph_matrix_ncol(res); + for (i = 0; i < nr; i++) { + for (j = 0; j < nc; j++) { + igraph_real_t x = MATRIX(*res, i, j); + MATRIX(*res, i, j) = 2 * x / (1 + x); + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_similarity_dice_pairs + * \brief Dice similarity coefficient for given vertex pairs. + * + * + * The Dice similarity coefficient of two vertices is twice the number of common + * neighbors divided by the sum of the degrees of the vertices. This function + * calculates the pairwise Dice similarities for a list of vertex pairs. + * + * \param graph The graph object to analyze + * \param res Pointer to a vector, the result of the calculation will + * be stored here. The number of elements is the same as the number + * of pairs in \p pairs. + * \param pairs A vector that contains the pairs for which the similarity + * will be calculated. Each pair is defined by two consecutive elements, + * i.e. the first and second element of the vector specifies the first + * pair, the third and fourth element specifies the second pair and so on. + * \param mode The type of neighbors to be used for the calculation in + * directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the outgoing edges will be considered for each node. + * \cli IGRAPH_IN + * the incoming edges will be considered for each node. + * \cli IGRAPH_ALL + * the directed graph is considered as an undirected one for the + * computation. + * \endclist + * \param loops Whether to include the vertices themselves as their own + * neighbors. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * invalid vertex id passed. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(nd), n is the number of pairs in the given vector, d is + * the (maximum) degree of the vertices in the graph. + * + * \sa \ref igraph_similarity_dice() to calculate the Dice similarity + * between all pairs of a vertex set, or \ref igraph_similarity_jaccard(), + * \ref igraph_similarity_jaccard_pairs() and \ref igraph_similarity_jaccard_es() + * for a measure very similar to the Dice coefficient + * + * \example examples/simple/igraph_similarity.c + */ +int igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_t *pairs, igraph_neimode_t mode, igraph_bool_t loops) { + long int i, n; + + IGRAPH_CHECK(igraph_similarity_jaccard_pairs(graph, res, pairs, mode, loops)); + n = igraph_vector_size(res); + for (i = 0; i < n; i++) { + igraph_real_t x = VECTOR(*res)[i]; + VECTOR(*res)[i] = 2 * x / (1 + x); + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_similarity_dice_es + * \brief Dice similarity coefficient for a given edge selector. + * + * + * The Dice similarity coefficient of two vertices is twice the number of common + * neighbors divided by the sum of the degrees of the vertices. This function + * calculates the pairwise Dice similarities for the endpoints of edges in a given + * edge selector. + * + * \param graph The graph object to analyze + * \param res Pointer to a vector, the result of the calculation will + * be stored here. The number of elements is the same as the number + * of edges in \p es. + * \param es An edge selector that specifies the edges to be included in the + * result. + * \param mode The type of neighbors to be used for the calculation in + * directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the outgoing edges will be considered for each node. + * \cli IGRAPH_IN + * the incoming edges will be considered for each node. + * \cli IGRAPH_ALL + * the directed graph is considered as an undirected one for the + * computation. + * \endclist + * \param loops Whether to include the vertices themselves as their own + * neighbors. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * invalid vertex id passed. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(nd), n is the number of pairs in the given vector, d is + * the (maximum) degree of the vertices in the graph. + * + * \sa \ref igraph_similarity_dice() and \ref igraph_similarity_dice_pairs() + * to calculate the Dice similarity between all pairs of a vertex set or + * some selected vertex pairs, or \ref igraph_similarity_jaccard(), + * \ref igraph_similarity_jaccard_pairs() and \ref igraph_similarity_jaccard_es() + * for a measure very similar to the Dice coefficient + * + * \example examples/simple/igraph_similarity.c + */ +int igraph_similarity_dice_es(const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t es, igraph_neimode_t mode, igraph_bool_t loops) { + long int i, n; + + IGRAPH_CHECK(igraph_similarity_jaccard_es(graph, res, es, mode, loops)); + n = igraph_vector_size(res); + for (i = 0; i < n; i++) { + igraph_real_t x = VECTOR(*res)[i]; + VECTOR(*res)[i] = 2 * x / (1 + x); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/misc/coloring.c b/src/rigraph/core/misc/coloring.c new file mode 100644 index 0000000..6fc0231 --- /dev/null +++ b/src/rigraph/core/misc/coloring.c @@ -0,0 +1,161 @@ +/* + Heuristic graph coloring algorithms. + Copyright (C) 2017 Szabolcs Horvat + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "igraph_coloring.h" +#include "igraph_interface.h" +#include "igraph_adjlist.h" + +#include "../core/indheap.h" +#include "../core/interruption.h" + +static int igraph_i_vertex_coloring_greedy_cn(const igraph_t *graph, igraph_vector_int_t *colors) { + long i, vertex, maxdeg; + long vc = igraph_vcount(graph); + igraph_2wheap_t cn; /* indexed heap storing number of already coloured neighbours */ + igraph_vector_int_t neigh_colors; + igraph_adjlist_t adjlist; + + IGRAPH_CHECK(igraph_vector_int_resize(colors, vc)); + igraph_vector_int_fill(colors, 0); + + /* Nothing to do for 0 or 1 vertices. + * Remember that colours are integers starting from 0, + * and the 'colors' vector is already 0-initialized above. + */ + if (vc <= 1) { + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + /* find maximum degree and a corresponding vertex */ + { + igraph_vector_t degree; + + IGRAPH_CHECK(igraph_vector_init(°ree, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, °ree); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, 0)); + + vertex = igraph_vector_which_max(°ree); + maxdeg = VECTOR(degree)[vertex]; + + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + } + + IGRAPH_CHECK(igraph_vector_int_init(&neigh_colors, 0)); + IGRAPH_CHECK(igraph_vector_int_reserve(&neigh_colors, maxdeg)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &neigh_colors); + + IGRAPH_CHECK(igraph_2wheap_init(&cn, vc)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &cn); + for (i = 0; i < vc; ++i) + if (i != vertex) { + igraph_2wheap_push_with_index(&cn, i, 0); /* should not fail since memory was already reserved */ + } + + while (1) { + igraph_vector_int_t *neighbors = igraph_adjlist_get(&adjlist, vertex); + long neigh_count = igraph_vector_int_size(neighbors); + + /* colour current vertex */ + { + igraph_integer_t col; + + IGRAPH_CHECK(igraph_vector_int_resize(&neigh_colors, neigh_count)); + for (i = 0; i < neigh_count; ++i) { + VECTOR(neigh_colors)[i] = VECTOR(*colors)[ VECTOR(*neighbors)[i] ]; + } + igraph_vector_int_sort(&neigh_colors); + + i = 0; + col = 0; + do { + while (i < neigh_count && VECTOR(neigh_colors)[i] == col) { + i++; + } + col++; + } while (i < neigh_count && VECTOR(neigh_colors)[i] == col); + + VECTOR(*colors)[vertex] = col; + } + + /* increment number of coloured neighbours for each neighbour of vertex */ + for (i = 0; i < neigh_count; ++i) { + long idx = VECTOR(*neighbors)[i]; + if (igraph_2wheap_has_elem(&cn, idx)) { + igraph_2wheap_modify(&cn, idx, igraph_2wheap_get(&cn, idx) + 1); + } + } + + /* stop if no more vertices left to colour */ + if (igraph_2wheap_empty(&cn)) { + break; + } + + igraph_2wheap_delete_max_index(&cn, &vertex); + + IGRAPH_ALLOW_INTERRUPTION(); + } + + /* subtract 1 from each colour value, so that colours start at 0 */ + igraph_vector_int_add_constant(colors, -1); + + /* free data structures */ + igraph_vector_int_destroy(&neigh_colors); + igraph_adjlist_destroy(&adjlist); + igraph_2wheap_destroy(&cn); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_vertex_coloring_greedy + * \brief Computes a vertex coloring using a greedy algorithm. + * + * + * This function assigns a "color"—represented as a non-negative integer—to + * each vertex of the graph in such a way that neighboring vertices never have + * the same color. The obtained coloring is not necessarily minimal. + * + * + * Vertices are colored one by one, choosing the smallest color index that + * differs from that of already colored neighbors. + * Colors are represented with non-negative integers 0, 1, 2, ... + * + * \param graph The input graph. + * \param colors Pointer to an initialized integer vector. The vertex colors will be stored here. + * \param heuristic The vertex ordering heuristic to use during greedy coloring. See \ref igraph_coloring_greedy_t + * + * \return Error code. + * + * \example examples/simple/igraph_coloring.c + */ +int igraph_vertex_coloring_greedy(const igraph_t *graph, igraph_vector_int_t *colors, igraph_coloring_greedy_t heuristic) { + switch (heuristic) { + case IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS: + return igraph_i_vertex_coloring_greedy_cn(graph, colors); + default: + return IGRAPH_EINVAL; + } +} diff --git a/src/rigraph/core/misc/conversion.c b/src/rigraph/core/misc/conversion.c new file mode 100644 index 0000000..918ebaf --- /dev/null +++ b/src/rigraph/core/misc/conversion.c @@ -0,0 +1,993 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_conversion.h" + +#include "igraph_iterators.h" +#include "igraph_interface.h" +#include "igraph_attributes.h" +#include "igraph_constructors.h" +#include "igraph_structural.h" +#include "igraph_sparsemat.h" +#include "igraph_random.h" + +#include "../core/fixed_vectorlist.h" +#include "../graph/attributes.h" +#include "conversion_internal.h" + +/** + * \ingroup conversion + * \function igraph_get_adjacency + * \brief Returns the adjacency matrix of a graph + * + * + * The result is an adjacency matrix. Entry i, j of the matrix + * contains the number of edges connecting vertex i to vertex j. + * \param graph Pointer to the graph to convert + * \param res Pointer to an initialized matrix object, it will be + * resized if needed. + * \param type Constant giving the type of the adjacency matrix to + * create for undirected graphs. It is ignored for directed + * graphs. Possible values: + * \clist + * \cli IGRAPH_GET_ADJACENCY_UPPER + * the upper right triangle of the matrix is used. + * \cli IGRAPH_GET_ADJACENCY_LOWER + * the lower left triangle of the matrix is used. + * \cli IGRAPH_GET_ADJACENCY_BOTH + * the whole matrix is used, a symmetric matrix is returned + * if the graph is undirected. + * \endclist + * \param type eids Logical, if true, then the edges ids plus one + * are stored in the adjacency matrix, instead of the number of + * edges between the two vertices. (The plus one is needed, since + * edge ids start from zero, and zero means no edge in this case.) + * \return Error code: + * \c IGRAPH_EINVAL invalid type argument. + * + * \sa igraph_get_adjacency_sparse if you want a sparse matrix representation + * + * Time complexity: O(|V||V|), + * |V| is the + * number of vertices in the graph. + */ + +int igraph_get_adjacency(const igraph_t *graph, igraph_matrix_t *res, + igraph_get_adjacency_t type, igraph_bool_t eids) { + + igraph_eit_t edgeit; + long int no_of_nodes = igraph_vcount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + int retval = 0; + long int from, to; + igraph_integer_t ffrom, fto; + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); + igraph_matrix_null(res); + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); + IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); + + if (directed) { + while (!IGRAPH_EIT_END(edgeit)) { + long int edge = IGRAPH_EIT_GET(edgeit); + igraph_edge(graph, (igraph_integer_t) edge, &ffrom, &fto); + from = ffrom; + to = fto; + if (eids) { + MATRIX(*res, from, to) = edge + 1; + } else { + MATRIX(*res, from, to) += 1; + } + IGRAPH_EIT_NEXT(edgeit); + } + } else if (type == IGRAPH_GET_ADJACENCY_UPPER) { + while (!IGRAPH_EIT_END(edgeit)) { + long int edge = IGRAPH_EIT_GET(edgeit); + igraph_edge(graph, (igraph_integer_t) edge, &ffrom, &fto); + from = ffrom; + to = fto; + if (to < from) { + if (eids) { + MATRIX(*res, to, from) = edge + 1; + } else { + MATRIX(*res, to, from) += 1; + } + } else { + if (eids) { + MATRIX(*res, from, to) = edge + 1; + } else { + MATRIX(*res, from, to) += 1; + } + } + IGRAPH_EIT_NEXT(edgeit); + } + } else if (type == IGRAPH_GET_ADJACENCY_LOWER) { + while (!IGRAPH_EIT_END(edgeit)) { + long int edge = IGRAPH_EIT_GET(edgeit); + igraph_edge(graph, (igraph_integer_t) edge, &ffrom, &fto); + from = ffrom; + to = fto; + if (to < from) { + if (eids) { + MATRIX(*res, from, to) = edge + 1; + } else { + MATRIX(*res, from, to) += 1; + } + } else { + if (eids) { + MATRIX(*res, to, from) = edge + 1; + } else { + MATRIX(*res, to, from) += 1; + } + } + IGRAPH_EIT_NEXT(edgeit); + } + } else if (type == IGRAPH_GET_ADJACENCY_BOTH) { + while (!IGRAPH_EIT_END(edgeit)) { + long int edge = IGRAPH_EIT_GET(edgeit); + igraph_edge(graph, (igraph_integer_t) edge, &ffrom, &fto); + from = ffrom; + to = fto; + if (eids) { + MATRIX(*res, from, to) = edge + 1; + } else { + MATRIX(*res, from, to) += 1; + } + if (from != to) { + if (eids) { + MATRIX(*res, to, from) = edge + 1; + } else { + MATRIX(*res, to, from) += 1; + } + } + IGRAPH_EIT_NEXT(edgeit); + } + } else { + IGRAPH_ERROR("Invalid type argument", IGRAPH_EINVAL); + } + + igraph_eit_destroy(&edgeit); + IGRAPH_FINALLY_CLEAN(1); + return retval; +} + +/** + * \ingroup conversion + * \function igraph_get_adjacency_sparse + * \brief Returns the adjacency matrix of a graph in sparse matrix format. + * + * + * The result is an adjacency matrix. Entry i, j of the matrix + * contains the number of edges connecting vertex i to vertex j. + * \param graph Pointer to the graph to convert. + * \param res Pointer to an initialized sparse matrix object, it will be + * resized if needed. + * \param type Constant giving the type of the adjacency matrix to + * create for undirected graphs. It is ignored for directed + * graphs. Possible values: + * \clist + * \cli IGRAPH_GET_ADJACENCY_UPPER + * the upper right triangle of the matrix is used. + * \cli IGRAPH_GET_ADJACENCY_LOWER + * the lower left triangle of the matrix is used. + * \cli IGRAPH_GET_ADJACENCY_BOTH + * the whole matrix is used, a symmetric matrix is returned. + * \endclist + * \return Error code: + * \c IGRAPH_EINVAL invalid type argument. + * + * \sa igraph_get_adjacency if you would like to get a normal matrix + * ( \type igraph_matrix_t ) + * + * Time complexity: O(|V||V|), + * |V| is the + * number of vertices in the graph. + */ + +int igraph_get_adjacency_sparse(const igraph_t *graph, igraph_spmatrix_t *res, + igraph_get_adjacency_t type) { + + igraph_eit_t edgeit; + long int no_of_nodes = igraph_vcount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + long int from, to; + igraph_integer_t ffrom, fto; + + igraph_spmatrix_null(res); + IGRAPH_CHECK(igraph_spmatrix_resize(res, no_of_nodes, no_of_nodes)); + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); + IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); + + if (directed) { + while (!IGRAPH_EIT_END(edgeit)) { + igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); + from = ffrom; + to = fto; + igraph_spmatrix_add_e(res, from, to, 1); + IGRAPH_EIT_NEXT(edgeit); + } + } else if (type == IGRAPH_GET_ADJACENCY_UPPER) { + while (!IGRAPH_EIT_END(edgeit)) { + igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); + from = ffrom; + to = fto; + if (to < from) { + igraph_spmatrix_add_e(res, to, from, 1); + } else { + igraph_spmatrix_add_e(res, from, to, 1); + } + IGRAPH_EIT_NEXT(edgeit); + } + } else if (type == IGRAPH_GET_ADJACENCY_LOWER) { + while (!IGRAPH_EIT_END(edgeit)) { + igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); + from = ffrom; + to = fto; + if (to > from) { + igraph_spmatrix_add_e(res, to, from, 1); + } else { + igraph_spmatrix_add_e(res, from, to, 1); + } + IGRAPH_EIT_NEXT(edgeit); + } + } else if (type == IGRAPH_GET_ADJACENCY_BOTH) { + while (!IGRAPH_EIT_END(edgeit)) { + igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); + from = ffrom; + to = fto; + igraph_spmatrix_add_e(res, from, to, 1); + if (from != to) { + igraph_spmatrix_add_e(res, to, from, 1); + } + IGRAPH_EIT_NEXT(edgeit); + } + } else { + IGRAPH_ERROR("Invalid type argument.", IGRAPH_EINVAL); + } + + igraph_eit_destroy(&edgeit); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \ingroup conversion + * \function igraph_get_edgelist + * \brief Returns the list of edges in a graph + * + * The order of the edges is given by the edge ids. + * \param graph Pointer to the graph object + * \param res Pointer to an initialized vector object, it will be + * resized. + * \param bycol Logical, if true, the edges will be returned + * columnwise, e.g. the first edge is + * res[0]->res[|E|], the second is + * res[1]->res[|E|+1], etc. + * \return Error code. + * + * \sa \ref igraph_edges() to return the result only for some edge ids. + * + * Time complexity: O(|E|), the + * number of edges in the graph. + */ + +int igraph_get_edgelist(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t bycol) { + + igraph_eit_t edgeit; + long int no_of_edges = igraph_ecount(graph); + long int vptr = 0; + igraph_integer_t from, to; + + IGRAPH_CHECK(igraph_vector_resize(res, no_of_edges * 2)); + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), + &edgeit)); + IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); + + if (bycol) { + while (!IGRAPH_EIT_END(edgeit)) { + igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &from, &to); + VECTOR(*res)[vptr] = from; + VECTOR(*res)[vptr + no_of_edges] = to; + vptr++; + IGRAPH_EIT_NEXT(edgeit); + } + } else { + while (!IGRAPH_EIT_END(edgeit)) { + igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &from, &to); + VECTOR(*res)[vptr++] = from; + VECTOR(*res)[vptr++] = to; + IGRAPH_EIT_NEXT(edgeit); + } + } + + igraph_eit_destroy(&edgeit); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \function igraph_to_directed + * \brief Convert an undirected graph to a directed one + * + * + * If the supplied graph is directed, this function does nothing. + * \param graph The graph object to convert. + * \param mode Constant, specifies the details of how exactly the + * conversion is done. Possible values: + * \clist + * \cli IGRAPH_TO_DIRECTED_ARBITRARY + * The number of edges in the + * graph stays the same, an arbitrarily directed edge is + * created for each undirected edge. + * \cli IGRAPH_TO_DIRECTED_MUTUAL + * Two directed edges are + * created for each undirected edge, one in each direction. + * \cli IGRAPH_TO_DIRECTED_RANDOM + * Each undirected edge is converted to a randomly oriented + * directed one. + * \cli IGRAPH_TO_DIRECTED_ACYCLIC + * Each undirected edge is converted to a directed edge oriented + * from a lower index vertex to a higher index one. If no self-loops + * were present, then the result is a directed acyclic graph. + * \endclist + * \return Error code. + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number + * of edges. + */ + +int igraph_to_directed(igraph_t *graph, + igraph_to_directed_t mode) { + long int no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + + if (igraph_is_directed(graph)) { + return IGRAPH_SUCCESS; + } + + switch (mode) { + case IGRAPH_TO_DIRECTED_ARBITRARY: + case IGRAPH_TO_DIRECTED_RANDOM: + case IGRAPH_TO_DIRECTED_ACYCLIC: + { + igraph_t newgraph; + igraph_vector_t edges; + long int size = no_of_edges * 2; + long int i; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, size); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + + if (mode == IGRAPH_TO_DIRECTED_RANDOM) { + RNG_BEGIN(); + + for (i=0; i < no_of_edges; ++i) { + if (RNG_INTEGER(0,1)) { + igraph_real_t temp = VECTOR(edges)[2*i]; + VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1]; + VECTOR(edges)[2*i+1] = temp; + } + } + + RNG_END(); + } else if (mode == IGRAPH_TO_DIRECTED_ACYCLIC) { + /* Currently, the endpoints of undirected edges are ordered in the + internal graph datastructure, i.e. it is always true that from < to. + However, it is not guaranteed that this will not be changed in + the future, and this ordering should not be relied on outside of + the implementation of the minimal API in type_indexededgelist.c. + + Therefore, we order the edge endpoints anyway in the following loop: */ + for (i=0; i < no_of_edges; ++i) { + if (VECTOR(edges)[2*i] > VECTOR(edges)[2*i+1]) { + igraph_real_t temp = VECTOR(edges)[2*i]; + VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1]; + VECTOR(edges)[2*i+1] = temp; + } + } + } + + IGRAPH_CHECK(igraph_create(&newgraph, &edges, + (igraph_integer_t) no_of_nodes, + IGRAPH_DIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 1); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(2); + + igraph_destroy(graph); + *graph = newgraph; + + break; + } + case IGRAPH_TO_DIRECTED_MUTUAL: + { + igraph_t newgraph; + igraph_vector_t edges; + igraph_vector_t index; + long int size = no_of_edges * 4; + long int i; + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, size)); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + IGRAPH_CHECK(igraph_vector_resize(&edges, no_of_edges * 4)); + IGRAPH_VECTOR_INIT_FINALLY(&index, no_of_edges * 2); + for (i = 0; i < no_of_edges; i++) { + VECTOR(edges)[no_of_edges * 2 + i * 2] = VECTOR(edges)[i * 2 + 1]; + VECTOR(edges)[no_of_edges * 2 + i * 2 + 1] = VECTOR(edges)[i * 2]; + VECTOR(index)[i] = VECTOR(index)[no_of_edges + i] = i; + } + + IGRAPH_CHECK(igraph_create(&newgraph, &edges, + (igraph_integer_t) no_of_nodes, + IGRAPH_DIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1,/*edges=*/0); + IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, &newgraph, &index)); + + igraph_vector_destroy(&index); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(3); + + igraph_destroy(graph); + *graph = newgraph; + + break; + } + default: + IGRAPH_ERROR("Cannot direct graph, invalid mode", IGRAPH_EINVAL); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_to_undirected + * \brief Convert a directed graph to an undirected one. + * + * + * If the supplied graph is undirected, this function does nothing. + * + * \param graph The graph object to convert. + * \param mode Constant, specifies the details of how exactly the + * conversion is done. Possible values: \c + * IGRAPH_TO_UNDIRECTED_EACH: the number of edges remains + * constant, an undirected edge is created for each directed + * one, this version might create graphs with multiple edges; + * \c IGRAPH_TO_UNDIRECTED_COLLAPSE: one undirected edge will + * be created for each pair of vertices that are connected + * with at least one directed edge, no multiple edges will be + * created. \c IGRAPH_TO_UNDIRECTED_MUTUAL creates an undirected + * edge for each pair of mutual edges in the directed graph. + * Non-mutual edges are lost; loop edges are kept unconditionally. + * This mode might create multiple edges. + * \param edge_comb What to do with the edge attributes. See the igraph + * manual section about attributes for details. \c NULL means that + * the edge attributes are lost during the conversion, \em except + * when \c mode is \c IGRAPH_TO_UNDIRECTED_EACH, in which case the + * edge attributes are kept intact. + * \return Error code. + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number + * of edges. + * + * \example examples/simple/igraph_to_undirected.c + */ + +int igraph_to_undirected(igraph_t *graph, + igraph_to_undirected_t mode, + const igraph_attribute_combination_t *edge_comb) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_vector_t edges; + igraph_t newgraph; + igraph_bool_t attr = edge_comb && igraph_has_attribute_table(); + + if (mode != IGRAPH_TO_UNDIRECTED_EACH && + mode != IGRAPH_TO_UNDIRECTED_COLLAPSE && + mode != IGRAPH_TO_UNDIRECTED_MUTUAL) { + IGRAPH_ERROR("Cannot undirect graph, invalid mode", IGRAPH_EINVAL); + } + + if (!igraph_is_directed(graph)) { + return 0; + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + + if (mode == IGRAPH_TO_UNDIRECTED_EACH) { + igraph_es_t es; + igraph_eit_t eit; + + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_ID)); + IGRAPH_FINALLY(igraph_es_destroy, &es); + IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + while (!IGRAPH_EIT_END(eit)) { + long int edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from, to; + igraph_edge(graph, (igraph_integer_t) edge, &from, &to); + IGRAPH_CHECK(igraph_vector_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + IGRAPH_EIT_NEXT(eit); + } + + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(&newgraph, &edges, + (igraph_integer_t) no_of_nodes, + IGRAPH_UNDIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + igraph_vector_destroy(&edges); + IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 1); + IGRAPH_FINALLY_CLEAN(2); + igraph_destroy(graph); + *graph = newgraph; + + } else if (mode == IGRAPH_TO_UNDIRECTED_COLLAPSE) { + igraph_vector_t inadj, outadj; + long int i; + igraph_vector_t mergeinto; + long int actedge = 0; + + if (attr) { + IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges); + } + + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + IGRAPH_VECTOR_INIT_FINALLY(&inadj, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outadj, 0); + + for (i = 0; i < no_of_nodes; i++) { + long int n_out, n_in; + long int p1 = -1, p2 = -1; + long int e1 = 0, e2 = 0, n1 = 0, n2 = 0, last; + IGRAPH_CHECK(igraph_incident(graph, &outadj, (igraph_integer_t) i, + IGRAPH_OUT)); + IGRAPH_CHECK(igraph_incident(graph, &inadj, (igraph_integer_t) i, + IGRAPH_IN)); + n_out = igraph_vector_size(&outadj); + n_in = igraph_vector_size(&inadj); + +#define STEPOUT() if ( (++p1) < n_out) { \ + e1 = (long int) VECTOR(outadj)[p1]; \ + n1 = IGRAPH_TO(graph, e1); \ + } +#define STEPIN() if ( (++p2) < n_in) { \ + e2 = (long int) VECTOR(inadj )[p2]; \ + n2 = IGRAPH_FROM(graph, e2); \ + } +#define ADD_NEW_EDGE() { \ + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); \ + IGRAPH_CHECK(igraph_vector_push_back(&edges, last)); \ +} +#define MERGE_INTO_CURRENT_EDGE(which) { \ + if (attr) { \ + VECTOR(mergeinto)[which] = actedge; \ + } \ +} + + STEPOUT(); + STEPIN(); + + while (p1 < n_out && n1 <= i && p2 < n_in && n2 <= i) { + last = (n1 <= n2) ? n1 : n2; + ADD_NEW_EDGE(); + while (p1 < n_out && last == n1) { + MERGE_INTO_CURRENT_EDGE(e1); + STEPOUT(); + } + while (p2 < n_in && last == n2) { + MERGE_INTO_CURRENT_EDGE(e2); + STEPIN(); + } + actedge++; + } + + while (p1 < n_out && n1 <= i) { + last = n1; + ADD_NEW_EDGE(); + while (p1 < n_out && last == n1) { + MERGE_INTO_CURRENT_EDGE(e1); + STEPOUT(); + } + actedge++; + } + + while (p2 < n_in && n2 <= i) { + last = n2; + ADD_NEW_EDGE(); + while (p2 < n_in && last == n2) { + MERGE_INTO_CURRENT_EDGE(e2); + STEPIN(); + } + actedge++; + } + } + +#undef MERGE_INTO_CURRENT_EDGE +#undef ADD_NEW_EDGE +#undef STEPOUT +#undef STEPIN + + igraph_vector_destroy(&outadj); + igraph_vector_destroy(&inadj); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(&newgraph, &edges, + (igraph_integer_t) no_of_nodes, + IGRAPH_UNDIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + igraph_vector_destroy(&edges); + IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 0); /* no edge attributes */ + + if (attr) { + igraph_fixed_vectorlist_t vl; + IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto, + actedge)); + IGRAPH_FINALLY(igraph_fixed_vectorlist_destroy, &vl); + + IGRAPH_CHECK(igraph_i_attribute_combine_edges(graph, &newgraph, &vl.v, + edge_comb)); + + igraph_fixed_vectorlist_destroy(&vl); + IGRAPH_FINALLY_CLEAN(1); + } + + IGRAPH_FINALLY_CLEAN(2); + igraph_destroy(graph); + *graph = newgraph; + + if (attr) { + igraph_vector_destroy(&mergeinto); + IGRAPH_FINALLY_CLEAN(1); + } + } else if (mode == IGRAPH_TO_UNDIRECTED_MUTUAL) { + igraph_vector_t inadj, outadj; + long int i; + igraph_vector_t mergeinto; + long int actedge = 0; + + if (attr) { + IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges); + igraph_vector_fill(&mergeinto, -1); + } + + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + IGRAPH_VECTOR_INIT_FINALLY(&inadj, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outadj, 0); + + for (i = 0; i < no_of_nodes; i++) { + long int n_out, n_in; + long int p1 = -1, p2 = -1; + long int e1 = 0, e2 = 0, n1 = 0, n2 = 0; + IGRAPH_CHECK(igraph_incident(graph, &outadj, (igraph_integer_t) i, + IGRAPH_OUT)); + IGRAPH_CHECK(igraph_incident(graph, &inadj, (igraph_integer_t) i, + IGRAPH_IN)); + n_out = igraph_vector_size(&outadj); + n_in = igraph_vector_size(&inadj); + +#define STEPOUT() if ( (++p1) < n_out) { \ + e1 = (long int) VECTOR(outadj)[p1]; \ + n1 = IGRAPH_TO(graph, e1); \ + } +#define STEPIN() if ( (++p2) < n_in) { \ + e2 = (long int) VECTOR(inadj )[p2]; \ + n2 = IGRAPH_FROM(graph, e2); \ + } + + STEPOUT(); + STEPIN(); + + while (p1 < n_out && n1 <= i && p2 < n_in && n2 <= i) { + if (n1 == n2) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, n1)); + if (attr) { + VECTOR(mergeinto)[e1] = actedge; + VECTOR(mergeinto)[e2] = actedge; + actedge++; + } + STEPOUT(); + STEPIN(); + } else if (n1 < n2) { + STEPOUT(); + } else { /* n2= 2 vertices can be represented by a + * sequence of n-2 integers, each between 0 and n-1 (inclusive). + * + * \param graph Pointer to an initialized graph object which + must be a tree on n >= 2 vertices. + * \param prufer A pointer to the integer vector that should hold the Prüfer sequence; + the vector must be initialized and will be resized to n - 2. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * there is not enough memory to perform the operation. + * \cli IGRAPH_EINVAL + * the graph is not a tree or it is has less than vertices + * \endclist + * + * \sa \ref igraph_from_prufer() + * + */ +int igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* prufer) { + /* For generating the Prüfer sequence, we enumerate the vertices u of the tree. + We keep track of the degrees of all vertices, treating vertices + of degree 0 as removed. We maintain the invariant that all leafs + that are still contained in the tree are >= u. + If u is a leaf, we remove it and add its unique neighbor to the prüfer + sequence. If the removal of u turns the neighbor into a leaf which is < u, + we repeat the procedure for the new leaf and so on. */ + igraph_integer_t u; + igraph_vector_t degrees, neighbors; + igraph_integer_t prufer_index = 0; + igraph_integer_t n = igraph_vcount(graph); + igraph_bool_t is_tree = 0; + + IGRAPH_CHECK(igraph_is_tree(graph, &is_tree, NULL, IGRAPH_ALL)); + + if (!is_tree) { + IGRAPH_ERROR("The graph must be a tree", IGRAPH_EINVAL); + } + + if (n < 2) { + IGRAPH_ERROR("The tree must have at least 2 vertices", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_int_resize(prufer, n - 2)); + IGRAPH_VECTOR_INIT_FINALLY(°rees, n); + IGRAPH_VECTOR_INIT_FINALLY(&neighbors, 1); + + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS)); + + for (u = 0; u < n; ++u) { + igraph_integer_t degree = VECTOR(degrees)[u]; + igraph_integer_t leaf = u; + + while (degree == 1 && leaf <= u) { + igraph_integer_t i; + igraph_integer_t neighbor = 0; + igraph_integer_t neighbor_count = 0; + + VECTOR(degrees)[leaf] = 0; /* mark leaf v as deleted */ + + IGRAPH_CHECK(igraph_neighbors(graph, &neighbors, leaf, IGRAPH_ALL)); + + /* Find the unique remaining neighbor of the leaf */ + neighbor_count = igraph_vector_size(&neighbors); + for (i = 0; i < neighbor_count; i++) { + neighbor = VECTOR(neighbors)[i]; + if (VECTOR(degrees)[neighbor] > 0) { + break; + } + } + + /* remember that we have removed the leaf */ + VECTOR(degrees)[neighbor]--; + degree = VECTOR(degrees)[neighbor]; + + /* Add the neighbor to the prufer sequence unless it is the last vertex + (i.e. degree == 0) */ + if (degree > 0) { + VECTOR(*prufer)[prufer_index] = neighbor; + prufer_index++; + } + leaf = neighbor; + } + } + + igraph_vector_destroy(°rees); + igraph_vector_destroy(&neighbors); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/misc/conversion_internal.h b/src/rigraph/core/misc/conversion_internal.h new file mode 100644 index 0000000..e234a9b --- /dev/null +++ b/src/rigraph/core/misc/conversion_internal.h @@ -0,0 +1,28 @@ +/* + IGraph library. + Copyright (C) 2021 The igraph development team + + 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 2 of the License, or + (at your option) 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 IGRAPH_MISC_CONVERSION_INTERNAL_H +#define IGRAPH_MISC_CONVERSION_INTERNAL_H + +#include "igraph_sparsemat.h" +#include "igraph_types.h" + +int igraph_i_normalize_sparsemat(igraph_sparsemat_t *sparsemat, + igraph_bool_t column_wise); + +#endif diff --git a/src/rigraph/core/misc/degree_sequence.cpp b/src/rigraph/core/misc/degree_sequence.cpp new file mode 100644 index 0000000..fcc1130 --- /dev/null +++ b/src/rigraph/core/misc/degree_sequence.cpp @@ -0,0 +1,785 @@ +/* + IGraph library. + Constructing realizations of degree sequences and bi-degree sequences. + Copyright (C) 2018-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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 . +*/ + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include +#include +#include +#include + +#define IGRAPH_I_MULTI_EDGES_SW 0x02 /* 010, more than one edge allowed between distinct vertices */ +#define IGRAPH_I_MULTI_LOOPS_SW 0x04 /* 100, more than one self-loop allowed on the same vertex */ + +/******************************/ +/***** Helper constructs ******/ +/******************************/ + +// (vertex, degree) pair +struct vd_pair { + long vertex; + igraph_integer_t degree; + + vd_pair(long vertex, igraph_integer_t degree) : vertex(vertex), degree(degree) {} +}; + +// (indegree, outdegree) +typedef std::pair bidegree; + +// (vertex, bidegree) pair +struct vbd_pair { + long vertex; + bidegree degree; + + vbd_pair(long vertex, bidegree degree) : vertex(vertex), degree(degree) {} +}; + +// Comparison function for vertex-degree pairs. +// Also used for lexicographic sorting of bi-degrees. +template inline bool degree_greater(const T &a, const T &b) { + return a.degree > b.degree; +} + +template inline bool degree_less(const T &a, const T &b) { + return a.degree < b.degree; +} + + +/*************************************/ +/***** Undirected simple graphs ******/ +/*************************************/ + +// Generate simple undirected realization as edge-list. +// If largest=true, always choose the vertex with the largest remaining degree to connect up next. +// Otherwise, always choose the one with the smallest remaining degree. +static int igraph_i_havel_hakimi(const igraph_vector_t *deg, igraph_vector_t *edges, bool largest) { + long n = igraph_vector_size(deg); + + long ec = 0; // number of edges added so far + + std::vector vertices; + vertices.reserve(n); + for (int i = 0; i < n; ++i) { + vertices.push_back(vd_pair(i, VECTOR(*deg)[i])); + } + + while (! vertices.empty()) { + if (largest) { + std::stable_sort(vertices.begin(), vertices.end(), degree_less); + } else { + std::stable_sort(vertices.begin(), vertices.end(), degree_greater); + } + + // take the next vertex to be connected up + vd_pair vd = vertices.back(); + vertices.pop_back(); + + if (vd.degree == 0) { + continue; + } + + if (vertices.size() < size_t(vd.degree)) { + goto fail; + } + + if (largest) { + for (int i = 0; i < vd.degree; ++i) { + if (--(vertices[vertices.size() - 1 - i].degree) < 0) { + goto fail; + } + + VECTOR(*edges)[2 * (ec + i)] = vd.vertex; + VECTOR(*edges)[2 * (ec + i) + 1] = vertices[vertices.size() - 1 - i].vertex; + } + } else { + // this loop can only be reached if all zero-degree nodes have already been removed + // therefore decrementing remaining degrees is safe + for (int i = 0; i < vd.degree; ++i) { + vertices[i].degree--; + + VECTOR(*edges)[2 * (ec + i)] = vd.vertex; + VECTOR(*edges)[2 * (ec + i) + 1] = vertices[i].vertex; + } + } + + ec += vd.degree; + } + + return IGRAPH_SUCCESS; + +fail: + IGRAPH_ERROR("The given degree sequence cannot be realized as a simple graph.", IGRAPH_EINVAL); +} + + +// Choose vertices in the order of their IDs. +static int igraph_i_havel_hakimi_index(const igraph_vector_t *deg, igraph_vector_t *edges) { + long n = igraph_vector_size(deg); + + long ec = 0; // number of edges added so far + + typedef std::list vlist; + vlist vertices; + for (int i = 0; i < n; ++i) { + vertices.push_back(vd_pair(i, VECTOR(*deg)[i])); + } + + std::vector pointers; + pointers.reserve(n); + for (vlist::iterator it = vertices.begin(); it != vertices.end(); ++it) { + pointers.push_back(it); + } + + for (std::vector::iterator pt = pointers.begin(); pt != pointers.end(); ++pt) { + vertices.sort(degree_greater); + + vd_pair vd = **pt; + vertices.erase(*pt); + + if (vd.degree == 0) { + continue; + } + + int k; + vlist::iterator it; + for (it = vertices.begin(), k = 0; + k != vd.degree && it != vertices.end(); + ++it, ++k) { + if (--(it->degree) < 0) { + goto fail; + } + + VECTOR(*edges)[2 * (ec + k)] = vd.vertex; + VECTOR(*edges)[2 * (ec + k) + 1] = it->vertex; + } + if (it == vertices.end() && k < vd.degree) { + goto fail; + } + + ec += vd.degree; + } + + return IGRAPH_SUCCESS; + +fail: + IGRAPH_ERROR("The given degree sequence cannot be realized as a simple graph.", IGRAPH_EINVAL); +} + + +/***********************************/ +/***** Undirected multigraphs ******/ +/***********************************/ + +// Given a sequence that is sorted, except for its first element, +// move the first element to the correct position fully sort the sequence. +template +static void bubble_up(It first, It last, Compare comp) { + if (first == last) + return; + It it = first; + it++; + while (it != last) { + if (comp(*first, *it)) { + break; + } else { + std::swap(*first, *it); + } + first = it; + it++; + } +} + +// In each step, choose a vertex (the largest degree one if largest=true, +// the smallest degree one otherwise) and connect it to the largest remaining degree vertex. +// This will create a connected loopless multigraph, if one exists. +// If loops=true, and a loopless multigraph does not exist, complete the procedure +// by adding loops on the last vertex. +// If largest=false, and the degree sequence was potentially connected, the resulting +// graph will be connected. +static int igraph_i_realize_undirected_multi(const igraph_vector_t *deg, igraph_vector_t *edges, bool loops, bool largest) { + long vcount = igraph_vector_size(deg); + + if (vcount == 0) + return IGRAPH_SUCCESS; + + std::vector vertices; + vertices.reserve(vcount); + for (int i = 0; i < vcount; ++i) { + long d = VECTOR(*deg)[i]; + vertices.push_back(vd_pair(i, d)); + } + + // Initial sort in non-increasing order. + std::stable_sort(vertices.begin(), vertices.end(), degree_greater); + + long ec = 0; + while (! vertices.empty()) { + // Remove any zero degrees, and error on negative ones. + + vd_pair &w = vertices.back(); + + if (w.degree == 0) { + vertices.pop_back(); + continue; + } + + // If only one vertex remains, then the degree sequence cannot be realized as + // a loopless multigraph. We either complete the graph by adding loops on this vertex + // or throw an error, depending on the 'loops' setting. + if (vertices.size() == 1) { + if (loops) { + for (long i=0; i < w.degree/2; ++i) { + VECTOR(*edges)[2*ec] = w.vertex; + VECTOR(*edges)[2*ec+1] = w.vertex; + ec++; + } + break; + } else { + IGRAPH_ERROR("The given degree sequence cannot be realized as a loopless multigraph.", IGRAPH_EINVAL); + } + } + + // At this point we are guaranteed to have at least two remaining vertices. + + vd_pair *u, *v; + if (largest) { + u = &vertices[0]; + v = &vertices[1]; + } else { + u = &vertices.front(); + v = &vertices.back(); + } + + u->degree -= 1; + v->degree -= 1; + + VECTOR(*edges)[2*ec] = u->vertex; + VECTOR(*edges)[2*ec+1] = v->vertex; + ec++; + + // Now the first element may be out of order. + // If largest=true, the first two elements may be out of order. + // Restore the sorted order using a single step of bubble sort. + if (largest) { + bubble_up(vertices.begin()+1, vertices.end(), degree_greater); + } + bubble_up(vertices.begin(), vertices.end(), degree_greater); + } + + return IGRAPH_SUCCESS; +} + + +static int igraph_i_realize_undirected_multi_index(const igraph_vector_t *deg, igraph_vector_t *edges, bool loops) { + long vcount = igraph_vector_size(deg); + + if (vcount == 0) + return IGRAPH_SUCCESS; + + typedef std::list vlist; + vlist vertices; + for (int i = 0; i < vcount; ++i) { + vertices.push_back(vd_pair(i, VECTOR(*deg)[i])); + } + + std::vector pointers; + pointers.reserve(vcount); + for (vlist::iterator it = vertices.begin(); it != vertices.end(); ++it) { + pointers.push_back(it); + } + + // Initial sort + vertices.sort(degree_greater); + + long ec = 0; + for (std::vector::iterator pt = pointers.begin(); pt != pointers.end(); ++pt) { + vd_pair vd = **pt; + vertices.erase(*pt); + + while (vd.degree > 0) { + vlist::iterator uit = vertices.begin(); + + if (vertices.empty() || uit->degree == 0) { + // We are out of non-zero degree vertices to connect to. + if (loops) { + for (long i=0; i < vd.degree/2; ++i) { + VECTOR(*edges)[2*ec] = vd.vertex; + VECTOR(*edges)[2*ec+1] = vd.vertex; + ec++; + } + return IGRAPH_SUCCESS; + } else { + IGRAPH_ERROR("The given degree sequence cannot be realized as a loopless multigraph.", IGRAPH_EINVAL); + } + } + + vd.degree -= 1; + uit->degree -= 1; + + VECTOR(*edges)[2*ec] = vd.vertex; + VECTOR(*edges)[2*ec+1] = uit->vertex; + ec++; + + // If there are at least two elements, and the first two are not in order, + // re-sort the list. A possible optimization would be a version of + // bubble_up() that can exchange list nodes instead of swapping their values. + if (vertices.size() > 1) { + vlist::iterator wit = uit; + ++wit; + + if (wit->degree > uit->degree) { + vertices.sort(degree_greater); + } + } + } + } + + return IGRAPH_SUCCESS; +} + + +/***********************************/ +/***** Directed simple graphs ******/ +/***********************************/ + +inline bool is_nonzero_outdeg(const vbd_pair &vd) { + return (vd.degree.second != 0); +} + + +// The below implementations of the Kleitman-Wang algorithm follow the description in https://arxiv.org/abs/0905.4913 + +// Realize bi-degree sequence as edge list +// If smallest=true, always choose the vertex with "smallest" bi-degree for connecting up next, +// otherwise choose the "largest" (based on lexicographic bi-degree ordering). +static int igraph_i_kleitman_wang(const igraph_vector_t *outdeg, const igraph_vector_t *indeg, igraph_vector_t *edges, bool smallest) { + long n = igraph_vector_size(indeg); // number of vertices + + long ec = 0; // number of edges added so far + + std::vector vertices; + vertices.reserve(n); + for (int i = 0; i < n; ++i) { + vertices.push_back(vbd_pair(i, bidegree(VECTOR(*indeg)[i], VECTOR(*outdeg)[i]))); + } + + while (true) { + // sort vertices by (in, out) degree pairs in decreasing order + std::stable_sort(vertices.begin(), vertices.end(), degree_greater); + + // remove (0,0)-degree vertices + while (!vertices.empty() && vertices.back().degree == bidegree(0, 0)) { + vertices.pop_back(); + } + + // if no vertices remain, stop + if (vertices.empty()) { + break; + } + + // choose a vertex the out-stubs of which will be connected + // note: a vertex with non-zero out-degree is guaranteed to exist + // because there are _some_ non-zero degrees and the sum of in- and out-degrees + // is the same + vbd_pair *vdp; + if (smallest) { + vdp = &*std::find_if(vertices.rbegin(), vertices.rend(), is_nonzero_outdeg); + } else { + vdp = &*std::find_if(vertices.begin(), vertices.end(), is_nonzero_outdeg); + } + + // are there a sufficient number of other vertices to connect to? + if (static_cast(vertices.size()) - 1 < vdp->degree.second) { + goto fail; + } + + // create the connections + int k = 0; + for (std::vector::iterator it = vertices.begin(); + k < vdp->degree.second; + ++it) { + if (it->vertex == vdp->vertex) { + continue; // do not create a self-loop + } + if (--(it->degree.first) < 0) { + goto fail; + } + + VECTOR(*edges)[2 * (ec + k)] = vdp->vertex; + VECTOR(*edges)[2 * (ec + k) + 1] = it->vertex; + + k++; + } + + ec += vdp->degree.second; + vdp->degree.second = 0; + } + + return IGRAPH_SUCCESS; + +fail: + IGRAPH_ERROR("The given directed degree sequences cannot be realized as a simple graph.", IGRAPH_EINVAL); +} + + +// Choose vertices in the order of their IDs. +static int igraph_i_kleitman_wang_index(const igraph_vector_t *outdeg, const igraph_vector_t *indeg, igraph_vector_t *edges) { + long n = igraph_vector_size(indeg); // number of vertices + + long ec = 0; // number of edges added so far + + typedef std::list vlist; + vlist vertices; + for (int i = 0; i < n; ++i) { + vertices.push_back(vbd_pair(i, bidegree(VECTOR(*indeg)[i], VECTOR(*outdeg)[i]))); + } + + std::vector pointers; + pointers.reserve(n); + for (vlist::iterator it = vertices.begin(); it != vertices.end(); ++it) { + pointers.push_back(it); + } + + for (std::vector::iterator pt = pointers.begin(); pt != pointers.end(); ++pt) { + // sort vertices by (in, out) degree pairs in decreasing order + // note: std::list::sort does a stable sort + vertices.sort(degree_greater); + + // choose a vertex the out-stubs of which will be connected + vbd_pair &vd = **pt; + + if (vd.degree.second == 0) { + continue; + } + + int k = 0; + vlist::iterator it; + for (it = vertices.begin(); + k != vd.degree.second && it != vertices.end(); + ++it) { + if (it->vertex == vd.vertex) { + continue; + } + + if (--(it->degree.first) < 0) { + goto fail; + } + + VECTOR(*edges)[2 * (ec + k)] = vd.vertex; + VECTOR(*edges)[2 * (ec + k) + 1] = it->vertex; + + ++k; + } + if (it == vertices.end() && k < vd.degree.second) { + goto fail; + } + + ec += vd.degree.second; + vd.degree.second = 0; + } + + return IGRAPH_SUCCESS; + +fail: + IGRAPH_ERROR("The given directed degree sequences cannot be realized as a simple graph.", IGRAPH_EINVAL); +} + + +/**************************/ +/***** Main functions *****/ +/**************************/ + +static int igraph_i_realize_undirected_degree_sequence( + igraph_t *graph, + const igraph_vector_t *deg, + igraph_edge_type_sw_t allowed_edge_types, + igraph_realize_degseq_t method) +{ + long node_count = igraph_vector_size(deg); + long deg_sum = long(igraph_vector_sum(deg)); + + if (deg_sum % 2 != 0) { + IGRAPH_ERROR("The sum of degrees must be even for an undirected graph.", IGRAPH_EINVAL); + } + + if (node_count > 0 && igraph_vector_min(deg) < 0) { + IGRAPH_ERROR("Vertex degrees must be non-negative.", IGRAPH_EINVAL); + } + + igraph_vector_t edges; + IGRAPH_CHECK(igraph_vector_init(&edges, deg_sum)); + IGRAPH_FINALLY(igraph_vector_destroy, &edges); + + if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) && (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW ) ) + { + switch (method) { + case IGRAPH_REALIZE_DEGSEQ_SMALLEST: + IGRAPH_CHECK(igraph_i_realize_undirected_multi(deg, &edges, true, false)); + break; + case IGRAPH_REALIZE_DEGSEQ_LARGEST: + IGRAPH_CHECK(igraph_i_realize_undirected_multi(deg, &edges, true, true)); + break; + case IGRAPH_REALIZE_DEGSEQ_INDEX: + IGRAPH_CHECK(igraph_i_realize_undirected_multi_index(deg, &edges, true)); + break; + default: + IGRAPH_ERROR("Invalid degree sequence realization method.", IGRAPH_EINVAL); + } + } + else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) + { + switch (method) { + case IGRAPH_REALIZE_DEGSEQ_SMALLEST: + IGRAPH_CHECK(igraph_i_realize_undirected_multi(deg, &edges, false, false)); + break; + case IGRAPH_REALIZE_DEGSEQ_LARGEST: + IGRAPH_CHECK(igraph_i_realize_undirected_multi(deg, &edges, false, true)); + break; + case IGRAPH_REALIZE_DEGSEQ_INDEX: + IGRAPH_CHECK(igraph_i_realize_undirected_multi_index(deg, &edges, false)); + break; + default: + IGRAPH_ERROR("Invalid degree sequence realization method.", IGRAPH_EINVAL); + } + } + else if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) + { + IGRAPH_ERROR("Graph realization with at most one self-loop per vertex is not implemented.", IGRAPH_UNIMPLEMENTED); + } + else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) + { + switch (method) { + case IGRAPH_REALIZE_DEGSEQ_SMALLEST: + IGRAPH_CHECK(igraph_i_havel_hakimi(deg, &edges, false)); + break; + case IGRAPH_REALIZE_DEGSEQ_LARGEST: + IGRAPH_CHECK(igraph_i_havel_hakimi(deg, &edges, true)); + break; + case IGRAPH_REALIZE_DEGSEQ_INDEX: + IGRAPH_CHECK(igraph_i_havel_hakimi_index(deg, &edges)); + break; + default: + IGRAPH_ERROR("Invalid degree sequence realization method.", IGRAPH_EINVAL); + } + } + else + { + /* Remainig cases: + * - At most one self-loop per vertex but multi-edges between distinct vertices allowed. + * - At most one edge between distinct vertices but multi-self-loops allowed. + * These cases cannot currently be requested through the documented API, + * so no explanatory error message for now. */ + return IGRAPH_UNIMPLEMENTED; + } + + igraph_create(graph, &edges, igraph_integer_t(node_count), false); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +static int igraph_i_realize_directed_degree_sequence( + igraph_t *graph, + const igraph_vector_t *outdeg, + const igraph_vector_t *indeg, + igraph_edge_type_sw_t allowed_edge_types, + igraph_realize_degseq_t method) +{ + long node_count = igraph_vector_size(outdeg); + long edge_count = long(igraph_vector_sum(outdeg)); + + if (igraph_vector_size(indeg) != node_count) { + IGRAPH_ERROR("In- and out-degree sequences must have the same length.", IGRAPH_EINVAL); + } + if (igraph_vector_sum(indeg) != edge_count) { + IGRAPH_ERROR("In- and out-degree sequences do not sum to the same value.", IGRAPH_EINVAL); + } + + if (node_count > 0 && (igraph_vector_min(outdeg) < 0 || igraph_vector_min(indeg) < 0)) { + IGRAPH_ERROR("Vertex degrees must be non-negative.", IGRAPH_EINVAL); + } + + /* TODO implement loopless and loopy multigraph case */ + if (allowed_edge_types != IGRAPH_SIMPLE_SW) { + IGRAPH_ERROR("Realizing directed degree sequences as non-simple graphs is not implemented.", IGRAPH_UNIMPLEMENTED); + } + + igraph_vector_t edges; + IGRAPH_CHECK(igraph_vector_init(&edges, 2 * edge_count)); + IGRAPH_FINALLY(igraph_vector_destroy, &edges); + + switch (method) { + case IGRAPH_REALIZE_DEGSEQ_SMALLEST: + IGRAPH_CHECK(igraph_i_kleitman_wang(outdeg, indeg, &edges, true)); + break; + case IGRAPH_REALIZE_DEGSEQ_LARGEST: + IGRAPH_CHECK(igraph_i_kleitman_wang(outdeg, indeg, &edges, false)); + break; + case IGRAPH_REALIZE_DEGSEQ_INDEX: + IGRAPH_CHECK(igraph_i_kleitman_wang_index(outdeg, indeg, &edges)); + break; + default: + IGRAPH_ERROR("Invalid directed degree sequence realization method.", IGRAPH_EINVAL); + } + + igraph_create(graph, &edges, igraph_integer_t(node_count), true); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup generators + * \function igraph_realize_degree_sequence + * \brief Generates a graph with the given degree sequence. + * + * This function generates an undirected graph that realizes a given degree sequence, + * or a directed graph that realized a given pair of out- and in-degree sequences. + * + * + * Simple undirected graphs are constructed using the Havel-Hakimi algorithm + * (undirected case), or the analogous Kleitman-Wang algorithm (directed case). + * These algorithms work by choosing an arbitrary vertex and connecting all its stubs + * to other vertices of highest degree. In the directed case, the "highest" (in, out) degree + * pairs are determined based on lexicographic ordering. This step is repeated until all degrees + * have been connected up. + * + * + * Loopless multigraphs are generated using an analogous algorithm: an arbitrary vertex is chosen, + * and it is connected with a single connection to a highest remaining degee vertex. If self-loops + * are also allowed, the same algorithm is used, but if a non-zero vertex remains at the end of the + * procedure, the graph is completed by adding self-loops to it. Thus, the result will contain at most + * one vertex with self-loops. + * + * + * The \c method parameter controls the order in which the vertices to be connected are chosen. + * + * + * References: + * + * + * V. Havel, + * Poznámka o existenci konečných grafů (A remark on the existence of finite graphs), + * Časopis pro pěstování matematiky 80, 477-480 (1955). + * http://eudml.org/doc/19050 + * + * + * S. L. Hakimi, + * On Realizability of a Set of Integers as Degrees of the Vertices of a Linear Graph, + * Journal of the SIAM 10, 3 (1962). + * https://www.jstor.org/stable/2098746 + * + * + * D. J. Kleitman and D. L. Wang, + * Algorithms for Constructing Graphs and Digraphs with Given Valences and Factors, + * Discrete Mathematics 6, 1 (1973). + * https://doi.org/10.1016/0012-365X%2873%2990037-X + * + * + * Sz. Horvát and C. D. Modes, + * Connectivity matters: Construction and exact random sampling of connected graphs (2020). + * https://arxiv.org/abs/2009.03747 + * + * \param graph Pointer to an uninitialized graph object. + * \param outdeg The degree sequence of an undirected graph + * (if \p indeg is NULL), or the out-degree sequence of + * a directed graph (if \p indeg is given). + * \param indeg The in-degree sequence of a directed graph. + * Pass \c NULL to generate an undirected graph. + * \param allowed_edge_types The types of edges to allow in the graph. For directed graphs, + * only \c IGRAPH_SIMPLE_SW is implemented at this moment. For undirected + * graphs, the following values are valid: + * \clist + * \cli IGRAPH_SIMPLE_SW + * simple graphs (i.e. no self-loops or multi-edges allowed). + * \cli IGRAPH_LOOPS_SW + * single self-loops are allowed, but not multi-edges; currently not implemented. + * \cli IGRAPH_MULTI_SW + * multi-edges are allowed, but not self-loops. + * \cli IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW + * both self-loops and multi-edges are allowed. + * \endclist + * \param method The method to generate the graph. Possible values: + * \clist + * \cli IGRAPH_REALIZE_DEGSEQ_SMALLEST + * The vertex with smallest remaining degree is selected first. The result is usually + * a graph with high negative degree assortativity. In the undirected case, this method + * is guaranteed to generate a connected graph, regardless of whether multi-edges are allowed, + * provided that a connected realization exists (see Horvát and Modes, 2020, as well as + * http://szhorvat.net/pelican/hh-connected-graphs.html). + * In the directed case it tends to generate weakly connected graphs, but this is not + * guaranteed. + * \cli IGRAPH_REALIZE_DEGSEQ_LARGEST + * The vertex with the largest remaining degree is selected first. The result + * is usually a graph with high positive degree assortativity, and is often disconnected. + * \cli IGRAPH_REALIZE_DEGSEQ_INDEX + * The vertices are selected in order of their index (i.e. their position in the degree vector). + * Note that sorting the degree vector and using the \c INDEX method is not equivalent + * to the \c SMALLEST method above, as \c SMALLEST uses the smallest \em remaining + * degree for selecting vertices, not the smallest \em initial degree. + * \endclist + * \return Error code: + * \clist + * \cli IGRAPH_UNIMPLEMENTED + * The requested method is not implemented. + * \cli IGRAPH_ENOMEM + * There is not enough memory to perform the operation. + * \cli IGRAPH_EINVAL + * Invalid method parameter, or invalid in- and/or out-degree vectors. + * The degree vectors should be non-negative, the length + * and sum of \p outdeg and \p indeg should match for directed graphs. + * \endclist + * + * \sa \ref igraph_is_graphical() to test graphicality without generating a graph; + * \ref igraph_degree_sequence_game() to generate random graphs with a given degree sequence; + * \ref igraph_k_regular_game() to generate random regular graphs; + * \ref igraph_rewire() to randomly rewire the edges of a graph while preserving its degree sequence. + * + */ + +int igraph_realize_degree_sequence( + igraph_t *graph, + const igraph_vector_t *outdeg, const igraph_vector_t *indeg, + igraph_edge_type_sw_t allowed_edge_types, + igraph_realize_degseq_t method) +{ + long n = igraph_vector_size(outdeg); + if (n != igraph_integer_t(n)) { // does the vector size fit into an igraph_integer_t ? + IGRAPH_ERROR("Degree sequence vector too long.", IGRAPH_EINVAL); + } + + bool directed = indeg != 0; + + try { + if (directed) { + return igraph_i_realize_directed_degree_sequence(graph, outdeg, indeg, allowed_edge_types, method); + } else { + return igraph_i_realize_undirected_degree_sequence(graph, outdeg, allowed_edge_types, method); + } + } catch (const std::bad_alloc &) { + IGRAPH_ERROR("Cannot realize degree sequence due to insufficient memory.", IGRAPH_ENOMEM); + } +} diff --git a/src/rigraph/core/misc/embedding.c b/src/rigraph/core/misc/embedding.c new file mode 100644 index 0000000..04b6d63 --- /dev/null +++ b/src/rigraph/core/misc/embedding.c @@ -0,0 +1,1176 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_embedding.h" + +#include "igraph_adjlist.h" +#include "igraph_blas.h" +#include "igraph_centrality.h" +#include "igraph_interface.h" +#include "igraph_structural.h" + +typedef struct { + const igraph_t *graph; + const igraph_vector_t *cvec; + const igraph_vector_t *cvec2; + igraph_adjlist_t *outlist, *inlist; + igraph_inclist_t *eoutlist, *einlist; + igraph_vector_t *tmp; + const igraph_vector_t *weights; +} igraph_i_asembedding_data_t; + +/* Adjacency matrix, unweighted, undirected. + Eigendecomposition is used */ +static int igraph_i_asembeddingu(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + igraph_i_asembedding_data_t *data = extra; + igraph_adjlist_t *outlist = data->outlist; + const igraph_vector_t *cvec = data->cvec; + igraph_vector_int_t *neis; + int i, j, nlen; + + /* to = (A+cD) from */ + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(outlist, i); + nlen = igraph_vector_int_size(neis); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + to[i] += from[nei]; + } + to[i] += VECTOR(*cvec)[i] * from[i]; + } + + return 0; +} + +/* Adjacency matrix, weighted, undirected. + Eigendecomposition is used. */ +static int igraph_i_asembeddinguw(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + igraph_i_asembedding_data_t *data = extra; + igraph_inclist_t *outlist = data->eoutlist; + const igraph_vector_t *cvec = data->cvec; + const igraph_vector_t *weights = data->weights; + const igraph_t *graph = data->graph; + igraph_vector_int_t *incs; + int i, j, nlen; + + /* to = (A+cD) from */ + for (i = 0; i < n; i++) { + incs = igraph_inclist_get(outlist, i); + nlen = igraph_vector_int_size(incs); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int edge = VECTOR(*incs)[j]; + long int nei = IGRAPH_OTHER(graph, edge, i); + igraph_real_t w = VECTOR(*weights)[edge]; + to[i] += w * from[nei]; + } + to[i] += VECTOR(*cvec)[i] * from[i]; + } + + return 0; +} + +/* Adjacency matrix, unweighted, directed. SVD. */ +static int igraph_i_asembedding(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + igraph_i_asembedding_data_t *data = extra; + igraph_adjlist_t *outlist = data->outlist; + igraph_adjlist_t *inlist = data->inlist; + const igraph_vector_t *cvec = data->cvec; + igraph_vector_t *tmp = data->tmp; + igraph_vector_int_t *neis; + int i, j, nlen; + + /* tmp = (A+cD)' from */ + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(inlist, i); + nlen = igraph_vector_int_size(neis); + VECTOR(*tmp)[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + VECTOR(*tmp)[i] += from[nei]; + } + VECTOR(*tmp)[i] += VECTOR(*cvec)[i] * from[i]; + } + + /* to = (A+cD) tmp */ + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(outlist, i); + nlen = igraph_vector_int_size(neis); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + to[i] += VECTOR(*tmp)[nei]; + } + to[i] += VECTOR(*cvec)[i] * VECTOR(*tmp)[i]; + } + + return 0; +} + +/* Adjacency matrix, unweighted, directed. SVD, right eigenvectors */ +static int igraph_i_asembedding_right(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + igraph_i_asembedding_data_t *data = extra; + igraph_adjlist_t *inlist = data->inlist; + const igraph_vector_t *cvec = data->cvec; + igraph_vector_int_t *neis; + int i, j, nlen; + + /* to = (A+cD)' from */ + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(inlist, i); + nlen = igraph_vector_int_size(neis); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + to[i] += from[nei]; + } + to[i] += VECTOR(*cvec)[i] * from[i]; + } + + return 0; +} + +/* Adjacency matrix, weighted, directed. SVD. */ +static int igraph_i_asembeddingw(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + igraph_i_asembedding_data_t *data = extra; + igraph_inclist_t *outlist = data->eoutlist; + igraph_inclist_t *inlist = data->einlist; + const igraph_vector_t *cvec = data->cvec; + const igraph_vector_t *weights = data->weights; + const igraph_t *graph = data->graph; + igraph_vector_t *tmp = data->tmp; + igraph_vector_int_t *incs; + int i, j, nlen; + + /* tmp = (A+cD)' from */ + for (i = 0; i < n; i++) { + incs = igraph_inclist_get(inlist, i); + nlen = igraph_vector_int_size(incs); + VECTOR(*tmp)[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int edge = VECTOR(*incs)[j]; + long int nei = IGRAPH_OTHER(graph, edge, i); + igraph_real_t w = VECTOR(*weights)[edge]; + VECTOR(*tmp)[i] += w * from[nei]; + } + VECTOR(*tmp)[i] += VECTOR(*cvec)[i] * from[i]; + } + + /* to = (A+cD) tmp */ + for (i = 0; i < n; i++) { + incs = igraph_inclist_get(outlist, i); + nlen = igraph_vector_int_size(incs); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int edge = VECTOR(*incs)[j]; + long int nei = IGRAPH_OTHER(graph, edge, i); + igraph_real_t w = VECTOR(*weights)[edge]; + to[i] += w * VECTOR(*tmp)[nei]; + } + to[i] += VECTOR(*cvec)[i] * VECTOR(*tmp)[i]; + } + + return 0; +} + +/* Adjacency matrix, weighted, directed. SVD, right eigenvectors. */ +static int igraph_i_asembeddingw_right(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + igraph_i_asembedding_data_t *data = extra; + igraph_inclist_t *inlist = data->einlist; + const igraph_vector_t *cvec = data->cvec; + const igraph_vector_t *weights = data->weights; + const igraph_t *graph = data->graph; + igraph_vector_int_t *incs; + int i, j, nlen; + + /* to = (A+cD)' from */ + for (i = 0; i < n; i++) { + incs = igraph_inclist_get(inlist, i); + nlen = igraph_vector_int_size(incs); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int edge = VECTOR(*incs)[j]; + long int nei = IGRAPH_OTHER(graph, edge, i); + igraph_real_t w = VECTOR(*weights)[edge]; + to[i] += w * from[nei]; + } + to[i] += VECTOR(*cvec)[i] * from[i]; + } + + return 0; +} + +/* Laplacian D-A, unweighted, undirected. Eigendecomposition. */ +static int igraph_i_lsembedding_da(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + igraph_i_asembedding_data_t *data = extra; + igraph_adjlist_t *outlist = data->outlist; + const igraph_vector_t *cvec = data->cvec; + igraph_vector_int_t *neis; + int i, j, nlen; + + /* to = (D-A) from */ + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(outlist, i); + nlen = igraph_vector_int_size(neis); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + to[i] -= from[nei]; + } + to[i] += VECTOR(*cvec)[i] * from[i]; + } + + return 0; +} + +/* Laplacian D-A, weighted, undirected. Eigendecomposition. */ +static int igraph_i_lsembedding_daw(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + igraph_i_asembedding_data_t *data = extra; + igraph_inclist_t *outlist = data->eoutlist; + const igraph_vector_t *cvec = data->cvec; + const igraph_vector_t *weights = data->weights; + const igraph_t *graph = data->graph; + igraph_vector_int_t *incs; + int i, j, nlen; + + /* to = (D-A) from */ + for (i = 0; i < n; i++) { + incs = igraph_inclist_get(outlist, i); + nlen = igraph_vector_int_size(incs); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int edge = VECTOR(*incs)[j]; + long int nei = IGRAPH_OTHER(graph, edge, i); + igraph_real_t w = VECTOR(*weights)[edge]; + to[i] -= w * from[nei]; + } + to[i] += VECTOR(*cvec)[i] * from[i]; + } + + return 0; +} + +/* Laplacian DAD, unweighted, undirected. Eigendecomposition. */ +static int igraph_i_lsembedding_dad(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + igraph_i_asembedding_data_t *data = extra; + igraph_adjlist_t *outlist = data->outlist; + const igraph_vector_t *cvec = data->cvec; + igraph_vector_t *tmp = data->tmp; + igraph_vector_int_t *neis; + int i, j, nlen; + + /* to = D^1/2 from */ + for (i = 0; i < n; i++) { + to[i] = VECTOR(*cvec)[i] * from[i]; + } + + /* tmp = A to */ + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(outlist, i); + nlen = igraph_vector_int_size(neis); + VECTOR(*tmp)[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + VECTOR(*tmp)[i] += to[nei]; + } + } + + /* to = D tmp */ + for (i = 0; i < n; i++) { + to[i] = VECTOR(*cvec)[i] * VECTOR(*tmp)[i]; + } + + return 0; +} + +static int igraph_i_lsembedding_dadw(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + igraph_i_asembedding_data_t *data = extra; + igraph_inclist_t *outlist = data->eoutlist; + const igraph_vector_t *cvec = data->cvec; + const igraph_vector_t *weights = data->weights; + const igraph_t *graph = data->graph; + igraph_vector_t *tmp = data->tmp; + igraph_vector_int_t *incs; + int i, j, nlen; + + /* to = D^-1/2 from */ + for (i = 0; i < n; i++) { + to[i] = VECTOR(*cvec)[i] * from[i]; + } + + /* tmp = A' to */ + for (i = 0; i < n; i++) { + incs = igraph_inclist_get(outlist, i); + nlen = igraph_vector_int_size(incs); + VECTOR(*tmp)[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int edge = VECTOR(*incs)[j]; + long int nei = IGRAPH_OTHER(graph, edge, i); + igraph_real_t w = VECTOR(*weights)[edge]; + VECTOR(*tmp)[i] += w * to[nei]; + } + } + + /* to = D tmp */ + for (i = 0; i < n; i++) { + to[i] = VECTOR(*cvec)[i] * VECTOR(*cvec)[i] * VECTOR(*tmp)[i]; + } + + /* tmp = A to */ + for (i = 0; i < n; i++) { + incs = igraph_inclist_get(outlist, i); + nlen = igraph_vector_int_size(incs); + VECTOR(*tmp)[i] = 0.0; + for (j = 0; j < nlen; j++) { + long int edge = VECTOR(*incs)[j]; + long int nei = IGRAPH_OTHER(graph, edge, i); + igraph_real_t w = VECTOR(*weights)[edge]; + VECTOR(*tmp)[i] += w * to[nei]; + } + } + + /* to = D^-1/2 tmp */ + for (i = 0; i < n; i++) { + to[i] = VECTOR(*cvec)[i] * VECTOR(*tmp)[i]; + } + + return 0; +} + +/* Laplacian I-DAD, unweighted, undirected. Eigendecomposition. */ +static int igraph_i_lsembedding_idad(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + int i; + + igraph_i_lsembedding_dad(to, from, n, extra); + for (i = 0; i < n; i++) { + to[i] = from[i] - to[i]; + } + + return 0; +} + +static int igraph_i_lsembedding_idadw(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + int i; + + igraph_i_lsembedding_dadw(to, from, n, extra); + for (i = 0; i < n; i++) { + to[i] = from[i] - to[i]; + } + + return 0; +} + +/* Laplacian OAP, unweighted, directed. SVD. */ +static int igraph_i_lseembedding_oap(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + igraph_i_asembedding_data_t *data = extra; + igraph_adjlist_t *outlist = data->outlist; + igraph_adjlist_t *inlist = data->inlist; + const igraph_vector_t *deg_in = data->cvec; + const igraph_vector_t *deg_out = data->cvec2; + igraph_vector_t *tmp = data->tmp; + igraph_vector_int_t *neis; + int i, j, nlen; + + /* tmp = O' from */ + for (i = 0; i < n; i++) { + VECTOR(*tmp)[i] = VECTOR(*deg_out)[i] * from[i]; + } + + /* to = A' tmp */ + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(inlist, i); + nlen = igraph_vector_int_size(neis); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + int nei = VECTOR(*neis)[j]; + to[i] += VECTOR(*tmp)[nei]; + } + } + + /* tmp = P' to */ + for (i = 0; i < n; i++) { + VECTOR(*tmp)[i] = VECTOR(*deg_in)[i] * to[i]; + } + + /* to = P tmp */ + for (i = 0; i < n; i++) { + to[i] = VECTOR(*deg_in)[i] * VECTOR(*tmp)[i]; + } + + /* tmp = A to */ + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(outlist, i); + nlen = igraph_vector_int_size(neis); + VECTOR(*tmp)[i] = 0.0; + for (j = 0; j < nlen; j++) { + int nei = VECTOR(*neis)[j]; + VECTOR(*tmp)[i] += to[nei]; + } + } + + /* to = O tmp */ + for (i = 0; i < n; i++) { + to[i] = VECTOR(*deg_out)[i] * VECTOR(*tmp)[i]; + } + + return 0; +} + +/* Laplacian OAP, unweighted, directed. SVD, right eigenvectors. */ +static int igraph_i_lseembedding_oap_right(igraph_real_t *to, + const igraph_real_t *from, + int n, void *extra) { + igraph_i_asembedding_data_t *data = extra; + igraph_adjlist_t *inlist = data->inlist; + const igraph_vector_t *deg_in = data->cvec; + const igraph_vector_t *deg_out = data->cvec2; + igraph_vector_t *tmp = data->tmp; + igraph_vector_int_t *neis; + int i, j, nlen; + + /* to = O' from */ + for (i = 0; i < n; i++) { + to[i] = VECTOR(*deg_out)[i] * from[i]; + } + + /* tmp = A' to */ + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(inlist, i); + nlen = igraph_vector_int_size(neis); + VECTOR(*tmp)[i] = 0.0; + for (j = 0; j < nlen; j++) { + int nei = VECTOR(*neis)[j]; + VECTOR(*tmp)[i] += to[nei]; + } + } + + /* to = P' tmp */ + for (i = 0; i < n; i++) { + to[i] = VECTOR(*deg_in)[i] * VECTOR(*tmp)[i]; + } + + return 0; +} + +/* Laplacian OAP, weighted, directed. SVD. */ +static int igraph_i_lseembedding_oapw(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + igraph_i_asembedding_data_t *data = extra; + igraph_inclist_t *outlist = data->eoutlist; + igraph_inclist_t *inlist = data->einlist; + const igraph_vector_t *deg_in = data->cvec; + const igraph_vector_t *deg_out = data->cvec2; + const igraph_vector_t *weights = data->weights; + const igraph_t *graph = data->graph; + igraph_vector_t *tmp = data->tmp; + igraph_vector_int_t *neis; + int i, j, nlen; + + /* tmp = O' from */ + for (i = 0; i < n; i++) { + VECTOR(*tmp)[i] = VECTOR(*deg_out)[i] * from[i]; + } + + /* to = A' tmp */ + for (i = 0; i < n; i++) { + neis = igraph_inclist_get(inlist, i); + nlen = igraph_vector_int_size(neis); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + int edge = VECTOR(*neis)[j]; + int nei = IGRAPH_OTHER(graph, edge, i); + igraph_real_t w = VECTOR(*weights)[edge]; + to[i] += w * VECTOR(*tmp)[nei]; + } + } + + /* tmp = P' to */ + for (i = 0; i < n; i++) { + VECTOR(*tmp)[i] = VECTOR(*deg_in)[i] * to[i]; + } + + /* to = P tmp */ + for (i = 0; i < n; i++) { + to[i] = VECTOR(*deg_in)[i] * VECTOR(*tmp)[i]; + } + + /* tmp = A to */ + for (i = 0; i < n; i++) { + neis = igraph_inclist_get(outlist, i); + nlen = igraph_vector_int_size(neis); + VECTOR(*tmp)[i] = 0.0; + for (j = 0; j < nlen; j++) { + int edge = VECTOR(*neis)[j]; + int nei = IGRAPH_OTHER(graph, edge, i); + igraph_real_t w = VECTOR(*weights)[edge]; + VECTOR(*tmp)[i] += w * to[nei]; + } + } + + /* to = O tmp */ + for (i = 0; i < n; i++) { + to[i] = VECTOR(*deg_out)[i] * VECTOR(*tmp)[i]; + } + + return 0; +} + +/* Laplacian OAP, weighted, directed. SVD, right eigenvectors. */ +static int igraph_i_lseembedding_oapw_right(igraph_real_t *to, + const igraph_real_t *from, + int n, void *extra) { + igraph_i_asembedding_data_t *data = extra; + igraph_inclist_t *inlist = data->einlist; + const igraph_vector_t *deg_in = data->cvec; + const igraph_vector_t *deg_out = data->cvec2; + const igraph_vector_t *weights = data->weights; + const igraph_t *graph = data->graph; + igraph_vector_t *tmp = data->tmp; + igraph_vector_int_t *neis; + int i, j, nlen; + + /* to = O' from */ + for (i = 0; i < n; i++) { + to[i] = VECTOR(*deg_out)[i] * from[i]; + } + + /* tmp = A' to */ + for (i = 0; i < n; i++) { + neis = igraph_inclist_get(inlist, i); + nlen = igraph_vector_int_size(neis); + VECTOR(*tmp)[i] = 0.0; + for (j = 0; j < nlen; j++) { + int edge = VECTOR(*neis)[j]; + int nei = IGRAPH_OTHER(graph, edge, i); + igraph_real_t w = VECTOR(*weights)[edge]; + VECTOR(*tmp)[i] += w * to[nei]; + } + } + + /* to = P' tmp */ + for (i = 0; i < n; i++) { + to[i] = VECTOR(*deg_in)[i] * VECTOR(*tmp)[i]; + } + + return 0; +} + +static int igraph_i_spectral_embedding(const igraph_t *graph, + igraph_integer_t no, + const igraph_vector_t *weights, + igraph_eigen_which_position_t which, + igraph_bool_t scaled, + igraph_matrix_t *X, + igraph_matrix_t *Y, + igraph_vector_t *D, + const igraph_vector_t *cvec, + const igraph_vector_t *cvec2, + igraph_arpack_options_t *options, + igraph_arpack_function_t *callback, + igraph_arpack_function_t *callback_right, + igraph_bool_t symmetric, + igraph_bool_t eigen, + igraph_bool_t zapsmall) { + + igraph_integer_t vc = igraph_vcount(graph); + igraph_vector_t tmp; + igraph_adjlist_t outlist, inlist; + igraph_inclist_t eoutlist, einlist; + int i, j, cveclen = igraph_vector_size(cvec); + igraph_i_asembedding_data_t data; + igraph_vector_t tmpD; + + data.graph = graph; + data.cvec = cvec; + data.cvec2 = cvec2; + data.outlist = &outlist; + data.inlist = &inlist; + data.eoutlist = &eoutlist; + data.einlist = &einlist; + data.tmp = &tmp; + data.weights = weights; + + if (weights && igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + } + + if (which != IGRAPH_EIGEN_LM && + which != IGRAPH_EIGEN_LA && + which != IGRAPH_EIGEN_SA) { + IGRAPH_ERROR("Invalid eigenvalue chosen, must be one of " + "`largest magnitude', `largest algebraic' or " + "`smallest algebraic'", IGRAPH_EINVAL); + } + + if (no > vc) { + IGRAPH_ERROR("Too many singular values requested", IGRAPH_EINVAL); + } + if (no <= 0) { + IGRAPH_ERROR("No singular values requested", IGRAPH_EINVAL); + } + + if (cveclen != 1 && cveclen != vc) { + IGRAPH_ERROR("Augmentation vector size is invalid, it should be " + "the number of vertices or scalar", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_matrix_resize(X, vc, no)); + if (Y) { + IGRAPH_CHECK(igraph_matrix_resize(Y, vc, no)); + } + + /* empty graph */ + if (igraph_ecount(graph) == 0) { + igraph_matrix_null(X); + if (Y) { + igraph_matrix_null(Y); + } + return 0; + } + + igraph_vector_init(&tmp, vc); + IGRAPH_FINALLY(igraph_vector_destroy, &tmp); + if (!weights) { + IGRAPH_CHECK(igraph_adjlist_init(graph, &outlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &outlist); + if (!symmetric) { + IGRAPH_CHECK(igraph_adjlist_init(graph, &inlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &inlist); + } + } else { + IGRAPH_CHECK(igraph_inclist_init(graph, &eoutlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &eoutlist); + if (!symmetric) { + IGRAPH_CHECK(igraph_inclist_init(graph, &einlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &einlist); + } + } + IGRAPH_VECTOR_INIT_FINALLY(&tmpD, no); + + options->n = vc; + options->start = 0; /* random start vector */ + options->nev = no; + switch (which) { + case IGRAPH_EIGEN_LM: + options->which[0] = 'L'; options->which[1] = 'M'; + break; + case IGRAPH_EIGEN_LA: + options->which[0] = 'L'; options->which[1] = 'A'; + break; + case IGRAPH_EIGEN_SA: + options->which[0] = 'S'; options->which[1] = 'A'; + break; + default: + break; + } + options->ncv = no + 3; + if (options->ncv > vc) { + options->ncv = vc; + } + + IGRAPH_CHECK(igraph_arpack_rssolve(callback, &data, options, 0, &tmpD, X)); + + if (!symmetric) { + /* calculate left eigenvalues */ + IGRAPH_CHECK(igraph_matrix_resize(Y, vc, no)); + for (i = 0; i < no; i++) { + igraph_real_t norm; + igraph_vector_t v; + callback_right(&MATRIX(*Y, 0, i), &MATRIX(*X, 0, i), vc, &data); + igraph_vector_view(&v, &MATRIX(*Y, 0, i), vc); + norm = 1.0 / igraph_blas_dnrm2(&v); + igraph_vector_scale(&v, norm); + } + } else if (Y) { + IGRAPH_CHECK(igraph_matrix_update(Y, X)); + } + + if (zapsmall) { + igraph_vector_zapsmall(&tmpD, 0); + igraph_matrix_zapsmall(X, 0); + if (Y) { + igraph_matrix_zapsmall(Y, 0); + } + } + + if (D) { + igraph_vector_update(D, &tmpD); + if (!eigen) { + for (i = 0; i < no; i++) { + VECTOR(*D)[i] = sqrt(VECTOR(*D)[i]); + } + } + } + + if (scaled) { + if (eigen) { + /* eigenvalues were calculated */ + for (i = 0; i < no; i++) { + VECTOR(tmpD)[i] = sqrt(fabs(VECTOR(tmpD)[i])); + } + } else { + /* singular values were calculated */ + for (i = 0; i < no; i++) { + VECTOR(tmpD)[i] = sqrt(sqrt(VECTOR(tmpD)[i])); + } + } + + for (j = 0; j < vc; j++) { + for (i = 0; i < no; i++) { + MATRIX(*X, j, i) *= VECTOR(tmpD)[i]; + } + } + + if (Y) { + for (j = 0; j < vc; j++) { + for (i = 0; i < no; i++) { + MATRIX(*Y, j, i) *= VECTOR(tmpD)[i]; + } + } + } + } + + igraph_vector_destroy(&tmpD); + if (!weights) { + if (!symmetric) { + igraph_adjlist_destroy(&inlist); + IGRAPH_FINALLY_CLEAN(1); + } + igraph_adjlist_destroy(&outlist); + } else { + if (!symmetric) { + igraph_inclist_destroy(&einlist); + IGRAPH_FINALLY_CLEAN(1); + } + igraph_inclist_destroy(&eoutlist); + } + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \function igraph_adjacency_spectral_embedding + * Adjacency spectral embedding + * + * Spectral decomposition of the adjacency matrices of graphs. + * This function computes an n-dimensional Euclidean + * representation of the graph based on its adjacency + * matrix, A. This representation is computed via the singular value + * decomposition of the adjacency matrix, A=U D V^T. In the case, + * where the graph is a random dot product graph generated using latent + * position vectors in R^n for each vertex, the embedding will + * provide an estimate of these latent vectors. + * + * + * For undirected graphs, the latent positions are calculated as + * X = U^n D^(1/2) where U^n equals to the first no columns of U, and + * D^(1/2) is a diagonal matrix containing the square root of the selected + * singular values on the diagonal. + * + * + * For directed graphs, the embedding is defined as the pair + * X = U^n D^(1/2), Y = V^n D^(1/2). + * (For undirected graphs U=V, so it is sufficient to keep one of them.) + * + * \param graph The input graph, can be directed or undirected. + * \param n An integer scalar. This value is the embedding dimension of + * the spectral embedding. Should be smaller than the number of + * vertices. The largest n-dimensional non-zero + * singular values are used for the spectral embedding. + * \param weights Optional edge weights. Supply a null pointer for + * unweighted graphs. + * \param which Which eigenvalues (or singular values, for directed + * graphs) to use, possible values: + * \clist + * \cli IGRAPH_EIGEN_LM + * the ones with the largest magnitude + * \cli IGRAPH_EIGEN_LA + * the (algebraic) largest ones + * \cli IGRAPH_EIGEN_SA + * the (algebraic) smallest ones. + * \endclist + * For directed graphs, IGRAPH_EIGEN_LM and + * IGRAPH_EIGEN_LA are the same because singular + * values are used for the ordering instead of eigenvalues. + * \param scaled Whether to return X and Y (if \c scaled is true), or + * U and V. + * \param X Initialized matrix, the estimated latent positions are + * stored here. + * \param Y Initialized matrix or a null pointer. If not a null + * pointer, then the second half of the latent positions are + * stored here. (For undirected graphs, this always equals X.) + * \param D Initialized vector or a null pointer. If not a null + * pointer, then the eigenvalues (for undirected graphs) or the + * singular values (for directed graphs) are stored here. + * \param cvec A numeric vector, its length is the number vertices in the + * graph. This vector is added to the diagonal of the adjacency + * matrix, before performing the SVD. + * \param options Options to ARPACK. See \ref igraph_arpack_options_t + * for details. Note that the function overwrites the + * n (number of vertices), nev and + * which parameters and it always starts the + * calculation from a random start vector. + * \return Error code. + * + */ + +int igraph_adjacency_spectral_embedding(const igraph_t *graph, + igraph_integer_t n, + const igraph_vector_t *weights, + igraph_eigen_which_position_t which, + igraph_bool_t scaled, + igraph_matrix_t *X, + igraph_matrix_t *Y, + igraph_vector_t *D, + const igraph_vector_t *cvec, + igraph_arpack_options_t *options) { + + igraph_arpack_function_t *callback, *callback_right; + igraph_bool_t directed = igraph_is_directed(graph); + + if (directed) { + callback = weights ? igraph_i_asembeddingw : igraph_i_asembedding; + callback_right = (weights ? igraph_i_asembeddingw_right : + igraph_i_asembedding_right); + } else { + callback = weights ? igraph_i_asembeddinguw : igraph_i_asembeddingu; + callback_right = 0; + } + + return igraph_i_spectral_embedding(graph, n, weights, which, scaled, + X, Y, D, cvec, /* deg2=*/ 0, + options, callback, callback_right, + /*symmetric=*/ !directed, + /*eigen=*/ !directed, /*zapsmall=*/ 1); +} + +static int igraph_i_lse_und(const igraph_t *graph, + igraph_integer_t no, + const igraph_vector_t *weights, + igraph_eigen_which_position_t which, + igraph_laplacian_spectral_embedding_type_t type, + igraph_bool_t scaled, + igraph_matrix_t *X, + igraph_matrix_t *Y, + igraph_vector_t *D, + igraph_arpack_options_t *options) { + + igraph_arpack_function_t *callback; + igraph_vector_t deg; + + switch (type) { + case IGRAPH_EMBEDDING_D_A: + callback = weights ? igraph_i_lsembedding_daw : igraph_i_lsembedding_da; + break; + case IGRAPH_EMBEDDING_DAD: + callback = weights ? igraph_i_lsembedding_dadw : igraph_i_lsembedding_dad; + break; + case IGRAPH_EMBEDDING_I_DAD: + callback = weights ? igraph_i_lsembedding_idadw : igraph_i_lsembedding_idad; + break; + default: + IGRAPH_ERROR("Invalid Laplacian spectral embedding type", + IGRAPH_EINVAL); + break; + } + + IGRAPH_VECTOR_INIT_FINALLY(°, 0); + igraph_strength(graph, °, igraph_vss_all(), IGRAPH_ALL, /*loops=*/ 1, + weights); + + switch (type) { + case IGRAPH_EMBEDDING_D_A: + break; + case IGRAPH_EMBEDDING_DAD: + case IGRAPH_EMBEDDING_I_DAD: { + int i, n = igraph_vector_size(°); + for (i = 0; i < n; i++) { + VECTOR(deg)[i] = 1.0 / sqrt(VECTOR(deg)[i]); + } + } + break; + default: + break; + } + + IGRAPH_CHECK(igraph_i_spectral_embedding(graph, no, weights, which, + scaled, X, Y, D, /*cvec=*/ °, /*deg2=*/ 0, + options, callback, 0, /*symmetric=*/ 1, + /*eigen=*/ 1, /*zapsmall=*/ 1)); + + igraph_vector_destroy(°); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_lse_dir(const igraph_t *graph, + igraph_integer_t no, + const igraph_vector_t *weights, + igraph_eigen_which_position_t which, + igraph_laplacian_spectral_embedding_type_t type, + igraph_bool_t scaled, + igraph_matrix_t *X, + igraph_matrix_t *Y, + igraph_vector_t *D, + igraph_arpack_options_t *options) { + + igraph_arpack_function_t *callback = + weights ? igraph_i_lseembedding_oapw : igraph_i_lseembedding_oap; + igraph_arpack_function_t *callback_right = + weights ? igraph_i_lseembedding_oapw_right : + igraph_i_lseembedding_oap_right; + igraph_vector_t deg_in, deg_out; + int i, n = igraph_vcount(graph); + + if (type != IGRAPH_EMBEDDING_OAP) { + IGRAPH_ERROR("Invalid Laplacian spectral embedding type", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(°_in, n); + IGRAPH_VECTOR_INIT_FINALLY(°_out, n); + igraph_strength(graph, °_in, igraph_vss_all(), IGRAPH_IN, /*loops=*/ 1, + weights); + igraph_strength(graph, °_out, igraph_vss_all(), IGRAPH_OUT, /*loops=*/ 1, + weights); + + for (i = 0; i < n; i++) { + VECTOR(deg_in)[i] = 1.0 / sqrt(VECTOR(deg_in)[i]); + VECTOR(deg_out)[i] = 1.0 / sqrt(VECTOR(deg_out)[i]); + } + + IGRAPH_CHECK(igraph_i_spectral_embedding(graph, no, weights, which, + scaled, X, Y, D, /*cvec=*/ °_in, + /*deg2=*/ °_out, options, callback, + callback_right, /*symmetric=*/ 0, /*eigen=*/ 0, + /*zapsmall=*/ 1)); + + igraph_vector_destroy(°_in); + igraph_vector_destroy(°_out); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \function igraph_laplacian_spectral_embedding + * Spectral embedding of the Laplacian of a graph + * + * This function essentially does the same as + * \ref igraph_adjacency_spectral_embedding, but works on the Laplacian + * of the graph, instead of the adjacency matrix. + * \param graph The input graph. + * \param n The number of eigenvectors (or singular vectors if the graph + * is directed) to use for the embedding. + * \param weights Optional edge weights. Supply a null pointer for + * unweighted graphs. + * \param which Which eigenvalues (or singular values, for directed + * graphs) to use, possible values: + * \clist + * \cli IGRAPH_EIGEN_LM + * the ones with the largest magnitude + * \cli IGRAPH_EIGEN_LA + * the (algebraic) largest ones + * \cli IGRAPH_EIGEN_SA + * the (algebraic) smallest ones. + * \endclist + * For directed graphs, IGRAPH_EIGEN_LM and + * IGRAPH_EIGEN_LA are the same because singular + * values are used for the ordering instead of eigenvalues. + * \param type The type of the Laplacian to use. Various definitions + * exist for the Laplacian of a graph, and one can choose + * between them with this argument. Possible values: + * \clist + * \cli IGRAPH_EMBEDDING_D_A + * means D - A where D is the + * degree matrix and A is the adjacency matrix + * \cli IGRAPH_EMBEDDING_DAD + * means Di times A times Di, + * where Di is the inverse of the square root of the degree matrix; + * \cli IGRAPH_EMBEDDING_I_DAD + * means I - Di A Di, where I + * is the identity matrix. + * \endclist + * \param scaled Whether to return X and Y (if \c scaled is true), or + * U and V. + * \param X Initialized matrix, the estimated latent positions are + * stored here. + * \param Y Initialized matrix or a null pointer. If not a null + * pointer, then the second half of the latent positions are + * stored here. (For undirected graphs, this always equals X.) + * \param D Initialized vector or a null pointer. If not a null + * pointer, then the eigenvalues (for undirected graphs) or the + * singular values (for directed graphs) are stored here. + * \param options Options to ARPACK. See \ref igraph_arpack_options_t + * for details. Note that the function overwrites the + * n (number of vertices), nev and + * which parameters and it always starts the + * calculation from a random start vector. + * \return Error code. + * + * \sa \ref igraph_adjacency_spectral_embedding to embed the adjacency + * matrix. + */ + +int igraph_laplacian_spectral_embedding(const igraph_t *graph, + igraph_integer_t n, + const igraph_vector_t *weights, + igraph_eigen_which_position_t which, + igraph_laplacian_spectral_embedding_type_t type, + igraph_bool_t scaled, + igraph_matrix_t *X, + igraph_matrix_t *Y, + igraph_vector_t *D, + igraph_arpack_options_t *options) { + + if (igraph_is_directed(graph)) { + return igraph_i_lse_dir(graph, n, weights, which, type, scaled, + X, Y, D, options); + } else { + return igraph_i_lse_und(graph, n, weights, which, type, scaled, + X, Y, D, options); + } +} + +/** + * \function igraph_dim_select + * Dimensionality selection + * + * Dimensionality selection for singular values using + * profile likelihood. + * + * + * The input of the function is a numeric vector which contains + * the measure of "importance" for each dimension. + * + * + * For spectral embedding, these are the singular values of the adjacency + * matrix. The singular values are assumed to be generated from a + * Gaussian mixture distribution with two components that have different + * means and same variance. The dimensionality d is chosen to + * maximize the likelihood when the d largest singular values are + * assigned to one component of the mixture and the rest of the singular + * values assigned to the other component. + * + * + * This function can also be used for the general separation problem, + * where we assume that the left and the right of the vector are coming + * from two normal distributions, with different means, and we want + * to know their border. + * + * \param sv A numeric vector, the ordered singular values. + * \param dim The result is stored here. + * \return Error code. + * + * Time complexity: O(n), n is the number of values in sv. + * + * \sa \ref igraph_adjacency_spectral_embedding(). + */ + +int igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim) { + + int i, n = igraph_vector_size(sv); + igraph_real_t x, x2, sum1 = 0.0, sum2 = igraph_vector_sum(sv); + igraph_real_t sumsq1 = 0.0, sumsq2 = 0.0; /* to be set */ + igraph_real_t oldmean1, oldmean2, mean1 = 0.0, mean2 = sum2 / n; + igraph_real_t varsq1 = 0.0, varsq2 = 0.0; /* to be set */ + igraph_real_t var1, var2, sd, profile, max = IGRAPH_NEGINFINITY; + + if (n == 0) { + IGRAPH_ERROR("Need at least one singular value for dimensionality " + "selection", IGRAPH_EINVAL); + } + + if (n == 1) { + *dim = 1; + return 0; + } + + for (i = 0; i < n; i++) { + x = VECTOR(*sv)[i]; + sumsq2 += x * x; + varsq2 += (mean2 - x) * (mean2 - x); + } + + for (i = 0; i < n - 1; i++) { + int n1 = i + 1, n2 = n - i - 1, n1m1 = n1 - 1, n2m1 = n2 - 1; + x = VECTOR(*sv)[i]; x2 = x * x; + sum1 += x; sum2 -= x; + sumsq1 += x2; sumsq2 -= x2; + oldmean1 = mean1; oldmean2 = mean2; + mean1 = sum1 / n1; mean2 = sum2 / n2; + varsq1 += (x - oldmean1) * (x - mean1); + varsq2 -= (x - oldmean2) * (x - mean2); + var1 = i == 0 ? 0 : varsq1 / n1m1; + var2 = i == n - 2 ? 0 : varsq2 / n2m1; + sd = sqrt(( n1m1 * var1 + n2m1 * var2) / (n - 2)); + profile = /* - n * log(2.0*M_PI)/2.0 */ /* This is redundant */ + - n * log(sd) - + ((sumsq1 - 2 * mean1 * sum1 + n1 * mean1 * mean1) + + (sumsq2 - 2 * mean2 * sum2 + n2 * mean2 * mean2)) / 2.0 / sd / sd; + if (profile > max) { + max = profile; + *dim = n1; + } + } + + /* Plus the last case, all elements in one group */ + x = VECTOR(*sv)[n - 1]; + sum1 += x; + oldmean1 = mean1; + mean1 = sum1 / n; + sumsq1 += x * x; + varsq1 += (x - oldmean1) * (x - mean1); + var1 = varsq1 / (n - 1); + sd = sqrt(var1); + profile = /* - n * log(2.0*M_PI)/2.0 */ /* This is redundant */ + - n * log(sd) - + (sumsq1 - 2 * mean1 * sum1 + n * mean1 * mean1) / 2.0 / sd / sd; + if (profile > max) { + max = profile; + *dim = n; + } + + return 0; +} diff --git a/src/rigraph/core/misc/feedback_arc_set.c b/src/rigraph/core/misc/feedback_arc_set.c new file mode 100644 index 0000000..8f0ccef --- /dev/null +++ b/src/rigraph/core/misc/feedback_arc_set.c @@ -0,0 +1,661 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_structural.h" +#include "igraph_visitor.h" + +#include "../internal/glpk_support.h" +#include "feedback_arc_set.h" + +int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, + const igraph_vector_t *weights); + + +/** + * \ingroup structural + * \function igraph_feedback_arc_set + * \brief Calculates a feedback arc set of the graph using different + * algorithms. + * + * + * A feedback arc set is a set of edges whose removal makes the graph acyclic. + * We are usually interested in \em minimum feedback arc sets, i.e. sets of edges + * whose total weight is minimal among all the feedback arc sets. + * + * + * For undirected graphs, the problem is simple: one has to find a maximum weight + * spanning tree and then remove all the edges not in the spanning tree. For directed + * graphs, this is an NP-hard problem, and various heuristics are usually used to + * find an approximate solution to the problem. This function implements a few of + * these heuristics. + * + * \param graph The graph object. + * \param result An initialized vector, the result will be returned here. + * \param weights Weight vector or NULL if no weights are specified. + * \param algo The algorithm to use to solve the problem if the graph is directed. + * Possible values: + * \clist + * \cli IGRAPH_FAS_EXACT_IP + * Finds a \em minimum feedback arc set using integer programming (IP). + * The complexity of this algorithm is exponential of course. + * \cli IGRAPH_FAS_APPROX_EADES + * Finds a feedback arc set using the heuristic of Eades, Lin and + * Smyth (1993). This is guaranteed to be smaller than |E|/2 - |V|/6, + * and it is linear in the number of edges (i.e. O(|E|)). + * For more details, see Eades P, Lin X and Smyth WF: A fast and effective + * heuristic for the feedback arc set problem. In: Proc Inf Process Lett + * 319-323, 1993. + * \endclist + * + * \return Error code: + * \c IGRAPH_EINVAL if an unknown method was specified or the weight vector + * is invalid. + * + * \example examples/simple/igraph_feedback_arc_set.c + * \example examples/simple/igraph_feedback_arc_set_ip.c + * + * Time complexity: depends on \p algo, see the time complexities there. + */ +int igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_t *result, + const igraph_vector_t *weights, igraph_fas_algorithm_t algo) { + + if (weights && igraph_vector_size(weights) < igraph_ecount(graph)) + IGRAPH_ERROR("cannot calculate feedback arc set, weight vector too short", + IGRAPH_EINVAL); + + if (!igraph_is_directed(graph)) { + return igraph_i_feedback_arc_set_undirected(graph, result, weights, 0); + } + + switch (algo) { + case IGRAPH_FAS_EXACT_IP: + return igraph_i_feedback_arc_set_ip(graph, result, weights); + + case IGRAPH_FAS_APPROX_EADES: + return igraph_i_feedback_arc_set_eades(graph, result, weights, 0); + + default: + IGRAPH_ERROR("Invalid algorithm", IGRAPH_EINVAL); + } +} + +/** + * Solves the feedback arc set problem for undirected graphs. + */ +int igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igraph_vector_t *result, + const igraph_vector_t *weights, igraph_vector_t *layering) { + igraph_vector_t edges; + long int i, j, n, no_of_nodes = igraph_vcount(graph); + + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_nodes > 0 ? no_of_nodes - 1 : 0); + if (weights) { + /* Find a maximum weight spanning tree. igraph has a routine for minimum + * spanning trees, so we negate the weights */ + igraph_vector_t vcopy; + IGRAPH_CHECK(igraph_vector_copy(&vcopy, weights)); + IGRAPH_FINALLY(igraph_vector_destroy, &vcopy); + igraph_vector_scale(&vcopy, -1); + IGRAPH_CHECK(igraph_minimum_spanning_tree(graph, &edges, &vcopy)); + igraph_vector_destroy(&vcopy); + IGRAPH_FINALLY_CLEAN(1); + } else { + /* Any spanning tree will do */ + IGRAPH_CHECK(igraph_minimum_spanning_tree(graph, &edges, 0)); + } + + /* Now we have a bunch of edges that constitute a spanning forest. We have + * to come up with a layering, and return those edges that are not in the + * spanning forest */ + igraph_vector_sort(&edges); + IGRAPH_CHECK(igraph_vector_push_back(&edges, -1)); /* guard element */ + + if (result != 0) { + igraph_vector_clear(result); + n = igraph_ecount(graph); + for (i = 0, j = 0; i < n; i++) { + if (i == VECTOR(edges)[j]) { + j++; + continue; + } + IGRAPH_CHECK(igraph_vector_push_back(result, i)); + } + } + + if (layering != 0) { + igraph_vector_t degrees; + igraph_vector_t roots; + + IGRAPH_VECTOR_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&roots, no_of_nodes); + + IGRAPH_CHECK(igraph_strength(graph, °rees, igraph_vss_all(), + IGRAPH_ALL, 0, weights)); + IGRAPH_CHECK((int) igraph_vector_qsort_ind(°rees, &roots, + /* descending = */ 1)); + IGRAPH_CHECK(igraph_bfs(graph, + /* root = */ 0, + /* roots = */ &roots, + /* mode = */ IGRAPH_OUT, + /* unreachable = */ 0, + /* restricted = */ 0, + /* order = */ 0, + /* rank = */ 0, + /* father = */ 0, + /* pred = */ 0, + /* succ = */ 0, + /* dist = */ layering, + /* callback = */ 0, + /* extra = */ 0)); + + igraph_vector_destroy(°rees); + igraph_vector_destroy(&roots); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * Solves the feedback arc set problem using the heuristics of Eades et al. + */ +int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *result, + const igraph_vector_t *weights, igraph_vector_t *layers) { + long int i, j, k, v, eid, no_of_nodes = igraph_vcount(graph), nodes_left; + igraph_dqueue_t sources, sinks; + igraph_vector_t neis; + igraph_vector_t indegrees, outdegrees; + igraph_vector_t instrengths, outstrengths; + long int* ordering; + long int order_next_pos = 0, order_next_neg = -1; + igraph_real_t diff, maxdiff; + + ordering = IGRAPH_CALLOC(no_of_nodes, long int); + IGRAPH_FINALLY(igraph_free, ordering); + + IGRAPH_VECTOR_INIT_FINALLY(&indegrees, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&outdegrees, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&instrengths, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&outstrengths, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_init(&sources, 0)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &sources); + IGRAPH_CHECK(igraph_dqueue_init(&sinks, 0)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &sinks); + + IGRAPH_CHECK(igraph_degree(graph, &indegrees, igraph_vss_all(), IGRAPH_IN, 0)); + IGRAPH_CHECK(igraph_degree(graph, &outdegrees, igraph_vss_all(), IGRAPH_OUT, 0)); + + if (weights) { + IGRAPH_CHECK(igraph_strength(graph, &instrengths, igraph_vss_all(), IGRAPH_IN, 0, weights)); + IGRAPH_CHECK(igraph_strength(graph, &outstrengths, igraph_vss_all(), IGRAPH_OUT, 0, weights)); + } else { + IGRAPH_CHECK(igraph_vector_update(&instrengths, &indegrees)); + IGRAPH_CHECK(igraph_vector_update(&outstrengths, &outdegrees)); + } + + /* Find initial sources and sinks */ + nodes_left = no_of_nodes; + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(indegrees)[i] == 0) { + if (VECTOR(outdegrees)[i] == 0) { + /* Isolated vertex, we simply ignore it */ + nodes_left--; + ordering[i] = order_next_pos++; + VECTOR(indegrees)[i] = VECTOR(outdegrees)[i] = -1; + } else { + /* This is a source */ + igraph_dqueue_push(&sources, i); + } + } else if (VECTOR(outdegrees)[i] == 0) { + /* This is a sink */ + igraph_dqueue_push(&sinks, i); + } + } + + /* While we have any nodes left... */ + while (nodes_left > 0) { + /* (1) Remove the sources one by one */ + while (!igraph_dqueue_empty(&sources)) { + i = (long)igraph_dqueue_pop(&sources); + /* Add the node to the ordering */ + ordering[i] = order_next_pos++; + /* Exclude the node from further searches */ + VECTOR(indegrees)[i] = VECTOR(outdegrees)[i] = -1; + /* Get the neighbors and decrease their degrees */ + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) i, + IGRAPH_OUT)); + j = igraph_vector_size(&neis); + for (i = 0; i < j; i++) { + eid = (long int) VECTOR(neis)[i]; + k = IGRAPH_TO(graph, eid); + if (VECTOR(indegrees)[k] <= 0) { + /* Already removed, continue */ + continue; + } + VECTOR(indegrees)[k]--; + VECTOR(instrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); + if (VECTOR(indegrees)[k] == 0) { + IGRAPH_CHECK(igraph_dqueue_push(&sources, k)); + } + } + nodes_left--; + } + + /* (2) Remove the sinks one by one */ + while (!igraph_dqueue_empty(&sinks)) { + i = (long)igraph_dqueue_pop(&sinks); + /* Maybe the vertex became sink and source at the same time, hence it + * was already removed in the previous iteration. Check it. */ + if (VECTOR(indegrees)[i] < 0) { + continue; + } + /* Add the node to the ordering */ + ordering[i] = order_next_neg--; + /* Exclude the node from further searches */ + VECTOR(indegrees)[i] = VECTOR(outdegrees)[i] = -1; + /* Get the neighbors and decrease their degrees */ + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) i, + IGRAPH_IN)); + j = igraph_vector_size(&neis); + for (i = 0; i < j; i++) { + eid = (long int) VECTOR(neis)[i]; + k = IGRAPH_FROM(graph, eid); + if (VECTOR(outdegrees)[k] <= 0) { + /* Already removed, continue */ + continue; + } + VECTOR(outdegrees)[k]--; + VECTOR(outstrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); + if (VECTOR(outdegrees)[k] == 0) { + IGRAPH_CHECK(igraph_dqueue_push(&sinks, k)); + } + } + nodes_left--; + } + + /* (3) No more sources or sinks. Find the node with the largest + * difference between its out-strength and in-strength */ + v = -1; maxdiff = -IGRAPH_INFINITY; + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(outdegrees)[i] < 0) { + continue; + } + diff = VECTOR(outstrengths)[i] - VECTOR(instrengths)[i]; + if (diff > maxdiff) { + maxdiff = diff; + v = i; + } + } + if (v >= 0) { + /* Remove vertex v */ + ordering[v] = order_next_pos++; + /* Remove outgoing edges */ + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) v, + IGRAPH_OUT)); + j = igraph_vector_size(&neis); + for (i = 0; i < j; i++) { + eid = (long int) VECTOR(neis)[i]; + k = IGRAPH_TO(graph, eid); + if (VECTOR(indegrees)[k] <= 0) { + /* Already removed, continue */ + continue; + } + VECTOR(indegrees)[k]--; + VECTOR(instrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); + if (VECTOR(indegrees)[k] == 0) { + IGRAPH_CHECK(igraph_dqueue_push(&sources, k)); + } + } + /* Remove incoming edges */ + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) v, + IGRAPH_IN)); + j = igraph_vector_size(&neis); + for (i = 0; i < j; i++) { + eid = (long int) VECTOR(neis)[i]; + k = IGRAPH_FROM(graph, eid); + if (VECTOR(outdegrees)[k] <= 0) { + /* Already removed, continue */ + continue; + } + VECTOR(outdegrees)[k]--; + VECTOR(outstrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); + if (VECTOR(outdegrees)[k] == 0 && VECTOR(indegrees)[k] > 0) { + IGRAPH_CHECK(igraph_dqueue_push(&sinks, k)); + } + } + + VECTOR(outdegrees)[v] = -1; + VECTOR(indegrees)[v] = -1; + nodes_left--; + } + } + + igraph_dqueue_destroy(&sinks); + igraph_dqueue_destroy(&sources); + igraph_vector_destroy(&neis); + igraph_vector_destroy(&outstrengths); + igraph_vector_destroy(&instrengths); + igraph_vector_destroy(&outdegrees); + igraph_vector_destroy(&indegrees); + IGRAPH_FINALLY_CLEAN(7); + + /* Tidy up the ordering */ + for (i = 0; i < no_of_nodes; i++) { + if (ordering[i] < 0) { + ordering[i] += no_of_nodes; + } + } + + /* Find the feedback edges based on the ordering */ + if (result != 0) { + igraph_vector_clear(result); + j = igraph_ecount(graph); + for (i = 0; i < j; i++) { + long int from = IGRAPH_FROM(graph, i), to = IGRAPH_TO(graph, i); + if (from == to || ordering[from] > ordering[to]) { + IGRAPH_CHECK(igraph_vector_push_back(result, i)); + } + } + } + + /* If we have also requested a layering, return that as well */ + if (layers != 0) { + igraph_vector_t ranks; + igraph_vector_long_t order_vec; + + IGRAPH_CHECK(igraph_vector_resize(layers, no_of_nodes)); + igraph_vector_null(layers); + + igraph_vector_long_view(&order_vec, ordering, no_of_nodes); + + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&ranks, 0); + + IGRAPH_CHECK((int) igraph_vector_long_qsort_ind(&order_vec, &ranks, 0)); + + for (i = 0; i < no_of_nodes; i++) { + long int from = (long int) VECTOR(ranks)[i]; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) from, + IGRAPH_OUT)); + k = igraph_vector_size(&neis); + for (j = 0; j < k; j++) { + long int to = (long int) VECTOR(neis)[j]; + if (from == to) { + continue; + } + if (ordering[from] > ordering[to]) { + continue; + } + if (VECTOR(*layers)[to] < VECTOR(*layers)[from] + 1) { + VECTOR(*layers)[to] = VECTOR(*layers)[from] + 1; + } + } + } + + igraph_vector_destroy(&neis); + igraph_vector_destroy(&ranks); + IGRAPH_FINALLY_CLEAN(2); + } + + /* Free the ordering vector */ + igraph_free(ordering); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * Solves the feedback arc set problem using integer programming. + */ +int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, + const igraph_vector_t *weights) { +#ifndef HAVE_GLPK + IGRAPH_ERROR("GLPK is not available", IGRAPH_UNIMPLEMENTED); +#else + + igraph_integer_t no_of_components; + igraph_integer_t no_of_vertices = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_t membership, ordering, vertex_remapping; + igraph_vector_ptr_t vertices_by_components, edges_by_components; + long int i, j, k, l, m, n, from, to; + igraph_real_t weight; + glp_prob *ip; + glp_iocp parm; + + IGRAPH_VECTOR_INIT_FINALLY(&membership, 0); + IGRAPH_VECTOR_INIT_FINALLY(&ordering, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vertex_remapping, no_of_vertices); + + igraph_vector_clear(result); + + /* Decompose the graph into connected components */ + IGRAPH_CHECK(igraph_clusters(graph, &membership, 0, &no_of_components, + IGRAPH_WEAK)); + + /* Construct vertex and edge lists for each of the components */ + IGRAPH_CHECK(igraph_vector_ptr_init(&vertices_by_components, no_of_components)); + IGRAPH_CHECK(igraph_vector_ptr_init(&edges_by_components, no_of_components)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vertices_by_components); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &edges_by_components); + for (i = 0; i < no_of_components; i++) { + igraph_vector_t* vptr; + vptr = IGRAPH_CALLOC(1, igraph_vector_t); + if (vptr == 0) { + IGRAPH_ERROR("cannot calculate feedback arc set using IP", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, vptr); + IGRAPH_CHECK(igraph_vector_init(vptr, 0)); + IGRAPH_FINALLY_CLEAN(1); + VECTOR(vertices_by_components)[i] = vptr; + } + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&vertices_by_components, igraph_vector_destroy); + for (i = 0; i < no_of_components; i++) { + igraph_vector_t* vptr; + vptr = IGRAPH_CALLOC(1, igraph_vector_t); + if (vptr == 0) { + IGRAPH_ERROR("cannot calculate feedback arc set using IP", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, vptr); + IGRAPH_CHECK(igraph_vector_init(vptr, 0)); + IGRAPH_FINALLY_CLEAN(1); + VECTOR(edges_by_components)[i] = vptr; + } + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&edges_by_components, igraph_vector_destroy); + for (i = 0; i < no_of_vertices; i++) { + j = (long int) VECTOR(membership)[i]; + IGRAPH_CHECK(igraph_vector_push_back(VECTOR(vertices_by_components)[j], i)); + } + for (i = 0; i < no_of_edges; i++) { + j = (long int) VECTOR(membership)[(long)IGRAPH_FROM(graph, i)]; + IGRAPH_CHECK(igraph_vector_push_back(VECTOR(edges_by_components)[j], i)); + } + +#define VAR2IDX(i, j) (i*(n-1)+j-(i+1)*i/2) + + /* Configure GLPK */ + IGRAPH_GLPK_SETUP(); + glp_init_iocp(&parm); + parm.br_tech = GLP_BR_DTH; + parm.bt_tech = GLP_BT_BLB; + parm.pp_tech = GLP_PP_ALL; + parm.presolve = GLP_ON; + parm.binarize = GLP_OFF; + parm.cb_func = igraph_i_glpk_interruption_hook; + + /* Solve an IP for feedback arc sets in each of the components */ + for (i = 0; i < no_of_components; i++) { + igraph_vector_t* vertices_in_comp = (igraph_vector_t*)VECTOR(vertices_by_components)[i]; + igraph_vector_t* edges_in_comp = (igraph_vector_t*)VECTOR(edges_by_components)[i]; + + /* + * Let x_ij denote whether layer(i) < layer(j). + * + * The standard formulation of the problem is as follows: + * + * max sum_{i,j} w_ij x_ij + * + * subject to + * + * (1) x_ij + x_ji = 1 (i.e. either layer(i) < layer(j) or layer(i) > layer(j)) + * for all i < j + * (2) x_ij + x_jk + x_ki <= 2 for all i < j, i < k, j != k + * + * Note that x_ij = 1 implies that x_ji = 0 and vice versa; in other words, + * x_ij = 1 - x_ji. Thus, we can get rid of the (1) constraints and half of the + * x_ij variables (where j < i) if we rewrite constraints of type (2) as follows: + * + * (2a) x_ij + x_jk - x_ik <= 1 for all i < j, i < k, j < k + * (2b) x_ij - x_kj - x_ik <= 0 for all i < j, i < k, j > k + * + * The goal function then becomes: + * + * max sum_{i 0) { + glp_add_cols(ip, (int) k); + for (j = 1; j <= k; j++) { + glp_set_col_kind(ip, (int) j, GLP_BV); + } + } + + /* Set up coefficients in the goal function */ + k = igraph_vector_size(edges_in_comp); + for (j = 0; j < k; j++) { + l = (long int) VECTOR(*edges_in_comp)[j]; + from = (long int) VECTOR(vertex_remapping)[(long)IGRAPH_FROM(graph, l)]; + to = (long int) VECTOR(vertex_remapping)[(long)IGRAPH_TO(graph, l)]; + if (from == to) { + continue; + } + + weight = weights ? VECTOR(*weights)[l] : 1; + + if (from < to) { + l = VAR2IDX(from, to); + glp_set_obj_coef(ip, (int) l, glp_get_obj_coef(ip, (int) l) + weight); + } else { + l = VAR2IDX(to, from); + glp_set_obj_coef(ip, (int) l, glp_get_obj_coef(ip, (int) l) - weight); + } + } + + /* Add constraints */ + if (n > 1) { + glp_add_rows(ip, (int)(n * (n - 1) / 2 + n * (n - 1) * (n - 2) / 3)); + m = 1; + for (j = 0; j < n; j++) { + int ind[4]; + double val[4] = {0, 1, 1, -1}; + for (k = j + 1; k < n; k++) { + ind[1] = (int) VAR2IDX(j, k); + /* Type (2a) */ + val[2] = 1; + for (l = k + 1; l < n; l++, m++) { + ind[2] = (int) VAR2IDX(k, l); + ind[3] = (int) VAR2IDX(j, l); + glp_set_row_bnds(ip, (int) m, GLP_UP, 1, 1); + glp_set_mat_row(ip, (int) m, 3, ind, val); + } + /* Type (2b) */ + val[2] = -1; + for (l = j + 1; l < k; l++, m++) { + ind[2] = (int) VAR2IDX(l, k); + ind[3] = (int) VAR2IDX(j, l); + glp_set_row_bnds(ip, (int) m, GLP_UP, 0, 0); + glp_set_mat_row(ip, (int) m, 3, ind, val); + } + } + } + } + + /* Solve the problem */ + IGRAPH_GLPK_CHECK(glp_intopt(ip, &parm), "Feedback arc set using IP failed"); + + /* Find the ordering of the vertices */ + IGRAPH_CHECK(igraph_vector_resize(&ordering, n)); + igraph_vector_null(&ordering); + m = n * (n - 1) / 2; + j = 0; k = 1; + for (l = 1; l <= m; l++) { + /* variable l always corresponds to the (j, k) vertex pair */ + /* printf("(%ld, %ld) = %g\n", i, j, glp_mip_col_val(ip, l)); */ + if (glp_mip_col_val(ip, (int) l) > 0) { + /* j comes earlier in the ordering than k */ + VECTOR(ordering)[j]++; + } else { + /* k comes earlier in the ordering than j */ + VECTOR(ordering)[k]++; + } + k++; + if (k == n) { + j++; k = j + 1; + } + } + + /* Find the feedback edges */ + k = igraph_vector_size(edges_in_comp); + for (j = 0; j < k; j++) { + l = (long int) VECTOR(*edges_in_comp)[j]; + from = (long int) VECTOR(vertex_remapping)[(long)IGRAPH_FROM(graph, l)]; + to = (long int) VECTOR(vertex_remapping)[(long)IGRAPH_TO(graph, l)]; + if (from == to || VECTOR(ordering)[from] < VECTOR(ordering)[to]) { + IGRAPH_CHECK(igraph_vector_push_back(result, l)); + } + } + + /* Clean up */ + glp_delete_prob(ip); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_ptr_destroy_all(&vertices_by_components); + igraph_vector_ptr_destroy_all(&edges_by_components); + igraph_vector_destroy(&vertex_remapping); + igraph_vector_destroy(&ordering); + igraph_vector_destroy(&membership); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +#endif +} diff --git a/src/rigraph/core/misc/feedback_arc_set.h b/src/rigraph/core/misc/feedback_arc_set.h new file mode 100644 index 0000000..7091e1f --- /dev/null +++ b/src/rigraph/core/misc/feedback_arc_set.h @@ -0,0 +1,39 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_FEEDBACK_ARC_SET_INTERNAL_H +#define IGRAPH_FEEDBACK_ARC_SET_INTERNAL_H + +#include "igraph_decls.h" +#include "igraph_datatype.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +int igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igraph_vector_t *result, + const igraph_vector_t *weights, igraph_vector_t *layering); +int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *result, + const igraph_vector_t *weights, igraph_vector_t *layering); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/misc/graphicality.c b/src/rigraph/core/misc/graphicality.c new file mode 100644 index 0000000..f468d34 --- /dev/null +++ b/src/rigraph/core/misc/graphicality.c @@ -0,0 +1,959 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2020 The igraph development team + + 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 2 of the License, or + (at your option) 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 . +*/ + +#include "igraph_graphicality.h" + +#include "igraph_qsort.h" + +#define IGRAPH_I_MULTI_EDGES_SW 0x02 /* 010, more than one edge allowed between distinct vertices */ +#define IGRAPH_I_MULTI_LOOPS_SW 0x04 /* 100, more than one self-loop allowed on the same vertex */ + +static int igraph_i_is_graphical_undirected_multi_loops(const igraph_vector_t *degrees, igraph_bool_t *res); +static int igraph_i_is_graphical_undirected_loopless_multi(const igraph_vector_t *degrees, igraph_bool_t *res); +static int igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_t *degrees, igraph_bool_t *res); +static int igraph_i_is_graphical_undirected_simple(const igraph_vector_t *degrees, igraph_bool_t *res); + +static int igraph_i_is_graphical_directed_loopy_multi(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res); +static int igraph_i_is_graphical_directed_loopless_multi(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res); +static int igraph_i_is_graphical_directed_loopy_simple(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res); +static int igraph_i_is_graphical_directed_simple(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res); + +static int igraph_i_is_bigraphical_multi(const igraph_vector_t *degrees1, const igraph_vector_t *degrees2, igraph_bool_t *res); +static int igraph_i_is_bigraphical_simple(const igraph_vector_t *degrees1, const igraph_vector_t *degrees2, igraph_bool_t *res); + + +/** + * \function igraph_is_graphical + * \brief Is there a graph with the given degree sequence? + * + * Determines whether a sequence of integers can be the degree sequence of some graph. + * The classical concept of graphicality assumes simple graphs. This function can perform + * the check also when either self-loops, multi-edge, or both are allowed in the graph. + * + * + * For simple undirected graphs, the Erdős-Gallai conditions are checked using the linear-time + * algorithm of Cloteaux. If both self-loops and multi-edges are allowed, + * it is sufficient to chek that that sum of degrees is even. If only multi-edges are allowed, but + * not self-loops, there is an additional condition that the sum of degrees be no smaller than twice + * the maximum degree. If at most one self-loop is allowed per vertex, but no multi-edges, a modified + * version of the Erdős-Gallai conditions are used (see Cairns & Mendan). + * + * + * For simple directed graphs, the Fulkerson-Chen-Anstee theorem is used with the relaxation by Berger. + * If both self-loops and multi-edges are allowed, then it is sufficient to check that the sum of + * in- and out-degrees is the same. If only multi-edges are allowed, but not self loops, there is an + * additional condition that the sum of out-degrees (or equivalently, in-degrees) is no smaller than + * the maximum total degree. If single self-loops are allowed, but not multi-edges, the problem is equivalent + * to realizability as a simple bipartite graph, thus the Gale-Ryser theorem can be used; see + * \ref igraph_is_bigraphical() for more information. + * + * + * References: + * + * + * P. Erdős and T. Gallai, Gráfok előírt fokú pontokkal, Matematikai Lapok 11, pp. 264–274 (1960). + * https://users.renyi.hu/~p_erdos/1961-05.pdf + * + * + * Z Király, Recognizing graphic degree sequences and generating all realizations. + * TR-2011-11, Egerváry Research Group, H-1117, Budapest, Hungary. ISSN 1587-4451 (2012). + * http://bolyai.cs.elte.hu/egres/tr/egres-11-11.pdf + * + * + * B. Cloteaux, Is This for Real? Fast Graphicality Testing, Comput. Sci. Eng. 17, 91 (2015). + * https://dx.doi.org/10.1109/MCSE.2015.125 + * + * + * A. Berger, A note on the characterization of digraphic sequences, Discrete Math. 314, 38 (2014). + * https://dx.doi.org/10.1016/j.disc.2013.09.010 + * + * + * G. Cairns and S. Mendan, Degree Sequence for Graphs with Loops (2013). + * https://arxiv.org/abs/1303.2145v1 + * + * \param out_degrees A vector of integers specifying the degree sequence for + * undirected graphs or the out-degree sequence for directed graphs. + * \param in_degrees A vector of integers specifying the in-degree sequence for + * directed graphs. For undirected graphs, it must be \c NULL. + * \param allowed_edge_types The types of edges to allow in the graph: + * \clist + * \cli IGRAPH_SIMPLE_SW + * simple graphs (i.e. no self-loops or multi-edges allowed). + * \cli IGRAPH_LOOPS_SW + * single self-loops are allowed, but not multi-edges. + * \cli IGRAPH_MULTI_SW + * multi-edges are allowed, but not self-loops. + * \cli IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW + * both self-loops and multi-edges are allowed. + * \endclist + * \param res Pointer to a Boolean. The result will be stored here. + * + * \return Error code. + * + * \sa \ref igraph_is_bigraphical() to check if a bi-degree-sequence can be realized as a bipartite graph; + * \ref igraph_realize_degree_sequence() to construct a graph with a given degree sequence. + * + * Time complexity: O(n^2) for simple directed graphs, O(n log n) for graphs with self-loops, + * and O(n) for all other cases, where n is the length of the degree sequence(s). + */ +int igraph_is_graphical(const igraph_vector_t *out_degrees, + const igraph_vector_t *in_degrees, + const igraph_edge_type_sw_t allowed_edge_types, + igraph_bool_t *res) +{ + /* Undirected case: */ + if (in_degrees == NULL) + { + if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW )) { + /* Typically this case is used when multiple edges are allowed both as self-loops and + * between distinct vertices. However, the conditions are the same even if multi-edges + * are not allowed between distinct vertices (only as self-loops). Therefore, we + * do not test IGRAPH_I_MULTI_EDGES_SW in the if (...). */ + return igraph_i_is_graphical_undirected_multi_loops(out_degrees, res); + } + else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) { + return igraph_i_is_graphical_undirected_loopless_multi(out_degrees, res); + } + else if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) { + return igraph_i_is_graphical_undirected_loopy_simple(out_degrees, res); + } + else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) { + return igraph_i_is_graphical_undirected_simple(out_degrees, res); + } else { + /* Remainig case: + * - At most one self-loop per vertex but multi-edges between distinct vertices allowed. + * These cases cannot currently be requested through the documented API, + * so no explanatory error message for now. */ + return IGRAPH_UNIMPLEMENTED; + } + } + /* Directed case: */ + else + { + if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) && (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW ) ) { + return igraph_i_is_graphical_directed_loopy_multi(out_degrees, in_degrees, res); + } + else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) { + return igraph_i_is_graphical_directed_loopless_multi(out_degrees, in_degrees, res); + } + else if ( (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) { + return igraph_i_is_graphical_directed_loopy_simple(out_degrees, in_degrees, res); + } + else if ( ! (allowed_edge_types & IGRAPH_LOOPS_SW) && ! (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) ) { + return igraph_i_is_graphical_directed_simple(out_degrees, in_degrees, res); + } else { + /* Remainig cases: + * - At most one self-loop per vertex but multi-edges between distinct vertices allowed. + * - At most one edge between distinct vertices but multi-self-loops allowed. + * These cases cannot currently be requested through the documented API, + * so no explanatory error message for now. */ + return IGRAPH_UNIMPLEMENTED; + } + } + + /* can't reach here */ +} + +/** + * \function igraph_is_bigraphical + * \brief Is there a bipartite graph with the given bi-degree-sequence? + * + * Determines whether two sequences of integers can be the degree sequences of + * a bipartite graph. Such a pair of degree sequence is called \em bigraphical. + * + * + * When multi-edges are allowed, it is sufficient to check that the sum of degrees is the + * same in the two partitions. For simple graphs, the Gale-Ryser theorem is used + * with Berger's relaxation. + * + * + * References: + * + * + * H. J. Ryser, Combinatorial Properties of Matrices of Zeros and Ones, Can. J. Math. 9, 371 (1957). + * https://dx.doi.org/10.4153/cjm-1957-044-3 + * + * + * D. Gale, A theorem on flows in networks, Pacific J. Math. 7, 1073 (1957). + * https://dx.doi.org/10.2140/pjm.1957.7.1073 + * + * + * A. Berger, A note on the characterization of digraphic sequences, Discrete Math. 314, 38 (2014). + * https://dx.doi.org/10.1016/j.disc.2013.09.010 + * + * \param degrees1 A vector of integers specifying the degrees in the first partition + * \param degrees2 A vector of integers specifying the degrees in the second partition + * \param allowed_edge_types The types of edges to allow in the graph: + * \clist + * \cli IGRAPH_SIMPLE_SW + * simple graphs (i.e. no multi-edges allowed). + * \cli IGRAPH_MULTI_SW + * multi-edges are allowed. + * \endclist + * \param res Pointer to a Boolean. The result will be stored here. + * + * \return Error code. + * + * \sa \ref igraph_is_graphical() + * + * Time complexity: O(n log n) for simple graphs, O(n) for multigraphs, + * where n is the length of the larger degree sequence. + */ +int igraph_is_bigraphical(const igraph_vector_t *degrees1, + const igraph_vector_t *degrees2, + const igraph_edge_type_sw_t allowed_edge_types, + igraph_bool_t *res) +{ + /* Note: Bipartite graphs can't have self-loops so we ignore the IGRAPH_LOOPS_SW bit. */ + if (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) { + return igraph_i_is_bigraphical_multi(degrees1, degrees2, res); + } else { + return igraph_i_is_bigraphical_simple(degrees1, degrees2, res); + } +} + + +/***** Undirected case *****/ + +/* Undirected graph with multi-self-loops: + * - Degrees must be non-negative. + * - The sum of degrees must be even. + * + * These conditions are valid regardless of whether multi-edges are allowed between distinct vertices. + */ +static int igraph_i_is_graphical_undirected_multi_loops(const igraph_vector_t *degrees, igraph_bool_t *res) { + long int sum_parity = 0; /* 0 if the degree sum is even, 1 if it is odd */ + long int n = igraph_vector_size(degrees); + long int i; + + for (i=0; i < n; ++i) { + long int d = VECTOR(*degrees)[i]; + + if (d < 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + sum_parity = (sum_parity + d) & 1; + } + + *res = (sum_parity == 0); + + return IGRAPH_SUCCESS; +} + + +/* Undirected loopless multigraph: + * - Degrees must be non-negative. + * - The sum of degrees must be even. + * - The sum of degrees must be no smaller than 2*d_max. + */ +static int igraph_i_is_graphical_undirected_loopless_multi(const igraph_vector_t *degrees, igraph_bool_t *res) { + long int i; + long int n = igraph_vector_size(degrees); + long int dsum, dmax; + + /* Zero-length sequences are considered graphical. */ + if (n == 0) { + *res = 1; + return IGRAPH_SUCCESS; + } + + dsum = 0; dmax = 0; + for (i=0; i < n; ++i) { + long int d = VECTOR(*degrees)[i]; + + if (d < 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + dsum += d; + if (d > dmax) { + dmax = d; + } + } + + *res = (dsum % 2 == 0) && (dsum >= 2*dmax); + + return IGRAPH_SUCCESS; +} + + +/* Undirected graph with no multi-edges and at most one self-loop per vertex: + * - Degrees must be non-negative. + * - The sum of degrees must be even. + * - Use the modification of the Erdős-Gallai theorem due to Cairns and Mendan. + */ +static int igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_t *degrees, igraph_bool_t *res) { + igraph_vector_t work; + long int w, b, s, c, n, k; + + n = igraph_vector_size(degrees); + + /* Zero-length sequences are considered graphical. */ + if (n == 0) { + *res = 1; + return IGRAPH_SUCCESS; + } + + /* The conditions from the loopy multigraph case are necessary here as well. */ + IGRAPH_CHECK(igraph_i_is_graphical_undirected_multi_loops(degrees, res)); + if (! *res) { + return IGRAPH_SUCCESS; + } + + /* + * We follow this paper: + * + * G. Cairns & S. Mendan: Degree Sequences for Graphs with Loops, 2013 + * https://arxiv.org/abs/1303.2145v1 + * + * They give the following modification of the Erdős-Gallai theorem: + * + * A non-increasing degree sequence d_1 >= ... >= d_n has a realization as + * a simple graph with loops (i.e. at most one self-loop allowed on each vertex) + * iff + * + * \sum_{i=1}^k d_i <= k(k+1) + \sum_{i=k+1}^{n} min(d_i, k) + * + * for each k=1..n + * + * The difference from Erdős-Gallai is that here we have the term + * k(k+1) instead of k(k-1). + * + * The implementation is analogous to igraph_i_is_graphical_undirected_simple(), + * which in turn is based on Király 2012. See comments in that function for details. + * w and k are zero-based here, unlike in the statement of the theorem above. + */ + + IGRAPH_CHECK(igraph_vector_copy(&work, degrees)); + IGRAPH_FINALLY(igraph_vector_destroy, &work); + + igraph_vector_reverse_sort(&work); + + *res = 1; + w = n - 1; b = 0; s = 0; c = 0; + for (k = 0; k < n; k++) { + b += VECTOR(work)[k]; + c += w; + while (w > k && VECTOR(work)[w] <= k + 1) { + s += VECTOR(work)[w]; + c -= (k + 1); + w--; + } + if (b > c + s + 2*(k + 1)) { + *res = 0; + break; + } + if (w == k) { + break; + } + } + + igraph_vector_destroy(&work); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/* Undirected simple graph: + * - Degrees must be non-negative. + * - The sum of degrees must be even. + * - Use the Erdős-Gallai theorem. + */ +static int igraph_i_is_graphical_undirected_simple(const igraph_vector_t *degrees, igraph_bool_t *res) { + igraph_vector_int_t num_degs; /* num_degs[d] is the # of vertices with degree d */ + const long int p = igraph_vector_size(degrees); + long int dmin, dmax, dsum; + long int n; /* number of non-zero degrees */ + long int k, sum_deg, sum_ni, sum_ini; + long int i, dk; + long int zverovich_bound; + + if (p == 0) { + *res = 1; + return IGRAPH_SUCCESS; + } + + /* The following implementation of the Erdős-Gallai test + * is mostly a direct translation of the Python code given in + * + * Brian Cloteaux, Is This for Real? Fast Graphicality Testing, + * Computing Prescriptions, pp. 91-95, vol. 17 (2015) + * https://dx.doi.org/10.1109/MCSE.2015.125 + * + * It uses counting sort to achieve linear runtime. + */ + + IGRAPH_VECTOR_INT_INIT_FINALLY(&num_degs, p); + + dmin = p; dmax = 0; dsum = 0; n = 0; + for (i=0; i < p; ++i) { + long int d = VECTOR(*degrees)[i]; + + if (d < 0 || d >= p) { + *res = 0; + goto finish; + } + + if (d > 0) { + dmax = d > dmax ? d : dmax; + dmin = d < dmin ? d : dmin; + dsum += d; + n++; + VECTOR(num_degs)[d] += 1; + } + } + + if (dsum % 2 != 0) { + *res = 0; + goto finish; + } + + if (n == 0) { + *res = 1; + goto finish; /* all degrees are zero => graphical */ + } + + /* According to: + * + * G. Cairns, S. Mendan, and Y. Nikolayevsky, A sharp refinement of a result of Zverovich-Zverovich, + * Discrete Math. 338, 1085 (2015). + * https://dx.doi.org/10.1016/j.disc.2015.02.001 + * + * a sufficient but not necessary condition of graphicality for a sequence of + * n strictly positive integers is that + * + * dmin * n >= floor( (dmax + dmin + 1)^2 / 4 ) - 1 + * if dmin is odd or (dmax + dmin) mod 4 == 1 + * + * or + * + * dmin * n >= floor( (dmax + dmin + 1)^2 / 4 ) + * otherwise. + */ + + zverovich_bound = ((dmax + dmin + 1) * (dmax + dmin + 1)) / 4; + if (dmin % 2 == 1 || (dmax + dmin) % 4 == 1) { + zverovich_bound -= 1; + } + + if (dmin*n >= zverovich_bound) { + *res = 1; + goto finish; + } + + k = 0; sum_deg = 0; sum_ni = 0; sum_ini = 0; + for (dk = dmax; dk >= dmin; --dk) { + long int run_size, v; + + if (dk < k+1) { + *res = 1; + goto finish; + } + + run_size = VECTOR(num_degs)[dk]; + if (run_size > 0) { + if (dk < k + run_size) { + run_size = dk - k; + } + sum_deg += run_size * dk; + for (v=0; v < run_size; ++v) { + sum_ni += VECTOR(num_degs)[k+v]; + sum_ini += (k+v) * VECTOR(num_degs)[k+v]; + } + k += run_size; + if (sum_deg > k*(n-1) - k*sum_ni + sum_ini) { + *res = 0; + goto finish; + } + } + } + + *res = 1; + +finish: + igraph_vector_int_destroy(&num_degs); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/***** Directed case *****/ + +/* Directed loopy multigraph: + * - Degrees must be non-negative. + * - The sum of in- and out-degrees must be the same. + */ +static int igraph_i_is_graphical_directed_loopy_multi(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res) { + long int sumdiff; /* difference between sum of in- and out-degrees */ + long int n = igraph_vector_size(out_degrees); + long int i; + + if (igraph_vector_size(in_degrees) != n) { + IGRAPH_ERROR("The length of out- and in-degree sequences must be the same.", IGRAPH_EINVAL); + } + + sumdiff = 0; + for (i=0; i < n; ++i) { + long int dout = VECTOR(*out_degrees)[i]; + long int din = VECTOR(*in_degrees)[i]; + + if (dout < 0 || din < 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + + sumdiff += din - dout; + } + + *res = sumdiff == 0; + + return IGRAPH_SUCCESS; +} + + +/* Directed loopless multigraph: + * - Degrees must be non-negative. + * - The sum of in- and out-degrees must be the same. + * - The sum of out-degrees must be no smaller than d_max, + * where d_max is the largest total degree. + */ +static int igraph_i_is_graphical_directed_loopless_multi(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res) { + long int i, sumin, sumout, dmax; + long int n = igraph_vector_size(out_degrees); + + if (igraph_vector_size(in_degrees) != n) { + IGRAPH_ERROR("The length of out- and in-degree sequences must be the same.", IGRAPH_EINVAL); + } + + sumin = 0; sumout = 0; + dmax = 0; + for (i=0; i < n; ++i) { + long int dout = VECTOR(*out_degrees)[i]; + long int din = VECTOR(*in_degrees)[i]; + long int d = dout + din; + + if (dout < 0 || din < 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + + sumin += din; sumout += dout; + + if (d > dmax) { + dmax = d; + } + } + + *res = (sumin == sumout) && (sumout >= dmax); + + return IGRAPH_SUCCESS; +} + + +/* Directed graph with no multi-edges and at most one self-loop per vertex: + * - Degrees must be non-negative. + * - Equivalent to bipartite simple graph. + */ +static int igraph_i_is_graphical_directed_loopy_simple(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res) { + long int n = igraph_vector_size(out_degrees); + + if (igraph_vector_size(in_degrees) != n) { + IGRAPH_ERROR("The length of out- and in-degree sequences must be the same.", IGRAPH_EINVAL); + } + + return igraph_i_is_bigraphical_simple(out_degrees, in_degrees, res); +} + + +/* Directed simple graph: + * - Degrees must be non-negative. + * - The sum of in- and out-degrees must be the same. + * - Use the Fulkerson-Chen-Anstee theorem + */ + +typedef struct { + const igraph_vector_t* first; + const igraph_vector_t* second; +} igraph_i_qsort_dual_vector_cmp_data_t; + +static int igraph_i_qsort_dual_vector_cmp_desc(void* data, const void *p1, const void *p2) { + igraph_i_qsort_dual_vector_cmp_data_t* sort_data = + (igraph_i_qsort_dual_vector_cmp_data_t*)data; + long int index1 = *((long int*)p1); + long int index2 = *((long int*)p2); + if (VECTOR(*sort_data->first)[index1] < VECTOR(*sort_data->first)[index2]) { + return 1; + } + if (VECTOR(*sort_data->first)[index1] > VECTOR(*sort_data->first)[index2]) { + return -1; + } + if (VECTOR(*sort_data->second)[index1] < VECTOR(*sort_data->second)[index2]) { + return 1; + } + if (VECTOR(*sort_data->second)[index1] > VECTOR(*sort_data->second)[index2]) { + return -1; + } + return 0; +} + +static int igraph_i_is_graphical_directed_simple(const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees, igraph_bool_t *res) { + igraph_vector_long_t index_array; + long int i, j, vcount, lhs, rhs; + igraph_i_qsort_dual_vector_cmp_data_t sort_data; + + /* The conditions from the loopy multigraph case are necessary here as well. */ + IGRAPH_CHECK(igraph_i_is_graphical_directed_loopy_multi(out_degrees, in_degrees, res)); + if (! *res) { + return IGRAPH_SUCCESS; + } + + vcount = igraph_vector_size(out_degrees); + if (vcount == 0) { + *res = 1; + return IGRAPH_SUCCESS; + } + + /* Create an index vector that sorts the vertices by decreasing in-degree */ + IGRAPH_CHECK(igraph_vector_long_init_seq(&index_array, 0, vcount - 1)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &index_array); + + /* Set up the auxiliary struct for sorting */ + sort_data.first = in_degrees; + sort_data.second = out_degrees; + + /* Sort the index vector */ + igraph_qsort_r(VECTOR(index_array), vcount, sizeof(long int), &sort_data, + igraph_i_qsort_dual_vector_cmp_desc); + + /* Be optimistic, then check whether the Fulkerson–Chen–Anstee condition + * holds for every k. In particular, for every k in [0; n), it must be true + * that: + * + * \sum_{i=0}^k indegree[i] <= + * \sum_{i=0}^k min(outdegree[i], k) + + * \sum_{i=k+1}^{n-1} min(outdegree[i], k + 1) + */ + +#define INDEGREE(x) (VECTOR(*in_degrees)[VECTOR(index_array)[x]]) +#define OUTDEGREE(x) (VECTOR(*out_degrees)[VECTOR(index_array)[x]]) + + *res = 1; + lhs = 0; + for (i = 0; i < vcount; i++) { + lhs += INDEGREE(i); + + /* It is enough to check for indexes where the in-degree is about to + * decrease in the next step; see "Stronger condition" in the Wikipedia + * entry for the Fulkerson-Chen-Anstee condition */ + if (i != vcount - 1 && INDEGREE(i) == INDEGREE(i + 1)) { + continue; + } + + rhs = 0; + for (j = 0; j <= i; j++) { + rhs += OUTDEGREE(j) < i ? OUTDEGREE(j) : i; + } + for (j = i + 1; j < vcount; j++) { + rhs += OUTDEGREE(j) < (i + 1) ? OUTDEGREE(j) : (i + 1); + } + + if (lhs > rhs) { + *res = 0; + break; + } + } + +#undef INDEGREE +#undef OUTDEGREE + + igraph_vector_long_destroy(&index_array); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + + +/***** Bipartite case *****/ + +/* Bipartite graph with multi-eges: + * - Degrees must be non-negative. + * - Sum of degrees must be the same in the two partitions. + */ +static int igraph_i_is_bigraphical_multi(const igraph_vector_t *degrees1, const igraph_vector_t *degrees2, igraph_bool_t *res) { + long int i; + long int sum1, sum2; + long int n1 = igraph_vector_size(degrees1), n2 = igraph_vector_size(degrees2); + + sum1 = 0; + for (i=0; i < n1; ++i) { + long int d = VECTOR(*degrees1)[i]; + + if (d < 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + + sum1 += d; + } + + sum2 = 0; + for (i=0; i < n2; ++i) { + long int d = VECTOR(*degrees2)[i]; + + if (d < 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + + sum2 += d; + } + + *res = (sum1 == sum2); + + return IGRAPH_SUCCESS; +} + + +/* Bipartite simple graph: + * - Degrees must be non-negative. + * - Sum of degrees must be the same in the two partitions. + * - Use the Gale-Ryser theorem. + */ +static int igraph_i_is_bigraphical_simple(const igraph_vector_t *degrees1, const igraph_vector_t *degrees2, igraph_bool_t *res) { + igraph_vector_t sorted_deg1, sorted_deg2; + long int n1 = igraph_vector_size(degrees1), n2 = igraph_vector_size(degrees2); + long int i, k; + long lhs_sum, partial_rhs_sum; + + if (n1 == 0 && n2 == 0) { + *res = 1; + return IGRAPH_SUCCESS; + } + + /* The conditions from the multigraph case are necessary here as well. */ + IGRAPH_CHECK(igraph_i_is_bigraphical_multi(degrees1, degrees2, res)); + if (! *res) { + return IGRAPH_SUCCESS; + } + + /* Ensure that degrees1 is the shorter vector as a minor optimization: */ + if (n2 < n1) { + const igraph_vector_t *tmp; + long int n; + + tmp = degrees1; + degrees1 = degrees2; + degrees2 = tmp; + + n = n1; + n1 = n2; + n2 = n; + } + + /* Copy and sort both vectors: */ + + IGRAPH_CHECK(igraph_vector_copy(&sorted_deg1, degrees1)); + IGRAPH_FINALLY(igraph_vector_destroy, &sorted_deg1); + igraph_vector_reverse_sort(&sorted_deg1); /* decreasing sort */ + + IGRAPH_CHECK(igraph_vector_copy(&sorted_deg2, degrees2)); + IGRAPH_FINALLY(igraph_vector_destroy, &sorted_deg2); + igraph_vector_sort(&sorted_deg2); /* increasing sort */ + + /* + * We follow the description of the Gale-Ryser theorem in: + * + * A. Berger, A note on the characterization of digraphic sequences, Discrete Math. 314, 38 (2014). + * http://dx.doi.org/10.1016/j.disc.2013.09.010 + * + * Gale-Ryser condition with 0-based indexing: + * + * a_i and b_i denote the degree sequences of the two partitions. + * + * Assuming that a_0 >= a_1 >= ... >= a_{n_1 - 1}, + * + * \sum_{i=0}^k a_i <= \sum_{j=0}^{n_2} min(b_i, k+1) + * + * for all 0 <= k < n_1 + */ + + /* While this formulation does not require sorting degree2, + * doing so allows for a linear-time incremental computation + * of the inequality's right-hand-side. + */ + + *res = 1; /* be optimistic */ + lhs_sum = 0; + partial_rhs_sum = 0; /* the sum of those elements in sorted_deg2 which are <= (k+1) */ + i = 0; /* points past the first element of sorted_deg2 which > (k+1) */ + for (k=0; k < n1; ++k) { + lhs_sum += VECTOR(sorted_deg1)[k]; + + /* Based on Theorem 3 in [Berger 2014], it is sufficient to do the check + * for k such that a_k > a_{k+1} and for k=(n_1-1). + */ + if (k < n1-1 && VECTOR(sorted_deg1)[k] == VECTOR(sorted_deg1)[k+1]) + continue; + + while (i < n2 && VECTOR(sorted_deg2)[i] <= k+1) { + partial_rhs_sum += VECTOR(sorted_deg2)[i]; + i++; + } + + /* rhs_sum for a given k is partial_rhs_sum + (n2 - i) * (k+1) */ + if (lhs_sum > partial_rhs_sum + (n2 - i) * (k+1) ) { + *res = 0; + break; + } + } + + igraph_vector_destroy(&sorted_deg2); + igraph_vector_destroy(&sorted_deg1); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/***** Legacy functions *****/ + +#define SUCCEED { \ + if (res) { \ + *res = 1; \ + } \ + return IGRAPH_SUCCESS; \ + } + +#define FAIL { \ + if (res) { \ + *res = 0; \ + } \ + return IGRAPH_SUCCESS; \ + } + +/** + * \function igraph_is_degree_sequence + * \brief Determines whether a degree sequence is valid. + * + * \deprecated-by igraph_is_graphical 0.9 + * + * + * A sequence of n integers is a valid degree sequence if there exists some + * graph where the degree of the i-th vertex is equal to the i-th element of the + * sequence. Note that the graph may contain multiple or loop edges; if you are + * interested in whether the degrees of some \em simple graph may realize the + * given sequence, use \ref igraph_is_graphical_degree_sequence. + * + * + * In particular, the function checks whether all the degrees are non-negative. + * For undirected graphs, it also checks whether the sum of degrees is even. + * For directed graphs, the function checks whether the lengths of the two + * degree vectors are equal and whether their sums are also equal. These are + * known sufficient and necessary conditions for a degree sequence to be + * valid. + * + * \param out_degrees an integer vector specifying the degree sequence for + * undirected graphs or the out-degree sequence for directed graphs. + * \param in_degrees an integer vector specifying the in-degrees of the + * vertices for directed graphs. For undirected graphs, this must be null. + * \param res pointer to a boolean variable, the result will be stored here + * \return Error code. + * + * Time complexity: O(n), where n is the length of the degree sequence. + */ +int igraph_is_degree_sequence(const igraph_vector_t *out_degrees, + const igraph_vector_t *in_degrees, igraph_bool_t *res) { + IGRAPH_WARNING("igraph_is_degree_sequence is deprecated, use igraph_is_graphical."); + + /* degrees must be non-negative */ + if (igraph_vector_any_smaller(out_degrees, 0)) { + FAIL; + } + if (in_degrees && igraph_vector_any_smaller(in_degrees, 0)) { + FAIL; + } + + if (in_degrees == 0) { + /* sum of degrees must be even */ + if (((long int)igraph_vector_sum(out_degrees) % 2) != 0) { + FAIL; + } + } else { + /* length of the two degree vectors must be equal */ + if (igraph_vector_size(out_degrees) != igraph_vector_size(in_degrees)) { + FAIL; + } + /* sum of in-degrees must be equal to sum of out-degrees */ + if (igraph_vector_sum(out_degrees) != igraph_vector_sum(in_degrees)) { + FAIL; + } + } + + SUCCEED; +} + +#undef SUCCEED +#undef FAIL + + +/** + * \function igraph_is_graphical_degree_sequence + * \brief Determines whether a sequence of integers can be the degree sequence of some simple graph. + * + * \deprecated-by igraph_is_graphical 0.9 + * + * + * References: + * + * + * Hakimi SL: On the realizability of a set of integers as degrees of the + * vertices of a simple graph. J SIAM Appl Math 10:496-506, 1962. + * + * + * PL Erdős, I Miklós and Z Toroczkai: A simple Havel-Hakimi type algorithm + * to realize graphical degree sequences of directed graphs. + * The Electronic Journal of Combinatorics 17(1):R66, 2010. + * https://dx.doi.org/10.1017/S0963548317000499 + * + * + * Z Kiraly: Recognizing graphic degree sequences and generating all + * realizations. TR-2011-11, Egervary Research Group, H-1117, Budapest, + * Hungary. ISSN 1587-4451, 2012. + * https://www.cs.elte.hu/egres/tr/egres-11-11.pdf + * + * \param out_degrees an integer vector specifying the degree sequence for + * undirected graphs or the out-degree sequence for directed graphs. + * \param in_degrees an integer vector specifying the in-degrees of the + * vertices for directed graphs. For undirected graphs, this must be null. + * \param res pointer to a boolean variable, the result will be stored here + * \return Error code. + * + * Time complexity: O(n log n) for undirected graphs, O(n^2) for directed + * graphs, where n is the length of the degree sequence. + */ +int igraph_is_graphical_degree_sequence(const igraph_vector_t *out_degrees, + const igraph_vector_t *in_degrees, igraph_bool_t *res) { + IGRAPH_WARNING("igraph_is_graphical_degree_sequence is deprecated, use igraph_is_graphical."); + return igraph_is_graphical(out_degrees, in_degrees, IGRAPH_SIMPLE_SW, res); +} diff --git a/src/rigraph/core/misc/matching.c b/src/rigraph/core/misc/matching.c new file mode 100644 index 0000000..7e58bd0 --- /dev/null +++ b/src/rigraph/core/misc/matching.c @@ -0,0 +1,1033 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2012 Tamas Nepusz + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_matching.h" + +#include "igraph_adjlist.h" +#include "igraph_constructors.h" +#include "igraph_conversion.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_structural.h" + +#include + +/* #define MATCHING_DEBUG */ + +#ifdef _MSC_VER +/* MSVC does not support variadic macros */ +#include +static void debug(const char* fmt, ...) { + va_list args; + va_start(args, fmt); +#ifdef MATCHING_DEBUG + vfprintf(stderr, fmt, args); +#endif + va_end(args); +} +#else +#ifdef MATCHING_DEBUG + #define debug(...) fprintf(stderr, __VA_ARGS__) +#else + #define debug(...) +#endif +#endif + +/** + * \function igraph_is_matching + * Checks whether the given matching is valid for the given graph. + * + * This function checks a matching vector and verifies whether its length + * matches the number of vertices in the given graph, its values are between + * -1 (inclusive) and the number of vertices (exclusive), and whether there + * exists a corresponding edge in the graph for every matched vertex pair. + * For bipartite graphs, it also verifies whether the matched vertices are + * in different parts of the graph. + * + * \param graph The input graph. It can be directed but the edge directions + * will be ignored. + * \param types If the graph is bipartite and you are interested in bipartite + * matchings only, pass the vertex types here. If the graph is + * non-bipartite, simply pass \c NULL. + * \param matching The matching itself. It must be a vector where element i + * contains the ID of the vertex that vertex i is matched to, + * or -1 if vertex i is unmatched. + * \param result Pointer to a boolean variable, the result will be returned + * here. + * + * \sa \ref igraph_is_maximal_matching() if you are also interested in whether + * the matching is maximal (i.e. non-extendable). + * + * Time complexity: O(|V|+|E|) where |V| is the number of vertices and + * |E| is the number of edges. + * + * \example examples/simple/igraph_maximum_bipartite_matching.c + */ +int igraph_is_matching(const igraph_t* graph, + const igraph_vector_bool_t* types, const igraph_vector_long_t* matching, + igraph_bool_t* result) { + long int i, j, no_of_nodes = igraph_vcount(graph); + igraph_bool_t conn; + + /* Checking match vector length */ + if (igraph_vector_long_size(matching) != no_of_nodes) { + *result = 0; return IGRAPH_SUCCESS; + } + + for (i = 0; i < no_of_nodes; i++) { + j = VECTOR(*matching)[i]; + + /* Checking range of each element in the match vector */ + if (j < -1 || j >= no_of_nodes) { + *result = 0; return IGRAPH_SUCCESS; + } + /* When i is unmatched, we're done */ + if (j == -1) { + continue; + } + /* Matches must be mutual */ + if (VECTOR(*matching)[j] != i) { + *result = 0; return IGRAPH_SUCCESS; + } + /* Matched vertices must be connected */ + IGRAPH_CHECK(igraph_are_connected(graph, (igraph_integer_t) i, + (igraph_integer_t) j, &conn)); + if (!conn) { + /* Try the other direction -- for directed graphs */ + IGRAPH_CHECK(igraph_are_connected(graph, (igraph_integer_t) j, + (igraph_integer_t) i, &conn)); + if (!conn) { + *result = 0; return IGRAPH_SUCCESS; + } + } + } + + if (types != 0) { + /* Matched vertices must be of different types */ + for (i = 0; i < no_of_nodes; i++) { + j = VECTOR(*matching)[i]; + if (j == -1) { + continue; + } + if (VECTOR(*types)[i] == VECTOR(*types)[j]) { + *result = 0; return IGRAPH_SUCCESS; + } + } + } + + *result = 1; + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_is_maximal_matching + * Checks whether a matching in a graph is maximal. + * + * A matching is maximal if and only if there exists no unmatched vertex in a + * graph such that one of its neighbors is also unmatched. + * + * \param graph The input graph. It can be directed but the edge directions + * will be ignored. + * \param types If the graph is bipartite and you are interested in bipartite + * matchings only, pass the vertex types here. If the graph is + * non-bipartite, simply pass \c NULL. + * \param matching The matching itself. It must be a vector where element i + * contains the ID of the vertex that vertex i is matched to, + * or -1 if vertex i is unmatched. + * \param result Pointer to a boolean variable, the result will be returned + * here. + * + * \sa \ref igraph_is_matching() if you are only interested in whether a + * matching vector is valid for a given graph. + * + * Time complexity: O(|V|+|E|) where |V| is the number of vertices and + * |E| is the number of edges. + * + * \example examples/simple/igraph_maximum_bipartite_matching.c + */ +int igraph_is_maximal_matching(const igraph_t* graph, + const igraph_vector_bool_t* types, const igraph_vector_long_t* matching, + igraph_bool_t* result) { + long int i, j, n, no_of_nodes = igraph_vcount(graph); + igraph_vector_t neis; + igraph_bool_t valid; + + IGRAPH_CHECK(igraph_is_matching(graph, types, matching, &valid)); + if (!valid) { + *result = 0; return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + valid = 1; + for (i = 0; i < no_of_nodes; i++) { + j = VECTOR(*matching)[i]; + if (j != -1) { + continue; + } + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, + IGRAPH_ALL)); + n = igraph_vector_size(&neis); + for (j = 0; j < n; j++) { + if (VECTOR(*matching)[(long int)VECTOR(neis)[j]] == -1) { + if (types == 0 || + VECTOR(*types)[i] != VECTOR(*types)[(long int)VECTOR(neis)[j]]) { + valid = 0; break; + } + } + } + } + + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + + *result = valid; + return IGRAPH_SUCCESS; +} + +static int igraph_i_maximum_bipartite_matching_unweighted( + const igraph_t* graph, + const igraph_vector_bool_t* types, igraph_integer_t* matching_size, + igraph_vector_long_t* matching); +static int igraph_i_maximum_bipartite_matching_weighted( + const igraph_t* graph, + const igraph_vector_bool_t* types, igraph_integer_t* matching_size, + igraph_real_t* matching_weight, igraph_vector_long_t* matching, + const igraph_vector_t* weights, igraph_real_t eps); + +#define MATCHED(v) (VECTOR(match)[v] != -1) +#define UNMATCHED(v) (!MATCHED(v)) + +/** + * \function igraph_maximum_bipartite_matching + * Calculates a maximum matching in a bipartite graph. + * + * A matching in a bipartite graph is a partial assignment of vertices + * of the first kind to vertices of the second kind such that each vertex of + * the first kind is matched to at most one vertex of the second kind and + * vice versa, and matched vertices must be connected by an edge in the graph. + * The size (or cardinality) of a matching is the number of edges. + * A matching is a maximum matching if there exists no other matching with + * larger cardinality. For weighted graphs, a maximum matching is a matching + * whose edges have the largest possible total weight among all possible + * matchings. + * + * + * Maximum matchings in bipartite graphs are found by the push-relabel algorithm + * with greedy initialization and a global relabeling after every n/2 steps where + * n is the number of vertices in the graph. + * + * + * References: Cherkassky BV, Goldberg AV, Martin P, Setubal JC and Stolfi J: + * Augment or push: A computational study of bipartite matching and + * unit-capacity flow algorithms. ACM Journal of Experimental Algorithmics 3, + * 1998. + * + * + * Kaya K, Langguth J, Manne F and Ucar B: Experiments on push-relabel-based + * maximum cardinality matching algorithms for bipartite graphs. Technical + * Report TR/PA/11/33 of the Centre Europeen de Recherche et de Formation + * Avancee en Calcul Scientifique, 2011. + * + * \param graph The input graph. It can be directed but the edge directions + * will be ignored. + * \param types Boolean vector giving the vertex types of the graph. + * \param matching_size The size of the matching (i.e. the number of matched + * vertex pairs will be returned here). It may be \c NULL + * if you don't need this. + * \param matching_weight The weight of the matching if the edges are weighted, + * or the size of the matching again if the edges are + * unweighted. It may be \c NULL if you don't need this. + * \param matching The matching itself. It must be a vector where element i + * contains the ID of the vertex that vertex i is matched to, + * or -1 if vertex i is unmatched. + * \param weights A null pointer (=no edge weights), or a vector giving the + * weights of the edges. Note that the algorithm is stable + * only for integer weights. + * \param eps A small real number used in equality tests in the weighted + * bipartite matching algorithm. Two real numbers are considered + * equal in the algorithm if their difference is smaller than + * \c eps. This is required to avoid the accumulation of numerical + * errors. It is advised to pass a value derived from the + * \c DBL_EPSILON constant in \c float.h here. If you are + * running the algorithm with no \c weights vector, this argument + * is ignored. + * \return Error code. + * + * Time complexity: O(sqrt(|V|) |E|) for unweighted graphs (according to the + * technical report referenced above), O(|V||E|) for weighted graphs. + * + * \example examples/simple/igraph_maximum_bipartite_matching.c + */ +int igraph_maximum_bipartite_matching(const igraph_t* graph, + const igraph_vector_bool_t* types, igraph_integer_t* matching_size, + igraph_real_t* matching_weight, igraph_vector_long_t* matching, + const igraph_vector_t* weights, igraph_real_t eps) { + + /* Sanity checks */ + if (igraph_vector_bool_size(types) < igraph_vcount(graph)) { + IGRAPH_ERROR("types vector too short", IGRAPH_EINVAL); + } + if (weights && igraph_vector_size(weights) < igraph_ecount(graph)) { + IGRAPH_ERROR("weights vector too short", IGRAPH_EINVAL); + } + + if (weights == 0) { + IGRAPH_CHECK(igraph_i_maximum_bipartite_matching_unweighted(graph, types, + matching_size, matching)); + if (matching_weight != 0) { + *matching_weight = *matching_size; + } + return IGRAPH_SUCCESS; + } else { + IGRAPH_CHECK(igraph_i_maximum_bipartite_matching_weighted(graph, types, + matching_size, matching_weight, matching, weights, eps)); + return IGRAPH_SUCCESS; + } +} + +static int igraph_i_maximum_bipartite_matching_unweighted_relabel( + const igraph_t* graph, + const igraph_vector_bool_t* types, igraph_vector_t* labels, + igraph_vector_long_t* matching, igraph_bool_t smaller_set); + +/** + * Finding maximum bipartite matchings on bipartite graphs using the + * push-relabel algorithm. + * + * The implementation follows the pseudocode in Algorithm 1 of the + * following paper: + * + * Kaya K, Langguth J, Manne F and Ucar B: Experiments on push-relabel-based + * maximum cardinality matching algorithms for bipartite graphs. Technical + * Report TR/PA/11/33 of CERFACS (Centre Européen de Recherche et de Formation + * Avancée en Calcul Scientifique). + * http://www.cerfacs.fr/algor/reports/2011/TR_PA_11_33.pdf + */ +static int igraph_i_maximum_bipartite_matching_unweighted( + const igraph_t* graph, + const igraph_vector_bool_t* types, igraph_integer_t* matching_size, + igraph_vector_long_t* matching) { + long int i, j, k, n, no_of_nodes = igraph_vcount(graph); + long int num_matched; /* number of matched vertex pairs */ + igraph_vector_long_t match; /* will store the matching */ + igraph_vector_t labels; /* will store the labels */ + igraph_vector_t neis; /* used to retrieve the neighbors of a node */ + igraph_dqueue_long_t q; /* a FIFO for push ordering */ + igraph_bool_t smaller_set; /* denotes which part of the bipartite graph is smaller */ + long int label_changed = 0; /* Counter to decide when to run a global relabeling */ + long int relabeling_freq = no_of_nodes / 2; + + /* We will use: + * - FIFO push ordering + * - global relabeling frequency: n/2 steps where n is the number of nodes + * - simple greedy matching for initialization + */ + + /* (1) Initialize data structures */ + IGRAPH_CHECK(igraph_vector_long_init(&match, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &match); + IGRAPH_VECTOR_INIT_FINALLY(&labels, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_long_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q); + + /* (2) Initially, every node is unmatched */ + igraph_vector_long_fill(&match, -1); + + /* (3) Find an initial matching in a greedy manner. + * At the same time, find which side of the graph is smaller. */ + num_matched = 0; j = 0; + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*types)[i]) { + j++; + } + if (MATCHED(i)) { + continue; + } + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, + IGRAPH_ALL)); + n = igraph_vector_size(&neis); + for (j = 0; j < n; j++) { + k = (long int) VECTOR(neis)[j]; + if (VECTOR(*types)[k] == VECTOR(*types)[i]) { + IGRAPH_ERROR("Graph is not bipartite with supplied types vector", IGRAPH_EINVAL); + } + if (UNMATCHED(k)) { + /* We match vertex i to vertex VECTOR(neis)[j] */ + VECTOR(match)[k] = i; + VECTOR(match)[i] = k; + num_matched++; + break; + } + } + } + smaller_set = (j <= no_of_nodes / 2); + + /* (4) Set the initial labeling -- lines 1 and 2 in the tech report */ + IGRAPH_CHECK(igraph_i_maximum_bipartite_matching_unweighted_relabel( + graph, types, &labels, &match, smaller_set)); + + /* (5) Fill the push queue with the unmatched nodes from the smaller set. */ + for (i = 0; i < no_of_nodes; i++) { + if (UNMATCHED(i) && VECTOR(*types)[i] == smaller_set) { + IGRAPH_CHECK(igraph_dqueue_long_push(&q, i)); + } + } + + /* (6) Main loop from the referenced tech report -- lines 4--13 */ + label_changed = 0; + while (!igraph_dqueue_long_empty(&q)) { + long int v = igraph_dqueue_long_pop(&q); /* Line 13 */ + long int u = -1, label_u = 2 * no_of_nodes; + long int w; + + if (label_changed >= relabeling_freq) { + /* Run global relabeling */ + IGRAPH_CHECK(igraph_i_maximum_bipartite_matching_unweighted_relabel( + graph, types, &labels, &match, smaller_set)); + label_changed = 0; + } + + debug("Considering vertex %ld\n", v); + + /* Line 5: find row u among the neighbors of v s.t. label(u) is minimal */ + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, + IGRAPH_ALL)); + n = igraph_vector_size(&neis); + for (i = 0; i < n; i++) { + if (VECTOR(labels)[(long int)VECTOR(neis)[i]] < label_u) { + u = (long int) VECTOR(neis)[i]; + label_u = (long int) VECTOR(labels)[u]; + label_changed++; + } + } + + debug(" Neighbor with smallest label: %ld (label=%ld)\n", u, label_u); + + if (label_u < no_of_nodes) { /* Line 6 */ + VECTOR(labels)[v] = VECTOR(labels)[u] + 1; /* Line 7 */ + if (MATCHED(u)) { /* Line 8 */ + w = VECTOR(match)[u]; + debug(" Vertex %ld is matched to %ld, performing a double push\n", u, w); + if (w != v) { + VECTOR(match)[u] = -1; VECTOR(match)[w] = -1; /* Line 9 */ + IGRAPH_CHECK(igraph_dqueue_long_push(&q, w)); /* Line 10 */ + debug(" Unmatching & activating vertex %ld\n", w); + num_matched--; + } + } + VECTOR(match)[u] = v; VECTOR(match)[v] = u; /* Line 11 */ + num_matched++; + VECTOR(labels)[u] += 2; /* Line 12 */ + label_changed++; + } + } + + /* Fill the output parameters */ + if (matching != 0) { + IGRAPH_CHECK(igraph_vector_long_update(matching, &match)); + } + if (matching_size != 0) { + *matching_size = (igraph_integer_t) num_matched; + } + + /* Release everything */ + igraph_dqueue_long_destroy(&q); + igraph_vector_destroy(&neis); + igraph_vector_destroy(&labels); + igraph_vector_long_destroy(&match); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_maximum_bipartite_matching_unweighted_relabel( + const igraph_t* graph, + const igraph_vector_bool_t* types, igraph_vector_t* labels, + igraph_vector_long_t* match, igraph_bool_t smaller_set) { + long int i, j, n, no_of_nodes = igraph_vcount(graph), matched_to; + igraph_dqueue_long_t q; + igraph_vector_t neis; + + debug("Running global relabeling.\n"); + + /* Set all the labels to no_of_nodes first */ + igraph_vector_fill(labels, no_of_nodes); + + /* Allocate vector for neighbors */ + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + /* Create a FIFO for the BFS and initialize it with the unmatched rows + * (i.e. members of the larger set) */ + IGRAPH_CHECK(igraph_dqueue_long_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*types)[i] != smaller_set && VECTOR(*match)[i] == -1) { + IGRAPH_CHECK(igraph_dqueue_long_push(&q, i)); + VECTOR(*labels)[i] = 0; + } + } + + /* Run the BFS */ + while (!igraph_dqueue_long_empty(&q)) { + long int v = igraph_dqueue_long_pop(&q); + long int w; + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, + IGRAPH_ALL)); + + n = igraph_vector_size(&neis); + for (j = 0; j < n; j++) { + w = (long int) VECTOR(neis)[j]; + if (VECTOR(*labels)[w] == no_of_nodes) { + VECTOR(*labels)[w] = VECTOR(*labels)[v] + 1; + matched_to = VECTOR(*match)[w]; + if (matched_to != -1 && VECTOR(*labels)[matched_to] == no_of_nodes) { + IGRAPH_CHECK(igraph_dqueue_long_push(&q, matched_to)); + VECTOR(*labels)[matched_to] = VECTOR(*labels)[w] + 1; + } + } + } + } + + igraph_dqueue_long_destroy(&q); + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * Finding maximum bipartite matchings on bipartite graphs using the + * Hungarian algorithm (a.k.a. Kuhn-Munkres algorithm). + * + * The algorithm uses a maximum cardinality matching on a subset of + * tight edges as a starting point. This is achieved by + * \c igraph_i_maximum_bipartite_matching_unweighted on the restricted + * graph. + * + * The algorithm works reliably only if the weights are integers. The + * \c eps parameter should specity a very small number; if the slack on + * an edge falls below \c eps, it will be considered tight. If all your + * weights are integers, you can safely set \c eps to zero. + */ +static int igraph_i_maximum_bipartite_matching_weighted( + const igraph_t* graph, + const igraph_vector_bool_t* types, igraph_integer_t* matching_size, + igraph_real_t* matching_weight, igraph_vector_long_t* matching, + const igraph_vector_t* weights, igraph_real_t eps) { + long int i, j, k, n, no_of_nodes, no_of_edges; + igraph_integer_t u, v, w, msize; + igraph_t newgraph; + igraph_vector_long_t match; /* will store the matching */ + igraph_vector_t slack; /* will store the slack on each edge */ + igraph_vector_t parent; /* parent vertices during a BFS */ + igraph_vector_t vec1, vec2; /* general temporary vectors */ + igraph_vector_t labels; /* will store the labels */ + igraph_dqueue_long_t q; /* a FIFO for BST */ + igraph_bool_t smaller_set_type; /* denotes which part of the bipartite graph is smaller */ + igraph_vector_t smaller_set; /* stores the vertex IDs of the smaller set */ + igraph_vector_t larger_set; /* stores the vertex IDs of the larger set */ + long int smaller_set_size; /* size of the smaller set */ + long int larger_set_size; /* size of the larger set */ + igraph_real_t dual; /* solution of the dual problem */ + IGRAPH_UNUSED(dual); /* We mark it as unused to prevent warnings about unused-but-set-variables. */ + igraph_adjlist_t tight_phantom_edges; /* adjacency list to manage tight phantom edges */ + igraph_integer_t alternating_path_endpoint; + igraph_vector_int_t* neis; + igraph_vector_int_t *neis2; + igraph_inclist_t inclist; /* incidence list of the original graph */ + + /* The Hungarian algorithm is originally for complete bipartite graphs. + * For non-complete bipartite graphs, a phantom edge of weight zero must be + * added between every pair of non-connected vertices. We don't do this + * explicitly of course. See the comments below about how phantom edges + * are taken into account. */ + + no_of_nodes = igraph_vcount(graph); + no_of_edges = igraph_ecount(graph); + if (eps < 0) { + IGRAPH_WARNING("negative epsilon given, clamping to zero"); + eps = 0; + } + + /* (1) Initialize data structures */ + IGRAPH_CHECK(igraph_vector_long_init(&match, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &match); + IGRAPH_CHECK(igraph_vector_init(&slack, no_of_edges)); + IGRAPH_FINALLY(igraph_vector_destroy, &slack); + IGRAPH_VECTOR_INIT_FINALLY(&vec1, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vec2, 0); + IGRAPH_VECTOR_INIT_FINALLY(&labels, no_of_nodes); + IGRAPH_CHECK(igraph_dqueue_long_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q); + IGRAPH_VECTOR_INIT_FINALLY(&parent, no_of_nodes); + IGRAPH_CHECK(igraph_adjlist_init_empty(&tight_phantom_edges, + (igraph_integer_t) no_of_nodes)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &tight_phantom_edges); + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + IGRAPH_VECTOR_INIT_FINALLY(&smaller_set, 0); + IGRAPH_VECTOR_INIT_FINALLY(&larger_set, 0); + + /* (2) Find which set is the smaller one */ + j = 0; + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*types)[i] == 0) { + j++; + } + } + smaller_set_type = (j > no_of_nodes / 2); + smaller_set_size = smaller_set_type ? (no_of_nodes - j) : j; + larger_set_size = no_of_nodes - smaller_set_size; + IGRAPH_CHECK(igraph_vector_reserve(&smaller_set, smaller_set_size)); + IGRAPH_CHECK(igraph_vector_reserve(&larger_set, larger_set_size)); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*types)[i] == smaller_set_type) { + IGRAPH_CHECK(igraph_vector_push_back(&smaller_set, i)); + } else { + IGRAPH_CHECK(igraph_vector_push_back(&larger_set, i)); + } + } + + /* (3) Calculate the initial labeling and the set of tight edges. Use the + * smaller set only. Here we can assume that there are no phantom edges + * among the tight ones. */ + dual = 0; + for (i = 0; i < no_of_nodes; i++) { + igraph_real_t max_weight = 0; + + if (VECTOR(*types)[i] != smaller_set_type) { + VECTOR(labels)[i] = 0; + continue; + } + + neis = igraph_inclist_get(&inclist, i); + n = igraph_vector_int_size(neis); + for (j = 0, k = 0; j < n; j++) { + k = (long int) VECTOR(*neis)[j]; + u = IGRAPH_OTHER(graph, k, i); + if (VECTOR(*types)[u] == VECTOR(*types)[i]) { + IGRAPH_ERROR("Graph is not bipartite with supplied types vector", IGRAPH_EINVAL); + } + if (VECTOR(*weights)[k] > max_weight) { + max_weight = VECTOR(*weights)[k]; + } + } + + VECTOR(labels)[i] = max_weight; + dual += max_weight; + } + + igraph_vector_clear(&vec1); + IGRAPH_CHECK(igraph_get_edgelist(graph, &vec2, 0)); +#define IS_TIGHT(i) (VECTOR(slack)[i] <= eps) + for (i = 0, j = 0; i < no_of_edges; i++, j += 2) { + u = (igraph_integer_t) VECTOR(vec2)[j]; + v = (igraph_integer_t) VECTOR(vec2)[j + 1]; + VECTOR(slack)[i] = VECTOR(labels)[u] + VECTOR(labels)[v] - VECTOR(*weights)[i]; + if (IS_TIGHT(i)) { + IGRAPH_CHECK(igraph_vector_push_back(&vec1, u)); + IGRAPH_CHECK(igraph_vector_push_back(&vec1, v)); + } + } + igraph_vector_clear(&vec2); + + /* (4) Construct a temporary graph on which the initial maximum matching + * will be calculated (only on the subset of tight edges) */ + IGRAPH_CHECK(igraph_create(&newgraph, &vec1, + (igraph_integer_t) no_of_nodes, 0)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + IGRAPH_CHECK(igraph_maximum_bipartite_matching(&newgraph, types, &msize, 0, &match, 0, 0)); + igraph_destroy(&newgraph); + IGRAPH_FINALLY_CLEAN(1); + + /* (5) Main loop until the matching becomes maximal */ + while (msize < smaller_set_size) { + igraph_real_t min_slack, min_slack_2; + igraph_integer_t min_slack_u, min_slack_v; + + /* mark min_slack_u as unused; it is actually used when debugging, but + * gcc complains when we are not debugging */ + IGRAPH_UNUSED(min_slack_u); + + /* (7) Fill the push queue with the unmatched nodes from the smaller set. */ + igraph_vector_clear(&vec1); + igraph_vector_clear(&vec2); + igraph_vector_fill(&parent, -1); + for (j = 0; j < smaller_set_size; j++) { + i = VECTOR(smaller_set)[j]; + if (UNMATCHED(i)) { + IGRAPH_CHECK(igraph_dqueue_long_push(&q, i)); + VECTOR(parent)[i] = i; + IGRAPH_CHECK(igraph_vector_push_back(&vec1, i)); + } + } + +#ifdef MATCHING_DEBUG + debug("Matching:"); + igraph_vector_long_print(&match); + debug("Unmatched vertices are marked by non-negative numbers:\n"); + igraph_vector_print(&parent); + debug("Labeling:"); + igraph_vector_print(&labels); + debug("Slacks:"); + igraph_vector_print(&slack); +#endif + + /* (8) Run the BFS */ + alternating_path_endpoint = -1; + while (!igraph_dqueue_long_empty(&q)) { + v = (int) igraph_dqueue_long_pop(&q); + + debug("Considering vertex %ld\n", (long int)v); + + /* v is always in the smaller set. Find the neighbors of v, which + * are all in the larger set. Find the pairs of these nodes in + * the smaller set and push them to the queue. Mark the traversed + * nodes as seen. + * + * Here we have to be careful as there are two types of incident + * edges on v: real edges and phantom ones. Real edges are + * given by igraph_inclist_get. Phantom edges are not given so we + * (ab)use an adjacency list data structure that lists the + * vertices connected to v by phantom edges only. */ + neis = igraph_inclist_get(&inclist, v); + n = igraph_vector_int_size(neis); + for (i = 0; i < n; i++) { + j = (long int) VECTOR(*neis)[i]; + /* We only care about tight edges */ + if (!IS_TIGHT(j)) { + continue; + } + /* Have we seen the other endpoint already? */ + u = IGRAPH_OTHER(graph, j, v); + if (VECTOR(parent)[u] >= 0) { + continue; + } + debug(" Reached vertex %ld via edge %ld\n", (long)u, (long)j); + VECTOR(parent)[u] = v; + IGRAPH_CHECK(igraph_vector_push_back(&vec2, u)); + w = (int) VECTOR(match)[u]; + if (w == -1) { + /* u is unmatched and it is in the larger set. Therefore, we + * could improve the matching by following the parents back + * from u to the root. + */ + alternating_path_endpoint = u; + break; /* since we don't need any more endpoints that come from v */ + } else { + IGRAPH_CHECK(igraph_dqueue_long_push(&q, w)); + VECTOR(parent)[w] = u; + } + IGRAPH_CHECK(igraph_vector_push_back(&vec1, w)); + } + + /* Now do the same with the phantom edges */ + neis2 = igraph_adjlist_get(&tight_phantom_edges, v); + n = igraph_vector_int_size(neis2); + for (i = 0; i < n; i++) { + u = (igraph_integer_t) VECTOR(*neis2)[i]; + /* Have we seen u already? */ + if (VECTOR(parent)[u] >= 0) { + continue; + } + /* Check if the edge is really tight; it might have happened that the + * edge became non-tight in the meanwhile. We do not remove these from + * tight_phantom_edges at the moment, so we check them once again here. + */ + if (fabs(VECTOR(labels)[(long int)v] + VECTOR(labels)[(long int)u]) > eps) { + continue; + } + debug(" Reached vertex %ld via tight phantom edge\n", (long)u); + VECTOR(parent)[u] = v; + IGRAPH_CHECK(igraph_vector_push_back(&vec2, u)); + w = (int) VECTOR(match)[u]; + if (w == -1) { + /* u is unmatched and it is in the larger set. Therefore, we + * could improve the matching by following the parents back + * from u to the root. + */ + alternating_path_endpoint = u; + break; /* since we don't need any more endpoints that come from v */ + } else { + IGRAPH_CHECK(igraph_dqueue_long_push(&q, w)); + VECTOR(parent)[w] = u; + } + IGRAPH_CHECK(igraph_vector_push_back(&vec1, w)); + } + } + + /* Okay; did we have an alternating path? */ + if (alternating_path_endpoint != -1) { +#ifdef MATCHING_DEBUG + debug("BFS parent tree:"); + igraph_vector_print(&parent); +#endif + /* Increase the size of the matching with the alternating path. */ + v = alternating_path_endpoint; + u = (igraph_integer_t) VECTOR(parent)[v]; + debug("Extending matching with alternating path ending in %ld.\n", (long int)v); + + while (u != v) { + w = (int) VECTOR(match)[v]; + if (w != -1) { + VECTOR(match)[w] = -1; + } + VECTOR(match)[v] = u; + + VECTOR(match)[v] = u; + w = (int) VECTOR(match)[u]; + if (w != -1) { + VECTOR(match)[w] = -1; + } + VECTOR(match)[u] = v; + + v = (igraph_integer_t) VECTOR(parent)[u]; + u = (igraph_integer_t) VECTOR(parent)[v]; + } + + msize++; + +#ifdef MATCHING_DEBUG + debug("New matching after update:"); + igraph_vector_long_print(&match); + debug("Matching size is now: %ld\n", (long)msize); +#endif + continue; + } + +#ifdef MATCHING_DEBUG + debug("Vertices reachable from unmatched ones via tight edges:\n"); + igraph_vector_print(&vec1); + igraph_vector_print(&vec2); +#endif + + /* At this point, vec1 contains the nodes in the smaller set (A) + * reachable from unmatched nodes in A via tight edges only, while vec2 + * contains the nodes in the larger set (B) reachable from unmatched + * nodes in A via tight edges only. Also, parent[i] >= 0 if node i + * is reachable */ + + /* Check the edges between reachable nodes in A and unreachable + * nodes in B, and find the minimum slack on them. + * + * Since the weights are positive, we do no harm if we first + * assume that there are no "real" edges between the two sets + * mentioned above and determine an upper bound for min_slack + * based on this. */ + min_slack = IGRAPH_INFINITY; + min_slack_u = min_slack_v = 0; + n = igraph_vector_size(&vec1); + for (j = 0; j < larger_set_size; j++) { + i = VECTOR(larger_set)[j]; + if (VECTOR(labels)[i] < min_slack) { + min_slack = VECTOR(labels)[i]; + min_slack_v = (igraph_integer_t) i; + } + } + min_slack_2 = IGRAPH_INFINITY; + for (i = 0; i < n; i++) { + u = (igraph_integer_t) VECTOR(vec1)[i]; + /* u is surely from the smaller set, but we are interested in it + * only if it is reachable from an unmatched vertex */ + if (VECTOR(parent)[u] < 0) { + continue; + } + if (VECTOR(labels)[u] < min_slack_2) { + min_slack_2 = VECTOR(labels)[u]; + min_slack_u = u; + } + } + min_slack += min_slack_2; + debug("Starting approximation for min_slack = %.4f (based on vertex pair %ld--%ld)\n", + min_slack, (long int)min_slack_u, (long int)min_slack_v); + + n = igraph_vector_size(&vec1); + for (i = 0; i < n; i++) { + u = (igraph_integer_t) VECTOR(vec1)[i]; + /* u is a reachable node in A; get its incident edges. + * + * There are two types of incident edges: 1) real edges, + * 2) phantom edges. Phantom edges were treated earlier + * when we determined the initial value for min_slack. */ + debug("Trying to expand along vertex %ld\n", (long int)u); + neis = igraph_inclist_get(&inclist, u); + k = igraph_vector_int_size(neis); + for (j = 0; j < k; j++) { + /* v is the vertex sitting at the other end of an edge incident + * on u; check whether it was reached */ + v = IGRAPH_OTHER(graph, VECTOR(*neis)[j], u); + debug(" Edge %ld -- %ld (ID=%ld)\n", (long int)u, (long int)v, (long int)VECTOR(*neis)[j]); + if (VECTOR(parent)[v] >= 0) { + /* v was reached, so we are not interested in it */ + debug(" %ld was reached, so we are not interested in it\n", (long int)v); + continue; + } + /* v is the ID of the edge from now on */ + v = (igraph_integer_t) VECTOR(*neis)[j]; + if (VECTOR(slack)[v] < min_slack) { + min_slack = VECTOR(slack)[v]; + min_slack_u = u; + min_slack_v = IGRAPH_OTHER(graph, v, u); + } + debug(" Slack of this edge: %.4f, min slack is now: %.4f\n", + VECTOR(slack)[v], min_slack); + } + } + debug("Minimum slack: %.4f on edge %d--%d\n", min_slack, (int)min_slack_u, (int)min_slack_v); + + if (min_slack > 0) { + /* Decrease the label of reachable nodes in A by min_slack. + * Also update the dual solution */ + n = igraph_vector_size(&vec1); + for (i = 0; i < n; i++) { + u = (igraph_integer_t) VECTOR(vec1)[i]; + VECTOR(labels)[u] -= min_slack; + neis = igraph_inclist_get(&inclist, u); + k = igraph_vector_int_size(neis); + for (j = 0; j < k; j++) { + debug(" Decreasing slack of edge %ld (%ld--%ld) by %.4f\n", + (long)VECTOR(*neis)[j], (long)u, + (long)IGRAPH_OTHER(graph, VECTOR(*neis)[j], u), min_slack); + VECTOR(slack)[(long int)VECTOR(*neis)[j]] -= min_slack; + } + dual -= min_slack; + } + + /* Increase the label of reachable nodes in B by min_slack. + * Also update the dual solution */ + n = igraph_vector_size(&vec2); + for (i = 0; i < n; i++) { + u = (igraph_integer_t) VECTOR(vec2)[i]; + VECTOR(labels)[u] += min_slack; + neis = igraph_inclist_get(&inclist, u); + k = igraph_vector_int_size(neis); + for (j = 0; j < k; j++) { + debug(" Increasing slack of edge %ld (%ld--%ld) by %.4f\n", + (long)VECTOR(*neis)[j], (long)u, + (long)IGRAPH_OTHER(graph, (long)VECTOR(*neis)[j], u), min_slack); + VECTOR(slack)[(long int)VECTOR(*neis)[j]] += min_slack; + } + dual += min_slack; + } + } + + /* Update the set of tight phantom edges. + * Note that we must do it even if min_slack is zero; the reason is that + * it can happen that min_slack is zero in the first step if there are + * isolated nodes in the input graph. + * + * TODO: this is O(n^2) here. Can we do it faster? */ + for (i = 0; i < smaller_set_size; i++) { + u = VECTOR(smaller_set)[i]; + for (j = 0; j < larger_set_size; j++) { + v = VECTOR(larger_set)[j]; + if (VECTOR(labels)[(long int)u] + VECTOR(labels)[(long int)v] <= eps) { + /* Tight phantom edge found. Note that we don't have to check whether + * u and v are connected; if they were, then the slack of this edge + * would be negative. */ + neis2 = igraph_adjlist_get(&tight_phantom_edges, u); + if (!igraph_vector_int_binsearch(neis2, v, &k)) { + debug("New tight phantom edge: %ld -- %ld\n", (long)u, (long)v); + IGRAPH_CHECK(igraph_vector_int_insert(neis2, k, v)); + } + } + } + } + +#ifdef MATCHING_DEBUG + debug("New labels:"); + igraph_vector_print(&labels); + debug("Slacks after updating with min_slack:"); + igraph_vector_print(&slack); +#endif + } + + /* Cleanup: remove phantom edges from the matching */ + for (i = 0; i < smaller_set_size; i++) { + u = VECTOR(smaller_set)[i]; + v = VECTOR(match)[u]; + if (v != -1) { + neis2 = igraph_adjlist_get(&tight_phantom_edges, u); + if (igraph_vector_int_binsearch(neis2, v, 0)) { + VECTOR(match)[u] = VECTOR(match)[v] = -1; + msize--; + } + } + } + + /* Fill the output parameters */ + if (matching != 0) { + IGRAPH_CHECK(igraph_vector_long_update(matching, &match)); + } + if (matching_size != 0) { + *matching_size = msize; + } + if (matching_weight != 0) { + *matching_weight = 0; + for (i = 0; i < no_of_edges; i++) { + if (IS_TIGHT(i)) { + IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) i, &u, &v)); + if (VECTOR(match)[u] == v) { + *matching_weight += VECTOR(*weights)[i]; + } + } + } + } + + /* Release everything */ +#undef IS_TIGHT + igraph_vector_destroy(&larger_set); + igraph_vector_destroy(&smaller_set); + igraph_inclist_destroy(&inclist); + igraph_adjlist_destroy(&tight_phantom_edges); + igraph_vector_destroy(&parent); + igraph_dqueue_long_destroy(&q); + igraph_vector_destroy(&labels); + igraph_vector_destroy(&vec1); + igraph_vector_destroy(&vec2); + igraph_vector_destroy(&slack); + igraph_vector_long_destroy(&match); + IGRAPH_FINALLY_CLEAN(11); + + return IGRAPH_SUCCESS; +} + +int igraph_maximum_matching(const igraph_t* graph, igraph_integer_t* matching_size, + igraph_real_t* matching_weight, igraph_vector_long_t* matching, + const igraph_vector_t* weights) { + IGRAPH_UNUSED(graph); + IGRAPH_UNUSED(matching_size); + IGRAPH_UNUSED(matching_weight); + IGRAPH_UNUSED(matching); + IGRAPH_UNUSED(weights); + IGRAPH_ERROR("maximum matching on general graphs not implemented yet", + IGRAPH_UNIMPLEMENTED); +} + +#ifdef MATCHING_DEBUG + #undef MATCHING_DEBUG +#endif diff --git a/src/rigraph/core/misc/microscopic_update.c b/src/rigraph/core/misc/microscopic_update.c new file mode 100644 index 0000000..80fc7c2 --- /dev/null +++ b/src/rigraph/core/misc/microscopic_update.c @@ -0,0 +1,1209 @@ +/* -*- mode: C -*- */ +/* + Microscopic update rules for dealing with agent-level strategy revision. + Copyright (C) 2011 Minh Van Nguyen + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "igraph_microscopic_update.h" + +#include "igraph_iterators.h" +#include "igraph_interface.h" +#include "igraph_random.h" +#include "igraph_error.h" + +/* + * Internal use only. + * Compute the cumulative proportionate values of a vector. The vector is + * assumed to hold values associated with edges. + * + * \param graph The graph object representing the game network. No error + * checks will be performed on this graph. You are responsible for + * ensuring that this is a valid graph for the particular + * microscopic update rule at hand. + * \param U A vector of edge values for which we want to compute cumulative + * proportionate values. So U[i] is the value of the edge with ID i. + * With a local perspective, we would only compute cumulative + * proportionate values for some combination of U. This vector could + * be, for example, a vector of weights for edges in \p graph. It is + * assumed that each value of U is nonnegative; it is your + * responsibility to ensure this. Furthermore, this vector must have a + * length the same as the number of edges in \p graph; you are + * responsible for ensuring this condition holds. + * \param V Pointer to an initialized vector. The cumulative proportionate + * values will be computed and stored here. No error checks will be + * performed on this parameter. + * \param islocal Boolean; this flag controls which perspective to use. If + * true then we use the local perspective; otherwise we use the global + * perspective. In the context of this function, the local perspective + * for a vertex v consists of all edges incident on v. In contrast, the + * global perspective for v consists of all edges in \p graph. + * \param vid The vertex to use if we are considering a local perspective, + * i.e. if \p islocal is true. This vertex will be ignored if + * \p islocal is false. That is, if \p islocal is false then it is safe + * pass the value -1 here. On the other hand, if \p islocal is true then + * it is assumed that this is indeed a vertex of \p graph. + * \param mode Defines the sort of neighbourhood to consider for \p vid. This + * is only relevant if we are considering the local perspective, i.e. if + * \p islocal is true. If we are considering the global perspective, + * then this parameter would be ignored. In other words, if \p islocal + * is false then it is safe to pass the value \p IGRAPH_ALL here. If + * \p graph is undirected, then we use all the immediate neighbours of + * \p vid. Thus if you know that \p graph is undirected, then it is + * safe to pass the value \p IGRAPH_ALL here. Supported values are: + * \clist + * \cli IGRAPH_OUT + * Use the out-neighbours of \p vid. This option is only relevant + * when \p graph is a digraph and we are considering the local + * perspective. + * \cli IGRAPH_IN + * Use the in-neighbours of \p vid. Again this option is only relevant + * when \p graph is a directed graph and we are considering the local + * perspective. + * \cli IGRAPH_ALL + * Use both the in- and out-neighbours of \p vid. This option is only + * relevant if \p graph is a digraph and we are considering a local + * perspective. Also use this value if \p graph is undirected or we + * are considering the global perspective. + * \endclist + * \return Codes: + * \clist + * \cli IGRAPH_EINVAL + * This error code is returned in the following case: The vector + * \p U, or some combination of its values, sums to zero. + * \cli IGRAPH_SUCCESS + * This signal is returned if the cumulative proportionate values + * were successfully computed. + * \endclist + * + * Time complexity: O(2n) where n is the number of edges in the perspective + * of \p vid. + */ + +static int igraph_i_ecumulative_proportionate_values(const igraph_t *graph, + const igraph_vector_t *U, + igraph_vector_t *V, + igraph_bool_t islocal, + igraph_integer_t vid, + igraph_neimode_t mode) { + igraph_eit_t A; /* all edges in v's perspective */ + igraph_es_t es; + igraph_integer_t e; + igraph_real_t C; /* cumulative probability */ + igraph_real_t P; /* probability */ + igraph_real_t S; /* sum of values */ + long int i; + + /* Set the perspective. Let v be the vertex under consideration. The local */ + /* perspective for v consists of edges incident on it. In contrast, the */ + /* global perspective for v are all edges in the given graph. Hence in the */ + /* global perspective, we will ignore the given vertex and the given */ + /* neighbourhood type, but instead consider all edges in the given graph. */ + if (islocal) { + IGRAPH_CHECK(igraph_es_incident(&es, vid, mode)); + } else { + IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_ID)); + } + IGRAPH_FINALLY(igraph_es_destroy, &es); + + /* Sum up all the values of vector U in the perspective for v. This sum */ + /* will be used in normalizing each value. */ + /* NOTE: Here we assume that each value to be summed is nonnegative, */ + /* and at least one of the values is nonzero. The behaviour resulting */ + /* from all values being zero would be division by zero later on when */ + /* we normalize each value. We check to see that the values sum to zero. */ + /* NOTE: In this function, the order in which we iterate through the */ + /* edges of interest should be the same as the order in which we do so */ + /* in the caller function. If the caller function doesn't care about the */ + /* order of values in the resulting vector V, then there's no need to take */ + /* special notice of that order. But in some cases the order of values in */ + /* V is taken into account, for example, in the Moran process. */ + S = 0.0; + IGRAPH_CHECK(igraph_eit_create(graph, es, &A)); + IGRAPH_FINALLY(igraph_eit_destroy, &A); + while (!IGRAPH_EIT_END(A)) { + e = (igraph_integer_t)IGRAPH_EIT_GET(A); + S += (igraph_real_t)VECTOR(*U)[e]; + IGRAPH_EIT_NEXT(A); + } + /* avoid division by zero later on */ + if (S == (igraph_real_t)0.0) { + igraph_eit_destroy(&A); + igraph_es_destroy(&es); + IGRAPH_FINALLY_CLEAN(2); + IGRAPH_ERROR("Vector of values sums to zero", IGRAPH_EINVAL); + } + + /* Get cumulative probability and relative value for each edge in the */ + /* perspective of v. The vector V holds the cumulative proportionate */ + /* values of all edges in v's perspective. The value V[0] is the */ + /* cumulative proportionate value of the first edge in the edge iterator */ + /* A. The value V[1] is the cumulative proportionate value of the second */ + /* edge in the iterator A. And so on. */ + C = 0.0; + i = 0; + IGRAPH_EIT_RESET(A); + IGRAPH_CHECK(igraph_vector_resize(V, IGRAPH_EIT_SIZE(A))); + while (!IGRAPH_EIT_END(A)) { + e = (igraph_integer_t)IGRAPH_EIT_GET(A); + /* NOTE: Beware of division by zero here. This can happen if the vector */ + /* of values, or the combination of interest, sums to zero. */ + P = (igraph_real_t)VECTOR(*U)[e] / S; + C += P; + VECTOR(*V)[i] = C; + i++; + IGRAPH_EIT_NEXT(A); + } + + igraph_eit_destroy(&A); + igraph_es_destroy(&es); + + /* Pop A and es from the finally stack -- that's three items */ + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/* + * Internal use only. + * Compute the cumulative proportionate values of a vector. The vector is + * assumed to hold values associated with vertices. + * + * \param graph The graph object representing the game network. No error + * checks will be performed on this graph. You are responsible for + * ensuring that this is a valid graph for the particular + * microscopic update rule at hand. + * \param U A vector of vertex values for which we want to compute cumulative + * proportionate values. The vector could be, for example, a vector of + * fitness for vertices of \p graph. It is assumed that each value of U + * is nonnegative; it is your responsibility to ensure this. Also U, or + * a combination of interest, is assumed to sum to a positive value; + * this condition will be checked. + * \param V Pointer to an initialized vector. The cumulative proportionate + * values will be computed and stored here. No error checks will be + * performed on this parameter. + * \param islocal Boolean; this flag controls which perspective to use. If + * true then we use the local perspective; otherwise we use the global + * perspective. The local perspective for a vertex v is the set of all + * immediate neighbours of v. In contrast, the global perspective + * for v is the vertex set of \p graph. + * \param vid The vertex to use if we are considering a local perspective, + * i.e. if \p islocal is true. This vertex will be ignored if + * \p islocal is false. That is, if \p islocal is false then it is safe + * pass the value -1 here. On the other hand, if \p islocal is true then + * it is assumed that this is indeed a vertex of \p graph. + * \param mode Defines the sort of neighbourhood to consider for \p vid. This + * is only relevant if we are considering the local perspective, i.e. if + * \p islocal is true. If we are considering the global perspective, + * then this parameter would be ignored. In other words, if \p islocal + * is false then it is safe to pass the value \p IGRAPH_ALL here. If + * \p graph is undirected, then we use all the immediate neighbours of + * \p vid. Thus if you know that \p graph is undirected, then it is + * safe to pass the value \p IGRAPH_ALL here. Supported values are: + * \clist + * \cli IGRAPH_OUT + * Use the out-neighbours of \p vid. This option is only relevant + * when \p graph is a digraph and we are considering the local + * perspective. + * \cli IGRAPH_IN + * Use the in-neighbours of \p vid. Again this option is only relevant + * when \p graph is a directed graph and we are considering the local + * perspective. + * \cli IGRAPH_ALL + * Use both the in- and out-neighbours of \p vid. This option is only + * relevant if \p graph is a digraph and we are considering a local + * perspective. Also use this value if \p graph is undirected or we + * are considering the global perspective. + * \endclist + * \return Codes: + * \clist + * \cli IGRAPH_EINVAL + * This error code is returned in the following case: The vector + * \p U, or some combination of its values, sums to zero. + * \cli IGRAPH_SUCCESS + * This signal is returned if the cumulative proportionate values + * were successfully computed. + * \endclist + * + * Time complexity: O(2n) where n is the number of vertices in the + * perspective of vid. + */ + +static int igraph_i_vcumulative_proportionate_values(const igraph_t *graph, + const igraph_vector_t *U, + igraph_vector_t *V, + igraph_bool_t islocal, + igraph_integer_t vid, + igraph_neimode_t mode) { + igraph_integer_t v; + igraph_real_t C; /* cumulative probability */ + igraph_real_t P; /* probability */ + igraph_real_t S; /* sum of values */ + igraph_vit_t A; /* all vertices in v's perspective */ + igraph_vs_t vs; + long int i; + + /* Set the perspective. Let v be the vertex under consideration; it might */ + /* be that we want to update v's strategy. The local perspective for v */ + /* consists of its immediate neighbours. In contrast, the global */ + /* perspective for v are all the vertices in the given graph. Hence in the */ + /* global perspective, we will ignore the given vertex and the given */ + /* neighbourhood type, but instead consider all vertices in the given */ + /* graph. */ + if (islocal) { + IGRAPH_CHECK(igraph_vs_adj(&vs, vid, mode)); + } else { + IGRAPH_CHECK(igraph_vs_all(&vs)); + } + IGRAPH_FINALLY(igraph_vs_destroy, &vs); + + /* Sum up all the values of vector U in the perspective for v. This */ + /* sum will be used in normalizing each value. If we are using a local */ + /* perspective, then we also need to consider the quantity of v in */ + /* computing the sum. */ + /* NOTE: Here we assume that each value to be summed is nonnegative, */ + /* and at least one of the values is nonzero. The behaviour resulting */ + /* from all values being zero would be division by zero later on when */ + /* we normalize each value. We check to see that the values sum to zero. */ + /* NOTE: In this function, the order in which we iterate through the */ + /* vertices of interest should be the same as the order in which we do so */ + /* in the caller function. If the caller function doesn't care about the */ + /* order of values in the resulting vector V, then there's no need to take */ + /* special notice of that order. But in some cases the order of values in */ + /* V is taken into account, for example, in roulette wheel selection. */ + S = 0.0; + IGRAPH_CHECK(igraph_vit_create(graph, vs, &A)); + IGRAPH_FINALLY(igraph_vit_destroy, &A); + while (!IGRAPH_VIT_END(A)) { + v = (igraph_integer_t)IGRAPH_VIT_GET(A); + S += (igraph_real_t)VECTOR(*U)[v]; + IGRAPH_VIT_NEXT(A); + } + if (islocal) { + S += (igraph_real_t)VECTOR(*U)[vid]; + } + /* avoid division by zero later on */ + if (S == (igraph_real_t)0.0) { + igraph_vit_destroy(&A); + igraph_vs_destroy(&vs); + IGRAPH_FINALLY_CLEAN(2); + IGRAPH_ERROR("Vector of values sums to zero", IGRAPH_EINVAL); + } + + /* Get cumulative probability and relative value for each vertex in the */ + /* perspective of v. The vector V holds the cumulative proportionate */ + /* values of all vertices in v's perspective. The value V[0] is the */ + /* cumulative proportionate value of the first vertex in the vertex */ + /* iterator A. The value V[1] is the cumulative proportionate value of */ + /* the second vertex in the iterator A. And so on. If we are using the */ + /* local perspective, then we also need to consider the cumulative */ + /* proportionate value of v. In the case of the local perspective, we */ + /* don't need to compute and store v's cumulative proportionate value, */ + /* but we pretend that such value is appended to the vector V. */ + C = 0.0; + i = 0; + IGRAPH_VIT_RESET(A); + IGRAPH_CHECK(igraph_vector_resize(V, IGRAPH_VIT_SIZE(A))); + while (!IGRAPH_VIT_END(A)) { + v = (igraph_integer_t)IGRAPH_VIT_GET(A); + /* NOTE: Beware of division by zero here. This can happen if the vector */ + /* of values, or a combination of interest, sums to zero. */ + P = (igraph_real_t)VECTOR(*U)[v] / S; + C += P; + VECTOR(*V)[i] = C; + i++; + IGRAPH_VIT_NEXT(A); + } + + igraph_vit_destroy(&A); + igraph_vs_destroy(&vs); + + /* Pop A and vs from the finally stack -- that's two items */ + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/* + * Internal use only. + * A set of standard tests to be performed prior to strategy updates. The + * tests contained in this function are common to many strategy revision + * functions in this file. This function is meant to be invoked from within + * a specific strategy update function in order to perform certain common + * tests, including sanity checks and conditions under which no strategy + * updates are necessary. + * + * \param graph The graph object representing the game network. This cannot + * be the empty or trivial graph, but must have at least two vertices + * and one edge. If \p graph has one vertex, then no strategy update + * would take place. Furthermore, if \p graph has at least two vertices + * but zero edges, then strategy update would also not take place. + * \param vid The vertex whose strategy is to be updated. It is assumed that + * \p vid represents a vertex in \p graph. No checking is performed and + * it is your responsibility to ensure that \p vid is indeed a vertex + * of \p graph. If an isolated vertex is provided, i.e. the input + * vertex has degree 0, then no strategy update would take place and + * \p vid would retain its current strategy. Strategy update would also + * not take place if the local neighbourhood of \p vid are its + * in-neighbours (respectively out-neighbours), but \p vid has zero + * in-neighbours (respectively out-neighbours). Loops are ignored in + * computing the degree (in, out, all) of \p vid. + * \param quantities A vector of quantities providing the quantity of each + * vertex in \p graph. Think of each entry of the vector as being + * generated by a function such as the fitness function for the game. + * So if the vector represents fitness quantities, then each vector + * entry is the fitness of some vertex. The length of this vector must + * be the same as the number of vertices in the vertex set of \p graph. + * \param strategies A vector of the current strategies for the vertex + * population. Each strategy is identified with a nonnegative integer, + * whose interpretation depends on the payoff matrix of the game. + * Generally we use the strategy ID as a row or column index of the + * payoff matrix. The length of this vector must be the same as the + * number of vertices in the vertex set of \p graph. + * \param mode Defines the sort of neighbourhood to consider for \p vid. If + * \p graph is undirected, then we use all the immediate neighbours of + * \p vid. Thus if you know that \p graph is undirected, then it is safe + * to pass the value \p IGRAPH_ALL here. Supported values are: + * \clist + * \cli IGRAPH_OUT + * Use the out-neighbours of \p vid. This option is only relevant + * when \p graph is a directed graph. + * \cli IGRAPH_IN + * Use the in-neighbours of \p vid. Again this option is only relevant + * when \p graph is a directed graph. + * \cli IGRAPH_ALL + * Use both the in- and out-neighbours of \p vid. This option is only + * relevant if \p graph is a digraph. Also use this value if + * \p graph is undirected. + * \endclist + * \param updates Boolean; at the end of this test suite, this flag + * indicates whether to proceed with strategy revision. If true then + * strategy revision should proceed; otherwise there is no need to + * continue with revising a vertex's strategy. A caller function that + * invokes this function would use the value of \p updates to + * determine whether to proceed with strategy revision. + * \param islocal Boolean; this flag controls which perspective to use. If + * true then we use the local perspective; otherwise we use the global + * perspective. The local perspective for \p vid is the set of all + * immediate neighbours of \p vid. In contrast, the global perspective + * for \p vid is the vertex set of \p graph. + * \return Codes: + * \clist + * \cli IGRAPH_EINVAL + * This error code is returned in each of the following cases: + * (1) Any of the parameters \p graph, \p quantities, or + * \p strategies is a null pointer. (2) The vector \p quantities + * or \p strategies has a length different from the number of + * vertices in \p graph. (3) The parameter \p graph is the empty + * or null graph, i.e. the graph with zero vertices and edges. + * \cli IGRAPH_SUCCESS + * This signal is returned if no errors were raised. You should use + * the value of the boolean \p updates to decide whether to go + * ahead with updating a vertex's strategy. + * \endclist + */ + +static int igraph_i_microscopic_standard_tests(const igraph_t *graph, + igraph_integer_t vid, + const igraph_vector_t *quantities, + const igraph_vector_t *strategies, + igraph_neimode_t mode, + igraph_bool_t *updates, + igraph_bool_t islocal) { + + igraph_integer_t nvert; + igraph_vector_t degv; + *updates = 1; + + /* sanity checks */ + if (graph == NULL) { + IGRAPH_ERROR("Graph is a null pointer", IGRAPH_EINVAL); + } + if (quantities == NULL) { + IGRAPH_ERROR("Quantities vector is a null pointer", IGRAPH_EINVAL); + } + if (strategies == NULL) { + IGRAPH_ERROR("Strategies vector is a null pointer", IGRAPH_EINVAL); + } + + /* the empty graph */ + nvert = igraph_vcount(graph); + if (nvert < 1) { + IGRAPH_ERROR("Graph cannot be the empty graph", IGRAPH_EINVAL); + } + /* invalid vector length */ + if (nvert != (igraph_integer_t)igraph_vector_size(quantities)) { + IGRAPH_ERROR("Size of quantities vector different from number of vertices", + IGRAPH_EINVAL); + } + if (nvert != (igraph_integer_t)igraph_vector_size(strategies)) { + IGRAPH_ERROR("Size of strategies vector different from number of vertices", + IGRAPH_EINVAL); + } + + /* Various conditions under which no strategy updates will take place. That + * is, the vertex retains its current strategy. + */ + /* given graph has < 2 vertices */ + if (nvert < 2) { + *updates = 0; + } + /* graph has >= 2 vertices, but no edges */ + if (igraph_ecount(graph) < 1) { + *updates = 0; + } + + /* Test for vertex isolation, depending on the perspective given. For + * undirected graphs, a given vertex v is isolated if its degree is zero. + * If we are considering in-neighbours (respectively out-neighbours), then + * we say that v is isolated if its in-degree (respectively out-degree) is + * zero. In general, this vertex isolation test is only relevant if we are + * using a local perspective, i.e. if we only consider the immediate + * neighbours (local perspective) of v as opposed to all vertices in the + * vertex set of the graph (global perspective). + */ + if (islocal) { + /* Moving on ahead with vertex isolation test, since local perspective */ + /* is requested. */ + IGRAPH_VECTOR_INIT_FINALLY(°v, 1); + IGRAPH_CHECK(igraph_degree(graph, °v, igraph_vss_1(vid), + mode, IGRAPH_NO_LOOPS)); + if (VECTOR(degv)[0] < 1) { + *updates = 0; + } + igraph_vector_destroy(°v); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup spatialgames + * \function igraph_deterministic_optimal_imitation + * \brief Adopt a strategy via deterministic optimal imitation. + * + * A simple deterministic imitation strategy where a vertex revises its + * strategy to that which yields a local optimal. Here "local" is with + * respect to the immediate neighbours of the vertex. The vertex retains its + * current strategy where this strategy yields a locally optimal quantity. + * The quantity in this case could be a measure such as fitness. + * + * \param graph The graph object representing the game network. This cannot + * be the empty or trivial graph, but must have at least two vertices + * and one edge. If \p graph has one vertex, then no strategy update + * would take place. Furthermore, if \p graph has at least two vertices + * but zero edges, then strategy update would also not take place. + * \param vid The vertex whose strategy is to be updated. It is assumed that + * \p vid represents a vertex in \p graph. No checking is performed and + * it is your responsibility to ensure that \p vid is indeed a vertex + * of \p graph. If an isolated vertex is provided, i.e. the input + * vertex has degree 0, then no strategy update would take place and + * \p vid would retain its current strategy. Strategy update would also + * not take place if the local neighbourhood of \p vid are its + * in-neighbours (respectively out-neighbours), but \p vid has zero + * in-neighbours (respectively out-neighbours). Loops are ignored in + * computing the degree (in, out, all) of \p vid. + * \param optimality Logical; controls the type of optimality to be used. + * Supported values are: + * \clist + * \cli IGRAPH_MAXIMUM + * Use maximum deterministic imitation, where the strategy of the + * vertex with maximum quantity (e.g. fitness) would be adopted. We + * update the strategy of \p vid to that which yields a local + * maximum. + * \cli IGRAPH_MINIMUM + * Use minimum deterministic imitation. That is, the strategy of the + * vertex with minimum quantity would be imitated. In other words, + * update to the strategy that yields a local minimum. + * \endclist + * \param quantities A vector of quantities providing the quantity of each + * vertex in \p graph. Think of each entry of the vector as being + * generated by a function such as the fitness function for the game. + * So if the vector represents fitness quantities, then each vector + * entry is the fitness of some vertex. The length of this vector must + * be the same as the number of vertices in the vertex set of \p graph. + * \param strategies A vector of the current strategies for the vertex + * population. The updated strategy for \p vid would be stored here. + * Each strategy is identified with a nonnegative integer, whose + * interpretation depends on the payoff matrix of the game. Generally + * we use the strategy ID as a row or column index of the payoff + * matrix. The length of this vector must be the same as the number of + * vertices in the vertex set of \p graph. + * \param mode Defines the sort of neighbourhood to consider for \p vid. If + * \p graph is undirected, then we use all the immediate neighbours of + * \p vid. Thus if you know that \p graph is undirected, then it is safe + * to pass the value \p IGRAPH_ALL here. Supported values are: + * \clist + * \cli IGRAPH_OUT + * Use the out-neighbours of \p vid. This option is only relevant + * when \p graph is a directed graph. + * \cli IGRAPH_IN + * Use the in-neighbours of \p vid. Again this option is only relevant + * when \p graph is a directed graph. + * \cli IGRAPH_ALL + * Use both the in- and out-neighbours of \p vid. This option is only + * relevant if \p graph is a digraph. Also use this value if + * \p graph is undirected. + * \endclist + * \return The error code \p IGRAPH_EINVAL is returned in each of the + * following cases: (1) Any of the parameters \p graph, \p quantities, + * or \p strategies is a null pointer. (2) The vector \p quantities + * or \p strategies has a length different from the number of vertices + * in \p graph. (3) The parameter \p graph is the empty or null graph, + * i.e. the graph with zero vertices and edges. + * + * Time complexity: O(2d), where d is the degree of the vertex \p vid. + * + * \example examples/simple/igraph_deterministic_optimal_imitation.c + */ + +int igraph_deterministic_optimal_imitation(const igraph_t *graph, + igraph_integer_t vid, + igraph_optimal_t optimality, + const igraph_vector_t *quantities, + igraph_vector_t *strategies, + igraph_neimode_t mode) { + igraph_integer_t i, k, v; + igraph_real_t q; + igraph_vector_t adj; + igraph_bool_t updates; + + IGRAPH_CHECK(igraph_i_microscopic_standard_tests(graph, vid, quantities, + strategies, mode, &updates, + /*is local?*/ 1)); + if (!updates) { + return IGRAPH_SUCCESS; /* Nothing to do */ + } + + /* Choose a locally optimal strategy to imitate. This can be either maximum + * or minimum deterministic imitation. By now we know that the given vertex v + * has degree >= 1 and at least 1 edge. Then within its immediate + * neighbourhood adj(v) and including v itself, there exists a vertex whose + * strategy yields a local optimal quantity. + */ + /* Random permutation of adj(v). This ensures that if there are multiple */ + /* candidates with an optimal strategy, then we choose one such candidate */ + /* at random. */ + IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &adj, vid, mode)); + IGRAPH_CHECK(igraph_vector_shuffle(&adj)); + /* maximum deterministic imitation */ + i = vid; + q = (igraph_real_t)VECTOR(*quantities)[vid]; + if (optimality == IGRAPH_MAXIMUM) { + for (k = 0; k < igraph_vector_size(&adj); k++) { + v = (igraph_integer_t) VECTOR(adj)[k]; + if ((igraph_real_t)VECTOR(*quantities)[v] > q) { + i = v; + q = (igraph_real_t)VECTOR(*quantities)[v]; + } + } + } else { /* minimum deterministic imitation */ + for (k = 0; k < igraph_vector_size(&adj); k++) { + v = (igraph_integer_t) VECTOR(adj)[k]; + if ((igraph_real_t)VECTOR(*quantities)[v] < q) { + i = v; + q = (igraph_real_t)VECTOR(*quantities)[v]; + } + } + } + /* Now i is a vertex with a locally optimal quantity, the value of which */ + /* is q. Update the strategy of vid to that of i. */ + VECTOR(*strategies)[vid] = VECTOR(*strategies)[i]; + igraph_vector_destroy(&adj); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup spatialgames + * \function igraph_moran_process + * \brief The Moran process in a network setting. + * + * This is an extension of the classic Moran process to a network setting. + * The Moran process is a model of haploid (asexual) reproduction within a + * population having a fixed size. In the network setting, the Moran process + * operates on a weighted graph. At each time step a vertex a is chosen for + * reproduction and another vertex b is chosen for death. Vertex a gives birth + * to an identical clone c, which replaces b. Vertex c is a clone of a in that + * c inherits both the current quantity (e.g. fitness) and current strategy + * of a. + * + * + * The graph G representing the game network is assumed to be simple, + * i.e. free of loops and without multiple edges. If, on the other hand, G has + * a loop incident on some vertex v, then it is possible that when v is chosen + * for reproduction it would forgo this opportunity. In particular, when v is + * chosen for reproduction and v is also chosen for death, the clone of v + * would be v itself with its current vertex ID. In effect v forgoes its + * chance for reproduction. + * + * \param graph The graph object representing the game network. This cannot + * be the empty or trivial graph, but must have at least two vertices + * and one edge. The Moran process will not take place in each of the + * following cases: (1) If \p graph has one vertex. (2) If \p graph has + * at least two vertices but zero edges. + * \param weights A vector of all edge weights for \p graph. Thus weights[i] + * means the weight of the edge with edge ID i. For the purpose of the + * Moran process, each weight is assumed to be positive; it is your + * responsibility to ensure this condition holds. The length of this + * vector must be the same as the number of edges in \p graph. + * \param quantities A vector of quantities providing the quantity of each + * vertex in \p graph. The quantity of the new clone will be stored + * here. Think of each entry of the vector as being generated by a + * function such as the fitness function for the game. So if the vector + * represents fitness quantities, then each vector entry is the fitness + * of some vertex. The length of this vector must be the same as the + * number of vertices in the vertex set of \p graph. For the purpose of + * the Moran process, each vector entry is assumed to be nonnegative; + * no checks will be performed for this. It is your responsibility to + * ensure that at least one entry is positive. Furthermore, this vector + * cannot be a vector of zeros; this condition will be checked. + * \param strategies A vector of the current strategies for the vertex + * population. The strategy of the new clone will be stored here. Each + * strategy is identified with a nonnegative integer, whose + * interpretation depends on the payoff matrix of the game. Generally + * we use the strategy ID as a row or column index of the payoff + * matrix. The length of this vector must be the same as the number of + * vertices in the vertex set of \p graph. + * \param mode Defines the sort of neighbourhood to consider for the vertex a + * chosen for reproduction. This is only relevant if \p graph is + * directed. If \p graph is undirected, then it is safe to pass the + * value \p IGRAPH_ALL here. Supported values are: + * \clist + * \cli IGRAPH_OUT + * Use the out-neighbours of a. This option is only relevant when + * \p graph is directed. + * \cli IGRAPH_IN + * Use the in-neighbours of a. Again this option is only relevant + * when \p graph is directed. + * \cli IGRAPH_ALL + * Use both the in- and out-neighbours of a. This option is only + * relevant if \p graph is directed. Also use this value if + * \p graph is undirected. + * \endclist + * \return The error code \p IGRAPH_EINVAL is returned in each of the following + * cases: (1) Any of the parameters \p graph, \p weights, + * \p quantities or \p strategies is a null pointer. (2) The vector + * \p quantities or \p strategies has a length different from the + * number of vertices in \p graph. (3) The vector \p weights has a + * length different from the number of edges in \p graph. (4) The + * parameter \p graph is the empty or null graph, i.e. the graph with + * zero vertices and edges. (5) The vector \p weights, or the + * combination of interest, sums to zero. (6) The vector \p quantities, + * or the combination of interest, sums to zero. + * + * Time complexity: depends on the random number generator, but is usually + * O(n) where n is the number of vertices in \p graph. + * + * + * References: + * \clist + * \cli (Lieberman et al. 2005) + * E. Lieberman, C. Hauert, and M. A. Nowak. Evolutionary dynamics on + * graphs. \emb Nature, \eme 433(7023):312--316, 2005. + * \cli (Moran 1958) + * P. A. P. Moran. Random processes in genetics. \emb Mathematical + * Proceedings of the Cambridge Philosophical Society, \eme 54(1):60--71, + * 1958. + * \endclist + */ + +int igraph_moran_process(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_t *quantities, + igraph_vector_t *strategies, + igraph_neimode_t mode) { + igraph_bool_t updates; + igraph_integer_t a = -1; /* vertex chosen for reproduction */ + igraph_integer_t b = -1; /* vertex chosen for death */ + igraph_integer_t e, nedge, u, v; + igraph_real_t r; /* random number */ + igraph_vector_t deg; + igraph_vector_t V; /* vector of cumulative proportionate values */ + igraph_vit_t vA; /* vertex list */ + igraph_eit_t eA; /* edge list */ + igraph_vs_t vs; + igraph_es_t es; + long int i; + + /* don't test for vertex isolation, hence vid = -1 and islocal = 0 */ + IGRAPH_CHECK(igraph_i_microscopic_standard_tests(graph, /*vid*/ -1, + quantities, strategies, mode, + &updates, /*is local?*/ 0)); + if (!updates) { + return IGRAPH_SUCCESS; /* nothing more to do */ + } + if (weights == NULL) { + IGRAPH_ERROR("Weights vector is a null pointer", IGRAPH_EINVAL); + } + nedge = igraph_ecount(graph); + if (nedge != (igraph_integer_t)igraph_vector_size(weights)) { + IGRAPH_ERROR("Size of weights vector different from number of edges", + IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&V, 0); + + /* Cumulative proportionate quantities. We are using the global */ + /* perspective, hence islocal = 0, vid = -1 and mode = IGRAPH_ALL. */ + IGRAPH_CHECK(igraph_i_vcumulative_proportionate_values(graph, quantities, &V, + /*is local?*/ 0, + /*vid*/ -1, + /*mode*/ IGRAPH_ALL)); + + /* Choose a vertex for reproduction from among all vertices in the graph. */ + /* The vertex is chosen proportionate to its quantity and such that its */ + /* degree is >= 1. In case we are considering in-neighbours (respectively */ + /* out-neighbours), the chosen vertex must have in-degree (respectively */ + /* out-degree) >= 1. All loops will be ignored. At this point, we know */ + /* that the graph has at least one edge, which may be directed or not. */ + /* Furthermore the quantities of all vertices sum to a positive value. */ + /* Hence at least one vertex will be chosen for reproduction. */ + IGRAPH_CHECK(igraph_vs_all(&vs)); + IGRAPH_FINALLY(igraph_vs_destroy, &vs); + IGRAPH_CHECK(igraph_vit_create(graph, vs, &vA)); + IGRAPH_FINALLY(igraph_vit_destroy, &vA); + RNG_BEGIN(); + r = RNG_UNIF01(); + RNG_END(); + i = 0; + IGRAPH_VECTOR_INIT_FINALLY(°, 1); + while (!IGRAPH_VIT_END(vA)) { + u = (igraph_integer_t)IGRAPH_VIT_GET(vA); + IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_1(u), mode, + IGRAPH_NO_LOOPS)); + if (VECTOR(deg)[0] < 1) { + i++; + IGRAPH_VIT_NEXT(vA); + continue; + } + if (r <= VECTOR(V)[i]) { + /* we have found our candidate vertex for reproduction */ + a = u; + break; + } + i++; + IGRAPH_VIT_NEXT(vA); + } + /* By now we should have chosen a vertex for reproduction. Check this. */ + IGRAPH_ASSERT(a >= 0); + + /* Cumulative proportionate weights. We are using the local perspective */ + /* with respect to vertex a, which has been chosen for reproduction. */ + /* The degree of a is deg(a) >= 1 with respect to the mode "mode", which */ + /* can flag either the in-degree, out-degree or all degree of a. But it */ + /* still might happen that the edge weights of interest would sum to zero. */ + /* An error would be raised in that case. */ + IGRAPH_CHECK(igraph_i_ecumulative_proportionate_values(graph, weights, &V, + /*is local?*/ 1, + /*vertex*/ a, mode)); + + /* Choose a vertex for death from among all vertices in a's perspective. */ + /* Let E be all the edges in the perspective of a. If (u,v) \in E is any */ + /* such edge, then we have a = u or a = v. That is, any edge in E has a */ + /* for one of its endpoints. As G is assumed to be a simple graph, then */ + /* exactly one of u or v is the vertex a. Without loss of generality, we */ + /* assume that each edge in E has the form (a, v_i). Then the vertex v_j */ + /* chosen for death is chosen proportionate to the weight of the edge */ + /* (a, v_j). */ + IGRAPH_CHECK(igraph_es_incident(&es, a, mode)); + IGRAPH_FINALLY(igraph_es_destroy, &es); + IGRAPH_CHECK(igraph_eit_create(graph, es, &eA)); + IGRAPH_FINALLY(igraph_eit_destroy, &eA); + RNG_BEGIN(); + r = RNG_UNIF01(); + RNG_END(); + i = 0; + while (!IGRAPH_EIT_END(eA)) { + e = (igraph_integer_t)IGRAPH_EIT_GET(eA); + if (r <= VECTOR(V)[i]) { + /* We have found our candidate vertex for death; call this vertex b. */ + /* As G is simple, then a =/= b. Check the latter condition. */ + IGRAPH_CHECK(igraph_edge(graph, /*edge ID*/ e, + /*tail vertex*/ &u, /*head vertex*/ &v)); + if (a == u) { + b = v; + } else { + b = u; + } + IGRAPH_ASSERT(a != b); /* always true if G is simple */ + break; + } + i++; + IGRAPH_EIT_NEXT(eA); + } + + /* By now a vertex a is chosen for reproduction and a vertex b is chosen */ + /* for death. Check that b has indeed been chosen. Clone vertex a and kill */ + /* vertex b. Let the clone c have the vertex ID of b, and the strategy and */ + /* quantity of a. */ + IGRAPH_ASSERT(b >= 0); + VECTOR(*quantities)[b] = VECTOR(*quantities)[a]; + VECTOR(*strategies)[b] = VECTOR(*strategies)[a]; + + igraph_eit_destroy(&eA); + igraph_es_destroy(&es); + igraph_vector_destroy(°); + igraph_vit_destroy(&vA); + igraph_vs_destroy(&vs); + igraph_vector_destroy(&V); + IGRAPH_FINALLY_CLEAN(6); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup spatialgames + * \function igraph_roulette_wheel_imitation + * \brief Adopt a strategy via roulette wheel selection. + * + * A simple stochastic imitation strategy where a vertex revises its + * strategy to that of a vertex u chosen proportionate to u's quantity + * (e.g. fitness). This is a special case of stochastic imitation, where a + * candidate is not chosen uniformly at random but proportionate to its + * quantity. + * + * \param graph The graph object representing the game network. This cannot + * be the empty or trivial graph, but must have at least two vertices + * and one edge. If \p graph has one vertex, then no strategy update + * would take place. Furthermore, if \p graph has at least two vertices + * but zero edges, then strategy update would also not take place. + * \param vid The vertex whose strategy is to be updated. It is assumed that + * \p vid represents a vertex in \p graph. No checking is performed and + * it is your responsibility to ensure that \p vid is indeed a vertex + * of \p graph. If an isolated vertex is provided, i.e. the input + * vertex has degree 0, then no strategy update would take place and + * \p vid would retain its current strategy. Strategy update would also + * not take place if the local neighbourhood of \p vid are its + * in-neighbours (respectively out-neighbours), but \p vid has zero + * in-neighbours (respectively out-neighbours). Loops are ignored in + * computing the degree (in, out, all) of \p vid. + * \param islocal Boolean; this flag controls which perspective to use in + * computing the relative quantity. If true then we use the local + * perspective; otherwise we use the global perspective. The local + * perspective for \p vid is the set of all immediate neighbours of + * \p vid. In contrast, the global perspective for \p vid is the + * vertex set of \p graph. + * \param quantities A vector of quantities providing the quantity of each + * vertex in \p graph. Think of each entry of the vector as being + * generated by a function such as the fitness function for the game. + * So if the vector represents fitness quantities, then each vector + * entry is the fitness of some vertex. The length of this vector must + * be the same as the number of vertices in the vertex set of \p graph. + * For the purpose of roulette wheel selection, each vector entry is + * assumed to be nonnegative; no checks will be performed for this. It + * is your responsibility to ensure that at least one entry is nonzero. + * Furthermore, this vector cannot be a vector of zeros; this condition + * will be checked. + * \param strategies A vector of the current strategies for the vertex + * population. The updated strategy for \p vid would be stored here. + * Each strategy is identified with a nonnegative integer, whose + * interpretation depends on the payoff matrix of the game. Generally + * we use the strategy ID as a row or column index of the payoff + * matrix. The length of this vector must be the same as the number of + * vertices in the vertex set of \p graph. + * \param mode Defines the sort of neighbourhood to consider for \p vid. This + * is only relevant if we are considering the local perspective, i.e. if + * \p islocal is true. If we are considering the global perspective, + * then it is safe to pass the value \p IGRAPH_ALL here. If \p graph is + * undirected, then we use all the immediate neighbours of \p vid. Thus + * if you know that \p graph is undirected, then it is safe to pass the + * value \p IGRAPH_ALL here. Supported values are: + * \clist + * \cli IGRAPH_OUT + * Use the out-neighbours of \p vid. This option is only relevant + * when \p graph is a digraph and we are considering the local + * perspective. + * \cli IGRAPH_IN + * Use the in-neighbours of \p vid. Again this option is only relevant + * when \p graph is a directed graph and we are considering the local + * perspective. + * \cli IGRAPH_ALL + * Use both the in- and out-neighbours of \p vid. This option is only + * relevant if \p graph is a digraph. Also use this value if + * \p graph is undirected or we are considering the global + * perspective. + * \endclist + * \return The error code \p IGRAPH_EINVAL is returned in each of the following + * cases: (1) Any of the parameters \p graph, \p quantities, or + * \p strategies is a null pointer. (2) The vector \p quantities or + * \p strategies has a length different from the number of vertices + * in \p graph. (3) The parameter \p graph is the empty or null graph, + * i.e. the graph with zero vertices and edges. (4) The vector + * \p quantities sums to zero. + * + * Time complexity: O(n) where n is the number of vertices in the perspective + * to consider. If we consider the global perspective, then n is the number + * of vertices in the vertex set of \p graph. On the other hand, for the local + * perspective n is the degree of \p vid, excluding loops. + * + * + * Reference: + * \clist + * \cli (Yu & Gen 2010) + * X. Yu and M. Gen. \emb Introduction to Evolutionary Algorithms. \eme + * Springer, 2010, pages 18--20. + * \endclist + * + * \example examples/simple/igraph_roulette_wheel_imitation.c + */ + +int igraph_roulette_wheel_imitation(const igraph_t *graph, + igraph_integer_t vid, + igraph_bool_t islocal, + const igraph_vector_t *quantities, + igraph_vector_t *strategies, + igraph_neimode_t mode) { + igraph_bool_t updates; + igraph_integer_t u; + igraph_real_t r; /* random number */ + igraph_vector_t V; /* vector of cumulative proportionate quantities */ + igraph_vit_t A; /* all vertices in v's perspective */ + igraph_vs_t vs; + long int i; + + IGRAPH_CHECK(igraph_i_microscopic_standard_tests(graph, vid, quantities, + strategies, mode, &updates, + islocal)); + if (!updates) { + return IGRAPH_SUCCESS; /* nothing further to do */ + } + + /* set the perspective */ + if (islocal) { + IGRAPH_CHECK(igraph_vs_adj(&vs, vid, mode)); + } else { + IGRAPH_CHECK(igraph_vs_all(&vs)); + } + IGRAPH_FINALLY(igraph_vs_destroy, &vs); + IGRAPH_CHECK(igraph_vit_create(graph, vs, &A)); + IGRAPH_FINALLY(igraph_vit_destroy, &A); + + IGRAPH_VECTOR_INIT_FINALLY(&V, 0); + + IGRAPH_CHECK(igraph_i_vcumulative_proportionate_values(graph, quantities, &V, + islocal, vid, mode)); + + /* Finally, choose a vertex u to imitate. The vertex u is chosen */ + /* proportionate to its quantity. In the case of a local perspective, we */ + /* pretend that v's cumulative proportionate quantity has been appended to */ + /* the vector V. Let V be of length n so that V[n-1] is the last element */ + /* of V, and let r be a real number chosen uniformly at random from the */ + /* unit interval [0,1]. If r > V[i] for all i < n, then v defaults to */ + /* retaining its current strategy. Similarly in the case of the global */ + /* perspective, if r > V[i] for all i < n - 1 then v would adopt the */ + /* strategy of the vertex whose cumulative proportionate quantity is */ + /* V[n-1]. */ + /* NOTE: Here we assume that the order in which we iterate through the */ + /* vertices in A is the same as the order in which we do so in the */ + /* invoked function igraph_vcumulative_proportionate_values(). */ + /* Otherwise we would incorrectly associate each V[i] with a vertex in A. */ + RNG_BEGIN(); + r = RNG_UNIF01(); + RNG_END(); + i = 0; + while (!IGRAPH_VIT_END(A)) { + if (r <= VECTOR(V)[i]) { + /* We have found our candidate vertex for imitation. Update strategy */ + /* of v to that of u, and exit the selection loop. */ + u = (igraph_integer_t)IGRAPH_VIT_GET(A); + VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; + break; + } + i++; + IGRAPH_VIT_NEXT(A); + } + + /* By now, vertex v should either retain its current strategy or it has */ + /* adopted the strategy of a vertex in its perspective. Nothing else to */ + /* do, but clean up. */ + igraph_vector_destroy(&V); + igraph_vit_destroy(&A); + igraph_vs_destroy(&vs); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup spatialgames + * \function igraph_stochastic_imitation + * \brief Adopt a strategy via stochastic imitation with uniform selection. + * + * A simple stochastic imitation strategy where a vertex revises its + * strategy to that of a vertex chosen uniformly at random from its local + * neighbourhood. This is called stochastic imitation via uniform selection, + * where the strategy to imitate is chosen via some random process. For the + * purposes of this function, we use uniform selection from a pool of + * candidates. + * + * \param graph The graph object representing the game network. This cannot + * be the empty or trivial graph, but must have at least two vertices + * and one edge. If \p graph has one vertex, then no strategy update + * would take place. Furthermore, if \p graph has at least two vertices + * but zero edges, then strategy update would also not take place. + * \param vid The vertex whose strategy is to be updated. It is assumed that + * \p vid represents a vertex in \p graph. No checking is performed and + * it is your responsibility to ensure that \p vid is indeed a vertex + * of \p graph. If an isolated vertex is provided, i.e. the input + * vertex has degree 0, then no strategy update would take place and + * \p vid would retain its current strategy. Strategy update would also + * not take place if the local neighbourhood of \p vid are its + * in-neighbours (respectively out-neighbours), but \p vid has zero + * in-neighbours (respectively out-neighbours). Loops are ignored in + * computing the degree (in, out, all) of \p vid. + * \param algo This flag controls which algorithm to use in stochastic + * imitation. Supported values are: + * \clist + * \cli IGRAPH_IMITATE_AUGMENTED + * Augmented imitation. Vertex \p vid imitates the strategy of the + * chosen vertex u provided that doing so would increase the + * quantity (e.g. fitness) of \p vid. Augmented imitation can be + * thought of as "imitate if better". + * \cli IGRAPH_IMITATE_BLIND + * Blind imitation. Vertex \p vid blindly imitates the strategy of + * the chosen vertex u, regardless of whether doing so would + * increase or decrease the quantity of \p vid. + * \cli IGRAPH_IMITATE_CONTRACTED + * Contracted imitation. Here vertex \p vid imitates the strategy of + * the chosen vertex u if doing so would decrease the quantity of + * \p vid. Think of contracted imitation as "imitate if worse". + * \endclist + * \param quantities A vector of quantities providing the quantity of each + * vertex in \p graph. Think of each entry of the vector as being + * generated by a function such as the fitness function for the game. + * So if the vector represents fitness quantities, then each vector + * entry is the fitness of some vertex. The length of this vector must + * be the same as the number of vertices in the vertex set of \p graph. + * \param strategies A vector of the current strategies for the vertex + * population. The updated strategy for \p vid would be stored here. + * Each strategy is identified with a nonnegative integer, whose + * interpretation depends on the payoff matrix of the game. Generally + * we use the strategy ID as a row or column index of the payoff + * matrix. The length of this vector must be the same as the number of + * vertices in the vertex set of \p graph. + * \param mode Defines the sort of neighbourhood to consider for \p vid. If + * \p graph is undirected, then we use all the immediate neighbours of + * \p vid. Thus if you know that \p graph is undirected, then it is safe + * to pass the value \p IGRAPH_ALL here. Supported values are: + * \clist + * \cli IGRAPH_OUT + * Use the out-neighbours of \p vid. This option is only relevant + * when \p graph is a directed graph. + * \cli IGRAPH_IN + * Use the in-neighbours of \p vid. Again this option is only relevant + * when \p graph is a directed graph. + * \cli IGRAPH_ALL + * Use both the in- and out-neighbours of \p vid. This option is only + * relevant if \p graph is a digraph. Also use this value if + * \p graph is undirected. + * \endclist + * \return The error code \p IGRAPH_EINVAL is returned in each of the following + * cases: (1) Any of the parameters \p graph, \p quantities, or + * \p strategies is a null pointer. (2) The vector \p quantities or + * \p strategies has a length different from the number of vertices + * in \p graph. (3) The parameter \p graph is the empty or null graph, + * i.e. the graph with zero vertices and edges. (4) The parameter + * \p algo refers to an unsupported stochastic imitation algorithm. + * + * Time complexity: depends on the uniform random number generator, but should + * usually be O(1). + * + * \example examples/simple/igraph_stochastic_imitation.c + */ + +int igraph_stochastic_imitation(const igraph_t *graph, + igraph_integer_t vid, + igraph_imitate_algorithm_t algo, + const igraph_vector_t *quantities, + igraph_vector_t *strategies, + igraph_neimode_t mode) { + igraph_bool_t updates; + igraph_integer_t u; + igraph_vector_t adj; + int i; + + /* sanity checks */ + if (algo != IGRAPH_IMITATE_AUGMENTED && + algo != IGRAPH_IMITATE_BLIND && + algo != IGRAPH_IMITATE_CONTRACTED) { + IGRAPH_ERROR("Unsupported stochastic imitation algorithm", + IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_i_microscopic_standard_tests(graph, vid, quantities, + strategies, mode, &updates, + /*is local?*/ 1)); + if (!updates) { + return IGRAPH_SUCCESS; /* nothing more to do */ + } + + /* immediate neighbours of v */ + IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &adj, vid, mode)); + + /* Blind imitation. Let v be the vertex whose strategy we want to revise. */ + /* Choose a vertex u uniformly at random from the immediate neighbours of */ + /* v, including v itself. Then blindly update the strategy of v to that of */ + /* u, irrespective of whether doing so would increase or decrease the */ + /* quantity (e.g. fitness) of v. Here v retains its current strategy if */ + /* the chosen vertex u is indeed v itself. */ + if (algo == IGRAPH_IMITATE_BLIND) { + IGRAPH_CHECK(igraph_vector_push_back(&adj, vid)); + RNG_BEGIN(); + i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1); + RNG_END(); + u = (igraph_integer_t) VECTOR(adj)[i]; + VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; + } + /* Augmented imitation. Let v be the vertex whose strategy we want to */ + /* revise. Let f be the quantity function for the game. Choose a vertex u */ + /* uniformly at random from the immediate neighbours of v; do not include */ + /* v. Then v imitates the strategy of u if f(u) > f(v). Otherwise v */ + /* retains its current strategy. */ + else if (algo == IGRAPH_IMITATE_AUGMENTED) { + RNG_BEGIN(); + i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1); + RNG_END(); + u = (igraph_integer_t) VECTOR(adj)[i]; + if (VECTOR(*quantities)[u] > VECTOR(*quantities)[vid]) { + VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; + } + } + /* Contracted imitation. Let v be the vertex whose strategy we want to */ + /* update and let f be the quantity function for the game. Choose a vertex */ + /* u uniformly at random from the immediate neighbours of v, excluding v */ + /* itself. Then v imitates the strategy of u provided that f(u) < f(v). */ + /* Otherwise v retains its current strategy. */ + else if (algo == IGRAPH_IMITATE_CONTRACTED) { + RNG_BEGIN(); + i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1); + RNG_END(); + u = (igraph_integer_t) VECTOR(adj)[i]; + if (VECTOR(*quantities)[u] < VECTOR(*quantities)[vid]) { + VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; + } + } + + /* clean up */ + igraph_vector_destroy(&adj); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/misc/mixing.c b/src/rigraph/core/misc/mixing.c new file mode 100644 index 0000000..d6da467 --- /dev/null +++ b/src/rigraph/core/misc/mixing.c @@ -0,0 +1,307 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_mixing.h" + +#include "igraph_interface.h" + +/** + * \function igraph_assortativity_nominal + * Assortativity of a graph based on vertex categories + * + * Assuming the vertices of the input graph belong to different + * categories, this function calculates the assortativity coefficient of + * the graph. The assortativity coefficient is between minus one and one + * and it is one if all connections stay within categories, it is + * minus one, if the network is perfectly disassortative. For a + * randomly connected network it is (asymptotically) zero. + * + * See equation (2) in M. E. J. Newman: Mixing patterns + * in networks, Phys. Rev. E 67, 026126 (2003) + * (http://arxiv.org/abs/cond-mat/0209450) for the proper + * definition. + * + * \param graph The input graph, it can be directed or undirected. + * \param types Vector giving the vertex types. They are assumed to be + * integer numbers, starting with zero. + * \param res Pointer to a real variable, the result is stored here. + * \param directed Boolean, it gives whether to consider edge + * directions in a directed graph. It is ignored for undirected + * graphs. + * \return Error code. + * + * Time complexity: O(|E|+t), |E| is the number of edges, t is the + * number of vertex types. + * + * \sa \ref igraph_assortativity if the vertex types are defines by + * numeric values (e.g. vertex degree), instead of categories. + * + * \example examples/simple/assortativity.c + */ + +int igraph_assortativity_nominal(const igraph_t *graph, + const igraph_vector_t *types, + igraph_real_t *res, + igraph_bool_t directed) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int no_of_types; + igraph_vector_t ai, bi, eii; + long int e, i; + igraph_real_t sumaibi = 0.0, sumeii = 0.0; + + if (igraph_vector_size(types) != no_of_nodes) { + IGRAPH_ERROR("Invalid `types' vector length", IGRAPH_EINVAL); + } + + if (no_of_nodes == 0) { + *res = IGRAPH_NAN; + return IGRAPH_SUCCESS; + } + + /* 'types' length > 0 here, safe to call vector_min() */ + if (igraph_vector_min(types) < 0) { + IGRAPH_ERROR("Invalid `types' vector", IGRAPH_EINVAL); + } + + directed = directed && igraph_is_directed(graph); + + no_of_types = (long int) igraph_vector_max(types) + 1; + IGRAPH_VECTOR_INIT_FINALLY(&ai, no_of_types); + IGRAPH_VECTOR_INIT_FINALLY(&bi, no_of_types); + IGRAPH_VECTOR_INIT_FINALLY(&eii, no_of_types); + + for (e = 0; e < no_of_edges; e++) { + long int from = IGRAPH_FROM(graph, e); + long int to = IGRAPH_TO(graph, e); + long int from_type = (long int) VECTOR(*types)[from]; + long int to_type = (long int) VECTOR(*types)[to]; + + VECTOR(ai)[from_type] += 1; + VECTOR(bi)[to_type] += 1; + if (from_type == to_type) { + VECTOR(eii)[from_type] += 1; + } + if (!directed) { + if (from_type == to_type) { + VECTOR(eii)[from_type] += 1; + } + VECTOR(ai)[to_type] += 1; + VECTOR(bi)[from_type] += 1; + } + } + + for (i = 0; i < no_of_types; i++) { + sumaibi += (VECTOR(ai)[i] / no_of_edges) * (VECTOR(bi)[i] / no_of_edges); + sumeii += (VECTOR(eii)[i] / no_of_edges); + } + + if (!directed) { + sumaibi /= 4.0; + sumeii /= 2.0; + } + + *res = (sumeii - sumaibi) / (1.0 - sumaibi); + + igraph_vector_destroy(&eii); + igraph_vector_destroy(&bi); + igraph_vector_destroy(&ai); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \function igraph_assortativity + * Assortativity based on numeric properties of vertices + * + * This function calculates the assortativity coefficient of the input + * graph. This coefficient is basically the correlation between the + * actual connectivity patterns of the vertices and the pattern + * expected from the distribution of the vertex types. + * + * See equation (21) in M. E. J. Newman: Mixing patterns + * in networks, Phys. Rev. E 67, 026126 (2003) + * (http://arxiv.org/abs/cond-mat/0209450) for the proper + * definition. The actual calculation is performed using equation (26) + * in the same paper for directed graphs, and equation (4) in + * M. E. J. Newman: Assortative mixing in networks, + * Phys. Rev. Lett. 89, 208701 (2002) + * (http://arxiv.org/abs/cond-mat/0205405/) for undirected graphs. + * + * \param graph The input graph, it can be directed or undirected. + * \param types1 The vertex values, these can be arbitrary numeric + * values. + * \param types2 A second value vector to be using for the incoming + * edges when calculating assortativity for a directed graph. + * Supply a null pointer here if you want to use the same values + * for outgoing and incoming edges. This argument is ignored + * (with a warning) if it is not a null pointer and undirected + * assortativity coefficient is being calculated. + * \param res Pointer to a real variable, the result is stored here. + * \param directed Boolean, whether to consider edge directions for + * directed graphs. It is ignored for undirected graphs. + * \return Error code. + * + * Time complexity: O(|E|), linear in the number of edges of the + * graph. + * + * \sa \ref igraph_assortativity_nominal() if you have discrete vertex + * categories instead of numeric labels, and \ref + * igraph_assortativity_degree() for the special case of assortativity + * based on vertex degree. + * + * \example examples/simple/assortativity.c + */ + +int igraph_assortativity(const igraph_t *graph, + const igraph_vector_t *types1, + const igraph_vector_t *types2, + igraph_real_t *res, + igraph_bool_t directed) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int e; + + directed = directed && igraph_is_directed(graph); + + if (!directed && types2) { + IGRAPH_WARNING("Only `types1' is used for undirected case"); + } + + if (igraph_vector_size(types1) != no_of_nodes) { + IGRAPH_ERROR("Invalid `types1' vector length", IGRAPH_EINVAL); + } + + if (types2 && igraph_vector_size(types2) != no_of_nodes) { + IGRAPH_ERROR("Invalid `types2' vector length", IGRAPH_EINVAL); + } + + if (!directed) { + igraph_real_t num1 = 0.0, num2 = 0.0, den1 = 0.0; + + for (e = 0; e < no_of_edges; e++) { + long int from = IGRAPH_FROM(graph, e); + long int to = IGRAPH_TO(graph, e); + igraph_real_t from_type = VECTOR(*types1)[from]; + igraph_real_t to_type = VECTOR(*types1)[to]; + + num1 += from_type * to_type; + num2 += from_type + to_type; + den1 += from_type * from_type + to_type * to_type; + } + + num1 /= no_of_edges; + den1 /= no_of_edges * 2; + num2 /= no_of_edges * 2; + num2 = num2 * num2; + + *res = (num1 - num2) / (den1 - num2); + + } else { + igraph_real_t num1 = 0.0, num2 = 0.0, num3 = 0.0, + den1 = 0.0, den2 = 0.0; + igraph_real_t num, den; + + if (!types2) { + types2 = types1; + } + + for (e = 0; e < no_of_edges; e++) { + long int from = IGRAPH_FROM(graph, e); + long int to = IGRAPH_TO(graph, e); + igraph_real_t from_type = VECTOR(*types1)[from]; + igraph_real_t to_type = VECTOR(*types2)[to]; + + num1 += from_type * to_type; + num2 += from_type; + num3 += to_type; + den1 += from_type * from_type; + den2 += to_type * to_type; + } + + num = num1 - num2 * num3 / no_of_edges; + den = sqrt(den1 - num2 * num2 / no_of_edges) * + sqrt(den2 - num3 * num3 / no_of_edges); + + *res = num / den; + } + + return 0; +} + +/** + * \function igraph_assortativity_degree + * Assortativity of a graph based on vertex degree + * + * Assortativity based on vertex degree, please see the discussion at + * the documentation of \ref igraph_assortativity() for details. + * + * \param graph The input graph, it can be directed or undirected. + * \param res Pointer to a real variable, the result is stored here. + * \param directed Boolean, whether to consider edge directions for + * directed graphs. This argument is ignored for undirected + * graphs. Supply 1 (=TRUE) here to do the natural thing, i.e. use + * directed version of the measure for directed graphs and the + * undirected version for undirected graphs. + * \return Error code. + * + * Time complexity: O(|E|+|V|), |E| is the number of edges, |V| is + * the number of vertices. + * + * \sa \ref igraph_assortativity() for the general function + * calculating assortativity for any kind of numeric vertex values. + * + * \example examples/simple/assortativity.c + */ + +int igraph_assortativity_degree(const igraph_t *graph, + igraph_real_t *res, + igraph_bool_t directed) { + + directed = directed && igraph_is_directed(graph); + + if (directed) { + igraph_vector_t indegree, outdegree; + igraph_vector_init(&indegree, 0); + igraph_vector_init(&outdegree, 0); + igraph_degree(graph, &indegree, igraph_vss_all(), IGRAPH_IN, /*loops=*/ 1); + igraph_degree(graph, &outdegree, igraph_vss_all(), IGRAPH_OUT, /*loops=*/ 1); + igraph_vector_add_constant(&indegree, -1); + igraph_vector_add_constant(&outdegree, -1); + igraph_assortativity(graph, &outdegree, &indegree, res, /*directed=*/ 1); + igraph_vector_destroy(&indegree); + igraph_vector_destroy(&outdegree); + } else { + igraph_vector_t degree; + igraph_vector_init(°ree, 0); + igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, /*loops=*/ 1); + igraph_vector_add_constant(°ree, -1); + igraph_assortativity(graph, °ree, 0, res, /*directed=*/ 0); + igraph_vector_destroy(°ree); + } + + return 0; +} diff --git a/src/rigraph/core/misc/motifs.c b/src/rigraph/core/misc/motifs.c new file mode 100644 index 0000000..0a496b9 --- /dev/null +++ b/src/rigraph/core/misc/motifs.c @@ -0,0 +1,1218 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_motifs.h" + +#include "igraph_memory.h" +#include "igraph_random.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_nongraph.h" +#include "igraph_stack.h" + +#include "../core/interruption.h" +#include "../isomorphism/isoclasses.h" +#include "../graph/neighbors.h" + +/** + * Callback function for igraph_motifs_randesu that counts the motifs by + * isomorphism class in a histogram. + */ +static igraph_bool_t igraph_i_motifs_randesu_update_hist( + const igraph_t *graph, + igraph_vector_t *vids, int isoclass, void* extra) { + igraph_vector_t *hist = (igraph_vector_t*)extra; + IGRAPH_UNUSED(graph); IGRAPH_UNUSED(vids); + VECTOR(*hist)[isoclass]++; + return 0; +} + +/** + * \function igraph_motifs_randesu + * \brief Count the number of motifs in a graph. + * + * + * Motifs are small weakly connected induced subgraphs of a given structure in a + * graph. It is argued that the motif profile (i.e. the number of + * different motifs in the graph) is characteristic for different + * types of networks and network function is related to the motifs in + * the graph. + * + * + * This function is able to find directed motifs of sizes three + * and four and undirected motifs of sizes three to six + * (i.e. the number of different subgraphs with three to six + * vertices in the network). + * + * + * In a big network the total number of motifs can be very large, so + * it takes a lot of time to find all of them, a sampling method can + * be used. This function is capable of doing sampling via the + * \p cut_prob argument. This argument gives the probability that + * a branch of the motif search tree will not be explored. See + * S. Wernicke and F. Rasche: FANMOD: a tool for fast network motif + * detection, Bioinformatics 22(9), 1152--1153, 2006 for details. + * https://doi.org/10.1093/bioinformatics/btl038 + * + * + * Set the \p cut_prob argument to a zero vector for finding all + * motifs. + * + * + * Directed motifs will be counted in directed graphs and undirected + * motifs in undirected graphs. + * + * \param graph The graph to find the motifs in. + * \param hist The result of the computation, it gives the number of + * motifs found for each isomorphism class. See + * \ref igraph_isoclass() for help about isomorphism classes. + * Note that this function does \em not count isomorphism + * classes that are not connected and will report NaN (more + * precisely \c IGRAPH_NAN) for them. + * \param size The size of the motifs to search for. For directed graphs, + * only 3 and 4 are implemented, for undirected, 3 to 6. + * The limitation is not in the motif finding code, but the graph + * isomorphism code. + * \param cut_prob Vector of probabilities for cutting the search tree + * at a given level. The first element is the first level, etc. + * Supply all zeros here (of length \p size) to find all motifs + * in a graph. + * \return Error code. + * + * \sa \ref igraph_motifs_randesu_estimate() for estimating the number + * of motifs in a graph, this can help to set the \p cut_prob + * parameter; \ref igraph_motifs_randesu_no() to calculate the total + * number of motifs of a given size in a graph; + * \ref igraph_motifs_randesu_callback() for calling a callback function + * for every motif found; \ref igraph_subisomorphic_lad() for finding + * subgraphs on more than 4 (directed) or 6 (undirected) vertices. + * + * Time complexity: TODO. + * + * \example examples/simple/igraph_motifs_randesu.c + */ +int igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, + int size, const igraph_vector_t *cut_prob) { + igraph_bool_t directed = igraph_is_directed(graph); + int histlen; + + if (directed) { + switch (size) { + case 3: + histlen = 16; + break; + case 4: + histlen = 218; + break; + default: + IGRAPH_ERROR("In directed graphs, only 3 and 4 vertex motifs are supported.", + IGRAPH_UNIMPLEMENTED); + } + } else { + switch (size) { + case 3: + histlen = 4; + break; + case 4: + histlen = 11; + break; + case 5: + histlen = 34; + break; + case 6: + histlen = 156; + break; + default: + IGRAPH_ERROR("In undirected graphs, only 3 to 6 vertex motifs are supported.", + IGRAPH_UNIMPLEMENTED); + } + } + + if (igraph_vector_size(cut_prob) != size) { + IGRAPH_ERRORF("Cut probability vector size (%ld) must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_size(cut_prob), size); + } + + IGRAPH_CHECK(igraph_vector_resize(hist, histlen)); + igraph_vector_null(hist); + + IGRAPH_CHECK(igraph_motifs_randesu_callback(graph, size, cut_prob, + &igraph_i_motifs_randesu_update_hist, hist)); + + if (size == 3) { + if (directed) { + VECTOR(*hist)[0] = VECTOR(*hist)[1] = VECTOR(*hist)[3] = IGRAPH_NAN; + } else { + VECTOR(*hist)[0] = VECTOR(*hist)[1] = IGRAPH_NAN; + } + } else if (size == 4) { + if (directed) { + int not_connected[] = { 0, 1, 2, 4, 5, 6, 9, 10, 11, 15, 22, 23, 27, + 28, 33, 34, 39, 62, 120 + }; + int i, n = sizeof(not_connected) / sizeof(int); + for (i = 0; i < n; i++) { + VECTOR(*hist)[not_connected[i]] = IGRAPH_NAN; + } + } else { + VECTOR(*hist)[0] = VECTOR(*hist)[1] = VECTOR(*hist)[2] = + VECTOR(*hist)[3] = VECTOR(*hist)[5] = IGRAPH_NAN; + } + } else if (size == 5) { + /* undirected only */ + int not_connected[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 19 }; + int i, n = sizeof(not_connected) / sizeof(int); + for (i = 0; i < n; i++) { + VECTOR(*hist)[not_connected[i]] = IGRAPH_NAN; + } + } else if (size == 6) { + /* undirected only */ + int not_connected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 38, + 44, 50, 51, 54, 74, 77, 89, 120}; + int i, n = sizeof(not_connected) / sizeof(int); + for (i = 0; i < n; i++) { + VECTOR(*hist)[not_connected[i]] = IGRAPH_NAN; + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_motifs_randesu_callback + * \brief Finds motifs in a graph and calls a function for each of them. + * + * + * Similarly to \ref igraph_motifs_randesu(), this function is able to find + * directed motifs of sizes three and four and undirected motifs of sizes + * three to six (i.e. the number of different subgraphs with three to six + * vertices in the network). However, instead of + * counting them, the function will call a callback function for each motif + * found to allow further tests or post-processing. + * + * + * The \p cut_prob argument also allows sampling the motifs, just like for + * \ref igraph_motifs_randesu(). Set the \p cut_prob argument to a zero vector + * for finding all motifs. + * + * \param graph The graph to find the motifs in. + * \param size The size of the motifs to search for. Only three and + * four are implemented currently. The limitation is not in the + * motif finding code, but the graph isomorphism code. + * \param cut_prob Vector of probabilities for cutting the search tree + * at a given level. The first element is the first level, etc. + * Supply all zeros here (of length \c size) to find all motifs + * in a graph. + * \param callback A pointer to a function of type \ref igraph_motifs_handler_t. + * This function will be called whenever a new motif is found. + * \param extra Extra argument to pass to the callback function. + * \return Error code. + * + * Time complexity: TODO. + * + * \example examples/simple/igraph_motifs_randesu.c + */ + +int igraph_motifs_randesu_callback(const igraph_t *graph, int size, + const igraph_vector_t *cut_prob, igraph_motifs_handler_t *callback, + void* extra) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_adjlist_t allneis, alloutneis; + igraph_vector_int_t *neis; + long int father; + long int i, j, s; + long int motifs = 0; + IGRAPH_UNUSED(motifs); /* We mark it as unused to prevent warnings about unused-but-set-variables. */ + + igraph_vector_t vids; /* this is G */ + igraph_vector_t adjverts; /* this is V_E */ + igraph_stack_t stack; /* this is S */ + long int *added; + char *subg; + + const unsigned int *arr_idx, *arr_code; + unsigned int code = 0; + unsigned int mul, idx; + + igraph_bool_t terminate = 0; + + if (igraph_is_directed(graph)) { + switch (size) { + case 3: + arr_idx = igraph_i_isoclass_3_idx; + arr_code = igraph_i_isoclass2_3; + mul = 3; + break; + case 4: + arr_idx = igraph_i_isoclass_4_idx; + arr_code = igraph_i_isoclass2_4; + mul = 4; + break; + default: + IGRAPH_ERROR("In directed graphs, only 3 and 4 vertex motifs are supported.", + IGRAPH_UNIMPLEMENTED); + } + } else { + switch (size) { + case 3: + arr_idx = igraph_i_isoclass_3u_idx; + arr_code = igraph_i_isoclass2_3u; + mul = 3; + break; + case 4: + arr_idx = igraph_i_isoclass_4u_idx; + arr_code = igraph_i_isoclass2_4u; + mul = 4; + break; + case 5: + arr_idx = igraph_i_isoclass_5u_idx; + arr_code = igraph_i_isoclass2_5u; + mul = 5; + break; + case 6: + arr_idx = igraph_i_isoclass_6u_idx; + arr_code = igraph_i_isoclass2_6u; + mul = 6; + break; + default: + IGRAPH_ERROR("In undirected graphs, only 3 to 6 vertex motifs are supported.", + IGRAPH_UNIMPLEMENTED); + } + } + + if (igraph_vector_size(cut_prob) != size) { + IGRAPH_ERRORF("Cut probability vector size (%ld) must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_size(cut_prob), size); + } + + added = IGRAPH_CALLOC(no_of_nodes, long int); + if (added == 0) { + IGRAPH_ERROR("Cannot find motifs", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added); + + subg = IGRAPH_CALLOC(no_of_nodes, char); + if (subg == 0) { + IGRAPH_ERROR("Cannot find motifs", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, subg); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + IGRAPH_CHECK(igraph_adjlist_init(graph, &alloutneis, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &alloutneis); + + IGRAPH_VECTOR_INIT_FINALLY(&vids, 0); + IGRAPH_VECTOR_INIT_FINALLY(&adjverts, 0); + IGRAPH_CHECK(igraph_stack_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_destroy, &stack); + + RNG_BEGIN(); + + for (father = 0; father < no_of_nodes; father++) { + long int level; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (VECTOR(*cut_prob)[0] == 1 || + RNG_UNIF01() < VECTOR(*cut_prob)[0]) { + continue; + } + + /* init G */ + igraph_vector_clear(&vids); level = 0; + IGRAPH_CHECK(igraph_vector_push_back(&vids, father)); + subg[father] = 1; added[father] += 1; level += 1; + + /* init V_E */ + igraph_vector_clear(&adjverts); + neis = igraph_adjlist_get(&allneis, father); + s = igraph_vector_int_size(neis); + for (i = 0; i < s; i++) { + long int nei = (long int) VECTOR(*neis)[i]; + if (!added[nei] && nei > father) { + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father)); + } + added[nei] += 1; + } + + /* init S */ + igraph_stack_clear(&stack); + + while (level > 1 || !igraph_vector_empty(&adjverts)) { + igraph_real_t cp = VECTOR(*cut_prob)[level]; + + if (level == size - 1) { + s = igraph_vector_size(&adjverts) / 2; + for (i = 0; i < s; i++) { + long int k, s2; + long int last; + + if (cp != 0 && RNG_UNIF01() < cp) { + continue; + } + motifs += 1; + + last = (long int) VECTOR(adjverts)[2 * i]; + IGRAPH_CHECK(igraph_vector_push_back(&vids, last)); + subg[last] = (char) size; + + code = 0; idx = 0; + for (k = 0; k < size; k++) { + long int from = (long int) VECTOR(vids)[k]; + neis = igraph_adjlist_get(&alloutneis, from); + s2 = igraph_vector_int_size(neis); + for (j = 0; j < s2; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + if (subg[nei] && k != subg[nei] - 1) { + idx = (unsigned char) (mul * k + (subg[nei] - 1)); + code |= arr_idx[idx]; + } + } + } + + if (callback(graph, &vids, (int) arr_code[code], extra)) { + terminate = 1; + break; + } + igraph_vector_pop_back(&vids); + subg[last] = 0; + } + } + + /* did the callback function asked us to terminate the search? */ + if (terminate) { + break; + } + + /* can we step down? */ + if (level < size - 1 && + !igraph_vector_empty(&adjverts)) { + /* we might step down */ + long int neifather = (long int) igraph_vector_pop_back(&adjverts); + long int nei = (long int) igraph_vector_pop_back(&adjverts); + + if (cp == 0 || RNG_UNIF01() > cp) { + /* yes, step down */ + IGRAPH_CHECK(igraph_vector_push_back(&vids, nei)); + subg[nei] = (char) level + 1; added[nei] += 1; level += 1; + + IGRAPH_CHECK(igraph_stack_push(&stack, neifather)); + IGRAPH_CHECK(igraph_stack_push(&stack, nei)); + IGRAPH_CHECK(igraph_stack_push(&stack, level)); + + neis = igraph_adjlist_get(&allneis, nei); + s = igraph_vector_int_size(neis); + for (i = 0; i < s; i++) { + long int nei2 = (long int) VECTOR(*neis)[i]; + if (!added[nei2] && nei2 > father) { + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); + } + added[nei2] += 1; + } + } + } else { + /* no, step back */ + long int nei, neifather; + while (!igraph_stack_empty(&stack) && + level == igraph_stack_top(&stack) - 1) { + igraph_stack_pop(&stack); + nei = (long int) igraph_stack_pop(&stack); + neifather = (long int) igraph_stack_pop(&stack); + igraph_vector_push_back(&adjverts, nei); + igraph_vector_push_back(&adjverts, neifather); + } + + nei = (long int) igraph_vector_pop_back(&vids); + subg[nei] = 0; added[nei] -= 1; level -= 1; + neis = igraph_adjlist_get(&allneis, nei); + s = igraph_vector_int_size(neis); + for (i = 0; i < s; i++) { + added[ (long int) VECTOR(*neis)[i] ] -= 1; + } + while (!igraph_vector_empty(&adjverts) && + igraph_vector_tail(&adjverts) == nei) { + igraph_vector_pop_back(&adjverts); + igraph_vector_pop_back(&adjverts); + } + } + + } /* while */ + + /* did the callback function asked us to terminate the search? */ + if (terminate) { + break; + } + + /* clear the added vector */ + added[father] -= 1; + subg[father] = 0; + neis = igraph_adjlist_get(&allneis, father); + s = igraph_vector_int_size(neis); + for (i = 0; i < s; i++) { + added[ (long int) VECTOR(*neis)[i] ] -= 1; + } + + } /* for father */ + + RNG_END(); + + IGRAPH_FREE(added); + IGRAPH_FREE(subg); + igraph_vector_destroy(&vids); + igraph_vector_destroy(&adjverts); + igraph_adjlist_destroy(&alloutneis); + igraph_adjlist_destroy(&allneis); + igraph_stack_destroy(&stack); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_motifs_randesu_estimate + * \brief Estimate the total number of motifs in a graph. + * + * This function estimates the total number of weakly connected induced + * subgraphs, called motifs, of a fixed number of vertices. For + * example, an undirected complete graph on \c n vertices + * will have one motif of size \c n, and \c n motifs + * of \p size n - 1. As another example, one triangle + * and a separate vertex will have zero motifs of size four. + * + * + * This function is useful for large graphs for which it is not + * feasible to count all the different motifs, because there are very + * many of them. + * + * + * The total number of motifs is estimated by taking a sample of + * vertices and counts all motifs in which these vertices are + * included. (There is also a \p cut_prob parameter which gives the + * probabilities to cut a branch of the search tree.) + * + * + * Directed motifs will be counted in directed graphs and undirected + * motifs in undirected graphs. + * + * \param graph The graph object to study. + * \param est Pointer to an integer type, the result will be stored + * here. + * \param size The size of the motifs to look for. + * \param cut_prob Vector giving the probabilities to cut a branch of + * the search tree and omit counting the motifs in that branch. + * It contains a probability for each level. Supply \p size + * zeros here to count all the motifs in the sample. + * \param sample_size The number of vertices to use as the + * sample. This parameter is only used if the \p parsample + * argument is a null pointer. + * \param parsample Either pointer to an initialized vector or a null + * pointer. If a vector then the vertex ids in the vector are + * used as a sample. If a null pointer then the \p sample_size + * argument is used to create a sample of vertices drawn with + * uniform probability. + * \return Error code. + * \sa \ref igraph_motifs_randesu(), \ref igraph_motifs_randesu_no(). + * + * Time complexity: TODO. + */ + +int igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, + int size, const igraph_vector_t *cut_prob, + igraph_integer_t sample_size, + const igraph_vector_t *parsample) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t neis; + + igraph_vector_t vids; /* this is G */ + igraph_vector_t adjverts; /* this is V_E */ + igraph_stack_t stack; /* this is S */ + long int *added; + igraph_vector_t *sample; + long int sam; + long int i; + + if (size < 3) { + IGRAPH_ERRORF("Motif size must be at least 3, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, (igraph_integer_t) size); + } + + if (igraph_vector_size(cut_prob) != size) { + IGRAPH_ERRORF("Cut probability vector size (%ld) must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_size(cut_prob), size); + } + + if (parsample && igraph_vector_size(parsample) != 0) { + igraph_real_t min, max; + igraph_vector_minmax(parsample, &min, &max); + if (min < 0 || max >= no_of_nodes) { + IGRAPH_ERROR("Sample vertex id out of range.", IGRAPH_EINVAL); + } + } + + if (no_of_nodes == 0) { + *est = 0; + return IGRAPH_SUCCESS; + } + + added = IGRAPH_CALLOC(no_of_nodes, long int); + if (added == 0) { + IGRAPH_ERROR("Cannot find motifs.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added); + + IGRAPH_VECTOR_INIT_FINALLY(&vids, 0); + IGRAPH_VECTOR_INIT_FINALLY(&adjverts, 0); + IGRAPH_CHECK(igraph_stack_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_destroy, &stack); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + if (parsample == NULL) { + sample = IGRAPH_CALLOC(1, igraph_vector_t); + if (sample == NULL) { + IGRAPH_ERROR("Cannot estimate motifs.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, sample); + IGRAPH_VECTOR_INIT_FINALLY(sample, 0); + IGRAPH_CHECK(igraph_random_sample(sample, 0, no_of_nodes - 1, sample_size)); + } else { + sample = (igraph_vector_t*)parsample; + sample_size = (igraph_integer_t) igraph_vector_size(sample); + } + + *est = 0; + + RNG_BEGIN(); + + for (sam = 0; sam < sample_size; sam++) { + long int father = (long int) VECTOR(*sample)[sam]; + long int level, s; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (VECTOR(*cut_prob)[0] == 1 || + RNG_UNIF01() < VECTOR(*cut_prob)[0]) { + continue; + } + + /* init G */ + igraph_vector_clear(&vids); level = 0; + IGRAPH_CHECK(igraph_vector_push_back(&vids, father)); + added[father] += 1; level += 1; + + /* init V_E */ + igraph_vector_clear(&adjverts); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) father, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); + for (i = 0; i < s; i++) { + long int nei = (long int) VECTOR(neis)[i]; + if (!added[nei] && nei > father) { + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father)); + } + added[nei] += 1; + } + + /* init S */ + igraph_stack_clear(&stack); + + while (level > 1 || !igraph_vector_empty(&adjverts)) { + igraph_real_t cp = VECTOR(*cut_prob)[level]; + + if (level == size - 1) { + s = igraph_vector_size(&adjverts) / 2; + for (i = 0; i < s; i++) { + if (cp != 0 && RNG_UNIF01() < cp) { + continue; + } + (*est) += 1; + } + } + + if (level < size - 1 && + !igraph_vector_empty(&adjverts)) { + /* We might step down */ + long int neifather = (long int) igraph_vector_pop_back(&adjverts); + long int nei = (long int) igraph_vector_pop_back(&adjverts); + + if (cp == 0 || RNG_UNIF01() > cp) { + /* Yes, step down */ + IGRAPH_CHECK(igraph_vector_push_back(&vids, nei)); + added[nei] += 1; level += 1; + + IGRAPH_CHECK(igraph_stack_push(&stack, neifather)); + IGRAPH_CHECK(igraph_stack_push(&stack, nei)); + IGRAPH_CHECK(igraph_stack_push(&stack, level)); + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); + for (i = 0; i < s; i++) { + long int nei2 = (long int) VECTOR(neis)[i]; + if (!added[nei2] && nei2 > father) { + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); + } + added[nei2] += 1; + } + } + } else { + /* no, step back */ + long int nei, neifather; + while (!igraph_stack_empty(&stack) && + level == igraph_stack_top(&stack) - 1) { + igraph_stack_pop(&stack); + nei = (long int) igraph_stack_pop(&stack); + neifather = (long int) igraph_stack_pop(&stack); + igraph_vector_push_back(&adjverts, nei); + igraph_vector_push_back(&adjverts, neifather); + } + + nei = (long int) igraph_vector_pop_back(&vids); + added[nei] -= 1; level -= 1; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); + for (i = 0; i < s; i++) { + added[ (long int) VECTOR(neis)[i] ] -= 1; + } + while (!igraph_vector_empty(&adjverts) && + igraph_vector_tail(&adjverts) == nei) { + igraph_vector_pop_back(&adjverts); + igraph_vector_pop_back(&adjverts); + } + } + + } /* while */ + + /* clear the added vector */ + added[father] -= 1; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) father, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); + for (i = 0; i < s; i++) { + added[ (long int) VECTOR(neis)[i] ] -= 1; + } + + } /* for father */ + + RNG_END(); + + (*est) *= ((double)no_of_nodes / sample_size); + + if (parsample == 0) { + igraph_vector_destroy(sample); + IGRAPH_FREE(sample); + IGRAPH_FINALLY_CLEAN(2); + } + + IGRAPH_FREE(added); + igraph_vector_destroy(&vids); + igraph_vector_destroy(&adjverts); + igraph_stack_destroy(&stack); + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(5); + return 0; +} + +/** + * \function igraph_motifs_randesu_no + * \brief Count the total number of motifs in a graph. + * + * + * This function counts the total number of motifs in a graph, + * i.e. the number of of (weakly) connected triplets or quadruplets, + * without assigning isomorphism classes to them. + * + * \param graph The graph object to study. + * \param no Pointer to an integer type, the result will be stored + * here. + * \param size The size of the motifs to count. + * \param cut_prob Vector giving the probabilities that a branch of + * the search tree will be cut at a given level. + * \return Error code. + * \sa \ref igraph_motifs_randesu(), \ref + * igraph_motifs_randesu_estimate(). + * + * Time complexity: TODO. + */ + +int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, + int size, const igraph_vector_t *cut_prob) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t neis; + + igraph_vector_t vids; /* this is G */ + igraph_vector_t adjverts; /* this is V_E */ + igraph_stack_t stack; /* this is S */ + long int *added; + long int father; + long int i; + + if (size < 3) { + IGRAPH_ERRORF("Motif size must be at least 3, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, (igraph_integer_t) size); + } + + if (igraph_vector_size(cut_prob) != size) { + IGRAPH_ERRORF("Cut probability vector size (%ld) must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_size(cut_prob), size); + } + added = IGRAPH_CALLOC(no_of_nodes, long int); + if (added == 0) { + IGRAPH_ERROR("Cannot find motifs.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added); + + IGRAPH_VECTOR_INIT_FINALLY(&vids, 0); + IGRAPH_VECTOR_INIT_FINALLY(&adjverts, 0); + IGRAPH_CHECK(igraph_stack_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_destroy, &stack); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + *no = 0; + + RNG_BEGIN(); + + for (father = 0; father < no_of_nodes; father++) { + long int level, s; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (VECTOR(*cut_prob)[0] == 1 || + RNG_UNIF01() < VECTOR(*cut_prob)[0]) { + continue; + } + + /* init G */ + igraph_vector_clear(&vids); level = 0; + IGRAPH_CHECK(igraph_vector_push_back(&vids, father)); + added[father] += 1; level += 1; + + /* init V_E */ + igraph_vector_clear(&adjverts); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) father, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); + for (i = 0; i < s; i++) { + long int nei = (long int) VECTOR(neis)[i]; + if (!added[nei] && nei > father) { + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father)); + } + added[nei] += 1; + } + + /* init S */ + igraph_stack_clear(&stack); + + while (level > 1 || !igraph_vector_empty(&adjverts)) { + igraph_real_t cp = VECTOR(*cut_prob)[level]; + + if (level == size - 1) { + s = igraph_vector_size(&adjverts) / 2; + for (i = 0; i < s; i++) { + if (cp != 0 && RNG_UNIF01() < cp) { + continue; + } + (*no) += 1; + } + } + + if (level < size - 1 && + !igraph_vector_empty(&adjverts)) { + /* We might step down */ + long int neifather = (long int) igraph_vector_pop_back(&adjverts); + long int nei = (long int) igraph_vector_pop_back(&adjverts); + + if (cp == 0 || RNG_UNIF01() > cp) { + /* Yes, step down */ + IGRAPH_CHECK(igraph_vector_push_back(&vids, nei)); + added[nei] += 1; level += 1; + + IGRAPH_CHECK(igraph_stack_push(&stack, neifather)); + IGRAPH_CHECK(igraph_stack_push(&stack, nei)); + IGRAPH_CHECK(igraph_stack_push(&stack, level)); + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); + for (i = 0; i < s; i++) { + long int nei2 = (long int) VECTOR(neis)[i]; + if (!added[nei2] && nei2 > father) { + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); + } + added[nei2] += 1; + } + } + } else { + /* no, step back */ + long int nei, neifather; + while (!igraph_stack_empty(&stack) && + level == igraph_stack_top(&stack) - 1) { + igraph_stack_pop(&stack); + nei = (long int) igraph_stack_pop(&stack); + neifather = (long int) igraph_stack_pop(&stack); + igraph_vector_push_back(&adjverts, nei); + igraph_vector_push_back(&adjverts, neifather); + } + + nei = (long int) igraph_vector_pop_back(&vids); + added[nei] -= 1; level -= 1; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); + for (i = 0; i < s; i++) { + added[ (long int) VECTOR(neis)[i] ] -= 1; + } + while (!igraph_vector_empty(&adjverts) && + igraph_vector_tail(&adjverts) == nei) { + igraph_vector_pop_back(&adjverts); + igraph_vector_pop_back(&adjverts); + } + } + + } /* while */ + + /* clear the added vector */ + added[father] -= 1; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) father, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); + for (i = 0; i < s; i++) { + added[ (long int) VECTOR(neis)[i] ] -= 1; + } + + } /* for father */ + + RNG_END(); + + IGRAPH_FREE(added); + igraph_vector_destroy(&vids); + igraph_vector_destroy(&adjverts); + igraph_stack_destroy(&stack); + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(5); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_dyad_census + * \brief Calculating the dyad census as defined by Holland and Leinhardt. + * + * + * Dyad census means classifying each pair of vertices of a directed + * graph into three categories: mutual (there is at least one edge from + * \c a to \c b and also from \c b to \c a); asymmetric (there is at least + * one edge either from \c a to \c b or from \c b to \c a, but not the other + * way) and null (no edges between \c a and \c b in either direction). + * + * + * Holland, P.W. and Leinhardt, S. (1970). A Method for Detecting + * Structure in Sociometric Data. American Journal of Sociology, + * 70, 492-513. + * + * \param graph The input graph. For an undirected graph, there are no + * asymmetric connections. + * \param mut Pointer to an integer, the number of mutual dyads is + * stored here. + * \param asym Pointer to an integer, the number of asymmetric dyads + * is stored here. + * \param null Pointer to an integer, the number of null dyads is + * stored here. In case of an integer overflow (i.e. too many + * null dyads), -1 will be returned. + * \return Error code. + * + * \sa \ref igraph_reciprocity(), \ref igraph_triad_census(). + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number + * of edges. + */ +int igraph_dyad_census(const igraph_t *graph, igraph_integer_t *mut, + igraph_integer_t *asym, igraph_integer_t *null) { + + igraph_integer_t nonrec = 0, rec = 0; + igraph_vector_t inneis, outneis; + igraph_integer_t vc = igraph_vcount(graph); + long int i; + + IGRAPH_VECTOR_INIT_FINALLY(&inneis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outneis, 0); + + for (i = 0; i < vc; i++) { + long int ideg, odeg; + long int ip, op; + + IGRAPH_CHECK(igraph_i_neighbors(graph, &inneis, i, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_CHECK(igraph_i_neighbors(graph, &outneis, i, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + + ideg = igraph_vector_size(&inneis); + odeg = igraph_vector_size(&outneis); + + ip = op = 0; + while (ip < ideg && op < odeg) { + if (VECTOR(inneis)[ip] < VECTOR(outneis)[op]) { + nonrec += 1; + ip++; + } else if (VECTOR(inneis)[ip] > VECTOR(outneis)[op]) { + nonrec += 1; + op++; + } else { + rec += 1; + ip++; + op++; + } + } + nonrec += (ideg - ip) + (odeg - op); + } + + igraph_vector_destroy(&inneis); + igraph_vector_destroy(&outneis); + IGRAPH_FINALLY_CLEAN(2); + + *mut = rec / 2; + *asym = nonrec / 2; + if (vc % 2) { + *null = vc * ((vc - 1) / 2); + } else { + *null = (vc / 2) * (vc - 1); + } + if (*null < vc && vc > 2) { + IGRAPH_WARNING("Integer overflow, returning -1."); + *null = -1; + } else { + *null = *null - (*mut) - (*asym); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_triad_census_24 + * TODO + */ + +int igraph_triad_census_24(const igraph_t *graph, igraph_real_t *res2, + igraph_real_t *res4) { + + long int vc = igraph_vcount(graph); + igraph_vector_long_t seen; + igraph_vector_int_t *neis, *neis2; + long int i, j, k, s, neilen, neilen2, ign; + igraph_adjlist_t adjlist; + + IGRAPH_CHECK(igraph_vector_long_init(&seen, vc)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &seen); + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + *res2 = *res4 = 0; + + for (i = 0; i < vc; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + + neis = igraph_adjlist_get(&adjlist, i); + neilen = igraph_vector_int_size(neis); + /* mark neighbors of i & i itself */ + VECTOR(seen)[i] = i + 1; + ign = 0; + for (j = 0; j < neilen; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + if (VECTOR(seen)[nei] == i + 1 || VECTOR(seen)[nei] == -(i + 1)) { + /* multiple edges or loop edge */ + VECTOR(seen)[nei] = -(i + 1); + ign++; + } else { + VECTOR(seen)[nei] = i + 1; + } + } + + for (j = 0; j < neilen; j++) { + long int nei = (long int) VECTOR(*neis)[j]; + if (nei <= i || (j > 0 && nei == VECTOR(*neis)[j - 1])) { + continue; + } + neis2 = igraph_adjlist_get(&adjlist, nei); + neilen2 = igraph_vector_int_size(neis2); + s = 0; + for (k = 0; k < neilen2; k++) { + long int nei2 = (long int) VECTOR(*neis2)[k]; + if (k > 0 && nei2 == VECTOR(*neis2)[k - 1]) { + continue; + } + if (VECTOR(seen)[nei2] != i + 1 && VECTOR(seen)[nei2] != -(i + 1)) { + s++; + } + } + if (VECTOR(seen)[nei] > 0) { + *res2 += vc - s - neilen + ign - 1; + } else { + *res4 += vc - s - neilen + ign - 1; + } + } + } + + igraph_adjlist_destroy(&adjlist); + igraph_vector_long_destroy(&seen); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \function igraph_triad_census + * \brief Triad census, as defined by Davis and Leinhardt + * + * + * Calculating the triad census means classifying every triple of + * vertices in a directed graph. A triple can be in one of 16 states: + * \clist + * \cli 003 + * A, B, C, the empty graph. + * \cli 012 + * A->B, C, a graph with a single directed edge. + * \cli 102 + * A<->B, C, a graph with a mutual connection between two vertices. + * \cli 021D + * A<-B->C, the binary out-tree. + * \cli 021U + * A->B<-C, the binary in-tree. + * \cli 021C + * A->B->C, the directed line. + * \cli 111D + * A<->B<-C. + * \cli 111U + * A<->B->C. + * \cli 030T + * A->B<-C, A->C. + * \cli 030C + * A<-B<-C, A->C. + * \cli 201 + * A<->B<->C. + * \cli 120D + * A<-B->C, A<->C. + * \cli 120U + * A->B<-C, A<->C. + * \cli 120C + * A->B->C, A<->C. + * \cli 210 + * A->B<->C, A<->C. + * \cli 300 + * A<->B<->C, A<->C, the complete graph. + * \endclist + * + * + * See also Davis, J.A. and Leinhardt, S. (1972). The Structure of + * Positive Interpersonal Relations in Small Groups. In J. Berger + * (Ed.), Sociological Theories in Progress, Volume 2, 218-251. + * Boston: Houghton Mifflin. + * + * + * This function calls \ref igraph_motifs_randesu() which is an + * implementation of the FANMOD motif finder tool, see \ref + * igraph_motifs_randesu() for details. Note that the order of the + * triads is not the same for \ref igraph_triad_census() and \ref + * igraph_motifs_randesu(). + * + * \param graph The input graph. A warning is given for undirected + * graphs, as the result is undefined for those. + * \param res Pointer to an initialized vector, the result is stored + * here in the same order as given in the list above. Note that this + * order is different than the one used by \ref igraph_motifs_randesu(). + * \return Error code. + * + * \sa \ref igraph_motifs_randesu(), \ref igraph_dyad_census(). + * + * Time complexity: TODO. + */ + +int igraph_triad_census(const igraph_t *graph, igraph_vector_t *res) { + + igraph_vector_t cut_prob; + igraph_real_t m2, m4; + igraph_vector_t tmp; + igraph_integer_t vc = igraph_vcount(graph); + igraph_real_t total; + + if (!igraph_is_directed(graph)) { + IGRAPH_WARNING("Triad census called on an undirected graph"); + } + + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + IGRAPH_VECTOR_INIT_FINALLY(&cut_prob, 3); /* all zeros */ + IGRAPH_CHECK(igraph_vector_resize(res, 16)); + igraph_vector_null(res); + IGRAPH_CHECK(igraph_motifs_randesu(graph, &tmp, 3, &cut_prob)); + IGRAPH_CHECK(igraph_triad_census_24(graph, &m2, &m4)); + + total = ((igraph_real_t)vc) * (vc - 1); + total *= (vc - 2); + total /= 6; + + /* Reorder */ + if (igraph_is_directed(graph)) { + VECTOR(tmp)[0] = 0; + VECTOR(tmp)[1] = m2; + VECTOR(tmp)[3] = m4; + VECTOR(tmp)[0] = total - igraph_vector_sum(&tmp); + + VECTOR(*res)[0] = VECTOR(tmp)[0]; + VECTOR(*res)[1] = VECTOR(tmp)[1]; + VECTOR(*res)[2] = VECTOR(tmp)[3]; + VECTOR(*res)[3] = VECTOR(tmp)[6]; + VECTOR(*res)[4] = VECTOR(tmp)[2]; + VECTOR(*res)[5] = VECTOR(tmp)[4]; + VECTOR(*res)[6] = VECTOR(tmp)[5]; + VECTOR(*res)[7] = VECTOR(tmp)[9]; + VECTOR(*res)[8] = VECTOR(tmp)[7]; + VECTOR(*res)[9] = VECTOR(tmp)[11]; + VECTOR(*res)[10] = VECTOR(tmp)[10]; + VECTOR(*res)[11] = VECTOR(tmp)[8]; + VECTOR(*res)[12] = VECTOR(tmp)[13]; + VECTOR(*res)[13] = VECTOR(tmp)[12]; + VECTOR(*res)[14] = VECTOR(tmp)[14]; + VECTOR(*res)[15] = VECTOR(tmp)[15]; + } else { + VECTOR(tmp)[0] = 0; + VECTOR(tmp)[1] = m2; + VECTOR(tmp)[0] = total - igraph_vector_sum(&tmp); + + VECTOR(*res)[0] = VECTOR(tmp)[0]; + VECTOR(*res)[2] = VECTOR(tmp)[1]; + VECTOR(*res)[10] = VECTOR(tmp)[2]; + VECTOR(*res)[15] = VECTOR(tmp)[3]; + } + + igraph_vector_destroy(&cut_prob); + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} diff --git a/src/rigraph/core/misc/other.c b/src/rigraph/core/misc/other.c new file mode 100644 index 0000000..7b81fb0 --- /dev/null +++ b/src/rigraph/core/misc/other.c @@ -0,0 +1,434 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_nongraph.h" +#include "igraph_random.h" +#include "igraph_types.h" + +#include "../core/interruption.h" +#include "../../vendor/plfit/plfit_error.h" +#include "../../vendor/plfit/plfit.h" + +#include + +/** + * \ingroup nongraph + * \function igraph_running_mean + * \brief Calculates the running mean of a vector. + * + * + * The running mean is defined by the mean of the + * previous \p binwidth values. + * \param data The vector containing the data. + * \param res The vector containing the result. This should be + * initialized before calling this function and will be + * resized. + * \param binwidth Integer giving the width of the bin for the running + * mean calculation. + * \return Error code. + * + * Time complexity: O(n), + * n is the length of + * the data vector. + */ + +int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, + igraph_integer_t binwidth) { + + double sum = 0; + long int i; + + /* Check */ + if (igraph_vector_size(data) < binwidth) { + IGRAPH_ERRORF("Data vector length (%ld) smaller than bin width (%" IGRAPH_PRId ").", IGRAPH_EINVAL, igraph_vector_size(data), binwidth); + } + if (binwidth < 1) { + IGRAPH_ERRORF("Bin width for running mean should be at least 1, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, binwidth); + } + + /* Memory for result */ + + IGRAPH_CHECK(igraph_vector_resize(res, (long int)(igraph_vector_size(data) - binwidth + 1))); + + /* Initial bin */ + for (i = 0; i < binwidth; i++) { + sum += VECTOR(*data)[i]; + } + + VECTOR(*res)[0] = sum / binwidth; + + for (i = 1; i < igraph_vector_size(data) - binwidth + 1; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + sum -= VECTOR(*data)[i - 1]; + sum += VECTOR(*data)[ (long int)(i + binwidth - 1)]; + VECTOR(*res)[i] = sum / binwidth; + } + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup nongraph + * \function igraph_convex_hull + * \brief Determines the convex hull of a given set of points in the 2D plane + * + * + * The convex hull is determined by the Graham scan algorithm. + * See the following reference for details: + * + * + * Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford + * Stein. Introduction to Algorithms, Second Edition. MIT Press and + * McGraw-Hill, 2001. ISBN 0262032937. Pages 949-955 of section 33.3: + * Finding the convex hull. + * + * \param data vector containing the coordinates. The length of the + * vector must be even, since it contains X-Y coordinate pairs. + * \param resverts the vector containing the result, e.g. the vector of + * vertex indices used as the corners of the convex hull. Supply + * \c NULL here if you are only interested in the coordinates of + * the convex hull corners. + * \param rescoords the matrix containing the coordinates of the selected + * corner vertices. Supply \c NULL here if you are only interested in + * the vertex indices. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory + * + * Time complexity: O(n log(n)) where n is the number of vertices + * + * \example examples/simple/igraph_convex_hull.c + */ +int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts, + igraph_matrix_t *rescoords) { + igraph_integer_t no_of_nodes; + long int i, pivot_idx = 0, last_idx, before_last_idx, next_idx, j; + igraph_vector_t angles, stack, order; + igraph_real_t px, py, cp; + + no_of_nodes = (igraph_integer_t) igraph_matrix_nrow(data); + if (igraph_matrix_ncol(data) != 2) { + IGRAPH_ERROR("matrix must have 2 columns", IGRAPH_EINVAL); + } + if (no_of_nodes == 0) { + if (resverts != 0) { + IGRAPH_CHECK(igraph_vector_resize(resverts, 0)); + } + if (rescoords != 0) { + IGRAPH_CHECK(igraph_matrix_resize(rescoords, 0, 2)); + } + /**************************** this is an exit here *********/ + return 0; + } + + IGRAPH_VECTOR_INIT_FINALLY(&angles, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&stack, 0); + + /* Search for the pivot vertex */ + for (i = 1; i < no_of_nodes; i++) { + if (MATRIX(*data, i, 1) < MATRIX(*data, pivot_idx, 1)) { + pivot_idx = i; + } else if (MATRIX(*data, i, 1) == MATRIX(*data, pivot_idx, 1) && + MATRIX(*data, i, 0) < MATRIX(*data, pivot_idx, 0)) { + pivot_idx = i; + } + } + px = MATRIX(*data, pivot_idx, 0); + py = MATRIX(*data, pivot_idx, 1); + + /* Create angle array */ + for (i = 0; i < no_of_nodes; i++) { + if (i == pivot_idx) { + /* We can't calculate the angle of the pivot point with itself, + * so we use 10 here. This way, after sorting the angle vector, + * the pivot point will always be the first one, since the range + * of atan2 is -3.14..3.14 */ + VECTOR(angles)[i] = 10; + } else { + VECTOR(angles)[i] = atan2(MATRIX(*data, i, 1) - py, MATRIX(*data, i, 0) - px); + } + } + + /* Sort points by angles */ + IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_CHECK(igraph_vector_qsort_ind(&angles, &order, 0)); + + /* Check if two points have the same angle. If so, keep only the point that + * is farthest from the pivot */ + j = 0; + last_idx = (long int) VECTOR(order)[0]; + pivot_idx = (long int) VECTOR(order)[no_of_nodes - 1]; + for (i = 1; i < no_of_nodes; i++) { + next_idx = (long int) VECTOR(order)[i]; + if (VECTOR(angles)[last_idx] == VECTOR(angles)[next_idx]) { + /* Keep the vertex that is farther from the pivot, drop the one that is + * closer */ + px = pow(MATRIX(*data, last_idx, 0) - MATRIX(*data, pivot_idx, 0), 2) + + pow(MATRIX(*data, last_idx, 1) - MATRIX(*data, pivot_idx, 1), 2); + py = pow(MATRIX(*data, next_idx, 0) - MATRIX(*data, pivot_idx, 0), 2) + + pow(MATRIX(*data, next_idx, 1) - MATRIX(*data, pivot_idx, 1), 2); + if (px > py) { + VECTOR(order)[i] = -1; + } else { + VECTOR(order)[j] = -1; + last_idx = next_idx; + j = i; + } + } else { + last_idx = next_idx; + j = i; + } + } + + j = 0; + last_idx = -1; + before_last_idx = -1; + while (!igraph_vector_empty(&order)) { + next_idx = (long int)VECTOR(order)[igraph_vector_size(&order) - 1]; + if (next_idx < 0) { + /* This vertex should be skipped; was excluded in an earlier step */ + igraph_vector_pop_back(&order); + continue; + } + /* Determine whether we are at a left or right turn */ + if (j < 2) { + /* Pretend that we are turning into the right direction if we have less + * than two items in the stack */ + cp = -1; + } else { + cp = (MATRIX(*data, last_idx, 0) - MATRIX(*data, before_last_idx, 0)) * + (MATRIX(*data, next_idx, 1) - MATRIX(*data, before_last_idx, 1)) - + (MATRIX(*data, next_idx, 0) - MATRIX(*data, before_last_idx, 0)) * + (MATRIX(*data, last_idx, 1) - MATRIX(*data, before_last_idx, 1)); + } + /* + printf("B L N cp: %ld, %ld, %ld, %f [", before_last_idx, last_idx, next_idx, (float)cp); + for (int k=0; k= 2) ? (long int) VECTOR(stack)[j - 2] : -1; + } + } + + /* Create result vector */ + if (resverts != 0) { + igraph_vector_clear(resverts); + IGRAPH_CHECK(igraph_vector_append(resverts, &stack)); + } + if (rescoords != 0) { + igraph_matrix_select_rows(data, rescoords, &stack); + } + + /* Free everything */ + igraph_vector_destroy(&order); + igraph_vector_destroy(&stack); + igraph_vector_destroy(&angles); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + + +static const char* igraph_i_plfit_error_message = 0; + +static void igraph_i_plfit_error_handler_store(const char *reason, const char *file, + int line, int plfit_errno) { + + IGRAPH_UNUSED(file); + IGRAPH_UNUSED(line); + IGRAPH_UNUSED(plfit_errno); + + igraph_i_plfit_error_message = reason; +} + +/** + * \ingroup nongraph + * \function igraph_power_law_fit + * \brief Fits a power-law distribution to a vector of numbers + * + * This function fits a power-law distribution to a vector containing samples + * from a distribution (that is assumed to follow a power-law of course). In + * a power-law distribution, it is generally assumed that P(X=x) is + * proportional to x-alpha, where x is a positive number and alpha + * is greater than 1. In many real-world cases, the power-law behaviour kicks + * in only above a threshold value \em xmin. The goal of this functions is to + * determine \em alpha if \em xmin is given, or to determine \em xmin and the + * corresponding value of \em alpha. + * + * + * The function uses the maximum likelihood principle to determine \em alpha + * for a given \em xmin; in other words, the function will return the \em alpha + * value for which the probability of drawing the given sample is the highest. + * When \em xmin is not given in advance, the algorithm will attempt to find + * the optimal \em xmin value for which the p-value of a Kolmogorov-Smirnov + * test between the fitted distribution and the original sample is the largest. + * The function uses the method of Clauset, Shalizi and Newman to calculate the + * parameters of the fitted distribution. See the following reference for + * details: + * + * + * Aaron Clauset, Cosma R .Shalizi and Mark E.J. Newman: Power-law + * distributions in empirical data. SIAM Review 51(4):661-703, 2009. + * + * \param data vector containing the samples for which a power-law distribution + * is to be fitted. Note that you have to provide the \em samples, + * not the probability density function or the cumulative + * distribution function. For example, if you wish to fit + * a power-law to the degrees of a graph, you can use the output of + * \ref igraph_degree directly as an input argument to + * \ref igraph_power_law_fit + * \param result the result of the fitting algorithm. See \ref igraph_plfit_result_t + * for more details. + * \param xmin the minimum value in the sample vector where the power-law + * behaviour is expected to kick in. Samples smaller than \c xmin + * will be ignored by the algorithm. Pass zero here if you want to + * include all the samples. If \c xmin is negative, the algorithm + * will attempt to determine its best value automatically. + * \param force_continuous assume that the samples in the \c data argument come + * from a continuous distribution even if the sample vector + * contains integer values only (by chance). If this argument is + * false, igraph will assume a continuous distribution if at least + * one sample is non-integer and assume a discrete distribution + * otherwise. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory + * \c IGRAPH_EINVAL: one of the arguments is invalid + * \c IGRAPH_EOVERFLOW: overflow during the fitting process + * \c IGRAPH_EUNDERFLOW: underflow during the fitting process + * \c IGRAPH_FAILURE: the underlying algorithm signaled a failure + * without returning a more specific error code + * + * Time complexity: in the continuous case, O(n log(n)) if \c xmin is given. + * In the discrete case, the time complexity is dominated by the complexity of + * the underlying L-BFGS algorithm that is used to optimize alpha. If \c xmin + * is not given, the time complexity is multiplied by the number of unique + * samples in the input vector (although it should be faster in practice). + * + * \example examples/simple/igraph_power_law_fit.c + */ +int igraph_power_law_fit(const igraph_vector_t* data, igraph_plfit_result_t* result, + igraph_real_t xmin, igraph_bool_t force_continuous) { + plfit_error_handler_t* plfit_stored_error_handler; + plfit_result_t plfit_result; + plfit_continuous_options_t cont_options; + plfit_discrete_options_t disc_options; + igraph_bool_t discrete = force_continuous ? 0 : 1; + igraph_bool_t finite_size_correction; + int retval; + size_t i, n; + + n = (size_t) igraph_vector_size(data); + finite_size_correction = (n < 50); + + if (discrete) { + /* Does the vector contain discrete values only? */ + for (i = 0; i < n; i++) { + if ((long int)(VECTOR(*data)[i]) != VECTOR(*data)[i]) { + discrete = 0; + break; + } + } + } + + RNG_BEGIN(); + + plfit_stored_error_handler = plfit_set_error_handler(igraph_i_plfit_error_handler_store); + if (discrete) { + plfit_discrete_options_init(&disc_options); + /* TODO: approximation method should be switched to PLFIT_P_VALUE_EXACT in igraph 0.9 */ + disc_options.p_value_method = PLFIT_P_VALUE_APPROXIMATE; + disc_options.finite_size_correction = (plfit_bool_t) finite_size_correction; + + if (xmin >= 0) { + retval = plfit_estimate_alpha_discrete(VECTOR(*data), n, xmin, + &disc_options, &plfit_result); + } else { + retval = plfit_discrete(VECTOR(*data), n, &disc_options, &plfit_result); + } + } else { + plfit_continuous_options_init(&cont_options); + /* TODO: approximation method should be switched to PLFIT_P_VALUE_EXACT in igraph 0.9 */ + cont_options.p_value_method = PLFIT_P_VALUE_APPROXIMATE; + /* TODO: xmin method should be switched to PLFIT_STRATIFIED_SAMPLING in igraph 0.9 */ + cont_options.xmin_method = PLFIT_GSS_OR_LINEAR; + cont_options.finite_size_correction = (plfit_bool_t) finite_size_correction; + + if (xmin >= 0) { + retval = plfit_estimate_alpha_continuous(VECTOR(*data), n, xmin, + &cont_options, &plfit_result); + } else { + retval = plfit_continuous(VECTOR(*data), n, &cont_options, &plfit_result); + } + } + plfit_set_error_handler(plfit_stored_error_handler); + + RNG_END(); + + switch (retval) { + case PLFIT_FAILURE: + IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_FAILURE); + break; + + case PLFIT_EINVAL: + IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EINVAL); + break; + + case PLFIT_UNDRFLOW: + IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EUNDERFLOW); + break; + + case PLFIT_OVERFLOW: + IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EOVERFLOW); + break; + + case PLFIT_ENOMEM: + IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_ENOMEM); + break; + + default: + break; + } + + if (result) { + result->continuous = !discrete; + result->alpha = plfit_result.alpha; + result->xmin = plfit_result.xmin; + result->L = plfit_result.L; + result->D = plfit_result.D; + result->p = plfit_result.p; + } + + return 0; +} diff --git a/src/rigraph/core/misc/scan.c b/src/rigraph/core/misc/scan.c new file mode 100644 index 0000000..573e426 --- /dev/null +++ b/src/rigraph/core/misc/scan.c @@ -0,0 +1,879 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_scan.h" + +#include "igraph_adjlist.h" +#include "igraph_arpack.h" +#include "igraph_centrality.h" +#include "igraph_dqueue.h" +#include "igraph_eigen.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_stack.h" +#include "igraph_structural.h" + +#include "../core/interruption.h" +#include "../properties/properties_internal.h" + +/** + * \section about_local_scan + * + * + * The scan statistic is a summary of the locality statistics that is computed + * from the local neighborhood of each vertex. For details, see + * Priebe, C. E., Conroy, J. M., Marchette, D. J., Park, Y. (2005). + * Scan Statistics on Enron Graphs. Computational and Mathematical Organization Theory. + * + */ + +/** + * \function igraph_local_scan_0 + * Local scan-statistics, k=0 + * + * K=0 scan-statistics is arbitrarily defined as the vertex degree for + * unweighted, and the vertex strength for weighted graphs. See \ref + * igraph_degree() and \ref igraph_strength(). + * + * \param graph The input graph + * \param res An initialized vector, the results are stored here. + * \param weights Weight vector for weighted graphs, null pointer for + * unweighted graphs. + * \param mode Type of the neighborhood, \c IGRAPH_OUT means outgoing, + * \c IGRAPH_IN means incoming and \c IGRAPH_ALL means all edges. + * \return Error code. + * + */ + +int igraph_local_scan_0(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + if (weights) { + igraph_strength(graph, res, igraph_vss_all(), mode, /*loops=*/ 1, + weights); + } else { + igraph_degree(graph, res, igraph_vss_all(), mode, /*loops=*/ 1); + } + return 0; +} + +/* This removes loop, multiple edges and edges that point + "backwards" according to the rank vector. It works on + edge lists */ + +static int igraph_i_trans4_il_simplify(const igraph_t *graph, igraph_inclist_t *il, + const igraph_vector_int_t *rank) { + + long int i; + long int n = il->length; + igraph_vector_int_t mark; + igraph_vector_int_init(&mark, n); + IGRAPH_FINALLY(igraph_vector_int_destroy, &mark); + + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = &il->incs[i]; + int j, l = igraph_vector_int_size(v); + int irank = VECTOR(*rank)[i]; + VECTOR(mark)[i] = i + 1; + for (j = 0; j < l; /* nothing */) { + long int edge = (long int) VECTOR(*v)[j]; + long int e = IGRAPH_OTHER(graph, edge, i); + if (VECTOR(*rank)[e] > irank && VECTOR(mark)[e] != i + 1) { + VECTOR(mark)[e] = i + 1; + j++; + } else { + VECTOR(*v)[j] = igraph_vector_int_tail(v); + igraph_vector_int_pop_back(v); + l--; + } + } + } + + igraph_vector_int_destroy(&mark); + IGRAPH_FINALLY_CLEAN(1); + return 0; + +} + +/* This one handles both weighted and unweighted cases */ + +static int igraph_i_local_scan_1_directed(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + + int no_of_nodes = igraph_vcount(graph); + igraph_inclist_t incs; + int i, node; + + igraph_vector_int_t neis; + + IGRAPH_CHECK(igraph_inclist_init(graph, &incs, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &incs); + + igraph_vector_int_init(&neis, no_of_nodes); + IGRAPH_FINALLY(igraph_vector_int_destroy, &neis); + + igraph_vector_resize(res, no_of_nodes); + igraph_vector_null(res); + + for (node = 0; node < no_of_nodes; node++) { + igraph_vector_int_t *edges1 = igraph_inclist_get(&incs, node); + int edgeslen1 = igraph_vector_int_size(edges1); + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Mark neighbors and self*/ + VECTOR(neis)[node] = node + 1; + for (i = 0; i < edgeslen1; i++) { + int e = VECTOR(*edges1)[i]; + int nei = IGRAPH_OTHER(graph, e, node); + igraph_real_t w = weights ? VECTOR(*weights)[e] : 1; + VECTOR(neis)[nei] = node + 1; + VECTOR(*res)[node] += w; + } + + /* Crawl neighbors */ + for (i = 0; i < edgeslen1; i++) { + int e2 = VECTOR(*edges1)[i]; + int nei = IGRAPH_OTHER(graph, e2, node); + if (nei == node) { + break; + } + igraph_vector_int_t *edges2 = igraph_inclist_get(&incs, nei); + int j, edgeslen2 = igraph_vector_int_size(edges2); + for (j = 0; j < edgeslen2; j++) { + int e2 = VECTOR(*edges2)[j]; + int nei2 = IGRAPH_OTHER(graph, e2, nei); + igraph_real_t w2 = weights ? VECTOR(*weights)[e2] : 1; + if (VECTOR(neis)[nei2] == node + 1) { + VECTOR(*res)[node] += w2; + } + } + } + + } /* node < no_of_nodes */ + + igraph_vector_int_destroy(&neis); + igraph_inclist_destroy(&incs); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +static int igraph_i_local_scan_1_directed_all(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vector_t *weights) { + + int no_of_nodes = igraph_vcount(graph); + igraph_inclist_t incs; + int i, node; + + igraph_vector_int_t neis; + + IGRAPH_CHECK(igraph_inclist_init(graph, &incs, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &incs); + + igraph_vector_int_init(&neis, no_of_nodes); + IGRAPH_FINALLY(igraph_vector_int_destroy, &neis); + + igraph_vector_resize(res, no_of_nodes); + igraph_vector_null(res); + + for (node = 0; node < no_of_nodes; node++) { + igraph_vector_int_t *edges1 = igraph_inclist_get(&incs, node); + int edgeslen1 = igraph_vector_int_size(edges1); + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Mark neighbors. We also count the edges that are incident to ego. + Note that this time we do not mark ego, because we don't want to + double count its incident edges later, when we are going over the + incident edges of ego's neighbors. */ + for (i = 0; i < edgeslen1; i++) { + int e = VECTOR(*edges1)[i]; + int nei = IGRAPH_OTHER(graph, e, node); + igraph_real_t w = weights ? VECTOR(*weights)[e] : 1; + VECTOR(neis)[nei] = node + 1; + VECTOR(*res)[node] += w; + } + + /* Crawl neighbors. We make sure that each neighbor of 'node' is + only crawed once. We count all qualifying edges of ego, and + then unmark ego to avoid double counting. */ + for (i = 0; i < edgeslen1; i++) { + int e2 = VECTOR(*edges1)[i]; + int nei = IGRAPH_OTHER(graph, e2, node); + igraph_vector_int_t *edges2; + int j, edgeslen2; + if (VECTOR(neis)[nei] != node + 1) { + continue; + } + edges2 = igraph_inclist_get(&incs, nei); + edgeslen2 = igraph_vector_int_size(edges2); + for (j = 0; j < edgeslen2; j++) { + int e2 = VECTOR(*edges2)[j]; + int nei2 = IGRAPH_OTHER(graph, e2, nei); + igraph_real_t w2 = weights ? VECTOR(*weights)[e2] : 1; + if (VECTOR(neis)[nei2] == node + 1) { + VECTOR(*res)[node] += w2; + } + } + VECTOR(neis)[nei] = 0; + } + + } /* node < no_of_nodes */ + + igraph_vector_int_destroy(&neis); + igraph_inclist_destroy(&incs); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +static int igraph_i_local_scan_1_sumweights(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vector_t *weights) { + + long int no_of_nodes = igraph_vcount(graph); + long int node, i, j, nn; + igraph_inclist_t allinc; + igraph_vector_int_t *neis1, *neis2; + long int neilen1, neilen2; + long int *neis; + long int maxdegree; + + igraph_vector_int_t order; + igraph_vector_int_t rank; + igraph_vector_t degree, *edge1 = °ree; /* reuse degree as edge1 */ + + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + } + + igraph_vector_int_init(&order, no_of_nodes); + IGRAPH_FINALLY(igraph_vector_int_destroy, &order); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS)); + maxdegree = (long int) igraph_vector_max(°ree) + 1; + igraph_vector_order1_int(°ree, &order, maxdegree); + igraph_vector_int_init(&rank, no_of_nodes); + IGRAPH_FINALLY(igraph_vector_int_destroy, &rank); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(rank)[ VECTOR(order)[i] ] = no_of_nodes - i - 1; + } + + IGRAPH_CHECK(igraph_inclist_init(graph, &allinc, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &allinc); + IGRAPH_CHECK(igraph_i_trans4_il_simplify(graph, &allinc, &rank)); + + neis = IGRAPH_CALLOC(no_of_nodes, long int); + if (neis == 0) { + IGRAPH_ERROR("undirected local transitivity failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, neis); + + IGRAPH_CHECK(igraph_strength(graph, res, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS, weights)); + + for (nn = no_of_nodes - 1; nn >= 0; nn--) { + node = VECTOR(order)[nn]; + + IGRAPH_ALLOW_INTERRUPTION(); + + neis1 = igraph_inclist_get(&allinc, node); + neilen1 = igraph_vector_int_size(neis1); + + /* Mark the neighbors of the node */ + for (i = 0; i < neilen1; i++) { + int edge = VECTOR(*neis1)[i]; + int nei = IGRAPH_OTHER(graph, edge, node); + VECTOR(*edge1)[nei] = VECTOR(*weights)[edge]; + neis[nei] = node + 1; + } + + for (i = 0; i < neilen1; i++) { + long int edge = VECTOR(*neis1)[i]; + long int nei = IGRAPH_OTHER(graph, edge, node); + igraph_real_t w = VECTOR(*weights)[edge]; + neis2 = igraph_inclist_get(&allinc, nei); + neilen2 = igraph_vector_int_size(neis2); + for (j = 0; j < neilen2; j++) { + long int edge2 = VECTOR(*neis2)[j]; + long int nei2 = IGRAPH_OTHER(graph, edge2, nei); + igraph_real_t w2 = VECTOR(*weights)[edge2]; + if (neis[nei2] == node + 1) { + VECTOR(*res)[node] += w2; + VECTOR(*res)[nei2] += w; + VECTOR(*res)[nei] += VECTOR(*edge1)[nei2]; + } + } + } + } + + igraph_free(neis); + igraph_inclist_destroy(&allinc); + igraph_vector_int_destroy(&rank); + igraph_vector_destroy(°ree); + igraph_vector_int_destroy(&order); + IGRAPH_FINALLY_CLEAN(5); + + return 0; +} + +/** + * \function igraph_local_scan_1_ecount + * Local scan-statistics, k=1, edge count and sum of weights + * + * Count the number of edges or the sum the edge weights in the + * 1-neighborhood of vertices. + * + * \param graph The input graph + * \param res An initialized vector, the results are stored here. + * \param weights Weight vector for weighted graphs, null pointer for + * unweighted graphs. + * \param mode Type of the neighborhood, \c IGRAPH_OUT means outgoing, + * \c IGRAPH_IN means incoming and \c IGRAPH_ALL means all edges. + * \return Error code. + * + */ + +int igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + + if (igraph_is_directed(graph)) { + if (mode != IGRAPH_ALL) { + return igraph_i_local_scan_1_directed(graph, res, weights, mode); + } else { + return igraph_i_local_scan_1_directed_all(graph, res, weights); + } + } else { + if (weights) { + return igraph_i_local_scan_1_sumweights(graph, res, weights); + } else { + return igraph_local_scan_k_ecount(graph, 1, res, weights, mode); + } + } + + return 0; +} + +static int igraph_i_local_scan_0_them_w(const igraph_t *us, const igraph_t *them, + igraph_vector_t *res, + const igraph_vector_t *weights_them, + igraph_neimode_t mode) { + + igraph_t is; + igraph_vector_t map2; + int i, m; + + if (!weights_them) { + IGRAPH_ERROR("Edge weights not given for weighted scan-0", + IGRAPH_EINVAL); + } + if (igraph_vector_size(weights_them) != igraph_ecount(them)) { + IGRAPH_ERROR("Invalid weights length for scan-0", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&map2, 0); + igraph_intersection(&is, us, them, /*map1=*/ 0, &map2); + IGRAPH_FINALLY(igraph_destroy, &is); + + /* Rewrite the map as edge weights */ + m = igraph_vector_size(&map2); + for (i = 0; i < m; i++) { + VECTOR(map2)[i] = VECTOR(*weights_them)[ (int) VECTOR(map2)[i] ]; + } + + igraph_strength(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS, + /*weights=*/ &map2); + + igraph_destroy(&is); + igraph_vector_destroy(&map2); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \function igraph_local_scan_0_them + * Local THEM scan-statistics, k=0 + * + * K=0 scan-statistics is arbitrarily defined as the vertex degree for + * unweighted, and the vertex strength for weighted graphs. See \ref + * igraph_degree() and \ref igraph_strength(). + * + * \param us The input graph, to use to extract the neighborhoods. + * \param them The input graph to use for the actually counting. + * \param res An initialized vector, the results are stored here. + * \param weights_them Weight vector for weighted graphs, null pointer for + * unweighted graphs. + * \param mode Type of the neighborhood, \c IGRAPH_OUT means outgoing, + * \c IGRAPH_IN means incoming and \c IGRAPH_ALL means all edges. + * \return Error code. + * + */ + +int igraph_local_scan_0_them(const igraph_t *us, const igraph_t *them, + igraph_vector_t *res, + const igraph_vector_t *weights_them, + igraph_neimode_t mode) { + + igraph_t is; + + if (igraph_vcount(us) != igraph_vcount(them)) { + IGRAPH_ERROR("Number of vertices don't match in scan-0", IGRAPH_EINVAL); + } + if (igraph_is_directed(us) != igraph_is_directed(them)) { + IGRAPH_ERROR("Directedness don't match in scan-0", IGRAPH_EINVAL); + } + + if (weights_them) { + return igraph_i_local_scan_0_them_w(us, them, res, weights_them, mode); + } + + igraph_intersection(&is, us, them, /*edgemap1=*/ 0, /*edgemap2=*/ 0); + IGRAPH_FINALLY(igraph_destroy, &is); + + igraph_degree(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS); + + igraph_destroy(&is); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_local_scan_1_ecount_them + * Local THEM scan-statistics, k=1, edge count and sum of weights + * + * Count the number of edges or the sum the edge weights in the + * 1-neighborhood of vertices. + * + * \param us The input graph to extract the neighborhoods. + * \param them The input graph to perform the counting. + * \param weights_them Weight vector for weighted graphs, null pointer for + * unweighted graphs. + * \param mode Type of the neighborhood, \c IGRAPH_OUT means outgoing, + * \c IGRAPH_IN means incoming and \c IGRAPH_ALL means all edges. + * \return Error code. + * + * \sa \ref igraph_local_scan_1_ecount() for the US statistics. + */ + +int igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them, + igraph_vector_t *res, + const igraph_vector_t *weights_them, + igraph_neimode_t mode) { + + int no_of_nodes = igraph_vcount(us); + igraph_adjlist_t adj_us; + igraph_inclist_t incs_them; + igraph_vector_int_t neis; + int node; + + if (igraph_vcount(them) != no_of_nodes) { + IGRAPH_ERROR("Number of vertices must match in scan-1", IGRAPH_EINVAL); + } + if (igraph_is_directed(us) != igraph_is_directed(them)) { + IGRAPH_ERROR("Directedness must match in scan-1", IGRAPH_EINVAL); + } + if (weights_them && + igraph_vector_size(weights_them) != igraph_ecount(them)) { + IGRAPH_ERROR("Invalid weight vector length in scan-1 (them)", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_adjlist_init( + us, &adj_us, mode, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE + )); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adj_us); + IGRAPH_CHECK(igraph_inclist_init(them, &incs_them, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &incs_them); + + IGRAPH_CHECK(igraph_vector_int_init(&neis, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &neis); + + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + igraph_vector_null(res); + + for (node = 0; node < no_of_nodes; node++) { + igraph_vector_int_t *neis_us = igraph_adjlist_get(&adj_us, node); + igraph_vector_int_t *edges1_them = igraph_inclist_get(&incs_them, node); + int len1_us = igraph_vector_int_size(neis_us); + int len1_them = igraph_vector_int_size(edges1_them); + int i; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Mark neighbors and self in us */ + VECTOR(neis)[node] = node + 1; + for (i = 0; i < len1_us; i++) { + int nei = VECTOR(*neis_us)[i]; + VECTOR(neis)[nei] = node + 1; + } + + /* Crawl neighbors in them, first ego */ + for (i = 0; i < len1_them; i++) { + int e = VECTOR(*edges1_them)[i]; + int nei = IGRAPH_OTHER(them, e, node); + if (VECTOR(neis)[nei] == node + 1) { + igraph_real_t w = weights_them ? VECTOR(*weights_them)[e] : 1; + VECTOR(*res)[node] += w; + } + } + /* Then the rest */ + for (i = 0; i < len1_us; i++) { + int nei = VECTOR(*neis_us)[i]; + igraph_vector_int_t *edges2_them = igraph_inclist_get(&incs_them, nei); + int j, len2_them = igraph_vector_int_size(edges2_them); + for (j = 0; j < len2_them; j++) { + int e2 = VECTOR(*edges2_them)[j]; + int nei2 = IGRAPH_OTHER(them, e2, nei); + if (VECTOR(neis)[nei2] == node + 1) { + igraph_real_t w = weights_them ? VECTOR(*weights_them)[e2] : 1; + VECTOR(*res)[node] += w; + } + } + } + + /* For undirected, it was double counted */ + if (mode == IGRAPH_ALL || ! igraph_is_directed(us)) { + VECTOR(*res)[node] /= 2.0; + } + + } /* node < no_of_nodes */ + + igraph_vector_int_destroy(&neis); + igraph_inclist_destroy(&incs_them); + igraph_adjlist_destroy(&adj_us); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \function igraph_local_scan_k_ecount + * \brief Sum the number of edges or the weights in k-neighborhood of every vertex. + * + * \param graph The input graph. + * \param k The size of the neighborhood, non-negative integer. + * The k=0 case is special, see \ref igraph_local_scan_0(). + * \param res An initialized vector, the results are stored here. + * \param weights Weight vector for weighted graphs, null pointer for + * unweighted graphs. + * \param mode Type of the neighborhood, \c IGRAPH_OUT means outgoing, + * \c IGRAPH_IN means incoming and \c IGRAPH_ALL means all edges. + * \return Error code. + * + */ + +int igraph_local_scan_k_ecount(const igraph_t *graph, int k, + igraph_vector_t *res, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + + int no_of_nodes = igraph_vcount(graph); + int node; + igraph_dqueue_int_t Q; + igraph_vector_int_t marked; + igraph_inclist_t incs; + + if (k < 0) { + IGRAPH_ERROR("k must be non-negative in k-scan.", IGRAPH_EINVAL); + } + if (weights && igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERRORF("The weight vector length (%ld) in k-scan should equal " + "the number of edges of the graph (%d).", + IGRAPH_EINVAL, igraph_vector_size(weights), + igraph_ecount(graph)); + } + + if (k == 0) { + return igraph_local_scan_0(graph, res, weights, mode); + } + if (k == 1 && igraph_is_directed(graph)) { + return igraph_local_scan_1_ecount(graph, res, weights, mode); + } + + /* We do a BFS form each node, and simply count the number + of edges on the way */ + + IGRAPH_CHECK(igraph_dqueue_int_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); + IGRAPH_CHECK(igraph_vector_int_init(&marked, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &marked); + IGRAPH_CHECK(igraph_inclist_init(graph, &incs, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &incs); + + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + igraph_vector_null(res); + + for (node = 0 ; node < no_of_nodes ; node++) { + igraph_dqueue_int_push(&Q, node); + igraph_dqueue_int_push(&Q, 0); + VECTOR(marked)[node] = node + 1; + while (!igraph_dqueue_int_empty(&Q)) { + int act = igraph_dqueue_int_pop(&Q); + int dist = igraph_dqueue_int_pop(&Q) + 1; + igraph_vector_int_t *edges = igraph_inclist_get(&incs, act); + int i, edgeslen = igraph_vector_int_size(edges); + for (i = 0; i < edgeslen; i++) { + int edge = VECTOR(*edges)[i]; + int nei = IGRAPH_OTHER(graph, edge, act); + if (dist <= k || VECTOR(marked)[nei] == node + 1) { + igraph_real_t w = weights ? VECTOR(*weights)[edge] : 1; + VECTOR(*res)[node] += w; + } + if (dist <= k && VECTOR(marked)[nei] != node + 1) { + igraph_dqueue_int_push(&Q, nei); + igraph_dqueue_int_push(&Q, dist); + VECTOR(marked)[nei] = node + 1; + } + } + } + + if (mode == IGRAPH_ALL || ! igraph_is_directed(graph)) { + VECTOR(*res)[node] /= 2.0; + } + + } /* node < no_of_nodes */ + + igraph_inclist_destroy(&incs); + igraph_vector_int_destroy(&marked); + igraph_dqueue_int_destroy(&Q); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_local_scan_k_ecount_them + * Local THEM scan-statistics, general function, edge count and sum of weights + * + * Count the number of edges or the sum the edge weights in the + * k-neighborhood of vertices. + * + * \param us The input graph to extract the neighborhoods. + * \param them The input graph to perform the counting. + * \param k The size of the neighborhood, non-negative integer. + * The k=0 case is special, see \ref igraph_local_scan_0_them(). + * \param weights_them Weight vector for weighted graphs, null pointer for + * unweighted graphs. + * \param mode Type of the neighborhood, \c IGRAPH_OUT means outgoing, + * \c IGRAPH_IN means incoming and \c IGRAPH_ALL means all edges. + * \return Error code. + * + * \sa \ref igraph_local_scan_1_ecount() for the US statistics. + */ + +int igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *them, + int k, igraph_vector_t *res, + const igraph_vector_t *weights_them, + igraph_neimode_t mode) { + + int no_of_nodes = igraph_vcount(us); + int node; + igraph_dqueue_int_t Q; + igraph_vector_int_t marked; + igraph_stack_int_t ST; + igraph_inclist_t incs_us, incs_them; + + if (igraph_vcount(them) != no_of_nodes) { + IGRAPH_ERROR("Number of vertices must match in scan-k", IGRAPH_EINVAL); + } + if (igraph_is_directed(us) != igraph_is_directed(them)) { + IGRAPH_ERROR("Directedness must match in scan-k", IGRAPH_EINVAL); + } + if (k < 0) { + IGRAPH_ERROR("k must be non-negative in k-scan", IGRAPH_EINVAL); + } + if (weights_them && + igraph_vector_size(weights_them) != igraph_ecount(them)) { + IGRAPH_ERROR("Invalid weight vector length in k-scan (them)", + IGRAPH_EINVAL); + } + + if (k == 0) { + return igraph_local_scan_0_them(us, them, res, weights_them, mode); + } + if (k == 1) { + return igraph_local_scan_1_ecount_them(us, them, res, weights_them, mode); + } + + /* We mark the nodes in US in a BFS. Then we check the outgoing edges + of all marked nodes in THEM. */ + + IGRAPH_CHECK(igraph_dqueue_int_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); + IGRAPH_CHECK(igraph_vector_int_init(&marked, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &marked); + IGRAPH_CHECK(igraph_inclist_init(us, &incs_us, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &incs_us); + IGRAPH_CHECK(igraph_inclist_init(them, &incs_them, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &incs_them); + IGRAPH_CHECK(igraph_stack_int_init(&ST, 100)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &ST); + + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + igraph_vector_null(res); + + for (node = 0; node < no_of_nodes; node++) { + + /* BFS to mark the nodes in US */ + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, node)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, 0)); + IGRAPH_CHECK(igraph_stack_int_push(&ST, node)); + VECTOR(marked)[node] = node + 1; + while (!igraph_dqueue_int_empty(&Q)) { + int act = igraph_dqueue_int_pop(&Q); + int dist = igraph_dqueue_int_pop(&Q) + 1; + igraph_vector_int_t *edges = igraph_inclist_get(&incs_us, act); + int i, edgeslen = igraph_vector_int_size(edges); + for (i = 0; i < edgeslen; i++) { + int edge = VECTOR(*edges)[i]; + int nei = IGRAPH_OTHER(us, edge, act); + if (dist <= k && VECTOR(marked)[nei] != node + 1) { + igraph_dqueue_int_push(&Q, nei); + igraph_dqueue_int_push(&Q, dist); + VECTOR(marked)[nei] = node + 1; + igraph_stack_int_push(&ST, nei); + } + } + } + + /* Now check the edges of all nodes in THEM */ + while (!igraph_stack_int_empty(&ST)) { + int act = igraph_stack_int_pop(&ST); + igraph_vector_int_t *edges = igraph_inclist_get(&incs_them, act); + int i, edgeslen = igraph_vector_int_size(edges); + for (i = 0; i < edgeslen; i++) { + int edge = VECTOR(*edges)[i]; + int nei = IGRAPH_OTHER(them, edge, act); + if (VECTOR(marked)[nei] == node + 1) { + igraph_real_t w = weights_them ? VECTOR(*weights_them)[edge] : 1; + VECTOR(*res)[node] += w; + } + } + } + + if (mode == IGRAPH_ALL || ! igraph_is_directed(us)) { + VECTOR(*res)[node] /= 2; + } + + } /* node < no_of_nodes */ + + igraph_stack_int_destroy(&ST); + igraph_inclist_destroy(&incs_them); + igraph_inclist_destroy(&incs_us); + igraph_vector_int_destroy(&marked); + igraph_dqueue_int_destroy(&Q); + IGRAPH_FINALLY_CLEAN(5); + + return 0; +} + +/** + * \function igraph_local_scan_neighborhood_ecount + * Local scan-statistics with pre-calculated neighborhoods + * + * Count the number of edges, or sum the edge weigths in + * neighborhoods given as a parameter. + * + * \param graph The graph to perform the counting/summing in. + * \param res Initialized vector, the result is stored here. + * \param weights Weight vector for weighted graphs, null pointer for + * unweighted graphs. + * \param neighborhoods List of igraph_vector_int_t + * objects, the neighborhoods, one for each vertex in the + * graph. + * \return Error code. + */ + +int igraph_local_scan_neighborhood_ecount(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vector_t *weights, + const igraph_vector_ptr_t *neighborhoods) { + + int node, no_of_nodes = igraph_vcount(graph); + igraph_inclist_t incs; + igraph_vector_int_t marked; + igraph_bool_t directed = igraph_is_directed(graph); + + if (weights && igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length in local scan", IGRAPH_EINVAL); + } + if (igraph_vector_ptr_size(neighborhoods) != no_of_nodes) { + IGRAPH_ERROR("Invalid neighborhood list length in local scan", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_int_init(&marked, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &marked); + IGRAPH_CHECK(igraph_inclist_init(graph, &incs, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &incs); + + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + igraph_vector_null(res); + + for (node = 0; node < no_of_nodes; node++) { + igraph_vector_int_t *nei = VECTOR(*neighborhoods)[node]; + int i, neilen = igraph_vector_int_size(nei); + VECTOR(marked)[node] = node + 1; + for (i = 0; i < neilen; i++) { + int vertex = VECTOR(*nei)[i]; + if (vertex < 0 || vertex >= no_of_nodes) { + IGRAPH_ERROR("Invalid vertex id in neighborhood list in local scan", + IGRAPH_EINVAL); + } + VECTOR(marked)[vertex] = node + 1; + } + + for (i = 0; i < neilen; i++) { + int vertex = VECTOR(*nei)[i]; + igraph_vector_int_t *edges = igraph_inclist_get(&incs, vertex); + int j, edgeslen = igraph_vector_int_size(edges); + for (j = 0; j < edgeslen; j++) { + int edge = VECTOR(*edges)[j]; + int nei2 = IGRAPH_OTHER(graph, edge, vertex); + if (VECTOR(marked)[nei2] == node + 1) { + igraph_real_t w = weights ? VECTOR(*weights)[edge] : 1; + VECTOR(*res)[node] += w; + } + } + } + if (!directed) { + VECTOR(*res)[node] /= 2.0; + } + } + + igraph_inclist_destroy(&incs); + igraph_vector_int_destroy(&marked); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} diff --git a/src/rigraph/core/misc/sir.c b/src/rigraph/core/misc/sir.c new file mode 100644 index 0000000..69cd737 --- /dev/null +++ b/src/rigraph/core/misc/sir.c @@ -0,0 +1,262 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2014 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_epidemics.h" + +#include "igraph_random.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_psumtree.h" +#include "igraph_memory.h" +#include "igraph_structural.h" + +#include "../core/interruption.h" + +int igraph_sir_init(igraph_sir_t *sir) { + IGRAPH_CHECK(igraph_vector_init(&sir->times, 1)); + IGRAPH_FINALLY(igraph_vector_destroy, &sir->times); + IGRAPH_CHECK(igraph_vector_int_init(&sir->no_s, 1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &sir->no_s); + IGRAPH_CHECK(igraph_vector_int_init(&sir->no_i, 1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &sir->no_i); + IGRAPH_CHECK(igraph_vector_int_init(&sir->no_r, 1)); + IGRAPH_FINALLY_CLEAN(3); + return 0; +} + +/** + * \function igraph_sir_destroy + * \brief Deallocates memory associated with a SIR simulation run. + * + * \param sir The \ref igraph_sir_t object storing the simulation. + */ + +void igraph_sir_destroy(igraph_sir_t *sir) { + igraph_vector_destroy(&sir->times); + igraph_vector_int_destroy(&sir->no_s); + igraph_vector_int_destroy(&sir->no_i); + igraph_vector_int_destroy(&sir->no_r); +} + +static void igraph_i_sir_destroy(igraph_vector_ptr_t *v) { + int i, n = igraph_vector_ptr_size(v); + for (i = 0; i < n; i++) { + if ( VECTOR(*v)[i] ) { + igraph_sir_destroy( VECTOR(*v)[i]) ; + IGRAPH_FREE( VECTOR(*v)[i] ); /* this also sets the vector_ptr element to NULL */ + } + } +} + +#define S_S 0 +#define S_I 1 +#define S_R 2 + +/** + * \function igraph_sir + * \brief Performs a number of SIR epidemics model runs on a graph. + * + * The SIR model is a simple model from epidemiology. The individuals + * of the population might be in three states: susceptible, infected + * and recovered. Recovered people are assumed to be immune to the + * disease. Susceptibles become infected with a rate that depends on + * their number of infected neigbors. Infected people become recovered + * with a constant rate. See these parameters below. + * + * + * This function runs multiple simulations, all starting with a + * single uniformly randomly chosen infected individual. A simulation + * is stopped when no infected individuals are left. + * + * \param graph The graph to perform the model on. For directed graphs + * edge directions are ignored and a warning is given. + * \param beta The rate of infection of an individual that is + * susceptible and has a single infected neighbor. + * The infection rate of a susceptible individual with n + * infected neighbors is n times beta. Formally + * this is the rate parameter of an exponential distribution. + * \param gamma The rate of recovery of an infected individual. + * Formally, this is the rate parameter of an exponential + * distribution. + * \param no_sim The number of simulation runs to perform. + * \param result The result of the simulation is stored here, + * in a list of \ref igraph_sir_t objects. To deallocate + * memory, the user needs to call \ref igraph_sir_destroy on + * each element, before destroying the pointer vector itself + * using \ref igraph_vector_ptr_destroy_all(). + * \return Error code. + * + * Time complexity: O(no_sim * (|V| + |E| log(|V|))). + */ + +int igraph_sir(const igraph_t *graph, igraph_real_t beta, + igraph_real_t gamma, igraph_integer_t no_sim, + igraph_vector_ptr_t *result) { + + int infected; + igraph_vector_int_t status; + igraph_adjlist_t adjlist; + int no_of_nodes = igraph_vcount(graph); + int i, j, ns, ni, nr; + igraph_vector_int_t *neis; + igraph_psumtree_t tree; + igraph_real_t psum; + int neilen; + igraph_bool_t simple; + + if (no_of_nodes == 0) { + IGRAPH_ERROR("Cannot run SIR model on empty graph.", IGRAPH_EINVAL); + } + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("Edge directions are ignored in SIR model."); + } + if (beta < 0) { + IGRAPH_ERROR("The infection rate beta must be non-negative in SIR model.", IGRAPH_EINVAL); + } + /* With a recovery rate of zero, the simulation would never stop. */ + if (gamma <= 0) { + IGRAPH_ERROR("The recovery rate gamma must be positive in SIR model.", IGRAPH_EINVAL); + } + if (no_sim <= 0) { + IGRAPH_ERROR("Number of SIR simulations must be positive.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_is_simple(graph, &simple)); + if (!simple) { + IGRAPH_ERROR("SIR model only works with simple graphs.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_int_init(&status, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &status); + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + IGRAPH_CHECK(igraph_psumtree_init(&tree, no_of_nodes)); + IGRAPH_FINALLY(igraph_psumtree_destroy, &tree); + + IGRAPH_CHECK(igraph_vector_ptr_resize(result, no_sim)); + igraph_vector_ptr_null(result); + IGRAPH_FINALLY(igraph_i_sir_destroy, result); + for (i = 0; i < no_sim; i++) { + igraph_sir_t *sir = IGRAPH_CALLOC(1, igraph_sir_t); + if (!sir) { + IGRAPH_ERROR("Cannot run SIR model.", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_sir_init(sir)); + VECTOR(*result)[i] = sir; + } + + RNG_BEGIN(); + + for (j = 0; j < no_sim; j++) { + + igraph_sir_t *sir = VECTOR(*result)[j]; + igraph_vector_t *times_v = &sir->times; + igraph_vector_int_t *no_s_v = &sir->no_s; + igraph_vector_int_t *no_i_v = &sir->no_i; + igraph_vector_int_t *no_r_v = &sir->no_r; + + infected = RNG_INTEGER(0, no_of_nodes - 1); + + /* Initially infected */ + igraph_vector_int_null(&status); + VECTOR(status)[infected] = S_I; + ns = no_of_nodes - 1; + ni = 1; + nr = 0; + + VECTOR(*times_v)[0] = 0.0; + VECTOR(*no_s_v)[0] = ns; + VECTOR(*no_i_v)[0] = ni; + VECTOR(*no_r_v)[0] = nr; + + if (igraph_psumtree_sum(&tree) != 0) { + igraph_psumtree_reset(&tree); + } + + /* Rates */ + IGRAPH_CHECK(igraph_psumtree_update(&tree, infected, gamma)); + neis = igraph_adjlist_get(&adjlist, infected); + neilen = igraph_vector_int_size(neis); + for (i = 0; i < neilen; i++) { + int nei = VECTOR(*neis)[i]; + IGRAPH_CHECK(igraph_psumtree_update(&tree, nei, beta)); + } + + while (ni > 0) { + igraph_real_t tt; + igraph_real_t r; + long int vchange; + + IGRAPH_ALLOW_INTERRUPTION(); + + psum = igraph_psumtree_sum(&tree); + tt = igraph_rng_get_exp(igraph_rng_default(), psum); + r = RNG_UNIF(0, psum); + + igraph_psumtree_search(&tree, &vchange, r); + neis = igraph_adjlist_get(&adjlist, vchange); + neilen = igraph_vector_int_size(neis); + + if (VECTOR(status)[vchange] == S_I) { + VECTOR(status)[vchange] = S_R; + ni--; nr++; + IGRAPH_CHECK(igraph_psumtree_update(&tree, vchange, 0.0)); + for (i = 0; i < neilen; i++) { + int nei = VECTOR(*neis)[i]; + if (VECTOR(status)[nei] == S_S) { + igraph_real_t rate = igraph_psumtree_get(&tree, nei); + IGRAPH_CHECK(igraph_psumtree_update(&tree, nei, rate - beta)); + } + } + + } else { /* S_S */ + VECTOR(status)[vchange] = S_I; + ns--; ni++; + IGRAPH_CHECK(igraph_psumtree_update(&tree, vchange, gamma)); + for (i = 0; i < neilen; i++) { + int nei = VECTOR(*neis)[i]; + if (VECTOR(status)[nei] == S_S) { + igraph_real_t rate = igraph_psumtree_get(&tree, nei); + IGRAPH_CHECK(igraph_psumtree_update(&tree, nei, rate + beta)); + } + } + } + + IGRAPH_CHECK(igraph_vector_push_back(times_v, tt + igraph_vector_tail(times_v))); + IGRAPH_CHECK(igraph_vector_int_push_back(no_s_v, ns)); + IGRAPH_CHECK(igraph_vector_int_push_back(no_i_v, ni)); + IGRAPH_CHECK(igraph_vector_int_push_back(no_r_v, nr)); + + } /* psum > 0 */ + + } /* j < no_sim */ + + RNG_END(); + + igraph_psumtree_destroy(&tree); + igraph_adjlist_destroy(&adjlist); + igraph_vector_int_destroy(&status); + IGRAPH_FINALLY_CLEAN(4); /* + result */ + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/misc/spanning_trees.c b/src/rigraph/core/misc/spanning_trees.c new file mode 100644 index 0000000..b4cc3d5 --- /dev/null +++ b/src/rigraph/core/misc/spanning_trees.c @@ -0,0 +1,513 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011 Gabor Csardi + Rue de l'Industrie 5, Lausanne 1005, Switzerland + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_progress.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +#include "../core/indheap.h" +#include "../core/interruption.h" + +static int igraph_i_minimum_spanning_tree_unweighted(const igraph_t *graph, + igraph_vector_t *result); +static int igraph_i_minimum_spanning_tree_prim(const igraph_t *graph, + igraph_vector_t *result, const igraph_vector_t *weights); + +/** + * \ingroup structural + * \function igraph_minimum_spanning_tree + * \brief Calculates one minimum spanning tree of a graph. + * + * + * If the graph has more minimum spanning trees (this is always the + * case, except if it is a forest) this implementation returns only + * the same one. + * + * + * Directed graphs are considered as undirected for this computation. + * + * + * If the graph is not connected then its minimum spanning forest is + * returned. This is the set of the minimum spanning trees of each + * component. + * + * \param graph The graph object. + * \param res An initialized vector, the IDs of the edges that constitute + * a spanning tree will be returned here. Use + * \ref igraph_subgraph_edges() to extract the spanning tree as + * a separate graph object. + * \param weights A vector containing the weights of the edges + * in the same order as the simple edge iterator visits them + * (i.e. in increasing order of edge IDs). + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * + * Time complexity: O(|V|+|E|) for the unweighted case, O(|E| log |V|) + * for the weighted case. |V| is the number of vertices, |E| the + * number of edges in the graph. + * + * \sa \ref igraph_minimum_spanning_tree_unweighted() and + * \ref igraph_minimum_spanning_tree_prim() if you only need the + * tree as a separate graph object. + * + * \example examples/simple/igraph_minimum_spanning_tree.c + */ +int igraph_minimum_spanning_tree(const igraph_t* graph, + igraph_vector_t* res, const igraph_vector_t* weights) { + if (weights == 0) { + IGRAPH_CHECK(igraph_i_minimum_spanning_tree_unweighted(graph, res)); + } else { + IGRAPH_CHECK(igraph_i_minimum_spanning_tree_prim(graph, res, weights)); + } + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_minimum_spanning_tree_unweighted + * \brief Calculates one minimum spanning tree of an unweighted graph. + * + * + * If the graph has more minimum spanning trees (this is always the + * case, except if it is a forest) this implementation returns only + * the same one. + * + * + * Directed graphs are considered as undirected for this computation. + * + * + * If the graph is not connected then its minimum spanning forest is + * returned. This is the set of the minimum spanning trees of each + * component. + * \param graph The graph object. + * \param mst The minimum spanning tree, another graph object. Do + * \em not initialize this object before passing it to + * this function, but be sure to call \ref igraph_destroy() on it if + * you don't need it any more. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * + * Time complexity: O(|V|+|E|), + * |V| is the + * number of vertices, |E| the number + * of edges in the graph. + * + * \sa \ref igraph_minimum_spanning_tree_prim() for weighted graphs, + * \ref igraph_minimum_spanning_tree() if you need the IDs of the + * edges that constitute the spanning tree. + */ + +int igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, + igraph_t *mst) { + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, igraph_vcount(graph) - 1); + IGRAPH_CHECK(igraph_i_minimum_spanning_tree_unweighted(graph, &edges)); + IGRAPH_CHECK(igraph_subgraph_edges(graph, mst, + igraph_ess_vector(&edges), /* delete_vertices = */ 0)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \ingroup structural + * \function igraph_minimum_spanning_tree_prim + * \brief Calculates one minimum spanning tree of a weighted graph. + * + * + * This function uses Prim's method for carrying out the computation, + * see Prim, R.C.: Shortest connection networks and some + * generalizations, Bell System Technical + * Journal, Vol. 36, + * 1957, 1389--1401. + * + * + * If the graph has more than one minimum spanning tree, the current + * implementation returns always the same one. + * + * + * Directed graphs are considered as undirected for this computation. + * + * + * If the graph is not connected then its minimum spanning forest is + * returned. This is the set of the minimum spanning trees of each + * component. + * + * \param graph The graph object. + * \param mst The result of the computation, a graph object containing + * the minimum spanning tree of the graph. + * Do \em not initialize this object before passing it to + * this function, but be sure to call \ref igraph_destroy() on it if + * you don't need it any more. + * \param weights A vector containing the weights of the edges + * in the same order as the simple edge iterator visits them + * (i.e. in increasing order of edge IDs). + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory. + * \c IGRAPH_EINVAL, length of weight vector does not + * match number of edges. + * + * Time complexity: O(|E| log |V|), + * |V| is the number of vertices, + * |E| the number of edges in the + * graph. + * + * \sa \ref igraph_minimum_spanning_tree_unweighted() for unweighted graphs, + * \ref igraph_minimum_spanning_tree() if you need the IDs of the + * edges that constitute the spanning tree. + * + * \example examples/simple/igraph_minimum_spanning_tree.c + */ + +int igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, + const igraph_vector_t *weights) { + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, igraph_vcount(graph) - 1); + IGRAPH_CHECK(igraph_i_minimum_spanning_tree_prim(graph, &edges, weights)); + IGRAPH_CHECK(igraph_subgraph_edges(graph, mst, + igraph_ess_vector(&edges), /* delete_vertices = */ 0)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + + +static int igraph_i_minimum_spanning_tree_unweighted(const igraph_t* graph, igraph_vector_t* res) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + char *already_added; + char *added_edges; + + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_vector_t tmp = IGRAPH_VECTOR_NULL; + long int i, j; + + igraph_vector_clear(res); + + added_edges = IGRAPH_CALLOC(no_of_edges, char); + if (added_edges == 0) { + IGRAPH_ERROR("unweighted spanning tree failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added_edges); + already_added = IGRAPH_CALLOC(no_of_nodes, char); + if (already_added == 0) { + IGRAPH_ERROR("unweighted spanning tree failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, already_added); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + for (i = 0; i < no_of_nodes; i++) { + if (already_added[i] > 0) { + continue; + } + + IGRAPH_ALLOW_INTERRUPTION(); + + already_added[i] = 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, i)); + while (! igraph_dqueue_empty(&q)) { + long int tmp_size; + long int act_node = (long int) igraph_dqueue_pop(&q); + IGRAPH_CHECK(igraph_incident(graph, &tmp, (igraph_integer_t) act_node, + IGRAPH_ALL)); + tmp_size = igraph_vector_size(&tmp); + for (j = 0; j < tmp_size; j++) { + long int edge = (long int) VECTOR(tmp)[j]; + if (added_edges[edge] == 0) { + igraph_integer_t to = IGRAPH_OTHER(graph, edge, act_node); + if (already_added[(long int) to] == 0) { + already_added[(long int) to] = 1; + added_edges[edge] = 1; + IGRAPH_CHECK(igraph_vector_push_back(res, edge)); + IGRAPH_CHECK(igraph_dqueue_push(&q, to)); + } + } + } + } + } + + igraph_dqueue_destroy(&q); + IGRAPH_FREE(already_added); + igraph_vector_destroy(&tmp); + IGRAPH_FREE(added_edges); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_minimum_spanning_tree_prim( + const igraph_t* graph, igraph_vector_t* res, const igraph_vector_t *weights) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + char *already_added; + char *added_edges; + + igraph_d_indheap_t heap = IGRAPH_D_INDHEAP_NULL; + igraph_integer_t mode = IGRAPH_ALL; + + igraph_vector_t adj; + + long int i, j; + + igraph_vector_clear(res); + + if (weights == 0) { + return igraph_i_minimum_spanning_tree_unweighted(graph, res); + } + + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weights length", IGRAPH_EINVAL); + } + + added_edges = IGRAPH_CALLOC(no_of_edges, char); + if (added_edges == 0) { + IGRAPH_ERROR("prim spanning tree failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added_edges); + already_added = IGRAPH_CALLOC(no_of_nodes, char); + if (already_added == 0) { + IGRAPH_ERROR("prim spanning tree failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, already_added); + IGRAPH_CHECK(igraph_d_indheap_init(&heap, 0)); + IGRAPH_FINALLY(igraph_d_indheap_destroy, &heap); + IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); + + for (i = 0; i < no_of_nodes; i++) { + long int adj_size; + if (already_added[i] > 0) { + continue; + } + IGRAPH_ALLOW_INTERRUPTION(); + + already_added[i] = 1; + /* add all edges of the first vertex */ + igraph_incident(graph, &adj, (igraph_integer_t) i, (igraph_neimode_t) mode); + adj_size = igraph_vector_size(&adj); + for (j = 0; j < adj_size; j++) { + igraph_integer_t edgeno = (long int) VECTOR(adj)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, edgeno, i); + if (already_added[(long int) neighbor] == 0) { + IGRAPH_CHECK(igraph_d_indheap_push(&heap, -VECTOR(*weights)[edgeno], i, + edgeno)); + } + } + + while (! igraph_d_indheap_empty(&heap)) { + /* Get minimal edge */ + long int from, edge; + igraph_d_indheap_max_index(&heap, &from, &edge); + + /* Erase it */ + igraph_d_indheap_delete_max(&heap); + + /* Is this edge already included? */ + if (added_edges[edge] == 0) { + igraph_integer_t to = IGRAPH_OTHER(graph, edge, from); + + /* Does it point to a visited node? */ + if (already_added[(long int)to] == 0) { + already_added[(long int)to] = 1; + added_edges[edge] = 1; + IGRAPH_CHECK(igraph_vector_push_back(res, edge)); + /* add all outgoing edges */ + igraph_incident(graph, &adj, to, (igraph_neimode_t) mode); + adj_size = igraph_vector_size(&adj); + for (j = 0; j < adj_size; j++) { + long int edgeno = (long int) VECTOR(adj)[j]; + long int neighbor = IGRAPH_OTHER(graph, edgeno, to); + if (already_added[neighbor] == 0) { + IGRAPH_CHECK(igraph_d_indheap_push(&heap, -VECTOR(*weights)[edgeno], to, + edgeno)); + } + } + } /* for */ + } /* if !already_added */ + } /* while in the same component */ + } /* for all nodes */ + + igraph_d_indheap_destroy(&heap); + IGRAPH_FREE(already_added); + igraph_vector_destroy(&adj); + IGRAPH_FREE(added_edges); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + + +/* igraph_random_spanning_tree */ + +/* Loop-erased random walk (LERW) implementation. + * res must be an initialized vector. The edge IDs of the spanning tree + * will be added to the end of it. res will not be cleared before doing this. + * + * The walk is started from vertex start. comp_size must be the size of the connected + * component containing start. + */ +static int igraph_i_lerw(const igraph_t *graph, igraph_vector_t *res, igraph_integer_t start, + igraph_integer_t comp_size, igraph_vector_bool_t *visited, const igraph_inclist_t *il) { + igraph_integer_t visited_count; + + IGRAPH_CHECK(igraph_vector_reserve(res, igraph_vector_size(res) + comp_size - 1)); + + RNG_BEGIN(); + + VECTOR(*visited)[start] = 1; + visited_count = 1; + + while (visited_count < comp_size) { + long degree, edge; + igraph_vector_int_t *edges; + + edges = igraph_inclist_get(il, start); + + /* choose a random edge */ + degree = igraph_vector_int_size(edges); + edge = VECTOR(*edges)[ RNG_INTEGER(0, degree - 1) ]; + + /* set 'start' to the next vertex */ + start = IGRAPH_OTHER(graph, edge, start); + + /* if the next vertex hasn't been visited yet, register the edge we just traversed */ + if (! VECTOR(*visited)[start]) { + IGRAPH_CHECK(igraph_vector_push_back(res, edge)); + VECTOR(*visited)[start] = 1; + visited_count++; + } + + IGRAPH_ALLOW_INTERRUPTION(); + } + + RNG_END(); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_random_spanning_tree + * \brief Uniformly sample the spanning trees of a graph + * + * Performs a loop-erased random walk on the graph to uniformly sample + * its spanning trees. Edge directions are ignored. + * + * + * Multi-graphs are supported, and edge multiplicities will affect the sampling + * frequency. For example, consider the 3-cycle graph 1=2-3-1, with two edges + * between vertices 1 and 2. Due to these parallel edges, the trees 1-2-3 + * and 3-1-2 will be sampled with multiplicity 2, while the tree + * 2-3-1 will be sampled with multiplicity 1. + * + * \param graph The input graph. Edge directions are ignored. + * \param res An initialized vector, the IDs of the edges that constitute + * a spanning tree will be returned here. Use + * \ref igraph_subgraph_edges() to extract the spanning tree as + * a separate graph object. + * \param vid This parameter is relevant if the graph is not connected. + * If negative, a random spanning forest of all components will be + * generated. Otherwise, it should be the ID of a vertex. A random + * spanning tree of the component containing the vertex will be + * generated. + * + * \return Error code. + * + * \sa \ref igraph_minimum_spanning_tree(), \ref igraph_random_walk() + * + */ +int igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_t *res, igraph_integer_t vid) { + igraph_inclist_t il; + igraph_vector_bool_t visited; + igraph_integer_t vcount = igraph_vcount(graph); + + if (vid >= vcount) { + IGRAPH_ERROR("Invalid vertex id given for random spanning tree", IGRAPH_EINVVID); + } + + IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + + IGRAPH_CHECK(igraph_vector_bool_init(&visited, vcount)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited); + + igraph_vector_clear(res); + + if (vid < 0) { /* generate random spanning forest: consider each component separately */ + igraph_vector_t membership, csize; + igraph_integer_t comp_count; + igraph_integer_t i; + + IGRAPH_VECTOR_INIT_FINALLY(&membership, 0); + IGRAPH_VECTOR_INIT_FINALLY(&csize, 0); + + IGRAPH_CHECK(igraph_clusters(graph, &membership, &csize, &comp_count, IGRAPH_WEAK)); + + /* for each component ... */ + for (i = 0; i < comp_count; ++i) { + /* ... find a vertex to start the LERW from */ + igraph_integer_t j = 0; + while (VECTOR(membership)[j] != i) { + ++j; + } + + IGRAPH_CHECK(igraph_i_lerw(graph, res, j, (igraph_integer_t) VECTOR(csize)[i], &visited, &il)); + } + + igraph_vector_destroy(&membership); + igraph_vector_destroy(&csize); + IGRAPH_FINALLY_CLEAN(2); + } else { /* consider the component containing vid */ + igraph_vector_t comp_vertices; + igraph_integer_t comp_size; + + /* we measure the size of the component */ + IGRAPH_VECTOR_INIT_FINALLY(&comp_vertices, 0); + IGRAPH_CHECK(igraph_subcomponent(graph, &comp_vertices, vid, IGRAPH_ALL)); + comp_size = (igraph_integer_t) igraph_vector_size(&comp_vertices); + igraph_vector_destroy(&comp_vertices); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_i_lerw(graph, res, vid, comp_size, &visited, &il)); + } + + igraph_vector_bool_destroy(&visited); + igraph_inclist_destroy(&il); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/operators/add_edge.c b/src/rigraph/core/operators/add_edge.c new file mode 100644 index 0000000..2f5bbce --- /dev/null +++ b/src/rigraph/core/operators/add_edge.c @@ -0,0 +1,64 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_interface.h" + +/** + * \function igraph_add_edge + * \brief Adds a single edge to a graph. + * + * + * For directed graphs the edge points from \p from to \p to. + * + * + * Note that if you want to add many edges to a big graph, then it is + * inefficient to add them one by one, it is better to collect them into + * a vector and add all of them via a single \ref igraph_add_edges() call. + * \param igraph The graph. + * \param from The id of the first vertex of the edge. + * \param to The id of the second vertex of the edge. + * \return Error code. + * + * \sa \ref igraph_add_edges() to add many edges, \ref + * igraph_delete_edges() to remove edges and \ref + * igraph_add_vertices() to add vertices. + * + * Time complexity: O(|V|+|E|), the number of edges plus the number of + * vertices. + */ +int igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to) { + igraph_vector_t edges; + int ret; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2); + + VECTOR(edges)[0] = from; + VECTOR(edges)[1] = to; + IGRAPH_CHECK(ret = igraph_add_edges(graph, &edges, 0)); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return ret; +} diff --git a/src/rigraph/core/operators/complementer.c b/src/rigraph/core/operators/complementer.c new file mode 100644 index 0000000..0c429b8 --- /dev/null +++ b/src/rigraph/core/operators/complementer.c @@ -0,0 +1,104 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "../graph/attributes.h" +#include "../core/interruption.h" + +/** + * \function igraph_complementer + * \brief Create the complementer of a graph + * + * The complementer graph means that all edges which are + * not part of the original graph will be included in the result. + * + * \param res Pointer to an uninitialized graph object. + * \param graph The original graph. + * \param loops Whether to add loop edges to the complementer graph. + * \return Error code. + * \sa \ref igraph_union(), \ref igraph_intersection() and \ref + * igraph_difference(). + * + * Time complexity: O(|V|+|E1|+|E2|), |V| is the number of + * vertices in the graph, |E1| is the number of edges in the original + * and |E2| in the complementer graph. + * + * \example examples/simple/igraph_complementer.c + */ +int igraph_complementer(igraph_t *res, const igraph_t *graph, + igraph_bool_t loops) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t edges; + igraph_vector_t neis; + long int i, j; + long int zero = 0, *limit; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + if (igraph_is_directed(graph)) { + limit = &zero; + } else { + limit = &i; + } + + for (i = 0; i < no_of_nodes; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, + IGRAPH_OUT)); + if (loops) { + for (j = no_of_nodes - 1; j >= *limit; j--) { + if (igraph_vector_empty(&neis) || j > igraph_vector_tail(&neis)) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); + } else { + igraph_vector_pop_back(&neis); + } + } + } else { + for (j = no_of_nodes - 1; j >= *limit; j--) { + if (igraph_vector_empty(&neis) || j > igraph_vector_tail(&neis)) { + if (i != j) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); + } + } else { + igraph_vector_pop_back(&neis); + } + } + } + } + + IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, + igraph_is_directed(graph))); + igraph_vector_destroy(&edges); + igraph_vector_destroy(&neis); + IGRAPH_I_ATTRIBUTE_DESTROY(res); + IGRAPH_I_ATTRIBUTE_COPY(res, graph, /*graph=*/1, /*vertex=*/1, /*edge=*/0); + IGRAPH_FINALLY_CLEAN(2); + return 0; +} diff --git a/src/rigraph/core/operators/compose.c b/src/rigraph/core/operators/compose.c new file mode 100644 index 0000000..702b236 --- /dev/null +++ b/src/rigraph/core/operators/compose.c @@ -0,0 +1,135 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "../core/interruption.h" + +/** + * \function igraph_compose + * \brief Calculates the composition of two graphs + * + * The composition of graphs contains the same number of vertices as + * the bigger graph of the two operands. It contains an (i,j) edge if + * and only if there is a k vertex, such that the first graphs + * contains an (i,k) edge and the second graph a (k,j) edge. + * + * This is of course exactly the composition of two + * binary relations. + * + * Two two graphs must have the same directedness, + * otherwise the function returns with an error message. + * Note that for undirected graphs the two relations are by definition + * symmetric. + * + * \param res Pointer to an uninitialized graph object, the result + * will be stored here. + * \param g1 The firs operand, a graph object. + * \param g2 The second operand, another graph object. + * \param edge_map1 If not a null pointer, then it must be a pointer + * to an initialized vector, and a mapping from the edges of + * the result graph to the edges of the first graph is stored + * here. + * \param edge_map1 If not a null pointer, then it must be a pointer + * to an initialized vector, and a mapping from the edges of + * the result graph to the edges of the second graph is stored + * here. + * \return Error code. + * + * Time complexity: O(|V|*d1*d2), |V| is the number of vertices in the + * first graph, d1 and d2 the average degree in the first and second + * graphs. + * + * \example examples/simple/igraph_compose.c + */ +int igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, + igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) { + + long int no_of_nodes_left = igraph_vcount(g1); + long int no_of_nodes_right = igraph_vcount(g2); + long int no_of_nodes; + igraph_bool_t directed = igraph_is_directed(g1); + igraph_vector_t edges; + igraph_vector_t neis1, neis2; + long int i; + + if (directed != igraph_is_directed(g2)) { + IGRAPH_ERROR("Cannot compose directed and undirected graph", + IGRAPH_EINVAL); + } + + no_of_nodes = no_of_nodes_left > no_of_nodes_right ? + no_of_nodes_left : no_of_nodes_right; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis1, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis2, 0); + + if (edge_map1) { + igraph_vector_clear(edge_map1); + } + if (edge_map2) { + igraph_vector_clear(edge_map2); + } + + for (i = 0; i < no_of_nodes_left; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_CHECK(igraph_incident(g1, &neis1, (igraph_integer_t) i, + IGRAPH_OUT)); + while (!igraph_vector_empty(&neis1)) { + long int con = (long int) igraph_vector_pop_back(&neis1); + long int v1 = IGRAPH_OTHER(g1, con, i); + if (v1 < no_of_nodes_right) { + IGRAPH_CHECK(igraph_incident(g2, &neis2, (igraph_integer_t) v1, + IGRAPH_OUT)); + } else { + continue; + } + while (!igraph_vector_empty(&neis2)) { + long int con2 = igraph_vector_pop_back(&neis2); + long int v2 = IGRAPH_OTHER(g2, con2, v1); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, v2)); + if (edge_map1) { + IGRAPH_CHECK(igraph_vector_push_back(edge_map1, con)); + } + if (edge_map2) { + IGRAPH_CHECK(igraph_vector_push_back(edge_map2, con2)); + } + } + } + } + + igraph_vector_destroy(&neis1); + igraph_vector_destroy(&neis2); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, + directed)); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/operators/connect_neighborhood.c b/src/rigraph/core/operators/connect_neighborhood.c new file mode 100644 index 0000000..1ab6f2a --- /dev/null +++ b/src/rigraph/core/operators/connect_neighborhood.c @@ -0,0 +1,163 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +/** + * \function igraph_connect_neighborhood + * \brief Connects every vertex to its neighborhood + * + * This function adds new edges to the input graph. Each vertex is connected + * to all vertices reachable by at most \p order steps from it + * (unless a connection already existed). In other words, the \p order power of + * the graph is computed. + * + * Note that the input graph is modified in place, no + * new graph is created. Call \ref igraph_copy() if you want to keep + * the original graph as well. + * + * For undirected graphs reachability is always + * symmetric: if vertex A can be reached from vertex B in at + * most \p order steps, then the opposite is also true. Only one + * undirected (A,B) edge will be added in this case. + * \param graph The input graph, this is the output graph as well. + * \param order Integer constant, it gives the distance within which + * the vertices will be connected to the source vertex. + * \param mode Constant, it specifies how the neighborhood search is + * performed for directed graphs. If \c IGRAPH_OUT then vertices + * reachable from the source vertex will be connected, \c IGRAPH_IN + * is the opposite. If \c IGRAPH_ALL then the directed graph is + * considered as an undirected one. + * \return Error code. + * + * \sa \ref igraph_lattice() uses this function to connect the + * neighborhood of the vertices. + * + * Time complexity: O(|V|*d^k), |V| is the number of vertices in the + * graph, d is the average degree and k is the \p order argument. + */ +int igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, + igraph_neimode_t mode) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_t q; + igraph_vector_t edges; + long int i, j, in; + long int *added; + igraph_vector_t neis; + + if (order < 0) { + IGRAPH_ERROR("Negative order, cannot connect neighborhood", IGRAPH_EINVAL); + } + + if (order < 2) { + IGRAPH_WARNING("Order smaller than two, graph will be unchanged"); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + added = IGRAPH_CALLOC(no_of_nodes, long int); + if (added == 0) { + IGRAPH_ERROR("Cannot connect neighborhood", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + + for (i = 0; i < no_of_nodes; i++) { + added[i] = i + 1; + igraph_neighbors(graph, &neis, (igraph_integer_t) i, mode); + in = igraph_vector_size(&neis); + if (order > 1) { + for (j = 0; j < in; j++) { + long int nei = (long int) VECTOR(neis)[j]; + added[nei] = i + 1; + igraph_dqueue_push(&q, nei); + igraph_dqueue_push(&q, 1); + } + } + + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + long int actdist = (long int) igraph_dqueue_pop(&q); + long int n; + igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, mode); + n = igraph_vector_size(&neis); + + if (actdist < order - 1) { + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + if (mode != IGRAPH_ALL || i < nei) { + if (mode == IGRAPH_IN) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + } else { + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, nei)); + } + } + } + } + } else { + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + if (mode != IGRAPH_ALL || i < nei) { + if (mode == IGRAPH_IN) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + } else { + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, nei)); + } + } + } + } + } + + } /* while q not empty */ + } /* for i < no_of_nodes */ + + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&q); + igraph_free(added); + IGRAPH_FINALLY_CLEAN(3); + + IGRAPH_CHECK(igraph_add_edges(graph, &edges, 0)); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} diff --git a/src/rigraph/core/operators/contract.c b/src/rigraph/core/operators/contract.c new file mode 100644 index 0000000..8c449a4 --- /dev/null +++ b/src/rigraph/core/operators/contract.c @@ -0,0 +1,167 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "../graph/attributes.h" + +static void igraph_i_simplify_free(igraph_vector_ptr_t *p) { + long int i, n = igraph_vector_ptr_size(p); + for (i = 0; i < n; i++) { + igraph_vector_t *v = VECTOR(*p)[i]; + if (v) { + igraph_vector_destroy(v); + } + } + igraph_vector_ptr_destroy(p); +} + +/** + * \function igraph_contract_vertices + * Replace multiple vertices with a single one. + * + * This function creates a new graph, by merging several + * vertices into one. The vertices in the new graph correspond + * to sets of vertices in the input graph. + * \param graph The input graph, it can be directed or + * undirected. + * \param mapping A vector giving the mapping. For each + * vertex in the original graph, it should contain + * its id in the new graph. + * \param vertex_comb What to do with the vertex attributes. + * \c NULL means that vertex attributes are not kept + * after the contraction (not even for unaffected + * vertices). See the igraph manual section about attributes + * for details. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number + * or vertices plus edges. + */ + +int igraph_contract_vertices(igraph_t *graph, + const igraph_vector_t *mapping, + const igraph_attribute_combination_t *vertex_comb) { + igraph_vector_t edges; + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_bool_t vattr = vertex_comb && igraph_has_attribute_table(); + igraph_t res; + long int e, last = -1; + long int no_new_vertices; + + if (igraph_vector_size(mapping) != no_of_nodes) { + IGRAPH_ERROR("Invalid mapping vector length", + IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + + if (no_of_nodes > 0) { + last = (long int) igraph_vector_max(mapping); + } + + for (e = 0; e < no_of_edges; e++) { + long int from = IGRAPH_FROM(graph, e); + long int to = IGRAPH_TO(graph, e); + + long int nfrom = (long int) VECTOR(*mapping)[from]; + long int nto = (long int) VECTOR(*mapping)[to]; + + igraph_vector_push_back(&edges, nfrom); + igraph_vector_push_back(&edges, nto); + + if (nfrom > last) { + last = nfrom; + } + if (nto > last) { + last = nto; + } + } + + no_new_vertices = last + 1; + + IGRAPH_CHECK(igraph_create(&res, &edges, (igraph_integer_t) no_new_vertices, + igraph_is_directed(graph))); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_FINALLY(igraph_destroy, &res); + + IGRAPH_I_ATTRIBUTE_DESTROY(&res); + IGRAPH_I_ATTRIBUTE_COPY(&res, graph, /*graph=*/ 1, + /*vertex=*/ 0, /*edge=*/ 1); + + if (vattr) { + long int i; + igraph_vector_ptr_t merges; + igraph_vector_t sizes; + igraph_vector_t *vecs; + + vecs = IGRAPH_CALLOC(no_new_vertices, igraph_vector_t); + if (!vecs) { + IGRAPH_ERROR("Cannot combine attributes while contracting" + " vertices", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, vecs); + IGRAPH_CHECK(igraph_vector_ptr_init(&merges, no_new_vertices)); + IGRAPH_FINALLY(igraph_i_simplify_free, &merges); + IGRAPH_VECTOR_INIT_FINALLY(&sizes, no_new_vertices); + + for (i = 0; i < no_of_nodes; i++) { + long int to = (long int) VECTOR(*mapping)[i]; + VECTOR(sizes)[to] += 1; + } + for (i = 0; i < no_new_vertices; i++) { + igraph_vector_t *v = &vecs[i]; + IGRAPH_CHECK(igraph_vector_init(v, (long int) VECTOR(sizes)[i])); + igraph_vector_clear(v); + VECTOR(merges)[i] = v; + } + for (i = 0; i < no_of_nodes; i++) { + long int to = (long int) VECTOR(*mapping)[i]; + igraph_vector_t *v = &vecs[to]; + igraph_vector_push_back(v, i); + } + + IGRAPH_CHECK(igraph_i_attribute_combine_vertices(graph, &res, + &merges, + vertex_comb)); + + igraph_vector_destroy(&sizes); + igraph_i_simplify_free(&merges); + igraph_free(vecs); + IGRAPH_FINALLY_CLEAN(3); + } + + IGRAPH_FINALLY_CLEAN(1); + igraph_destroy(graph); + *graph = res; + + return 0; +} diff --git a/src/rigraph/core/operators/difference.c b/src/rigraph/core/operators/difference.c new file mode 100644 index 0000000..37f05a6 --- /dev/null +++ b/src/rigraph/core/operators/difference.c @@ -0,0 +1,183 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_adjlist.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "../graph/attributes.h" +#include "../core/interruption.h" + +/** + * \function igraph_difference + * \brief Calculate the difference of two graphs + * + * + * The number of vertices in the result is the number of vertices in + * the original graph, i.e. the left, first operand. In the results + * graph only edges will be included from \p orig which are not + * present in \p sub. + * + * \param res Pointer to an uninitialized graph object, the result + * will be stored here. + * \param orig The left operand of the operator, a graph object. + * \param sub The right operand of the operator, a graph object. + * \return Error code. + * \sa \ref igraph_intersection() and \ref igraph_union() for other + * operators. + * + * Time complexity: O(|V|+|E|), |V| is the number vertices in + * the smaller graph, |E| is the + * number of edges in the result graph. + * + * \example examples/simple/igraph_difference.c + */ +int igraph_difference(igraph_t *res, + const igraph_t *orig, const igraph_t *sub) { + + /* Quite nasty, but we will use that an edge adjacency list + contains the vertices according to the order of the + vertex ids at the "other" end of the edge. */ + + long int no_of_nodes_orig = igraph_vcount(orig); + long int no_of_nodes_sub = igraph_vcount(sub); + long int no_of_nodes = no_of_nodes_orig; + long int smaller_nodes; + igraph_bool_t directed = igraph_is_directed(orig); + igraph_vector_t edges; + igraph_vector_t edge_ids; + igraph_vector_int_t *nei1, *nei2; + igraph_inclist_t inc_orig, inc_sub; + long int i; + igraph_integer_t v1, v2; + + if (directed != igraph_is_directed(sub)) { + IGRAPH_ERROR("Cannot subtract directed and undirected graphs", + IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edge_ids, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_inclist_init(orig, &inc_orig, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inc_orig); + IGRAPH_CHECK(igraph_inclist_init(sub, &inc_sub, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inc_sub); + + smaller_nodes = no_of_nodes_orig > no_of_nodes_sub ? + no_of_nodes_sub : no_of_nodes_orig; + + for (i = 0; i < smaller_nodes; i++) { + long int n1, n2, e1, e2; + IGRAPH_ALLOW_INTERRUPTION(); + nei1 = igraph_inclist_get(&inc_orig, i); + nei2 = igraph_inclist_get(&inc_sub, i); + n1 = igraph_vector_int_size(nei1) - 1; + n2 = igraph_vector_int_size(nei2) - 1; + while (n1 >= 0 && n2 >= 0) { + e1 = (long int) VECTOR(*nei1)[n1]; + e2 = (long int) VECTOR(*nei2)[n2]; + v1 = IGRAPH_OTHER(orig, e1, i); + v2 = IGRAPH_OTHER(sub, e2, i); + + if (!directed && v1 < i) { + n1--; + } else if (!directed && v2 < i) { + n2--; + } else if (v1 > v2) { + IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, v1)); + n1--; + /* handle loop edges properly in undirected graphs */ + if (!directed && i == v1) { + n1--; + } + } else if (v2 > v1) { + n2--; + } else { + n1--; + n2--; + } + } + + /* Copy remaining edges */ + while (n1 >= 0) { + e1 = (long int) VECTOR(*nei1)[n1]; + v1 = IGRAPH_OTHER(orig, e1, i); + if (directed || v1 >= i) { + IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, v1)); + + /* handle loop edges properly in undirected graphs */ + if (!directed && v1 == i) { + n1--; + } + } + n1--; + } + } + + /* copy remaining edges, use the previous value of 'i' */ + for (; i < no_of_nodes_orig; i++) { + long int n1, e1; + nei1 = igraph_inclist_get(&inc_orig, i); + n1 = igraph_vector_int_size(nei1) - 1; + while (n1 >= 0) { + e1 = (long int) VECTOR(*nei1)[n1]; + v1 = IGRAPH_OTHER(orig, e1, i); + if (directed || v1 >= i) { + IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, v1)); + + /* handle loop edges properly in undirected graphs */ + if (!directed && v1 == i) { + n1--; + } + } + n1--; + } + } + + igraph_inclist_destroy(&inc_sub); + igraph_inclist_destroy(&inc_orig); + IGRAPH_FINALLY_CLEAN(2); + IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, + directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + /* Attributes */ + if (orig->attr) { + IGRAPH_I_ATTRIBUTE_DESTROY(res); + IGRAPH_I_ATTRIBUTE_COPY(res, orig, /*graph=*/1, /*vertex=*/1, /*edge=*/0); + IGRAPH_CHECK(igraph_i_attribute_permute_edges(orig, res, &edge_ids)); + } + + igraph_vector_destroy(&edge_ids); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} diff --git a/src/rigraph/core/operators/disjoint_union.c b/src/rigraph/core/operators/disjoint_union.c new file mode 100644 index 0000000..97e8da0 --- /dev/null +++ b/src/rigraph/core/operators/disjoint_union.c @@ -0,0 +1,176 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "../operators/misc_internal.h" + +/** + * \function igraph_disjoint_union + * \brief Creates the union of two disjoint graphs + * + * + * First the vertices of the second graph will be relabeled with new + * vertex ids to have two disjoint sets of vertex ids, then the union + * of the two graphs will be formed. + * If the two graphs have |V1| and |V2| vertices and |E1| and |E2| + * edges respectively then the new graph will have |V1|+|V2| vertices + * and |E1|+|E2| edges. + * + * + * Both graphs need to have the same directedness, i.e. either both + * directed or both undirected. + * + * + * The current version of this function cannot handle graph, vertex + * and edge attributes, they will be lost. + * + * \param res Pointer to an uninitialized graph object, the result + * will stored here. + * \param left The first graph. + * \param right The second graph. + * \return Error code. + * \sa \ref igraph_disjoint_union_many() for creating the disjoint union + * of more than two graphs, \ref igraph_union() for non-disjoint + * union. + * + * Time complexity: O(|V1|+|V2|+|E1|+|E2|). + * + * \example examples/simple/igraph_disjoint_union.c + */ +int igraph_disjoint_union(igraph_t *res, const igraph_t *left, + const igraph_t *right) { + + long int no_of_nodes_left = igraph_vcount(left); + long int no_of_nodes_right = igraph_vcount(right); + long int no_of_edges_left = igraph_ecount(left); + long int no_of_edges_right = igraph_ecount(right); + igraph_vector_t edges; + igraph_bool_t directed_left = igraph_is_directed(left); + igraph_integer_t from, to; + long int i; + + if (directed_left != igraph_is_directed(right)) { + IGRAPH_ERROR("Cannot union directed and undirected graphs", + IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, + 2 * (no_of_edges_left + no_of_edges_right))); + for (i = 0; i < no_of_edges_left; i++) { + igraph_edge(left, (igraph_integer_t) i, &from, &to); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + } + for (i = 0; i < no_of_edges_right; i++) { + igraph_edge(right, (igraph_integer_t) i, &from, &to); + igraph_vector_push_back(&edges, from + no_of_nodes_left); + igraph_vector_push_back(&edges, to + no_of_nodes_left); + } + + IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) + (no_of_nodes_left + no_of_nodes_right), + directed_left)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +/** + * \function igraph_disjoint_union_many + * \brief The disjint union of many graphs. + * + * + * First the vertices in the graphs will be relabeled with new vertex + * ids to have pairwise disjoint vertex id sets and then the union of + * the graphs is formed. + * The number of vertices and edges in the result is the total number + * of vertices and edges in the graphs. + * + * + * All graphs need to have the same directedness, i.e. either all + * directed or all undirected. If the graph list has length zero, + * the result will be a \em directed graph with no vertices. + * + * + * The current version of this function cannot handle graph, vertex + * and edge attributes, they will be lost. + * + * \param res Pointer to an uninitialized graph object, the result of + * the operation will be stored here. + * \param graphs Pointer vector, contains pointers to initialized + * graph objects. + * \return Error code. + * \sa \ref igraph_disjoint_union() for an easier syntax if you have + * only two graphs, \ref igraph_union_many() for non-disjoint union. + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number + * of edges in the result. + */ +int igraph_disjoint_union_many(igraph_t *res, + const igraph_vector_ptr_t *graphs) { + long int no_of_graphs = igraph_vector_ptr_size(graphs); + igraph_bool_t directed = 1; + igraph_vector_t edges; + long int no_of_edges = 0; + long int shift = 0; + igraph_t *graph; + long int i, j; + igraph_integer_t from, to; + + if (no_of_graphs != 0) { + graph = VECTOR(*graphs)[0]; + directed = igraph_is_directed(graph); + for (i = 0; i < no_of_graphs; i++) { + graph = VECTOR(*graphs)[i]; + no_of_edges += igraph_ecount(graph); + if (directed != igraph_is_directed(graph)) { + IGRAPH_ERROR("Cannot union directed and undirected graphs", + IGRAPH_EINVAL); + } + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * no_of_edges)); + + for (i = 0; i < no_of_graphs; i++) { + long int ec; + graph = VECTOR(*graphs)[i]; + ec = igraph_ecount(graph); + for (j = 0; j < ec; j++) { + igraph_edge(graph, (igraph_integer_t) j, &from, &to); + igraph_vector_push_back(&edges, from + shift); + igraph_vector_push_back(&edges, to + shift); + } + shift += igraph_vcount(graph); + } + + IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) shift, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/operators/intersection.c b/src/rigraph/core/operators/intersection.c new file mode 100644 index 0000000..abfab78 --- /dev/null +++ b/src/rigraph/core/operators/intersection.c @@ -0,0 +1,296 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_conversion.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_qsort.h" + +#include "../operators/misc_internal.h" + +#include + +/** + * \function igraph_intersection + * \brief Collect the common edges from two graphs. + * + * + * The result graph contains only edges present both in the first and + * the second graph. The number of vertices in the result graph is the + * same as the larger from the two arguments. + * + * \param res Pointer to an uninitialized graph object. This will + * contain the result of the operation. + * \param left The first operand, a graph object. + * \param right The second operand, a graph object. + * \param edge_map1 Null pointer, or an initialized \type igraph_vector_t. + * If the latter, then a mapping from the edges of the result graph, to + * the edges of the \p left input graph is stored here. + * \param edge_map2 Null pointer, or an \type igraph_vector_t. The same + * as \p edge_map1, but for the \p right input graph. + * \return Error code. + * \sa \ref igraph_intersection_many() to calculate the intersection + * of many graphs at once, \ref igraph_union(), \ref + * igraph_difference() for other operators. + * + * Time complexity: O(|V|+|E|), |V| is the number of nodes, |E| + * is the number of edges in the smaller graph of the two. (The one + * containing less vertices is considered smaller.) + * + * \example examples/simple/igraph_intersection.c + */ +int igraph_intersection(igraph_t *res, + const igraph_t *left, const igraph_t *right, + igraph_vector_t *edge_map1, + igraph_vector_t *edge_map2) { + return igraph_i_merge(res, IGRAPH_MERGE_MODE_INTERSECTION, left, right, + edge_map1, edge_map2); +} + +/** + * \function igraph_intersection_many + * \brief The intersection of more than two graphs. + * + * + * This function calculates the intersection of the graphs stored in + * the \p graphs argument. Only those edges will be included in the + * result graph which are part of every graph in \p graphs. + * + * + * The number of vertices in the result graph will be the maximum + * number of vertices in the argument graphs. + * + * \param res Pointer to an uninitialized graph object, the result of + * the operation will be stored here. + * \param graphs Pointer vector, contains pointers to graphs objects, + * the operands of the intersection operator. + * \param edgemaps If not a null pointer, then it must be an initialized + * pointer vector and the mappings of edges from the graphs to the + * result graph will be stored here, in the same order as + * \p graphs. Each mapping is stored in a separate + * \type igraph_vector_t object. For the edges that are not in + * the intersection, -1 is stored. + * \return Error code. + * \sa \ref igraph_intersection() for the intersection of two graphs, + * \ref igraph_union_many(), \ref igraph_union() and \ref + * igraph_difference() for other operators. + * + * Time complexity: O(|V|+|E|), |V| is the number of vertices, + * |E| is the number of edges in the smallest graph (i.e. the graph having + * the less vertices). + */ +int igraph_intersection_many(igraph_t *res, + const igraph_vector_ptr_t *graphs, + igraph_vector_ptr_t *edgemaps) { + + long int no_of_graphs = igraph_vector_ptr_size(graphs); + long int no_of_nodes = 0; + igraph_bool_t directed = 1; + igraph_vector_t edges; + igraph_vector_ptr_t edge_vects, order_vects; + long int i, j, tailfrom = no_of_graphs > 0 ? 0 : -1, tailto = -1; + igraph_vector_long_t no_edges; + igraph_bool_t allne = no_of_graphs == 0 ? 0 : 1, allsame = 0; + long int idx = 0; + + /* Check directedness */ + if (no_of_graphs != 0) { + directed = igraph_is_directed(VECTOR(*graphs)[0]); + } + for (i = 1; i < no_of_graphs; i++) { + if (directed != igraph_is_directed(VECTOR(*graphs)[i])) { + IGRAPH_ERROR("Cannot intersect directed and undirected graphs", + IGRAPH_EINVAL); + } + } + + if (edgemaps) { + IGRAPH_CHECK(igraph_vector_ptr_resize(edgemaps, no_of_graphs)); + igraph_vector_ptr_null(edgemaps); + IGRAPH_FINALLY(igraph_i_union_intersection_destroy_vectors, edgemaps); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_long_init(&no_edges, no_of_graphs)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &no_edges); + + /* Calculate number of nodes, query number of edges */ + for (i = 0; i < no_of_graphs; i++) { + long int n = igraph_vcount(VECTOR(*graphs)[i]); + if (n > no_of_nodes) { + no_of_nodes = n; + } + VECTOR(no_edges)[i] = igraph_ecount(VECTOR(*graphs)[i]); + allne = allne && VECTOR(no_edges)[i] > 0; + } + + if (edgemaps) { + for (i = 0; i < no_of_graphs; i++) { + VECTOR(*edgemaps)[i] = IGRAPH_CALLOC(1, igraph_vector_t); + if (!VECTOR(*edgemaps)[i]) { + IGRAPH_ERROR("Cannot intersect graphs", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(VECTOR(*edgemaps)[i], + VECTOR(no_edges)[i])); + igraph_vector_fill(VECTOR(*edgemaps)[i], -1); + } + } + + /* Allocate memory for the edge lists and their index vectors */ + if (no_of_graphs != 0) { + IGRAPH_CHECK(igraph_vector_ptr_init(&edge_vects, no_of_graphs)); + IGRAPH_FINALLY(igraph_i_union_intersection_destroy_vectors, &edge_vects); + IGRAPH_CHECK(igraph_vector_ptr_init(&order_vects, no_of_graphs)); + IGRAPH_FINALLY(igraph_i_union_intersection_destroy_vector_longs, &order_vects); + } + for (i = 0; i < no_of_graphs; i++) { + VECTOR(edge_vects)[i] = IGRAPH_CALLOC(1, igraph_vector_t); + VECTOR(order_vects)[i] = IGRAPH_CALLOC(1, igraph_vector_long_t); + if (! VECTOR(edge_vects)[i] || ! VECTOR(order_vects)[i]) { + IGRAPH_ERROR("Cannot intersect graphs", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(VECTOR(edge_vects)[i], + 2 * VECTOR(no_edges)[i])); + IGRAPH_CHECK(igraph_vector_long_init(VECTOR(order_vects)[i], + VECTOR(no_edges)[i])); + } + + /* Query and sort the edge lists */ + for (i = 0; i < no_of_graphs; i++) { + long int k, j, n = VECTOR(no_edges)[i]; + igraph_vector_t *edges = VECTOR(edge_vects)[i]; + igraph_vector_long_t *order = VECTOR(order_vects)[i]; + IGRAPH_CHECK(igraph_get_edgelist(VECTOR(*graphs)[i], edges, /*bycol=*/0)); + if (!directed) { + for (k = 0, j = 0; k < n; k++, j += 2) { + if (VECTOR(*edges)[j] > VECTOR(*edges)[j + 1]) { + long int tmp = VECTOR(*edges)[j]; + VECTOR(*edges)[j] = VECTOR(*edges)[j + 1]; + VECTOR(*edges)[j + 1] = tmp; + } + } + } + for (k = 0; k < n; k++) { + VECTOR(*order)[k] = k; + } + igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), edges, + igraph_i_order_edgelist_cmp); + } + + /* Do the merge. We work from the end of the edge lists, + because then we don't have to keep track of where we are right + now in the edge and order lists. We find the "largest" edge, + and if it is present in all graphs, then we copy it to the + result. We remove all instances of this edge. */ + + while (allne) { + + /* Look for the smallest tail element */ + for (j = 0, tailfrom = LONG_MAX, tailto = LONG_MAX; j < no_of_graphs; j++) { + long int edge = igraph_vector_long_tail(VECTOR(order_vects)[j]); + igraph_vector_t *ev = VECTOR(edge_vects)[j]; + long int from = VECTOR(*ev)[2 * edge]; + long int to = VECTOR(*ev)[2 * edge + 1]; + if (from < tailfrom || (from == tailfrom && to < tailto)) { + tailfrom = from; tailto = to; + } + } + + /* OK, now remove all elements from the tail(s) that are bigger + than the smallest tail element. */ + for (j = 0, allsame = 1; j < no_of_graphs; j++) { + long int from = -1, to = -1; + while (1) { + long int edge = igraph_vector_long_tail(VECTOR(order_vects)[j]); + igraph_vector_t *ev = VECTOR(edge_vects)[j]; + from = VECTOR(*ev)[2 * edge]; + to = VECTOR(*ev)[2 * edge + 1]; + if (from > tailfrom || (from == tailfrom && to > tailto)) { + igraph_vector_long_pop_back(VECTOR(order_vects)[j]); + if (igraph_vector_long_empty(VECTOR(order_vects)[j])) { + allne = 0; + break; + } + } else { + break; + } + } + if (from != tailfrom || to != tailto) { + allsame = 0; + } + } + + /* Add the edge, if the smallest tail element was present + in all graphs. */ + if (allsame) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, tailfrom)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, tailto)); + } + + /* Drop edges matching the smalles tail elements + from the order vectors, build edge maps */ + if (allne) { + for (j = 0; j < no_of_graphs; j++) { + long int edge = igraph_vector_long_tail(VECTOR(order_vects)[j]); + igraph_vector_t *ev = VECTOR(edge_vects)[j]; + long int from = VECTOR(*ev)[2 * edge]; + long int to = VECTOR(*ev)[2 * edge + 1]; + if (from == tailfrom && to == tailto) { + igraph_vector_long_pop_back(VECTOR(order_vects)[j]); + if (igraph_vector_long_empty(VECTOR(order_vects)[j])) { + allne = 0; + } + if (edgemaps && allsame) { + igraph_vector_t *map = VECTOR(*edgemaps)[j]; + VECTOR(*map)[edge] = idx; + } + } + } + if (allsame) { + idx++; + } + } + + } /* while allne */ + + if (no_of_graphs > 0) { + igraph_i_union_intersection_destroy_vector_longs(&order_vects); + igraph_i_union_intersection_destroy_vectors(&edge_vects); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_vector_long_destroy(&no_edges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, + directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + if (edgemaps) { + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} diff --git a/src/rigraph/core/operators/misc_internal.c b/src/rigraph/core/operators/misc_internal.c new file mode 100644 index 0000000..e3acb73 --- /dev/null +++ b/src/rigraph/core/operators/misc_internal.c @@ -0,0 +1,259 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "../operators/misc_internal.h" + +#include "igraph_constructors.h" +#include "igraph_conversion.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_qsort.h" + +void igraph_i_union_intersection_destroy_vectors(igraph_vector_ptr_t *v) { + long int i, n = igraph_vector_ptr_size(v); + for (i = 0; i < n; i++) { + if (VECTOR(*v)[i] != 0) { + igraph_vector_destroy(VECTOR(*v)[i]); + IGRAPH_FREE(VECTOR(*v)[i]); + } + } + igraph_vector_ptr_destroy(v); +} + +void igraph_i_union_intersection_destroy_vector_longs(igraph_vector_ptr_t *v) { + long int i, n = igraph_vector_ptr_size(v); + for (i = 0; i < n; i++) { + if (VECTOR(*v)[i] != 0) { + igraph_vector_long_destroy(VECTOR(*v)[i]); + IGRAPH_FREE(VECTOR(*v)[i]); + } + } + igraph_vector_ptr_destroy(v); +} + +int igraph_i_order_edgelist_cmp(void *edges, const void *e1, const void *e2) { + igraph_vector_t *edgelist = edges; + long int edge1 = (*(const long int*) e1) * 2; + long int edge2 = (*(const long int*) e2) * 2; + long int from1 = VECTOR(*edgelist)[edge1]; + long int from2 = VECTOR(*edgelist)[edge2]; + if (from1 < from2) { + return -1; + } else if (from1 > from2) { + return 1; + } else { + long int to1 = VECTOR(*edgelist)[edge1 + 1]; + long int to2 = VECTOR(*edgelist)[edge2 + 1]; + if (to1 < to2) { + return -1; + } else if (to1 > to2) { + return 1; + } else { + return 0; + } + } +} + +int igraph_i_merge(igraph_t *res, int mode, + const igraph_t *left, const igraph_t *right, + igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) { + + long int no_of_nodes_left = igraph_vcount(left); + long int no_of_nodes_right = igraph_vcount(right); + long int no_of_nodes; + long int no_edges_left = igraph_ecount(left); + long int no_edges_right = igraph_ecount(right); + igraph_bool_t directed = igraph_is_directed(left); + igraph_vector_t edges; + igraph_vector_t edges1, edges2; + igraph_vector_long_t order1, order2; + long int i, j, eptr = 0; + long int idx1, idx2, edge1 = -1, edge2 = -1, from1 = -1, from2 = -1, to1 = -1, to2 = -1; + igraph_bool_t l; + + if (directed != igraph_is_directed(right)) { + IGRAPH_ERROR("Cannot make union or intersection of directed " + "and undirected graph", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges1, no_edges_left * 2); + IGRAPH_VECTOR_INIT_FINALLY(&edges2, no_edges_right * 2); + IGRAPH_CHECK(igraph_vector_long_init(&order1, no_edges_left)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &order1); + IGRAPH_CHECK(igraph_vector_long_init(&order2, no_edges_right)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &order2); + + if (edge_map1) { + switch (mode) { + case IGRAPH_MERGE_MODE_UNION: + IGRAPH_CHECK(igraph_vector_resize(edge_map1, no_edges_left)); + break; + case IGRAPH_MERGE_MODE_INTERSECTION: + igraph_vector_clear(edge_map1); + break; + } + } + if (edge_map2) { + switch (mode) { + case IGRAPH_MERGE_MODE_UNION: + IGRAPH_CHECK(igraph_vector_resize(edge_map2, no_edges_right)); + break; + case IGRAPH_MERGE_MODE_INTERSECTION: + igraph_vector_clear(edge_map2); + break; + } + } + + no_of_nodes = no_of_nodes_left > no_of_nodes_right ? + no_of_nodes_left : no_of_nodes_right; + + /* We merge the two edge lists. We need to sort them first. + For undirected graphs, we also need to make sure that + for every edge, that larger (non-smaller) vertex id is in the + second column. */ + + IGRAPH_CHECK(igraph_get_edgelist(left, &edges1, /*bycol=*/ 0)); + IGRAPH_CHECK(igraph_get_edgelist(right, &edges2, /*bycol=*/ 0)); + if (!directed) { + for (i = 0, j = 0; i < no_edges_left; i++, j += 2) { + if (VECTOR(edges1)[j] > VECTOR(edges1)[j + 1]) { + long int tmp = VECTOR(edges1)[j]; + VECTOR(edges1)[j] = VECTOR(edges1)[j + 1]; + VECTOR(edges1)[j + 1] = tmp; + } + } + for (i = 0, j = 0; i < no_edges_right; i++, j += 2) { + if (VECTOR(edges2)[j] > VECTOR(edges2)[j + 1]) { + long int tmp = VECTOR(edges2)[j]; + VECTOR(edges2)[j] = VECTOR(edges2)[j + 1]; + VECTOR(edges2)[j + 1] = tmp; + } + } + } + + for (i = 0; i < no_edges_left; i++) { + VECTOR(order1)[i] = i; + } + for (i = 0; i < no_edges_right; i++) { + VECTOR(order2)[i] = i; + } + + igraph_qsort_r(VECTOR(order1), no_edges_left, sizeof(VECTOR(order1)[0]), + &edges1, igraph_i_order_edgelist_cmp); + igraph_qsort_r(VECTOR(order2), no_edges_right, sizeof(VECTOR(order2)[0]), + &edges2, igraph_i_order_edgelist_cmp); + +#define INC1() if ( (++idx1) < no_edges_left) { \ + edge1 = VECTOR(order1)[idx1]; \ + from1 = VECTOR(edges1)[2*edge1]; \ + to1 = VECTOR(edges1)[2*edge1+1]; \ + } +#define INC2() if ( (++idx2) < no_edges_right) { \ + edge2 = VECTOR(order2)[idx2]; \ + from2 = VECTOR(edges2)[2*edge2]; \ + to2 = VECTOR(edges2)[2*edge2+1]; \ + } + + idx1 = idx2 = -1; + INC1(); + INC2(); + +#define CONT() switch (mode) { \ + case IGRAPH_MERGE_MODE_UNION: \ + l = idx1 < no_edges_left || idx2 < no_edges_right; \ + break; \ + case IGRAPH_MERGE_MODE_INTERSECTION: \ + l = idx1 < no_edges_left && idx2 < no_edges_right; \ + break; \ + default: \ + IGRAPH_ASSERT(! "Invalid merge mode."); \ + } + + CONT(); + while (l) { + if (idx2 >= no_edges_right || + (idx1 < no_edges_left && from1 < from2) || + (idx1 < no_edges_left && from1 == from2 && to1 < to2)) { + /* Edge from first graph */ + if (mode == IGRAPH_MERGE_MODE_UNION) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, from1)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to1)); + if (edge_map1) { + VECTOR(*edge_map1)[edge1] = eptr; + } + eptr++; + } + INC1(); + } else if (idx1 >= no_edges_left || + (idx2 < no_edges_right && from2 < from1) || + (idx2 < no_edges_right && from1 == from2 && to2 < to1)) { + /* Edge from second graph */ + if (mode == IGRAPH_MERGE_MODE_UNION) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, from2)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to2)); + if (edge_map2) { + VECTOR(*edge_map2)[edge2] = eptr; + } + eptr++; + } + INC2(); + } else { + /* Edge from both */ + IGRAPH_CHECK(igraph_vector_push_back(&edges, from1)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to1)); + if (mode == IGRAPH_MERGE_MODE_UNION) { + if (edge_map1) { + VECTOR(*edge_map1)[edge1] = eptr; + } + if (edge_map2) { + VECTOR(*edge_map2)[edge2] = eptr; + } + } else if (mode == IGRAPH_MERGE_MODE_INTERSECTION) { + if (edge_map1) { + IGRAPH_CHECK(igraph_vector_push_back(edge_map1, edge1)); + } + if (edge_map2) { + IGRAPH_CHECK(igraph_vector_push_back(edge_map2, edge2)); + } + } + eptr++; + INC1(); + INC2(); + } + CONT(); + } + +#undef INC1 +#undef INC2 + + igraph_vector_long_destroy(&order2); + igraph_vector_long_destroy(&order1); + igraph_vector_destroy(&edges2); + igraph_vector_destroy(&edges1); + IGRAPH_FINALLY_CLEAN(4); + + IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/operators/misc_internal.h b/src/rigraph/core/operators/misc_internal.h new file mode 100644 index 0000000..2701fcc --- /dev/null +++ b/src/rigraph/core/operators/misc_internal.h @@ -0,0 +1,46 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2020 The igraph development team + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_OPERATORS_MISC_INTERNAL_H +#define IGRAPH_OPERATORS_MISC_INTERNAL_H + +#include "igraph_decls.h" +#include "igraph_datatype.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" + +__BEGIN_DECLS + +#define IGRAPH_MERGE_MODE_UNION 1 +#define IGRAPH_MERGE_MODE_INTERSECTION 2 + +int igraph_i_order_edgelist_cmp(void *edges, const void *e1, const void *e2); +int igraph_i_merge(igraph_t *res, int mode, + const igraph_t *left, const igraph_t *right, + igraph_vector_t *edge_map1, igraph_vector_t *edge_map2); +void igraph_i_union_intersection_destroy_vectors(igraph_vector_ptr_t *v); +void igraph_i_union_intersection_destroy_vector_longs(igraph_vector_ptr_t *v); + +__END_DECLS + +#endif diff --git a/src/rigraph/core/operators/permute.c b/src/rigraph/core/operators/permute.c new file mode 100644 index 0000000..befb987 --- /dev/null +++ b/src/rigraph/core/operators/permute.c @@ -0,0 +1,96 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "../graph/attributes.h" + +/** + * \function igraph_permute_vertices + * Permute the vertices + * + * This function creates a new graph from the input graph by permuting + * its vertices according to the specified mapping. Call this function + * with the output of \ref igraph_canonical_permutation() to create + * the canonical form of a graph. + * \param graph The input graph. + * \param res Pointer to an uninitialized graph object. The new graph + * is created here. + * \param permutation The permutation to apply. Vertex 0 is mapped to + * the first element of the vector, vertex 1 to the second, + * etc. Note that it is not checked that the vector contains every + * element only once, and no range checking is performed either. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in terms of the number of + * vertices and edges. + */ +int igraph_permute_vertices(const igraph_t *graph, igraph_t *res, + const igraph_vector_t *permutation) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_vector_t edges; + long int i, p = 0; + + if (igraph_vector_size(permutation) != no_of_nodes) { + IGRAPH_ERROR("Permute vertices: invalid permutation vector size", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + + for (i = 0; i < no_of_edges; i++) { + VECTOR(edges)[p++] = VECTOR(*permutation)[ (long int) IGRAPH_FROM(graph, i) ]; + VECTOR(edges)[p++] = VECTOR(*permutation)[ (long int) IGRAPH_TO(graph, i) ]; + } + + IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, + igraph_is_directed(graph))); + + /* Attributes */ + if (graph->attr) { + igraph_vector_t index; + igraph_vector_t vtypes; + IGRAPH_I_ATTRIBUTE_DESTROY(res); + IGRAPH_I_ATTRIBUTE_COPY(res, graph, /*graph=*/1, /*vertex=*/0, /*edge=*/1); + IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0); + IGRAPH_CHECK(igraph_i_attribute_get_info(graph, 0, 0, 0, &vtypes, 0, 0)); + if (igraph_vector_size(&vtypes) != 0) { + IGRAPH_VECTOR_INIT_FINALLY(&index, no_of_nodes); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(index)[ (long int) VECTOR(*permutation)[i] ] = i; + } + IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, res, &index)); + igraph_vector_destroy(&index); + IGRAPH_FINALLY_CLEAN(1); + } + igraph_vector_destroy(&vtypes); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} diff --git a/src/rigraph/core/operators/rewire.c b/src/rigraph/core/operators/rewire.c new file mode 100644 index 0000000..a32138c --- /dev/null +++ b/src/rigraph/core/operators/rewire.c @@ -0,0 +1,268 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_adjlist.h" +#include "igraph_conversion.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" +#include "igraph_progress.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +#include "../core/interruption.h" +#include "../operators/rewire_internal.h" + +/* Threshold that defines when to switch over to using adjacency lists during + * rewiring */ +#define REWIRE_ADJLIST_THRESHOLD 10 + +/* Not declared static so that the testsuite can use it, but not part of the public API. */ +int igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, igraph_bool_t use_adjlist) { + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + char message[256]; + igraph_integer_t a, b, c, d, dummy, num_swaps, num_successful_swaps; + igraph_vector_t eids, edgevec, alledges; + igraph_bool_t directed, loops, ok; + igraph_es_t es; + igraph_adjlist_t al; + + if (no_of_nodes < 4) { + IGRAPH_ERROR("graph unsuitable for rewiring", IGRAPH_EINVAL); + } + + directed = igraph_is_directed(graph); + loops = (mode & IGRAPH_REWIRING_SIMPLE_LOOPS); + + RNG_BEGIN(); + + IGRAPH_VECTOR_INIT_FINALLY(&eids, 2); + + if (use_adjlist) { + /* As well as the sorted adjacency list, we maintain an unordered + * list of edges for picking a random edge in constant time. + */ + IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + IGRAPH_VECTOR_INIT_FINALLY(&alledges, no_of_edges * 2); + igraph_get_edgelist(graph, &alledges, /*bycol=*/ 0); + } else { + IGRAPH_VECTOR_INIT_FINALLY(&edgevec, 4); + es = igraph_ess_vector(&eids); + } + + /* We don't want the algorithm to get stuck in an infinite loop when + * it can't choose two edges satisfying the conditions. Instead of + * this, we choose two arbitrary edges and if they have endpoints + * in common, we just decrease the number of trials left and continue + * (so unsuccessful rewirings still count as a trial) + */ + + num_swaps = num_successful_swaps = 0; + while (num_swaps < n) { + + IGRAPH_ALLOW_INTERRUPTION(); + if (num_swaps % 1000 == 0) { + snprintf(message, sizeof(message), + "Random rewiring (%.2f%% of the trials were successful)", + num_swaps > 0 ? ((100.0 * num_successful_swaps) / num_swaps) : 0.0); + IGRAPH_PROGRESS(message, (100.0 * num_swaps) / n, 0); + } + + switch (mode) { + case IGRAPH_REWIRING_SIMPLE: + case IGRAPH_REWIRING_SIMPLE_LOOPS: + ok = 1; + + /* Choose two edges randomly */ + VECTOR(eids)[0] = RNG_INTEGER(0, no_of_edges - 1); + do { + VECTOR(eids)[1] = RNG_INTEGER(0, no_of_edges - 1); + } while (VECTOR(eids)[0] == VECTOR(eids)[1]); + + /* Get the endpoints */ + if (use_adjlist) { + a = VECTOR(alledges)[((igraph_integer_t)VECTOR(eids)[0]) * 2]; + b = VECTOR(alledges)[(((igraph_integer_t)VECTOR(eids)[0]) * 2) + 1]; + c = VECTOR(alledges)[((igraph_integer_t)VECTOR(eids)[1]) * 2]; + d = VECTOR(alledges)[(((igraph_integer_t)VECTOR(eids)[1]) * 2) + 1]; + } else { + IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) VECTOR(eids)[0], + &a, &b)); + IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) VECTOR(eids)[1], + &c, &d)); + } + + /* For an undirected graph, we have two "variants" of each edge, i.e. + * a -- b and b -- a. Since some rewirings can be performed only when we + * "swap" the endpoints, we do it now with probability 0.5 */ + if (!directed && RNG_UNIF01() < 0.5) { + dummy = c; c = d; d = dummy; + if (use_adjlist) { + /* Flip the edge in the unordered edge-list, so the update later on + * hits the correct end. */ + VECTOR(alledges)[((igraph_integer_t)VECTOR(eids)[1]) * 2] = c; + VECTOR(alledges)[(((igraph_integer_t)VECTOR(eids)[1]) * 2) + 1] = d; + } + } + + /* If we do not touch loops, check whether a == b or c == d and disallow + * the swap if needed */ + if (!loops && (a == b || c == d)) { + ok = 0; + } else { + /* Check whether they are suitable for rewiring */ + if (a == c || b == d) { + /* Swapping would have no effect */ + ok = 0; + } else { + /* a != c && b != d */ + /* If a == d or b == c, the swap would generate at least one loop, so + * we disallow them unless we want to have loops */ + ok = loops || (a != d && b != c); + /* Also, if a == b and c == d and we allow loops, doing the swap + * would result in a multiple edge if the graph is undirected */ + ok = ok && (directed || a != b || c != d); + } + } + + /* All good so far. Now check for the existence of a --> d and c --> b to + * disallow the creation of multiple edges */ + if (ok) { + if (use_adjlist) { + if (igraph_adjlist_has_edge(&al, a, d, directed)) { + ok = 0; + } + } else { + IGRAPH_CHECK(igraph_are_connected(graph, a, d, &ok)); + ok = !ok; + } + } + if (ok) { + if (use_adjlist) { + if (igraph_adjlist_has_edge(&al, c, b, directed)) { + ok = 0; + } + } else { + IGRAPH_CHECK(igraph_are_connected(graph, c, b, &ok)); + ok = !ok; + } + } + + /* If we are still okay, we can perform the rewiring */ + if (ok) { + /* printf("Deleting: %ld -> %ld, %ld -> %ld\n", + (long)a, (long)b, (long)c, (long)d); */ + if (use_adjlist) { + /* Replace entry in sorted adjlist: */ + IGRAPH_CHECK(igraph_adjlist_replace_edge(&al, a, b, d, directed)); + IGRAPH_CHECK(igraph_adjlist_replace_edge(&al, c, d, b, directed)); + /* Also replace in unsorted edgelist: */ + VECTOR(alledges)[(((igraph_integer_t)VECTOR(eids)[0]) * 2) + 1] = d; + VECTOR(alledges)[(((igraph_integer_t)VECTOR(eids)[1]) * 2) + 1] = b; + } else { + IGRAPH_CHECK(igraph_delete_edges(graph, es)); + VECTOR(edgevec)[0] = a; VECTOR(edgevec)[1] = d; + VECTOR(edgevec)[2] = c; VECTOR(edgevec)[3] = b; + /* printf("Adding: %ld -> %ld, %ld -> %ld\n", + (long)a, (long)d, (long)c, (long)b); */ + igraph_add_edges(graph, &edgevec, 0); + } + num_successful_swaps++; + } + break; + default: + RNG_END(); + IGRAPH_ERROR("unknown rewiring mode", IGRAPH_EINVMODE); + } + num_swaps++; + } + + if (use_adjlist) { + /* Replace graph edges with the adjlist current state */ + IGRAPH_CHECK(igraph_delete_edges(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID))); + IGRAPH_CHECK(igraph_add_edges(graph, &alledges, 0)); + } + + IGRAPH_PROGRESS("Random rewiring: ", 100.0, 0); + + if (use_adjlist) { + igraph_vector_destroy(&alledges); + igraph_adjlist_destroy(&al); + } else { + igraph_vector_destroy(&edgevec); + } + + igraph_vector_destroy(&eids); + IGRAPH_FINALLY_CLEAN(use_adjlist ? 3 : 2); + + RNG_END(); + + return 0; +} + +/** + * \ingroup structural + * \function igraph_rewire + * \brief Randomly rewires a graph while preserving the degree distribution. + * + * + * This function generates a new graph based on the original one by randomly + * rewiring edges while preserving the original graph's degree distribution. + * Please note that the rewiring is done "in place", so no new graph will + * be allocated. If you would like to keep the original graph intact, use + * \ref igraph_copy() beforehand. + * + * \param graph The graph object to be rewired. + * \param n Number of rewiring trials to perform. + * \param mode The rewiring algorithm to be used. It can be one of the following flags: + * \clist + * \cli IGRAPH_REWIRING_SIMPLE + * Simple rewiring algorithm which chooses two arbitrary edges + * in each step (namely (a,b) and (c,d)) and substitutes them + * with (a,d) and (c,b) if they don't exist. The method will + * neither destroy nor create self-loops. + * \cli IGRAPH_REWIRING_SIMPLE_LOOPS + * Same as \c IGRAPH_REWIRING_SIMPLE but allows the creation or + * destruction of self-loops. + * \endclist + * + * \return Error code: + * \clist + * \cli IGRAPH_EINVMODE + * Invalid rewiring mode. + * \cli IGRAPH_EINVAL + * Graph unsuitable for rewiring (e.g. it has + * less than 4 nodes in case of \c IGRAPH_REWIRING_SIMPLE) + * \cli IGRAPH_ENOMEM + * Not enough memory for temporary data. + * \endclist + * + * Time complexity: TODO. + */ +int igraph_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode) { + igraph_bool_t use_adjlist = n >= REWIRE_ADJLIST_THRESHOLD; + return igraph_i_rewire(graph, n, mode, use_adjlist); +} diff --git a/src/rigraph/core/operators/rewire_edges.c b/src/rigraph/core/operators/rewire_edges.c new file mode 100644 index 0000000..134bb48 --- /dev/null +++ b/src/rigraph/core/operators/rewire_edges.c @@ -0,0 +1,394 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_games.h" + +#include "igraph_conversion.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_random.h" + +#include "../graph/attributes.h" + +static int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob, + igraph_bool_t loops, + igraph_vector_t *edges) { + + int no_verts = igraph_vcount(graph); + int no_edges = igraph_ecount(graph); + igraph_vector_t eorder, tmp; + igraph_vector_int_t first, next, prev, marked; + int i, to_rewire, last_other = -1; + + /* Create our special graph representation */ + +# define ADD_STUB(vertex, stub) do { \ + if (VECTOR(first)[(vertex)]) { \ + VECTOR(prev)[(int) VECTOR(first)[(vertex)]-1]=(stub)+1; \ + } \ + VECTOR(next)[(stub)]=VECTOR(first)[(vertex)]; \ + VECTOR(prev)[(stub)]=0; \ + VECTOR(first)[(vertex)]=(stub)+1; \ + } while (0) + +# define DEL_STUB(vertex, stub) do { \ + if (VECTOR(next)[(stub)]) { \ + VECTOR(prev)[VECTOR(next)[(stub)]-1]=VECTOR(prev)[(stub)]; \ + } \ + if (VECTOR(prev)[(stub)]) { \ + VECTOR(next)[VECTOR(prev)[(stub)]-1]=VECTOR(next)[(stub)]; \ + } else { \ + VECTOR(first)[(vertex)]=VECTOR(next)[(stub)]; \ + } \ + } while (0) + +# define MARK_NEIGHBORS(vertex) do { \ + int xxx_ =VECTOR(first)[(vertex)]; \ + while (xxx_) { \ + int o= (int) VECTOR(*edges)[xxx_ % 2 ? xxx_ : xxx_-2]; \ + VECTOR(marked)[o]=other+1; \ + xxx_=VECTOR(next)[xxx_-1]; \ + } \ + } while (0) + + IGRAPH_CHECK(igraph_vector_int_init(&first, no_verts)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &first); + IGRAPH_CHECK(igraph_vector_int_init(&next, no_edges * 2)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &next); + IGRAPH_CHECK(igraph_vector_int_init(&prev, no_edges * 2)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &prev); + IGRAPH_CHECK(igraph_get_edgelist(graph, edges, /*bycol=*/ 0)); + IGRAPH_VECTOR_INIT_FINALLY(&eorder, no_edges); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, no_edges); + for (i = 0; i < no_edges; i++) { + int idx1 = 2 * i, idx2 = idx1 + 1, + from = (int) VECTOR(*edges)[idx1], to = (int) VECTOR(*edges)[idx2]; + VECTOR(tmp)[i] = from; + ADD_STUB(from, idx1); + ADD_STUB(to, idx2); + } + IGRAPH_CHECK(igraph_vector_order1(&tmp, &eorder, no_verts)); + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_vector_int_init(&marked, no_verts)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &marked); + + /* Rewire the stubs, part I */ + + to_rewire = (int) RNG_GEOM(prob); + while (to_rewire < no_edges) { + int stub = (int) (2 * VECTOR(eorder)[to_rewire] + 1); + int v = (int) VECTOR(*edges)[stub]; + int ostub = stub - 1; + int other = (int) VECTOR(*edges)[ostub]; + int pot; + if (last_other != other) { + MARK_NEIGHBORS(other); + } + /* Do the rewiring */ + do { + if (loops) { + pot = (int) RNG_INTEGER(0, no_verts - 1); + } else { + pot = (int) RNG_INTEGER(0, no_verts - 2); + pot = pot != other ? pot : no_verts - 1; + } + } while (VECTOR(marked)[pot] == other + 1 && pot != v); + + if (pot != v) { + DEL_STUB(v, stub); + ADD_STUB(pot, stub); + VECTOR(marked)[v] = 0; + VECTOR(marked)[pot] = other + 1; + VECTOR(*edges)[stub] = pot; + } + + to_rewire += RNG_GEOM(prob) + 1; + last_other = other; + } + + /* Create the new index, from the potentially rewired stubs */ + + IGRAPH_VECTOR_INIT_FINALLY(&tmp, no_edges); + for (i = 0; i < no_edges; i++) { + VECTOR(tmp)[i] = VECTOR(*edges)[2 * i + 1]; + } + IGRAPH_CHECK(igraph_vector_order1(&tmp, &eorder, no_verts)); + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + /* Rewire the stubs, part II */ + + igraph_vector_int_null(&marked); + last_other = -1; + + to_rewire = (int) RNG_GEOM(prob); + while (to_rewire < no_edges) { + int stub = (int) (2 * VECTOR(eorder)[to_rewire]); + int v = (int) VECTOR(*edges)[stub]; + int ostub = stub + 1; + int other = (int) VECTOR(*edges)[ostub]; + int pot; + if (last_other != other) { + MARK_NEIGHBORS(other); + } + /* Do the rewiring */ + do { + if (loops) { + pot = (int) RNG_INTEGER(0, no_verts - 1); + } else { + pot = (int) RNG_INTEGER(0, no_verts - 2); + pot = pot != other ? pot : no_verts - 1; + } + } while (VECTOR(marked)[pot] == other + 1 && pot != v); + if (pot != v) { + DEL_STUB(v, stub); + ADD_STUB(pot, stub); + VECTOR(marked)[v] = 0; + VECTOR(marked)[pot] = other + 1; + VECTOR(*edges)[stub] = pot; + } + + to_rewire += RNG_GEOM(prob) + 1; + last_other = other; + } + + igraph_vector_int_destroy(&marked); + igraph_vector_int_destroy(&prev); + igraph_vector_int_destroy(&next); + igraph_vector_int_destroy(&first); + igraph_vector_destroy(&eorder); + IGRAPH_FINALLY_CLEAN(5); + + return 0; +} + +#undef ADD_STUB +#undef DEL_STUB +#undef MARK_NEIGHBORS + +/** + * \function igraph_rewire_edges + * \brief Rewires the edges of a graph with constant probability. + * + * This function rewires the edges of a graph with a constant + * probability. More precisely each end point of each edge is rewired + * to a uniformly randomly chosen vertex with constant probability \p + * prob. + * + * Note that this function modifies the input \p graph, + * call \ref igraph_copy() if you want to keep it. + * + * \param graph The input graph, this will be rewired, it can be + * directed or undirected. + * \param prob The rewiring probability a constant between zero and + * one (inclusive). + * \param loops Boolean, whether loop edges are allowed in the new + * graph, or not. + * \param multiple Boolean, whether multiple edges are allowed in the + * new graph. + * \return Error code. + * + * \sa \ref igraph_watts_strogatz_game() uses this function for the + * rewiring. + * + * Time complexity: O(|V|+|E|). + */ +int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, + igraph_bool_t loops, igraph_bool_t multiple) { + + igraph_t newgraph; + long int no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + long int endpoints = no_of_edges * 2; + long int to_rewire; + igraph_vector_t edges; + + if (prob < 0 || prob > 1) { + IGRAPH_ERROR("Rewiring probability should be between zero and one", + IGRAPH_EINVAL); + } + + if (prob == 0) { + /* This is easy, just leave things as they are */ + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, endpoints); + + RNG_BEGIN(); + + if (prob != 0 && no_of_edges > 0) { + if (multiple) { + /* If multiple edges are allowed, then there is an easy and fast + method. Each endpoint of an edge is rewired with probability p, + so the "skips" between the really rewired endpoints follow a + geometric distribution. */ + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + to_rewire = (long int) RNG_GEOM(prob); + while (to_rewire < endpoints) { + if (loops) { + VECTOR(edges)[to_rewire] = RNG_INTEGER(0, no_of_nodes - 1); + } else { + long int opos = to_rewire % 2 ? to_rewire - 1 : to_rewire + 1; + long int nei = (long int) VECTOR(edges)[opos]; + long int r = RNG_INTEGER(0, no_of_nodes - 2); + VECTOR(edges)[ to_rewire ] = (r != nei ? r : no_of_nodes - 1); + } + to_rewire += RNG_GEOM(prob) + 1; + } + + } else { + IGRAPH_CHECK(igraph_i_rewire_edges_no_multiple(graph, prob, loops, + &edges)); + } + } + + RNG_END(); + + IGRAPH_CHECK(igraph_create(&newgraph, &edges, (igraph_integer_t) no_of_nodes, + igraph_is_directed(graph))); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_FINALLY(igraph_destroy, &newgraph); + IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 1); + IGRAPH_FINALLY_CLEAN(1); + igraph_destroy(graph); + *graph = newgraph; + + return 0; +} + +/** + * \function igraph_rewire_directed_edges + * \brief Rewires the chosen endpoint of directed edges. + * + * This function rewires either the start or end of directed edges in a graph + * with a constant probability. Correspondingly, either the in-degree sequence + * or the out-degree sequence of the graph will be preserved. + * + * Note that this function modifies the input \p graph, + * call \ref igraph_copy() if you want to keep it. + * + * This function can produce multiple edges between two vertices. + * + * \param graph The input graph, this will be rewired, it can be + * directed or undirected. If it is undirected or \p mode is set to + * IGRAPH_ALL, \ref igraph_rewire_edges() will be called. + * \param prob The rewiring probability, a constant between zero and + * one (inclusive). + * \param loops Boolean, whether loop edges are allowed in the new + * graph, or not. + * \param mode The endpoints of directed edges to rewire. It is ignored for + * undirected graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * rewire the end of each directed edge + * \cli IGRAPH_IN + * rewire the start of each directed edge + * \cli IGRAPH_ALL + * rewire both endpoints of each edge + * \endclist + * \return Error code. + * + * \sa \ref igraph_rewire_edges(), \ref igraph_rewire() + * + * Time complexity: O(|E|). + */ +int igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, + igraph_bool_t loops, igraph_neimode_t mode) { + + if (prob < 0 || prob > 1) { + IGRAPH_ERROR("Rewiring probability should be between zero and one", + IGRAPH_EINVAL); + } + + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && + mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); + } + + if (prob == 0) { + return IGRAPH_SUCCESS; + } + + if (igraph_is_directed(graph) && mode != IGRAPH_ALL) { + igraph_t newgraph; + long int no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + long int to_rewire; + long int offset = 0; + igraph_vector_t edges; + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * no_of_edges); + + switch (mode) { + case IGRAPH_IN: + offset = 0; + break; + case IGRAPH_OUT: + offset = 1; + break; + case IGRAPH_ALL: + break; /* suppress compiler warning */ + } + + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + + RNG_BEGIN(); + + to_rewire = RNG_GEOM(prob); + while (to_rewire < no_of_edges) { + if (loops) { + VECTOR(edges)[2 * to_rewire + offset] = RNG_INTEGER(0, no_of_nodes - 1); + } else { + long int nei = (long int) VECTOR(edges)[2 * to_rewire + (1 - offset)]; + long int r = RNG_INTEGER(0, no_of_nodes - 2); + VECTOR(edges)[2 * to_rewire + offset] = (r != nei ? r : no_of_nodes - 1); + } + to_rewire += RNG_GEOM(prob) + 1; + } + + RNG_END(); + + IGRAPH_CHECK(igraph_create(&newgraph, &edges, (igraph_integer_t) no_of_nodes, + igraph_is_directed(graph))); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_FINALLY(igraph_destroy, &newgraph); + IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 1); + IGRAPH_FINALLY_CLEAN(1); + igraph_destroy(graph); + *graph = newgraph; + + } else { + IGRAPH_CHECK(igraph_rewire_edges(graph, prob, loops, /* multiple = */ 1)); + } + + return 0; +} diff --git a/src/rigraph/core/operators/rewire_internal.h b/src/rigraph/core/operators/rewire_internal.h new file mode 100644 index 0000000..c9e5d7e --- /dev/null +++ b/src/rigraph/core/operators/rewire_internal.h @@ -0,0 +1,8 @@ +#ifndef IGRAPH_OPERATORS_REWIRE_INTERNAL_H +#define IGRAPH_OPERATORS_REWIRE_INTERNAL_H + +#include "igraph_interface.h" + +IGRAPH_PRIVATE_EXPORT int igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, igraph_bool_t use_adjlist); + +#endif diff --git a/src/rigraph/core/operators/simplify.c b/src/rigraph/core/operators/simplify.c new file mode 100644 index 0000000..80300c8 --- /dev/null +++ b/src/rigraph/core/operators/simplify.c @@ -0,0 +1,179 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "../core/fixed_vectorlist.h" +#include "../graph/attributes.h" + +/** + * \ingroup structural + * \function igraph_simplify + * \brief Removes loop and/or multiple edges from the graph. + * + * \param graph The graph object. + * \param multiple Logical, if true, multiple edges will be removed. + * \param loops Logical, if true, loops (self edges) will be removed. + * \param edge_comb What to do with the edge attributes. \c NULL means to + * discard the edge attributes after the operation, even for edges + * that were unaffeccted. See the igraph manual section about attributes + * for details. + * \return Error code: + * \c IGRAPH_ENOMEM if we are out of memory. + * + * Time complexity: O(|V|+|E|). + * + * \example examples/simple/igraph_simplify.c + */ + +int igraph_simplify(igraph_t *graph, igraph_bool_t multiple, + igraph_bool_t loops, + const igraph_attribute_combination_t *edge_comb) { + + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int edge; + igraph_bool_t attr = edge_comb && igraph_has_attribute_table(); + long int from, to, pfrom = -1, pto = -2; + igraph_t res; + igraph_es_t es; + igraph_eit_t eit; + igraph_vector_t mergeinto; + long int actedge; + + if (!multiple && !loops) + /* nothing to do */ + { + return IGRAPH_SUCCESS; + } + + if (!multiple) { + /* removing loop edges only, this is simple. No need to combine anything + * and the whole process can be done in-place */ + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_ID)); + IGRAPH_FINALLY(igraph_es_destroy, &es); + IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + while (!IGRAPH_EIT_END(eit)) { + edge = IGRAPH_EIT_GET(eit); + from = IGRAPH_FROM(graph, edge); + to = IGRAPH_TO(graph, edge); + if (from == to) { + IGRAPH_CHECK(igraph_vector_push_back(&edges, edge)); + } + IGRAPH_EIT_NEXT(eit); + } + + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + IGRAPH_FINALLY_CLEAN(2); + + if (igraph_vector_size(&edges) > 0) { + IGRAPH_CHECK(igraph_delete_edges(graph, igraph_ess_vector(&edges))); + } + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; + } + + if (attr) { + IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges); + } + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + + IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_FROM)); + IGRAPH_FINALLY(igraph_es_destroy, &es); + IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + for (actedge = -1; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { + edge = IGRAPH_EIT_GET(eit); + from = IGRAPH_FROM(graph, edge); + to = IGRAPH_TO(graph, edge); + + if (loops && from == to) { + /* Loop edge to be removed */ + if (attr) { + VECTOR(mergeinto)[edge] = -1; + } + } else if (multiple && from == pfrom && to == pto) { + /* Multiple edge to be contracted */ + if (attr) { + VECTOR(mergeinto)[edge] = actedge; + } + } else { + /* Edge to be kept */ + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + if (attr) { + actedge++; + VECTOR(mergeinto)[edge] = actedge; + } + } + pfrom = from; pto = to; + } + + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(&res, &edges, (igraph_integer_t) no_of_nodes, + igraph_is_directed(graph))); + + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_FINALLY(igraph_destroy, &res); + + IGRAPH_I_ATTRIBUTE_DESTROY(&res); + IGRAPH_I_ATTRIBUTE_COPY(&res, graph, /*graph=*/ 1, + /*vertex=*/ 1, /*edge=*/ 0); + + if (attr) { + igraph_fixed_vectorlist_t vl; + IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto, + actedge + 1)); + IGRAPH_FINALLY(igraph_fixed_vectorlist_destroy, &vl); + + IGRAPH_CHECK(igraph_i_attribute_combine_edges(graph, &res, &vl.v, + edge_comb)); + + igraph_fixed_vectorlist_destroy(&vl); + igraph_vector_destroy(&mergeinto); + IGRAPH_FINALLY_CLEAN(2); + } + + IGRAPH_FINALLY_CLEAN(1); + igraph_destroy(graph); + *graph = res; + + return 0; +} diff --git a/src/rigraph/core/operators/subgraph.c b/src/rigraph/core/operators/subgraph.c new file mode 100644 index 0000000..d46c9df --- /dev/null +++ b/src/rigraph/core/operators/subgraph.c @@ -0,0 +1,477 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "../core/interruption.h" +#include "../graph/attributes.h" +#include "subgraph.h" + +/** + * Subgraph creation, old version: it copies the graph and then deletes + * unneeded vertices. + */ +static int igraph_i_induced_subgraph_copy_and_delete( + const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, + igraph_vector_t *map, igraph_vector_t *invmap +) { + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t delete = IGRAPH_VECTOR_NULL; + char *remain; + long int i; + igraph_vit_t vit; + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + IGRAPH_VECTOR_INIT_FINALLY(&delete, 0); + remain = IGRAPH_CALLOC(no_of_nodes, char); + if (remain == 0) { + IGRAPH_ERROR("subgraph failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, remain); + IGRAPH_CHECK(igraph_vector_reserve(&delete, no_of_nodes - IGRAPH_VIT_SIZE(vit))); + + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + remain[ (long int) IGRAPH_VIT_GET(vit) ] = 1; + } + + for (i = 0; i < no_of_nodes; i++) { + + IGRAPH_ALLOW_INTERRUPTION(); + + if (remain[i] == 0) { + IGRAPH_CHECK(igraph_vector_push_back(&delete, i)); + } + } + + IGRAPH_FREE(remain); + IGRAPH_FINALLY_CLEAN(1); + + /* must set res->attr to 0 before calling igraph_copy */ + res->attr = 0; /* Why is this needed? TODO */ + IGRAPH_CHECK(igraph_copy(res, graph)); + IGRAPH_FINALLY(igraph_destroy, res); + IGRAPH_CHECK(igraph_delete_vertices_idx(res, igraph_vss_vector(&delete), + map, invmap)); + + igraph_vector_destroy(&delete); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(3); + return 0; +} + +/** + * Subgraph creation, new version: creates the new graph instead of + * copying the old one. + * + * map_is_prepared is an indicator that the caller has already prepared the + * 'map' vector and that this function should not resize or clear it. This + * is used to spare an O(n) operation (where n is the number of vertices in + * the _original_ graph) in cases when induced_subgraph() is repeatedly + * called on the same graph; one example is igraph_decompose(). + */ +static int igraph_i_induced_subgraph_create_from_scratch( + const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, + igraph_vector_t *map, igraph_vector_t *invmap, + igraph_bool_t map_is_prepared +) { + igraph_bool_t directed = igraph_is_directed(graph); + long int no_of_nodes = igraph_vcount(graph); + long int no_of_new_nodes = 0; + long int i, j, n; + long int to; + igraph_integer_t eid; + igraph_vector_t vids_old2new, vids_new2old; + igraph_vector_t eids_new2old; + igraph_vector_t nei_edges; + igraph_vector_t new_edges; + igraph_vit_t vit; + igraph_vector_t *my_vids_old2new = &vids_old2new, + *my_vids_new2old = &vids_new2old; + + /* The order of initialization is important here, they will be destroyed in the + * opposite order */ + IGRAPH_VECTOR_INIT_FINALLY(&eids_new2old, 0); + if (invmap) { + my_vids_new2old = invmap; + igraph_vector_clear(my_vids_new2old); + } else { + IGRAPH_VECTOR_INIT_FINALLY(&vids_new2old, 0); + } + IGRAPH_VECTOR_INIT_FINALLY(&new_edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&nei_edges, 0); + if (map) { + my_vids_old2new = map; + if (!map_is_prepared) { + IGRAPH_CHECK(igraph_vector_resize(map, no_of_nodes)); + igraph_vector_null(map); + } + } else { + IGRAPH_VECTOR_INIT_FINALLY(&vids_old2new, no_of_nodes); + } + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + /* Calculate the mapping from the old node IDs to the new ones. The other + * igraph_simplify implementation in igraph_i_simplify_copy_and_delete + * ensures that the order of vertex IDs is kept during remapping (i.e. + * if the old ID of vertex A is less than the old ID of vertex B, then + * the same will also be true for the new IDs). To ensure compatibility + * with the other implementation, we have to fetch the vertex IDs into + * a vector first and then sort it. We temporarily use new_edges for that. + */ + IGRAPH_CHECK(igraph_vit_as_vector(&vit, &nei_edges)); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + igraph_vector_sort(&nei_edges); + n = igraph_vector_size(&nei_edges); + for (i = 0; i < n; i++) { + long int vid = (long int) VECTOR(nei_edges)[i]; + if (VECTOR(*my_vids_old2new)[vid] == 0) { + IGRAPH_CHECK(igraph_vector_push_back(my_vids_new2old, vid)); + no_of_new_nodes++; + VECTOR(*my_vids_old2new)[vid] = no_of_new_nodes; + } + } + + /* Create the new edge list */ + for (i = 0; i < no_of_new_nodes; i++) { + long int old_vid = (long int) VECTOR(*my_vids_new2old)[i]; + long int new_vid = i; + igraph_bool_t skip_loop_edge; + + IGRAPH_CHECK(igraph_incident(graph, &nei_edges, old_vid, IGRAPH_OUT)); + n = igraph_vector_size(&nei_edges); + + if (directed) { + /* directed graph; this is easier */ + for (j = 0; j < n; j++) { + eid = (igraph_integer_t) VECTOR(nei_edges)[j]; + + to = (long int) VECTOR(*my_vids_old2new)[ (long int)IGRAPH_TO(graph, eid) ]; + if (!to) { + continue; + } + + IGRAPH_CHECK(igraph_vector_push_back(&new_edges, new_vid)); + IGRAPH_CHECK(igraph_vector_push_back(&new_edges, to - 1)); + IGRAPH_CHECK(igraph_vector_push_back(&eids_new2old, eid)); + } + } else { + /* undirected graph. We need to be careful with loop edges as each + * loop edge will appear twice. We use a boolean flag to skip every + * second loop edge */ + skip_loop_edge = 0; + for (j = 0; j < n; j++) { + eid = (igraph_integer_t) VECTOR(nei_edges)[j]; + + if (IGRAPH_FROM(graph, eid) != old_vid) { + /* avoid processing edges twice */ + continue; + } + + to = (long int) VECTOR(*my_vids_old2new)[ (long int)IGRAPH_TO(graph, eid) ]; + if (!to) { + continue; + } + to -= 1; + + if (new_vid == to) { + /* this is a loop edge; check whether we need to skip it */ + skip_loop_edge = !skip_loop_edge; + if (skip_loop_edge) { + continue; + } + } + + IGRAPH_CHECK(igraph_vector_push_back(&new_edges, new_vid)); + IGRAPH_CHECK(igraph_vector_push_back(&new_edges, to)); + IGRAPH_CHECK(igraph_vector_push_back(&eids_new2old, eid)); + } + } + } + + /* Get rid of some vectors that are not needed anymore */ + if (!map) { + igraph_vector_destroy(&vids_old2new); + IGRAPH_FINALLY_CLEAN(1); + } + igraph_vector_destroy(&nei_edges); + IGRAPH_FINALLY_CLEAN(1); + + /* Create the new graph */ + IGRAPH_CHECK(igraph_create(res, &new_edges, (igraph_integer_t) + no_of_new_nodes, directed)); + IGRAPH_I_ATTRIBUTE_DESTROY(res); + + /* Now we can also get rid of the new_edges vector */ + igraph_vector_destroy(&new_edges); + IGRAPH_FINALLY_CLEAN(1); + + /* Make sure that the newly created graph is destroyed if something happens from + * now on */ + IGRAPH_FINALLY(igraph_destroy, res); + + /* Copy the graph attributes */ + IGRAPH_CHECK(igraph_i_attribute_copy(res, graph, + /* ga = */ 1, /* va = */ 0, /* ea = */ 0)); + + /* Copy the vertex attributes */ + IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, res, + my_vids_new2old)); + + /* Copy the edge attributes */ + IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, res, &eids_new2old)); + + if (!invmap) { + igraph_vector_destroy(my_vids_new2old); + IGRAPH_FINALLY_CLEAN(1); + } + igraph_vector_destroy(&eids_new2old); + IGRAPH_FINALLY_CLEAN(2); /* 1 + 1 since we don't need to destroy res */ + + return 0; +} + +/** + * \ingroup structural + * \function igraph_induced_subgraph + * \brief Creates a subgraph induced by the specified vertices. + * + * + * This function collects the specified vertices and all edges between + * them to a new graph. + * As the vertex ids in a graph always start with zero, this function + * very likely needs to reassign ids to the vertices. + * \param graph The graph object. + * \param res The subgraph, another graph object will be stored here, + * do \em not initialize this object before calling this + * function, and call \ref igraph_destroy() on it if you don't need + * it any more. + * \param vids A vertex selector describing which vertices to keep. + * \param impl This parameter selects which implementation should we + * use when constructing the new graph. Basically there are two + * possibilities: \c IGRAPH_SUBGRAPH_COPY_AND_DELETE copies the + * existing graph and deletes the vertices that are not needed + * in the new graph, while \c IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH + * constructs the new graph from scratch without copying the old + * one. The latter is more efficient if you are extracting a + * relatively small subpart of a very large graph, while the + * former is better if you want to extract a subgraph whose size + * is comparable to the size of the whole graph. There is a third + * possibility: \c IGRAPH_SUBGRAPH_AUTO will select one of the + * two methods automatically based on the ratio of the number + * of vertices in the new and the old graph. + * + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * \c IGRAPH_EINVVID, invalid vertex id in + * \p vids. + * + * Time complexity: O(|V|+|E|), + * |V| and + * |E| are the number of vertices and + * edges in the original graph. + * + * \sa \ref igraph_delete_vertices() to delete the specified set of + * vertices from a graph, the opposite of this function. + */ +int igraph_induced_subgraph(const igraph_t *graph, igraph_t *res, + const igraph_vs_t vids, igraph_subgraph_implementation_t impl) { + return igraph_induced_subgraph_map(graph, res, vids, impl, /* map= */ 0, + /* invmap= */ 0); +} + +static int igraph_i_induced_subgraph_suggest_implementation( + const igraph_t *graph, const igraph_vs_t vids, + igraph_subgraph_implementation_t *result) { + double ratio; + igraph_integer_t num_vs; + + if (igraph_vs_is_all(&vids)) { + ratio = 1.0; + } else { + IGRAPH_CHECK(igraph_vs_size(graph, &vids, &num_vs)); + ratio = (igraph_real_t) num_vs / igraph_vcount(graph); + } + + /* TODO: needs benchmarking; threshold was chosen totally arbitrarily */ + if (ratio > 0.5) { + *result = IGRAPH_SUBGRAPH_COPY_AND_DELETE; + } else { + *result = IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH; + } + + return 0; +} + +int igraph_i_induced_subgraph_map(const igraph_t *graph, igraph_t *res, + const igraph_vs_t vids, + igraph_subgraph_implementation_t impl, + igraph_vector_t *map, + igraph_vector_t *invmap, + igraph_bool_t map_is_prepared) { + + if (impl == IGRAPH_SUBGRAPH_AUTO) { + IGRAPH_CHECK(igraph_i_induced_subgraph_suggest_implementation(graph, vids, &impl)); + } + + switch (impl) { + case IGRAPH_SUBGRAPH_COPY_AND_DELETE: + return igraph_i_induced_subgraph_copy_and_delete(graph, res, vids, map, invmap); + + case IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH: + return igraph_i_induced_subgraph_create_from_scratch(graph, res, vids, map, + invmap, /* map_is_prepared = */ map_is_prepared); + + default: + IGRAPH_ERROR("unknown subgraph implementation type", IGRAPH_EINVAL); + } +} + +int igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res, + const igraph_vs_t vids, + igraph_subgraph_implementation_t impl, + igraph_vector_t *map, + igraph_vector_t *invmap) { + return igraph_i_induced_subgraph_map(graph, res,vids, impl, map, invmap, /* map_is_prepared = */ 0); +} + +/** + * \ingroup structural + * \function igraph_subgraph_edges + * \brief Creates a subgraph with the specified edges and their endpoints. + * + * + * This function collects the specified edges and their endpoints to a new + * graph. + * As the vertex ids in a graph always start with zero, this function + * very likely needs to reassign ids to the vertices. + * \param graph The graph object. + * \param res The subgraph, another graph object will be stored here, + * do \em not initialize this object before calling this + * function, and call \ref igraph_destroy() on it if you don't need + * it any more. + * \param eids An edge selector describing which edges to keep. + * \param delete_vertices Whether to delete the vertices not incident on any + * of the specified edges as well. If \c FALSE, the number of vertices + * in the result graph will always be equal to the number of vertices + * in the input graph. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * \c IGRAPH_EINVEID, invalid edge id in + * \p eids. + * + * Time complexity: O(|V|+|E|), + * |V| and + * |E| are the number of vertices and + * edges in the original graph. + * + * \sa \ref igraph_delete_edges() to delete the specified set of + * edges from a graph, the opposite of this function. + */ + +int igraph_subgraph_edges(const igraph_t *graph, igraph_t *res, + const igraph_es_t eids, igraph_bool_t delete_vertices) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_vector_t delete = IGRAPH_VECTOR_NULL; + char *vremain, *eremain; + long int i; + igraph_eit_t eit; + + IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_VECTOR_INIT_FINALLY(&delete, 0); + vremain = IGRAPH_CALLOC(no_of_nodes, char); + if (vremain == 0) { + IGRAPH_ERROR("subgraph_edges failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, vremain); + eremain = IGRAPH_CALLOC(no_of_edges, char); + if (eremain == 0) { + IGRAPH_ERROR("subgraph_edges failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, eremain); + IGRAPH_CHECK(igraph_vector_reserve(&delete, no_of_edges - IGRAPH_EIT_SIZE(eit))); + + /* Collect the vertex and edge IDs that will remain */ + for (IGRAPH_EIT_RESET(eit); !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { + igraph_integer_t from, to; + long int eid = (long int) IGRAPH_EIT_GET(eit); + IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) eid, &from, &to)); + eremain[eid] = vremain[(long int)from] = vremain[(long int)to] = 1; + } + + /* Collect the edge IDs to be deleted */ + for (i = 0; i < no_of_edges; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + if (eremain[i] == 0) { + IGRAPH_CHECK(igraph_vector_push_back(&delete, i)); + } + } + + IGRAPH_FREE(eremain); + IGRAPH_FINALLY_CLEAN(1); + + /* Delete the unnecessary edges */ + /* must set res->attr to 0 before calling igraph_copy */ + res->attr = 0; /* Why is this needed? TODO */ + IGRAPH_CHECK(igraph_copy(res, graph)); + IGRAPH_FINALLY(igraph_destroy, res); + IGRAPH_CHECK(igraph_delete_edges(res, igraph_ess_vector(&delete))); + + if (delete_vertices) { + /* Collect the vertex IDs to be deleted */ + igraph_vector_clear(&delete); + for (i = 0; i < no_of_nodes; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + if (vremain[i] == 0) { + IGRAPH_CHECK(igraph_vector_push_back(&delete, i)); + } + } + } + + IGRAPH_FREE(vremain); + IGRAPH_FINALLY_CLEAN(1); + + /* Delete the unnecessary vertices */ + if (delete_vertices) { + IGRAPH_CHECK(igraph_delete_vertices(res, igraph_vss_vector(&delete))); + } + + igraph_vector_destroy(&delete); + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(3); + return 0; +} diff --git a/src/rigraph/core/operators/subgraph.h b/src/rigraph/core/operators/subgraph.h new file mode 100644 index 0000000..a27aa8e --- /dev/null +++ b/src/rigraph/core/operators/subgraph.h @@ -0,0 +1,35 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2003-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_OPERATORS_SUBGRAPH_INTERNAL_H +#define IGRAPH_OPERATORS_SUBGRAPH_INTERNAL_H + +#include "igraph_interface.h" + +IGRAPH_PRIVATE_EXPORT int igraph_i_induced_subgraph_map( + const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, + igraph_subgraph_implementation_t impl, igraph_vector_t *map, + igraph_vector_t *invmap, igraph_bool_t map_is_prepared +); + +#endif diff --git a/src/rigraph/core/operators/union.c b/src/rigraph/core/operators/union.c new file mode 100644 index 0000000..09663b8 --- /dev/null +++ b/src/rigraph/core/operators/union.c @@ -0,0 +1,261 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_conversion.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_qsort.h" + +#include "misc_internal.h" + +/** + * \function igraph_union + * \brief Calculates the union of two graphs. + * + * + * The number of vertices in the result is that of the larger graph + * from the two arguments. The result graph contains edges which are + * present in at least one of the operand graphs. + * + * \param res Pointer to an uninitialized graph object, the result + * will be stored here. + * \param left The first graph. + * \param right The second graph. + * \param edge_map1 Pointer to an initialized vector or a null pointer. + * If not a null pointer, it will contain a mapping from the edges + * of the first argument graph (\p left) to the edges of the + * result graph. + * \param edge_map2 The same as \p edge_map1, but for the second + * graph, \p right. + * \return Error code. + * \sa \ref igraph_union_many() for the union of many graphs, + * \ref igraph_intersection() and \ref igraph_difference() for other + * operators. + * + * Time complexity: O(|V|+|E|), |V| is the number of + * vertices, |E| the number of edges in the result graph. + * + * \example examples/simple/igraph_union.c + */ +int igraph_union(igraph_t *res, + const igraph_t *left, const igraph_t *right, + igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) { + return igraph_i_merge(res, IGRAPH_MERGE_MODE_UNION, left, right, + edge_map1, edge_map2); +} + +/** + * \function igraph_union_many + * \brief Creates the union of many graphs. + * + * + * The result graph will contain as many vertices as the largest graph + * among the arguments does, and an edge will be included in it if it + * is part of at least one operand graph. + * + * + * The directedness of the operand graphs must be the same. + * If the graph list has length zero, the result will be a \em directed + * graph with no vertices. + * + * \param res Pointer to an uninitialized graph object, this will + * contain the result. + * \param graphs Pointer vector, contains pointers to the operands of + * the union operator, graph objects of course. + * \param edgemaps If not a null pointer, then it must be an initialized + * pointer vector and the mappings of edges from the graphs to the + * result graph will be stored here, in the same order as + * \p graphs. Each mapping is stored in a separate + * \type igraph_vector_t object. + * \return Error code. + * \sa \ref igraph_union() for the union of two graphs, \ref + * igraph_intersection_many(), \ref igraph_intersection() and \ref + * igraph_difference for other operators. + * + * + * Time complexity: O(|V|+|E|), |V| is the number of vertices + * in largest graph and |E| is the number of edges in the result graph. + * + * \example examples/simple/igraph_union.c + */ +int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, + igraph_vector_ptr_t *edgemaps) { + + long int no_of_graphs = igraph_vector_ptr_size(graphs); + long int no_of_nodes = 0; + igraph_bool_t directed = 1; + igraph_vector_t edges; + igraph_vector_ptr_t edge_vects, order_vects; + igraph_vector_long_t no_edges; + long int i, j, tailfrom = no_of_graphs > 0 ? 0 : -1, tailto = -1; + long int idx = 0; + + /* Check directedness */ + if (no_of_graphs != 0) { + directed = igraph_is_directed(VECTOR(*graphs)[0]); + no_of_nodes = igraph_vcount(VECTOR(*graphs)[0]); + } + for (i = 1; i < no_of_graphs; i++) { + if (directed != igraph_is_directed(VECTOR(*graphs)[i])) { + IGRAPH_ERROR("Cannot union directed and undirected graphs", + IGRAPH_EINVAL); + } + } + + if (edgemaps) { + IGRAPH_CHECK(igraph_vector_ptr_resize(edgemaps, no_of_graphs)); + igraph_vector_ptr_null(edgemaps); + IGRAPH_FINALLY(igraph_i_union_intersection_destroy_vectors, edgemaps); + } + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_long_init(&no_edges, no_of_graphs)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &no_edges); + + /* Calculate number of nodes, query number of edges */ + for (i = 0; i < no_of_graphs; i++) { + long int n = igraph_vcount(VECTOR(*graphs)[i]); + if (n > no_of_nodes) { + no_of_nodes = n; + } + VECTOR(no_edges)[i] = igraph_ecount(VECTOR(*graphs)[i]); + } + + if (edgemaps) { + for (i = 0; i < no_of_graphs; i++) { + VECTOR(*edgemaps)[i] = IGRAPH_CALLOC(1, igraph_vector_t); + if (!VECTOR(*edgemaps)[i]) { + IGRAPH_ERROR("Cannot union graphs", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(VECTOR(*edgemaps)[i], + VECTOR(no_edges)[i])); + } + } + + /* Allocate memory for the edge lists and their index vectors */ + if (no_of_graphs != 0) { + IGRAPH_CHECK(igraph_vector_ptr_init(&edge_vects, no_of_graphs)); + IGRAPH_FINALLY(igraph_i_union_intersection_destroy_vectors, &edge_vects); + IGRAPH_CHECK(igraph_vector_ptr_init(&order_vects, no_of_graphs)); + IGRAPH_FINALLY(igraph_i_union_intersection_destroy_vector_longs, &order_vects); + } + for (i = 0; i < no_of_graphs; i++) { + VECTOR(edge_vects)[i] = IGRAPH_CALLOC(1, igraph_vector_t); + VECTOR(order_vects)[i] = IGRAPH_CALLOC(1, igraph_vector_long_t); + if (! VECTOR(edge_vects)[i] || ! VECTOR(order_vects)[i]) { + IGRAPH_ERROR("Cannot union graphs", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(VECTOR(edge_vects)[i], + 2 * VECTOR(no_edges)[i])); + IGRAPH_CHECK(igraph_vector_long_init(VECTOR(order_vects)[i], + VECTOR(no_edges)[i])); + } + + /* Query and sort the edge lists */ + for (i = 0; i < no_of_graphs; i++) { + long int k, j, n = VECTOR(no_edges)[i]; + igraph_vector_t *edges = VECTOR(edge_vects)[i]; + igraph_vector_long_t *order = VECTOR(order_vects)[i]; + IGRAPH_CHECK(igraph_get_edgelist(VECTOR(*graphs)[i], edges, /*bycol=*/0)); + if (!directed) { + for (k = 0, j = 0; k < n; k++, j += 2) { + if (VECTOR(*edges)[j] > VECTOR(*edges)[j + 1]) { + long int tmp = VECTOR(*edges)[j]; + VECTOR(*edges)[j] = VECTOR(*edges)[j + 1]; + VECTOR(*edges)[j + 1] = tmp; + } + } + } + for (k = 0; k < n; k++) { + VECTOR(*order)[k] = k; + } + igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), edges, + igraph_i_order_edgelist_cmp); + } + + while (tailfrom >= 0) { + + /* Get the largest tail element */ + tailfrom = tailto = -1; + for (j = 0; j < no_of_graphs; j++) { + if (!igraph_vector_long_empty(VECTOR(order_vects)[j])) { + long int edge = igraph_vector_long_tail(VECTOR(order_vects)[j]); + igraph_vector_t *ev = VECTOR(edge_vects)[j]; + long int from = VECTOR(*ev)[2 * edge]; + long int to = VECTOR(*ev)[2 * edge + 1]; + if (from > tailfrom || (from == tailfrom && to > tailto)) { + tailfrom = from; tailto = to; + } + } + } + if (tailfrom < 0) { + continue; + } + + /* add the edge */ + IGRAPH_CHECK(igraph_vector_push_back(&edges, tailfrom)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, tailto)); + + /* update edge lists, we just modify the 'order' vectors */ + for (j = 0; j < no_of_graphs; j++) { + if (!igraph_vector_long_empty(VECTOR(order_vects)[j])) { + long int edge = igraph_vector_long_tail(VECTOR(order_vects)[j]); + igraph_vector_t *ev = VECTOR(edge_vects)[j]; + long int from = VECTOR(*ev)[2 * edge]; + long int to = VECTOR(*ev)[2 * edge + 1]; + if (from == tailfrom && to == tailto) { + igraph_vector_long_pop_back(VECTOR(order_vects)[j]); + if (edgemaps) { + igraph_vector_t *map = VECTOR(*edgemaps)[j]; + VECTOR(*map)[edge] = idx; + } + } + } + } + idx++; + + } + + if (no_of_graphs > 0) { + igraph_i_union_intersection_destroy_vector_longs(&order_vects); + igraph_i_union_intersection_destroy_vectors(&edge_vects); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_vector_long_destroy(&no_edges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, + directed)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + if (edgemaps) { + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} diff --git a/src/rigraph/core/paths/all_shortest_paths.c b/src/rigraph/core/paths/all_shortest_paths.c new file mode 100644 index 0000000..8c4a539 --- /dev/null +++ b/src/rigraph/core/paths/all_shortest_paths.c @@ -0,0 +1,318 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "../core/interruption.h" + +#include /* memset */ + +static void igraph_i_gasp_paths_destroy(igraph_vector_ptr_t *v) { + long int i; + for (i = 0; i < igraph_vector_ptr_size(v); i++) { + if (VECTOR(*v)[i] != 0) { + igraph_vector_destroy(VECTOR(*v)[i]); + IGRAPH_FREE(VECTOR(*v)[i]); + } + } + igraph_vector_ptr_destroy(v); +} + +/** + * \function igraph_get_all_shortest_paths + * \brief All shortest paths (geodesics) from a vertex. + * + * When there is more than one shortest path between two vertices, + * all of them will be returned. + * + * \param graph The graph object. + * \param res Pointer to an initialized pointer vector, the result + * will be stored here in \ref igraph_vector_t objects. Each vector + * object contains the vertices along a shortest path from \p from + * to another vertex. The vectors are ordered according to their + * target vertex: first the shortest paths to vertex 0, then to + * vertex 1, etc. No data is included for unreachable vertices. + * \param nrgeo Pointer to an initialized \ref igraph_vector_t object or + * \c NULL. If not \c NULL the number of shortest paths from \p from are + * stored here for every vertex in the graph. Note that the values + * will be accurate only for those vertices that are in the target + * vertex sequence (see \p to), since the search terminates as soon + * as all the target vertices have been found. + * \param from The id of the vertex from/to which the geodesics are + * calculated. + * \param to Vertex sequence with the ids of the vertices to/from which the + * shortest paths will be calculated. A vertex might be given multiple + * times. + * \param mode The type of shortest paths to be use for the + * calculation in directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the lengths of the outgoing paths are calculated. + * \cli IGRAPH_IN + * the lengths of the incoming paths are calculated. + * \cli IGRAPH_ALL + * the directed graph is considered as an + * undirected one for the computation. + * \endclist + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * \p from is invalid vertex id. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Added in version 0.2. + * + * Time complexity: O(|V|+|E|) for most graphs, O(|V|^2) in the worst + * case. + */ +int igraph_get_all_shortest_paths(const igraph_t *graph, + igraph_vector_ptr_t *res, + igraph_vector_t *nrgeo, + igraph_integer_t from, const igraph_vs_t to, + igraph_neimode_t mode) { + + long int no_of_nodes = igraph_vcount(graph); + long int *geodist; + igraph_vector_ptr_t paths; + igraph_dqueue_t q; + igraph_vector_t *vptr; + igraph_vector_t neis; + igraph_vector_t ptrlist; + igraph_vector_t ptrhead; + long int n, j, i; + long int to_reach, reached = 0, maxdist = 0; + + igraph_vit_t vit; + + if (from < 0 || from >= no_of_nodes) { + IGRAPH_ERROR("cannot get shortest paths", IGRAPH_EINVVID); + } + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && + mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); + } + + IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + /* paths will store the shortest paths during the search */ + IGRAPH_CHECK(igraph_vector_ptr_init(&paths, 0)); + IGRAPH_FINALLY(igraph_i_gasp_paths_destroy, &paths); + /* neis is a temporary vector holding the neighbors of the + * node being examined */ + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + /* ptrlist stores indices into the paths vector, in the order + * of how they were found. ptrhead is a second-level index that + * will be used to find paths that terminate in a given vertex */ + IGRAPH_VECTOR_INIT_FINALLY(&ptrlist, 0); + /* ptrhead contains indices into ptrlist. + * ptrhead[i] = j means that element #j-1 in ptrlist contains + * the shortest path from the root to node i. ptrhead[i] = 0 + * means that node i was not reached so far */ + IGRAPH_VECTOR_INIT_FINALLY(&ptrhead, no_of_nodes); + /* geodist[i] == 0 if i was not reached yet and it is not in the + * target vertex sequence, or -1 if i was not reached yet and it + * is in the target vertex sequence. Otherwise it is + * one larger than the length of the shortest path from the + * source */ + geodist = IGRAPH_CALLOC(no_of_nodes, long int); + if (geodist == 0) { + IGRAPH_ERROR("Cannot calculate shortest paths", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, geodist); + /* dequeue to store the BFS queue -- odd elements are the vertex indices, + * even elements are the distances from the root */ + IGRAPH_CHECK(igraph_dqueue_init(&q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &q); + + if (nrgeo) { + IGRAPH_CHECK(igraph_vector_resize(nrgeo, no_of_nodes)); + igraph_vector_null(nrgeo); + } + + /* use geodist to count how many vertices we have to reach */ + to_reach = IGRAPH_VIT_SIZE(vit); + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + if (geodist[ (long int) IGRAPH_VIT_GET(vit) ] == 0) { + geodist[ (long int) IGRAPH_VIT_GET(vit) ] = -1; + } else { + to_reach--; /* this node was given multiple times */ + } + } + + if (geodist[ (long int) from ] < 0) { + reached++; + } + + /* from -> from */ + vptr = IGRAPH_CALLOC(1, igraph_vector_t); /* TODO: dirty */ + IGRAPH_CHECK(igraph_vector_ptr_push_back(&paths, vptr)); + IGRAPH_CHECK(igraph_vector_init(vptr, 1)); + VECTOR(*vptr)[0] = from; + geodist[(long int)from] = 1; + VECTOR(ptrhead)[(long int)from] = 1; + IGRAPH_CHECK(igraph_vector_push_back(&ptrlist, 0)); + if (nrgeo) { + VECTOR(*nrgeo)[(long int)from] = 1; + } + + /* Init queue */ + IGRAPH_CHECK(igraph_dqueue_push(&q, from)); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0.0)); + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + long int actdist = (long int) igraph_dqueue_pop(&q); + + IGRAPH_ALLOW_INTERRUPTION(); + + if (reached >= to_reach) { + /* all nodes were reached. Since we need all the shortest paths + * to all these nodes, we can stop the search only if the distance + * of the current node to the root is larger than the distance of + * any of the nodes we wanted to reach */ + if (actdist > maxdist) { + /* safety check, maxdist should have been set when we reached the last node */ + if (maxdist < 0) { + IGRAPH_ERROR("possible bug in igraph_get_all_shortest_paths, " + "maxdist is negative", IGRAPH_EINVAL); + } + break; + } + } + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, + mode)); + n = igraph_vector_size(&neis); + for (j = 0; j < n; j++) { + long int neighbor = (long int) VECTOR(neis)[j]; + long int fatherptr; + + if (geodist[neighbor] > 0 && + geodist[neighbor] - 1 < actdist + 1) { + /* this node was reached via a shorter path before */ + continue; + } + + /* yay, found another shortest path to neighbor */ + + if (nrgeo) { + /* the number of geodesics leading to neighbor must be + * increased by the number of geodesics leading to actnode */ + VECTOR(*nrgeo)[neighbor] += VECTOR(*nrgeo)[actnode]; + } + if (geodist[neighbor] <= 0) { + /* this node was not reached yet, push it into the queue */ + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + if (geodist[neighbor] < 0) { + reached++; + } + if (reached == to_reach) { + maxdist = actdist; + } + } + geodist[neighbor] = actdist + 2; + + /* copy all existing paths to the parent */ + fatherptr = (long int) VECTOR(ptrhead)[actnode]; + while (fatherptr != 0) { + /* allocate a new igraph_vector_t at the end of paths */ + vptr = IGRAPH_CALLOC(1, igraph_vector_t); + IGRAPH_CHECK(igraph_vector_ptr_push_back(&paths, vptr)); + IGRAPH_CHECK(igraph_vector_copy(vptr, VECTOR(paths)[fatherptr - 1])); + IGRAPH_CHECK(igraph_vector_reserve(vptr, actdist + 2)); + IGRAPH_CHECK(igraph_vector_push_back(vptr, neighbor)); + + IGRAPH_CHECK(igraph_vector_push_back(&ptrlist, + VECTOR(ptrhead)[neighbor])); + VECTOR(ptrhead)[neighbor] = igraph_vector_size(&ptrlist); + + fatherptr = (long int) VECTOR(ptrlist)[fatherptr - 1]; + } + } + } + + igraph_dqueue_destroy(&q); + IGRAPH_FINALLY_CLEAN(1); + + /* mark the nodes for which we need the result */ + memset(geodist, 0, sizeof(long int) * (size_t) no_of_nodes); + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + geodist[ (long int) IGRAPH_VIT_GET(vit) ] = 1; + } + + /* count the number of paths in the result */ + n = 0; + for (i = 0; i < no_of_nodes; i++) { + long int fatherptr = (long int) VECTOR(ptrhead)[i]; + if (geodist[i] > 0) { + while (fatherptr != 0) { + n++; + fatherptr = (long int) VECTOR(ptrlist)[fatherptr - 1]; + } + } + } + + IGRAPH_CHECK(igraph_vector_ptr_resize(res, n)); + j = 0; + for (i = 0; i < no_of_nodes; i++) { + long int fatherptr = (long int) VECTOR(ptrhead)[i]; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* do we need the paths leading to vertex i? */ + if (geodist[i] > 0) { + /* yes, copy them to the result vector */ + while (fatherptr != 0) { + VECTOR(*res)[j++] = VECTOR(paths)[fatherptr - 1]; + fatherptr = (long int) VECTOR(ptrlist)[fatherptr - 1]; + } + } else { + /* no, free them */ + while (fatherptr != 0) { + igraph_vector_destroy(VECTOR(paths)[fatherptr - 1]); + IGRAPH_FREE(VECTOR(paths)[fatherptr - 1]); + fatherptr = (long int) VECTOR(ptrlist)[fatherptr - 1]; + } + } + } + + IGRAPH_FREE(geodist); + igraph_vector_destroy(&ptrlist); + igraph_vector_destroy(&ptrhead); + igraph_vector_destroy(&neis); + igraph_vector_ptr_destroy(&paths); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(6); + + return 0; +} diff --git a/src/rigraph/core/paths/bellman_ford.c b/src/rigraph/core/paths/bellman_ford.c new file mode 100644 index 0000000..18e6e04 --- /dev/null +++ b/src/rigraph/core/paths/bellman_ford.c @@ -0,0 +1,570 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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 . +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_stack.h" + +#include "../core/indheap.h" +#include "../core/interruption.h" + +#include + +/** + * \function igraph_shortest_paths_bellman_ford + * \brief Weighted shortest path lengths between vertices, allowing negative weights. + * + * This function implements the Bellman-Ford algorithm to find the weighted + * shortest paths to all vertices from a single source, allowing negative weights. + * It is run independently for the given sources. If there are no negative + * weights, you are better off with \ref igraph_shortest_paths_dijkstra() . + * + * \param graph The input graph, can be directed. + * \param res The result, a matrix. A pointer to an initialized matrix + * should be passed here, the matrix will be resized if needed. + * Each row contains the distances from a single source, to all + * vertices in the graph, in the order of vertex ids. For unreachable + * vertices the matrix contains \c IGRAPH_INFINITY. + * \param from The source vertices. + * \param to The target vertices. It is not allowed to include a + * vertex twice or more. + * \param weights The edge weights. There mustn't be any closed loop in + * the graph that has a negative total weight (since this would allow + * us to decrease the weight of any path containing at least a single + * vertex of this loop infinitely). Additionally, no edge weight may + * be NaN. If either case does not hold, an error is returned. If this + * is a null pointer, then the unweighted version, + * \ref igraph_shortest_paths() is called. + * \param mode For directed graphs; whether to follow paths along edge + * directions (\c IGRAPH_OUT), or the opposite (\c IGRAPH_IN), or + * ignore edge directions completely (\c IGRAPH_ALL). It is ignored + * for undirected graphs. + * \return Error code. + * + * Time complexity: O(s*|E|*|V|), where |V| is the number of + * vertices, |E| the number of edges and s the number of sources. + * + * \sa \ref igraph_shortest_paths() for a faster unweighted version + * or \ref igraph_shortest_paths_dijkstra() if you do not have negative + * edge weights. + * + * \example examples/simple/bellman_ford.c + */ +int igraph_shortest_paths_bellman_ford(const igraph_t *graph, + igraph_matrix_t *res, + const igraph_vs_t from, + const igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_lazy_inclist_t inclist; + long int i, j, k; + long int no_of_from, no_of_to; + igraph_dqueue_t Q; + igraph_vector_t clean_vertices; + igraph_vector_t num_queued; + igraph_vit_t fromvit, tovit; + igraph_real_t my_infinity = IGRAPH_INFINITY; + igraph_bool_t all_to; + igraph_vector_t dist; + + /* + - speedup: a vertex is marked clean if its distance from the source + did not change during the last phase. Neighbors of a clean vertex + are not relaxed again, since it would mean no change in the + shortest path values. Dirty vertices are queued. Negative loops can + be detected by checking whether a vertex has been queued at least + n times. + */ + if (!weights) { + return igraph_shortest_paths(graph, res, from, to, mode); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); + } + if (no_of_edges > 0 && igraph_vector_is_any_nan(weights)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit)); + IGRAPH_FINALLY(igraph_vit_destroy, &fromvit); + no_of_from = IGRAPH_VIT_SIZE(fromvit); + + IGRAPH_DQUEUE_INIT_FINALLY(&Q, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&clean_vertices, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&num_queued, no_of_nodes); + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + all_to = igraph_vs_is_all(&to); + if (all_to) { + no_of_to = no_of_nodes; + } else { + IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit)); + IGRAPH_FINALLY(igraph_vit_destroy, &tovit); + no_of_to = IGRAPH_VIT_SIZE(tovit); + } + + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to)); + + for (IGRAPH_VIT_RESET(fromvit), i = 0; + !IGRAPH_VIT_END(fromvit); + IGRAPH_VIT_NEXT(fromvit), i++) { + long int source = IGRAPH_VIT_GET(fromvit); + + igraph_vector_fill(&dist, my_infinity); + VECTOR(dist)[source] = 0; + igraph_vector_null(&clean_vertices); + igraph_vector_null(&num_queued); + + /* Fill the queue with vertices to be checked */ + for (j = 0; j < no_of_nodes; j++) { + IGRAPH_CHECK(igraph_dqueue_push(&Q, j)); + } + + while (!igraph_dqueue_empty(&Q)) { + igraph_vector_int_t *neis; + long int nlen; + + j = (long int) igraph_dqueue_pop(&Q); + VECTOR(clean_vertices)[j] = 1; + VECTOR(num_queued)[j] += 1; + if (VECTOR(num_queued)[j] > no_of_nodes) { + IGRAPH_ERROR("cannot run Bellman-Ford algorithm", IGRAPH_ENEGLOOP); + } + + /* If we cannot get to j in finite time yet, there is no need to relax + * its edges */ + if (!IGRAPH_FINITE(VECTOR(dist)[j])) { + continue; + } + + neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) j); + nlen = igraph_vector_int_size(neis); + + for (k = 0; k < nlen; k++) { + long int nei = (long int) VECTOR(*neis)[k]; + long int target = IGRAPH_OTHER(graph, nei, j); + if (VECTOR(dist)[target] > VECTOR(dist)[j] + VECTOR(*weights)[nei]) { + /* relax the edge */ + VECTOR(dist)[target] = VECTOR(dist)[j] + VECTOR(*weights)[nei]; + if (VECTOR(clean_vertices)[target]) { + VECTOR(clean_vertices)[target] = 0; + IGRAPH_CHECK(igraph_dqueue_push(&Q, target)); + } + } + } + } + + /* Copy it to the result */ + if (all_to) { + igraph_matrix_set_row(res, &dist, i); + } else { + for (IGRAPH_VIT_RESET(tovit), j = 0; !IGRAPH_VIT_END(tovit); + IGRAPH_VIT_NEXT(tovit), j++) { + long int v = IGRAPH_VIT_GET(tovit); + MATRIX(*res, i, j) = VECTOR(dist)[v]; + } + } + } + + igraph_vector_destroy(&dist); + IGRAPH_FINALLY_CLEAN(1); + + if (!all_to) { + igraph_vit_destroy(&tovit); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vit_destroy(&fromvit); + igraph_dqueue_destroy(&Q); + igraph_vector_destroy(&clean_vertices); + igraph_vector_destroy(&num_queued); + igraph_lazy_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(5); + + return 0; +} + + +/** + * \ingroup structural + * \function igraph_get_shortest_paths_bellman_ford + * \brief Weighted shortest paths from a vertex, allowing negative weights. + * + * This function calculates weighted shortest paths from or to a single vertex, + * and allows negative weights. When there is more than one shortest path between + * two vertices, only one of them is returned. + * + * If there are no negative weights, you are better off with + * \ref igraph_get_shortest_paths_dijkstra() . + * + * \param graph The input graph, can be directed. + * \param vertices The result, the ids of the vertices along the paths. + * This is a pointer vector, each element points to a vector + * object. These should be initialized before passing them to + * the function, which will properly clear and/or resize them + * and fill the ids of the vertices along the geodesics from/to + * the vertices. Supply a null pointer here if you don't need + * these vectors. Normally, either this argument, or the \c + * edges should be non-null, but no error or warning is given + * if they are both null pointers. + * \param edges The result, the ids of the edges along the paths. + * This is a pointer vector, each element points to a vector + * object. These should be initialized before passing them to + * the function, which will properly clear and/or resize them + * and fill the ids of the vertices along the geodesics from/to + * the vertices. Supply a null pointer here if you don't need + * these vectors. Normally, either this argument, or the \c + * vertices should be non-null, but no error or warning is given + * if they are both null pointers. + * \param from The id of the vertex from/to which the geodesics are + * calculated. + * \param to Vertex sequence with the ids of the vertices to/from which the + * shortest paths will be calculated. A vertex might be given multiple + * times. + * \param weights The edge weights. There mustn't be any closed loop in + * the graph that has a negative total weight (since this would allow + * us to decrease the weight of any path containing at least a single + * vertex of this loop infinitely). If this is a null pointer, then the + * unweighted version, \ref igraph_shortest_paths() is called. + * \param mode For directed graphs; whether to follow paths along edge + * directions (\c IGRAPH_OUT), or the opposite (\c IGRAPH_IN), or + * ignore edge directions completely (\c IGRAPH_ALL). It is ignored + * for undirected graphs. + * \param predecessors A pointer to an initialized igraph vector or null. + * If not null, a vector containing the predecessor of each vertex in + * the single source shortest path tree is returned here. The + * predecessor of vertex i in the tree is the vertex from which vertex i + * was reached. The predecessor of the start vertex (in the \c from + * argument) is itself by definition. If the predecessor is -1, it means + * that the given vertex was not reached from the source during the + * search. Note that the search terminates if all the vertices in + * \c to are reached. + * \param inbound_edges A pointer to an initialized igraph vector or null. + * If not null, a vector containing the inbound edge of each vertex in + * the single source shortest path tree is returned here. The + * inbound edge of vertex i in the tree is the edge via which vertex i + * was reached. The start vertex and vertices that were not reached + * during the search will have -1 in the corresponding entry of the + * vector. Note that the search terminates if all the vertices in + * \c to are reached. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * Not enough memory for temporary data. + * \cli IGRAPH_EINVAL + * The weight vector doesn't math the number of edges. + * \cli IGRAPH_EINVVID + * \p from is invalid vertex id, or the length of \p to is + * not the same as the length of \p vertices or \p edges. + * \cli IGRAPH_ENEGLOOP + * Bellman-ford algorithm encounted a negative loop. + * \endclist + * + * Time complexity: O(|E|*|V|), where |V| is the number of + * vertices, |E| the number of edges. + * + * \sa \ref igraph_shortest_paths() for a faster unweighted version + * or \ref igraph_shortest_paths_dijkstra() if you do not have negative + * edge weights. + */ + +int igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, + igraph_vector_ptr_t *vertices, + igraph_vector_ptr_t *edges, + igraph_integer_t from, + igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_vector_long_t *predecessors, + igraph_vector_long_t *inbound_edges) { + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int *parents; + igraph_lazy_inclist_t inclist; + long int i, j, k; + igraph_dqueue_t Q; + igraph_vector_t clean_vertices; + igraph_vector_t num_queued; + igraph_vit_t tovit; + igraph_real_t my_infinity = IGRAPH_INFINITY; + igraph_vector_t dist; + + if (!weights) { + return igraph_get_shortest_paths(graph, vertices, edges, from, to, mode, + predecessors, inbound_edges); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length must match number of edges.", IGRAPH_EINVAL); + } + + IGRAPH_DQUEUE_INIT_FINALLY(&Q, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&clean_vertices, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&num_queued, no_of_nodes); + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit)); + IGRAPH_FINALLY(igraph_vit_destroy, &tovit); + + if (vertices && IGRAPH_VIT_SIZE(tovit) != igraph_vector_ptr_size(vertices)) { + IGRAPH_ERROR("Size of `vertices' and `to' should match.", IGRAPH_EINVAL); + } + if (edges && IGRAPH_VIT_SIZE(tovit) != igraph_vector_ptr_size(edges)) { + IGRAPH_ERROR("Size of `edges' and `to' should match.", IGRAPH_EINVAL); + } + + parents = IGRAPH_CALLOC(no_of_nodes, long int); + if (parents == 0) { + IGRAPH_ERROR("Insufficient memory for shortest paths with Bellman-Ford.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, parents); + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + + igraph_vector_fill(&dist, my_infinity); + VECTOR(dist)[from] = 0; + igraph_vector_null(&clean_vertices); + igraph_vector_null(&num_queued); + + /* Fill the queue with vertices to be checked */ + for (j = 0; j < no_of_nodes; j++) { + IGRAPH_CHECK(igraph_dqueue_push(&Q, j)); + } + + while (!igraph_dqueue_empty(&Q)) { + igraph_vector_int_t *neis; + long int nlen; + + j = (long int) igraph_dqueue_pop(&Q); + VECTOR(clean_vertices)[j] = 1; + VECTOR(num_queued)[j] += 1; + if (VECTOR(num_queued)[j] > no_of_nodes) { + IGRAPH_ERROR("cannot run Bellman-Ford algorithm", IGRAPH_ENEGLOOP); + } + + /* If we cannot get to j in finite time yet, there is no need to relax + * its edges */ + if (!IGRAPH_FINITE(VECTOR(dist)[j])) { + continue; + } + + neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) j); + nlen = igraph_vector_int_size(neis); + + for (k = 0; k < nlen; k++) { + long int nei = (long int) VECTOR(*neis)[k]; + long int target = IGRAPH_OTHER(graph, nei, j); + if (VECTOR(dist)[target] > VECTOR(dist)[j] + VECTOR(*weights)[nei]) { + /* relax the edge */ + VECTOR(dist)[target] = VECTOR(dist)[j] + VECTOR(*weights)[nei]; + parents[target] = nei + 1; + if (VECTOR(clean_vertices)[target]) { + VECTOR(clean_vertices)[target] = 0; + IGRAPH_CHECK(igraph_dqueue_push(&Q, target)); + } + } + } + } + + /* Create `predecessors' if needed */ + if (predecessors) { + IGRAPH_CHECK(igraph_vector_long_resize(predecessors, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (i == from) { + /* i is the start vertex */ + VECTOR(*predecessors)[i] = i; + } else if (parents[i] <= 0) { + /* i was not reached */ + VECTOR(*predecessors)[i] = -1; + } else { + /* i was reached via the edge with ID = parents[i] - 1 */ + VECTOR(*predecessors)[i] = IGRAPH_OTHER(graph, parents[i] - 1, i); + } + } + } + + /* Create `inbound_edges' if needed */ + if (inbound_edges) { + IGRAPH_CHECK(igraph_vector_long_resize(inbound_edges, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (parents[i] <= 0) { + /* i was not reached */ + VECTOR(*inbound_edges)[i] = -1; + } else { + /* i was reached via the edge with ID = parents[i] - 1 */ + VECTOR(*inbound_edges)[i] = parents[i] - 1; + } + } + } + + /* Reconstruct the shortest paths based on vertex and/or edge IDs */ + if (vertices || edges) { + for (IGRAPH_VIT_RESET(tovit), i = 0; !IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit), i++) { + long int node = IGRAPH_VIT_GET(tovit); + long int size, act, edge; + igraph_vector_t *vvec = 0, *evec = 0; + if (vertices) { + vvec = VECTOR(*vertices)[i]; + igraph_vector_clear(vvec); + } + if (edges) { + evec = VECTOR(*edges)[i]; + igraph_vector_clear(evec); + } + + IGRAPH_ALLOW_INTERRUPTION(); + + size = 0; + act = node; + while (parents[act]) { + size++; + edge = parents[act] - 1; + act = IGRAPH_OTHER(graph, edge, act); + } + if (vvec && (size > 0 || node == from)) { + IGRAPH_CHECK(igraph_vector_resize(vvec, size + 1)); + VECTOR(*vvec)[size] = node; + } + if (evec) { + IGRAPH_CHECK(igraph_vector_resize(evec, size)); + } + act = node; + while (parents[act]) { + edge = parents[act] - 1; + act = IGRAPH_OTHER(graph, edge, act); + size--; + if (vvec) { + VECTOR(*vvec)[size] = act; + } + if (evec) { + VECTOR(*evec)[size] = edge; + } + } + } + } + + igraph_vector_destroy(&dist); + IGRAPH_FINALLY_CLEAN(1); + + igraph_vit_destroy(&tovit); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_FREE(parents); + igraph_dqueue_destroy(&Q); + igraph_vector_destroy(&clean_vertices); + igraph_vector_destroy(&num_queued); + igraph_lazy_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_shortest_path_bellman_ford + * \brief Weighted shortest path from one vertex to another one. + * + * Calculates a single (positively) weighted shortest path from + * a single vertex to another one, using Bellman-Ford algorithm. + * + * + * This function is a special case (and a wrapper) to + * \ref igraph_get_shortest_paths_bellman_ford(). + * + * \param graph The input graph, it can be directed or undirected. + * \param vertices Pointer to an initialized vector or a null + * pointer. If not a null pointer, then the vertex ids along + * the path are stored here, including the source and target + * vertices. + * \param edges Pointer to an uninitialized vector or a null + * pointer. If not a null pointer, then the edge ids along the + * path are stored here. + * \param from The id of the source vertex. + * \param to The id of the target vertex. + * \param weights The edge weights. There mustn't be any closed loop in + * the graph that has a negative total weight (since this would allow + * us to decrease the weight of any path containing at least a single + * vertex of this loop infinitely). If this is a null pointer, then the + * unweighted version is called. + * \param mode A constant specifying how edge directions are + * considered in directed graphs. \c IGRAPH_OUT follows edge + * directions, \c IGRAPH_IN follows the opposite directions, + * and \c IGRAPH_ALL ignores edge directions. This argument is + * ignored for undirected graphs. + * \return Error code. + * + * Time complexity: O(|E|log|E|+|V|), |V| is the number of vertices, + * |E| is the number of edges in the graph. + * + * \sa \ref igraph_get_shortest_paths_bellman_ford() for the version with + * more target vertices. + */ + +int igraph_get_shortest_path_bellman_ford(const igraph_t *graph, + igraph_vector_t *vertices, + igraph_vector_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + + igraph_vector_ptr_t vertices2, *vp = &vertices2; + igraph_vector_ptr_t edges2, *ep = &edges2; + + if (vertices) { + IGRAPH_CHECK(igraph_vector_ptr_init(&vertices2, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vertices2); + VECTOR(vertices2)[0] = vertices; + } else { + vp = NULL; + } + if (edges) { + IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2); + VECTOR(edges2)[0] = edges; + } else { + ep = NULL; + } + + IGRAPH_CHECK(igraph_get_shortest_paths_bellman_ford(graph, vp, ep, + from, igraph_vss_1(to), + weights, mode, NULL, NULL)); + + if (edges) { + igraph_vector_ptr_destroy(&edges2); + IGRAPH_FINALLY_CLEAN(1); + } + if (vertices) { + igraph_vector_ptr_destroy(&vertices2); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/paths/dijkstra.c b/src/rigraph/core/paths/dijkstra.c new file mode 100644 index 0000000..3e91ab1 --- /dev/null +++ b/src/rigraph/core/paths/dijkstra.c @@ -0,0 +1,1068 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_stack.h" + +#include "../core/indheap.h" +#include "../core/interruption.h" +#include "../core/math.h" + +#include /* memset */ + +/** + * \function igraph_shortest_paths_dijkstra + * \brief Weighted shortest path lengths between vertices. + * + * This function implements Dijkstra's algorithm to find the weighted + * shortest path lengths to all vertices from a single source. It is run + * independently for the given sources. It uses a binary heap for + * efficient implementation. + * + * \param graph The input graph, can be directed. + * \param res The result, a matrix. A pointer to an initialized matrix + * should be passed here. The matrix will be resized as needed. + * Each row contains the distances from a single source, to the + * vertices given in the \c to argument. + * Unreachable vertices has distance + * \c IGRAPH_INFINITY. + * \param from The source vertices. + * \param to The target vertices. It is not allowed to include a + * vertex twice or more. + * \param weights The edge weights. All edge weights must be + * non-negative for Dijkstra's algorithm to work. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. If this is a null pointer, then the unweighted + * version, \ref igraph_shortest_paths() is called. + * \param mode For directed graphs; whether to follow paths along edge + * directions (\c IGRAPH_OUT), or the opposite (\c IGRAPH_IN), or + * ignore edge directions completely (\c IGRAPH_ALL). It is ignored + * for undirected graphs. + * \return Error code. + * + * Time complexity: O(s*|E|log|E|+|V|), where |V| is the number of + * vertices, |E| the number of edges and s the number of sources. + * + * \sa \ref igraph_shortest_paths() for a (slightly) faster unweighted + * version or \ref igraph_shortest_paths_bellman_ford() for a weighted + * variant that works in the presence of negative edge weights (but no + * negative loops). + * + * \example examples/simple/dijkstra.c + */ +int igraph_shortest_paths_dijkstra(const igraph_t *graph, + igraph_matrix_t *res, + const igraph_vs_t from, + const igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + + /* Implementation details. This is the basic Dijkstra algorithm, + with a binary heap. The heap is indexed, i.e. it stores not only + the distances, but also which vertex they belong to. + + From now on we use a 2-way heap, so the distances can be queried + directly from the heap. + + Dirty tricks: + - the opposite of the distance is stored in the heap, as it is a + maximum heap and we need a minimum heap. + - we don't use IGRAPH_INFINITY in the res matrix during the + computation, as IGRAPH_FINITE() might involve a function call + and we want to spare that. -1 will denote infinity instead. + */ + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_2wheap_t Q; + igraph_vit_t fromvit, tovit; + long int no_of_from, no_of_to; + igraph_lazy_inclist_t inclist; + long int i, j; + igraph_real_t my_infinity = IGRAPH_INFINITY; + igraph_bool_t all_to; + igraph_vector_t indexv; + + if (!weights) { + return igraph_shortest_paths(graph, res, from, to, mode); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit)); + IGRAPH_FINALLY(igraph_vit_destroy, &fromvit); + no_of_from = IGRAPH_VIT_SIZE(fromvit); + + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + all_to = igraph_vs_is_all(&to); + if (all_to) { + no_of_to = no_of_nodes; + } else { + IGRAPH_VECTOR_INIT_FINALLY(&indexv, no_of_nodes); + IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit)); + IGRAPH_FINALLY(igraph_vit_destroy, &tovit); + no_of_to = IGRAPH_VIT_SIZE(tovit); + for (i = 0; !IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit)) { + long int v = IGRAPH_VIT_GET(tovit); + if (VECTOR(indexv)[v]) { + IGRAPH_ERROR("Duplicate vertices in `to', this is not allowed", + IGRAPH_EINVAL); + } + VECTOR(indexv)[v] = ++i; + } + } + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to)); + igraph_matrix_fill(res, my_infinity); + + for (IGRAPH_VIT_RESET(fromvit), i = 0; + !IGRAPH_VIT_END(fromvit); + IGRAPH_VIT_NEXT(fromvit), i++) { + + long int reached = 0; + long int source = IGRAPH_VIT_GET(fromvit); + igraph_2wheap_clear(&Q); + igraph_2wheap_push_with_index(&Q, source, -1.0); + + while (!igraph_2wheap_empty(&Q)) { + long int minnei = igraph_2wheap_max_index(&Q); + igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); + igraph_vector_int_t *neis; + long int nlen; + + if (all_to) { + MATRIX(*res, i, minnei) = mindist - 1.0; + } else { + if (VECTOR(indexv)[minnei]) { + MATRIX(*res, i, (long int)(VECTOR(indexv)[minnei] - 1)) = mindist - 1.0; + reached++; + if (reached == no_of_to) { + igraph_2wheap_clear(&Q); + break; + } + } + } + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); + nlen = igraph_vector_int_size(neis); + for (j = 0; j < nlen; j++) { + long int edge = (long int) VECTOR(*neis)[j]; + long int tto = IGRAPH_OTHER(graph, edge, minnei); + igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; + igraph_bool_t active = igraph_2wheap_has_active(&Q, tto); + igraph_bool_t has = igraph_2wheap_has_elem(&Q, tto); + igraph_real_t curdist = active ? -igraph_2wheap_get(&Q, tto) : 0.0; + if (!has) { + /* This is the first non-infinite distance */ + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist)); + } else if (altdist < curdist) { + /* This is a shorter path */ + IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist)); + } + } + + } /* !igraph_2wheap_empty(&Q) */ + + } /* !IGRAPH_VIT_END(fromvit) */ + + if (!all_to) { + igraph_vit_destroy(&tovit); + igraph_vector_destroy(&indexv); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_lazy_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + igraph_vit_destroy(&fromvit); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \ingroup structural + * \function igraph_get_shortest_paths_dijkstra + * \brief Weighted shortest paths from a vertex. + * + * + * If there is more than one path with the smallest weight between two vertices, this + * function gives only one of them. + * \param graph The graph object. + * \param vertices The result, the ids of the vertices along the paths. + * This is a pointer vector, each element points to a vector + * object. These should be initialized before passing them to + * the function, which will properly clear and/or resize them + * and fill the ids of the vertices along the geodesics from/to + * the vertices. Supply a null pointer here if you don't need + * these vectors. Normally, either this argument, or the \c + * edges should be non-null, but no error or warning is given + * if they are both null pointers. + * \param edges The result, the ids of the edges along the paths. + * This is a pointer vector, each element points to a vector + * object. These should be initialized before passing them to + * the function, which will properly clear and/or resize them + * and fill the ids of the vertices along the geodesics from/to + * the vertices. Supply a null pointer here if you don't need + * these vectors. Normally, either this argument, or the \c + * vertices should be non-null, but no error or warning is given + * if they are both null pointers. + * \param from The id of the vertex from/to which the geodesics are + * calculated. + * \param to Vertex sequence with the ids of the vertices to/from which the + * shortest paths will be calculated. A vertex might be given multiple + * times. +* \param weights The edge weights. All edge weights must be + * non-negative for Dijkstra's algorithm to work. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. If this is a null pointer, then the unweighted + * version, \ref igraph_get_shortest_paths() is called. + * \param mode The type of shortest paths to be use for the + * calculation in directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the outgoing paths are calculated. + * \cli IGRAPH_IN + * the incoming paths are calculated. + * \cli IGRAPH_ALL + * the directed graph is considered as an + * undirected one for the computation. + * \endclist + * \param predecessors A pointer to an initialized igraph vector or null. + * If not null, a vector containing the predecessor of each vertex in + * the single source shortest path tree is returned here. The + * predecessor of vertex i in the tree is the vertex from which vertex i + * was reached. The predecessor of the start vertex (in the \c from + * argument) is itself by definition. If the predecessor is -1, it means + * that the given vertex was not reached from the source during the + * search. Note that the search terminates if all the vertices in + * \c to are reached. + * \param inbound_edges A pointer to an initialized igraph vector or null. + * If not null, a vector containing the inbound edge of each vertex in + * the single source shortest path tree is returned here. The + * inbound edge of vertex i in the tree is the edge via which vertex i + * was reached. The start vertex and vertices that were not reached + * during the search will have -1 in the corresponding entry of the + * vector. Note that the search terminates if all the vertices in + * \c to are reached. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * \p from is invalid vertex id, or the length of \p to is + * not the same as the length of \p res. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(|E|log|E|+|V|), where |V| is the number of + * vertices and |E| is the number of edges + * + * \sa \ref igraph_shortest_paths_dijkstra() if you only need the path length but + * not the paths themselves, \ref igraph_get_shortest_paths() if all edge + * weights are equal. + * + * \example examples/simple/igraph_get_shortest_paths_dijkstra.c + */ +int igraph_get_shortest_paths_dijkstra(const igraph_t *graph, + igraph_vector_ptr_t *vertices, + igraph_vector_ptr_t *edges, + igraph_integer_t from, + igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_vector_long_t *predecessors, + igraph_vector_long_t *inbound_edges) { + /* Implementation details. This is the basic Dijkstra algorithm, + with a binary heap. The heap is indexed, i.e. it stores not only + the distances, but also which vertex they belong to. The other + mapping, i.e. getting the distance for a vertex is not in the + heap (that would by the double-indexed heap), but in the result + matrix. + + Dirty tricks: + - the opposite of the distance is stored in the heap, as it is a + maximum heap and we need a minimum heap. + - we don't use IGRAPH_INFINITY in the distance vector during the + computation, as IGRAPH_FINITE() might involve a function call + and we want to spare that. So we store distance+1.0 instead of + distance, and zero denotes infinity. + - `parents' assigns the inbound edge IDs of all vertices in the + shortest path tree to the vertices. In this implementation, the + edge ID + 1 is stored, zero means unreachable vertices. + */ + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_vit_t vit; + igraph_2wheap_t Q; + igraph_lazy_inclist_t inclist; + igraph_vector_t dists; + long int *parents; + igraph_bool_t *is_target; + long int i, to_reach; + + if (!weights) { + return igraph_get_shortest_paths(graph, vertices, edges, from, to, mode, + predecessors, inbound_edges); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + if (vertices && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(vertices)) { + IGRAPH_ERROR("Size of `vertices' and `to' should match", IGRAPH_EINVAL); + } + if (edges && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(edges)) { + IGRAPH_ERROR("Size of `edges' and `to' should match", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + IGRAPH_VECTOR_INIT_FINALLY(&dists, no_of_nodes); + igraph_vector_fill(&dists, -1.0); + + parents = IGRAPH_CALLOC(no_of_nodes, long int); + if (parents == 0) { + IGRAPH_ERROR("Can't calculate shortest paths", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, parents); + is_target = IGRAPH_CALLOC(no_of_nodes, igraph_bool_t); + if (is_target == 0) { + IGRAPH_ERROR("Can't calculate shortest paths", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, is_target); + + /* Mark the vertices we need to reach */ + to_reach = IGRAPH_VIT_SIZE(vit); + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + if (!is_target[ (long int) IGRAPH_VIT_GET(vit) ]) { + is_target[ (long int) IGRAPH_VIT_GET(vit) ] = 1; + } else { + to_reach--; /* this node was given multiple times */ + } + } + + VECTOR(dists)[(long int)from] = 0.0; /* zero distance */ + parents[(long int)from] = 0; + igraph_2wheap_push_with_index(&Q, from, 0); + + while (!igraph_2wheap_empty(&Q) && to_reach > 0) { + long int nlen, minnei = igraph_2wheap_max_index(&Q); + igraph_real_t mindist = -igraph_2wheap_delete_max(&Q); + igraph_vector_int_t *neis; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (is_target[minnei]) { + is_target[minnei] = 0; + to_reach--; + } + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); + nlen = igraph_vector_int_size(neis); + for (i = 0; i < nlen; i++) { + long int edge = (long int) VECTOR(*neis)[i]; + long int tto = IGRAPH_OTHER(graph, edge, minnei); + igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; + igraph_real_t curdist = VECTOR(dists)[tto]; + if (curdist < 0) { + /* This is the first finite distance */ + VECTOR(dists)[tto] = altdist; + parents[tto] = edge + 1; + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist)); + } else if (altdist < curdist) { + /* This is a shorter path */ + VECTOR(dists)[tto] = altdist; + parents[tto] = edge + 1; + IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist)); + } + } + } /* !igraph_2wheap_empty(&Q) */ + + if (to_reach > 0) { + IGRAPH_WARNING("Couldn't reach some vertices"); + } + + /* Create `predecessors' if needed */ + if (predecessors) { + IGRAPH_CHECK(igraph_vector_long_resize(predecessors, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (i == from) { + /* i is the start vertex */ + VECTOR(*predecessors)[i] = i; + } else if (parents[i] <= 0) { + /* i was not reached */ + VECTOR(*predecessors)[i] = -1; + } else { + /* i was reached via the edge with ID = parents[i] - 1 */ + VECTOR(*predecessors)[i] = IGRAPH_OTHER(graph, parents[i] - 1, i); + } + } + } + + /* Create `inbound_edges' if needed */ + if (inbound_edges) { + IGRAPH_CHECK(igraph_vector_long_resize(inbound_edges, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (parents[i] <= 0) { + /* i was not reached */ + VECTOR(*inbound_edges)[i] = -1; + } else { + /* i was reached via the edge with ID = parents[i] - 1 */ + VECTOR(*inbound_edges)[i] = parents[i] - 1; + } + } + } + + /* Reconstruct the shortest paths based on vertex and/or edge IDs */ + if (vertices || edges) { + for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + long int node = IGRAPH_VIT_GET(vit); + long int size, act, edge; + igraph_vector_t *vvec = 0, *evec = 0; + if (vertices) { + vvec = VECTOR(*vertices)[i]; + igraph_vector_clear(vvec); + } + if (edges) { + evec = VECTOR(*edges)[i]; + igraph_vector_clear(evec); + } + + IGRAPH_ALLOW_INTERRUPTION(); + + size = 0; + act = node; + while (parents[act]) { + size++; + edge = parents[act] - 1; + act = IGRAPH_OTHER(graph, edge, act); + } + if (vvec && (size > 0 || node == from)) { + IGRAPH_CHECK(igraph_vector_resize(vvec, size + 1)); + VECTOR(*vvec)[size] = node; + } + if (evec) { + IGRAPH_CHECK(igraph_vector_resize(evec, size)); + } + act = node; + while (parents[act]) { + edge = parents[act] - 1; + act = IGRAPH_OTHER(graph, edge, act); + size--; + if (vvec) { + VECTOR(*vvec)[size] = act; + } + if (evec) { + VECTOR(*evec)[size] = edge; + } + } + } + } + + igraph_lazy_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + igraph_vector_destroy(&dists); + IGRAPH_FREE(is_target); + IGRAPH_FREE(parents); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(6); + + return 0; +} + +/** + * \function igraph_get_shortest_path_dijkstra + * \brief Weighted shortest path from one vertex to another one. + * + * Calculates a single (positively) weighted shortest path from + * a single vertex to another one, using Dijkstra's algorithm. + * + * This function is a special case (and a wrapper) to + * \ref igraph_get_shortest_paths_dijkstra(). + * + * \param graph The input graph, it can be directed or undirected. + * \param vertices Pointer to an initialized vector or a null + * pointer. If not a null pointer, then the vertex ids along + * the path are stored here, including the source and target + * vertices. + * \param edges Pointer to an uninitialized vector or a null + * pointer. If not a null pointer, then the edge ids along the + * path are stored here. + * \param from The id of the source vertex. + * \param to The id of the target vertex. + * \param weights The edge weights. All edge weights must be + * non-negative for Dijkstra's algorithm to work. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. If this is a null pointer, then the unweighted + * version, \ref igraph_get_shortest_paths() is called. + * \param mode A constant specifying how edge directions are + * considered in directed graphs. \c IGRAPH_OUT follows edge + * directions, \c IGRAPH_IN follows the opposite directions, + * and \c IGRAPH_ALL ignores edge directions. This argument is + * ignored for undirected graphs. + * \return Error code. + * + * Time complexity: O(|E|log|E|+|V|), |V| is the number of vertices, + * |E| is the number of edges in the graph. + * + * \sa \ref igraph_get_shortest_paths_dijkstra() for the version with + * more target vertices. + */ + +int igraph_get_shortest_path_dijkstra(const igraph_t *graph, + igraph_vector_t *vertices, + igraph_vector_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + + igraph_vector_ptr_t vertices2, *vp = &vertices2; + igraph_vector_ptr_t edges2, *ep = &edges2; + + if (vertices) { + IGRAPH_CHECK(igraph_vector_ptr_init(&vertices2, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vertices2); + VECTOR(vertices2)[0] = vertices; + } else { + vp = 0; + } + if (edges) { + IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2); + VECTOR(edges2)[0] = edges; + } else { + ep = 0; + } + + IGRAPH_CHECK(igraph_get_shortest_paths_dijkstra(graph, vp, ep, + from, igraph_vss_1(to), + weights, mode, 0, 0)); + + if (edges) { + igraph_vector_ptr_destroy(&edges2); + IGRAPH_FINALLY_CLEAN(1); + } + if (vertices) { + igraph_vector_ptr_destroy(&vertices2); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/* Compares two paths based on their last elements. Required by + * igraph_get_all_shortest_paths_dijkstra to put the final result + * in order. Assumes that both paths are pointers to igraph_vector_t + * objects and that they are not empty + */ +static int igraph_i_vector_tail_cmp(const void* path1, const void* path2) { + return (int) (igraph_vector_tail(*(const igraph_vector_t**)path1) - + igraph_vector_tail(*(const igraph_vector_t**)path2)); +} + +/** + * \ingroup structural + * \function igraph_get_all_shortest_paths_dijkstra + * \brief All weighted shortest paths (geodesics) from a vertex. + * + * \param graph The graph object. + * \param res Pointer to an initialized pointer vector, the result + * will be stored here in igraph_vector_t objects. Each vector + * object contains the vertices along a shortest path from \p from + * to another vertex. The vectors are ordered according to their + * target vertex: first the shortest paths to vertex 0, then to + * vertex 1, etc. No data is included for unreachable vertices. + * \param nrgeo Pointer to an initialized igraph_vector_t object or + * NULL. If not NULL the number of shortest paths from \p from are + * stored here for every vertex in the graph. Note that the values + * will be accurate only for those vertices that are in the target + * vertex sequence (see \p to), since the search terminates as soon + * as all the target vertices have been found. + * \param from The id of the vertex from/to which the geodesics are + * calculated. + * \param to Vertex sequence with the ids of the vertices to/from which the + * shortest paths will be calculated. A vertex might be given multiple + * times. + * \param weights The edge weights. All edge weights must be + * non-negative for Dijkstra's algorithm to work. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. If this is a null pointer, then the unweighted + * version, \ref igraph_get_all_shortest_paths() is called. + * \param mode The type of shortest paths to be use for the + * calculation in directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the outgoing paths are calculated. + * \cli IGRAPH_IN + * the incoming paths are calculated. + * \cli IGRAPH_ALL + * the directed graph is considered as an + * undirected one for the computation. + * \endclist + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * \p from is invalid vertex id, or the length of \p to is + * not the same as the length of \p res. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(|E|log|E|+|V|), where |V| is the number of + * vertices and |E| is the number of edges + * + * \sa \ref igraph_shortest_paths_dijkstra() if you only need the path + * length but not the paths themselves, \ref igraph_get_all_shortest_paths() + * if all edge weights are equal. + * + * \example examples/simple/igraph_get_all_shortest_paths_dijkstra.c + */ +int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, + igraph_vector_ptr_t *res, + igraph_vector_t *nrgeo, + igraph_integer_t from, igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + /* Implementation details: see igraph_get_shortest_paths_dijkstra, + it's basically the same. + */ + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_vit_t vit; + igraph_2wheap_t Q; + igraph_lazy_inclist_t inclist; + igraph_vector_t dists, order; + igraph_vector_ptr_t parents; + igraph_finally_func_t *res_item_destructor; + unsigned char *is_target; + long int i, n, to_reach; + int cmp_result; + const double eps = IGRAPH_SHORTEST_PATH_EPSILON; + + if (!weights) { + return igraph_get_all_shortest_paths(graph, res, nrgeo, from, to, mode); + } + + if (res == 0 && nrgeo == 0) { + return IGRAPH_SUCCESS; + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + } + } + + /* parents stores a vector for each vertex, listing the parent vertices + * of each vertex in the traversal */ + IGRAPH_CHECK(igraph_vector_ptr_init(&parents, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &parents); + igraph_vector_ptr_set_item_destructor(&parents, (igraph_finally_func_t*)igraph_vector_destroy); + for (i = 0; i < no_of_nodes; i++) { + igraph_vector_t* parent_vec; + parent_vec = IGRAPH_CALLOC(1, igraph_vector_t); + if (parent_vec == 0) { + IGRAPH_ERROR("cannot run igraph_get_all_shortest_paths", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(parent_vec, 0)); + VECTOR(parents)[i] = parent_vec; + } + + /* distance of each vertex from the root */ + IGRAPH_VECTOR_INIT_FINALLY(&dists, no_of_nodes); + igraph_vector_fill(&dists, -1.0); + + /* order lists the order of vertices in which they were found during + * the traversal */ + IGRAPH_VECTOR_INIT_FINALLY(&order, 0); + + /* boolean array to mark whether a given vertex is a target or not */ + is_target = IGRAPH_CALLOC(no_of_nodes, unsigned char); + if (is_target == 0) { + IGRAPH_ERROR("Can't calculate shortest paths", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, is_target); + + /* two-way heap storing vertices and distances */ + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + + /* lazy adjacency edge list to query neighbours efficiently */ + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + /* Mark the vertices we need to reach */ + IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + to_reach = IGRAPH_VIT_SIZE(vit); + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + if (!is_target[ (long int) IGRAPH_VIT_GET(vit) ]) { + is_target[ (long int) IGRAPH_VIT_GET(vit) ] = 1; + } else { + to_reach--; /* this node was given multiple times */ + } + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + VECTOR(dists)[(long int)from] = 0.0; /* zero distance */ + igraph_2wheap_push_with_index(&Q, from, 0); + + while (!igraph_2wheap_empty(&Q) && to_reach > 0) { + long int nlen, minnei = igraph_2wheap_max_index(&Q); + igraph_real_t mindist = -igraph_2wheap_delete_max(&Q); + igraph_vector_int_t *neis; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* + printf("Reached vertex %ld, is_target[%ld] = %d, %ld to go\n", + minnei, minnei, (int)is_target[minnei], to_reach - is_target[minnei]); + */ + + if (is_target[minnei]) { + is_target[minnei] = 0; + to_reach--; + } + + /* Mark that we have reached this vertex */ + IGRAPH_CHECK(igraph_vector_push_back(&order, minnei)); + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); + nlen = igraph_vector_int_size(neis); + for (i = 0; i < nlen; i++) { + long int edge = (long int) VECTOR(*neis)[i]; + long int tto = IGRAPH_OTHER(graph, edge, minnei); + igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; + igraph_real_t curdist = VECTOR(dists)[tto]; + igraph_vector_t *parent_vec; + + cmp_result = igraph_cmp_epsilon(curdist, altdist, eps); + if (curdist < 0) { + /* This is the first non-infinite distance */ + VECTOR(dists)[tto] = altdist; + parent_vec = (igraph_vector_t*)VECTOR(parents)[tto]; + IGRAPH_CHECK(igraph_vector_push_back(parent_vec, minnei)); + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist)); + } else if (cmp_result == 0 /* altdist == curdist */ && VECTOR(*weights)[edge] > 0) { + /* This is an alternative path with exactly the same length. + * Note that we consider this case only if the edge via which we + * reached the node has a nonzero weight; otherwise we could create + * infinite loops in undirected graphs by traversing zero-weight edges + * back-and-forth */ + parent_vec = (igraph_vector_t*)VECTOR(parents)[tto]; + IGRAPH_CHECK(igraph_vector_push_back(parent_vec, minnei)); + } else if (cmp_result > 0 /* altdist < curdist */) { + /* This is a shorter path */ + VECTOR(dists)[tto] = altdist; + parent_vec = (igraph_vector_t*)VECTOR(parents)[tto]; + igraph_vector_clear(parent_vec); + IGRAPH_CHECK(igraph_vector_push_back(parent_vec, minnei)); + IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist)); + } + } + } /* !igraph_2wheap_empty(&Q) */ + + if (to_reach > 0) { + IGRAPH_WARNING("Couldn't reach some vertices"); + } + + /* we don't need these anymore */ + igraph_lazy_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + IGRAPH_FINALLY_CLEAN(2); + + /* + printf("Order:\n"); + igraph_vector_print(&order); + + printf("Parent vertices:\n"); + for (i = 0; i < no_of_nodes; i++) { + if (igraph_vector_size(VECTOR(parents)[i]) > 0) { + printf("[%ld]: ", (long int)i); + igraph_vector_print(VECTOR(parents)[i]); + } + } + */ + + if (nrgeo) { + IGRAPH_CHECK(igraph_vector_resize(nrgeo, no_of_nodes)); + igraph_vector_null(nrgeo); + + /* Theoretically, we could calculate nrgeo in parallel with the traversal. + * However, that way we would have to check whether nrgeo is null or not + * every time we want to update some element in nrgeo. Since we need the + * order vector anyway for building the final result, we could just as well + * build nrgeo here. + */ + VECTOR(*nrgeo)[(long int)from] = 1; + n = igraph_vector_size(&order); + for (i = 1; i < n; i++) { + long int node, j, k; + igraph_vector_t *parent_vec; + + node = (long int)VECTOR(order)[i]; + /* now, take the parent vertices */ + parent_vec = (igraph_vector_t*)VECTOR(parents)[node]; + k = igraph_vector_size(parent_vec); + for (j = 0; j < k; j++) { + VECTOR(*nrgeo)[node] += VECTOR(*nrgeo)[(long int)VECTOR(*parent_vec)[j]]; + } + } + } + + if (res) { + igraph_vector_t *path, *paths_index, *parent_vec; + igraph_stack_t stack; + long int j, node; + + /* a shortest path from the starting vertex to vertex i can be + * obtained by calculating the shortest paths from the "parents" + * of vertex i in the traversal. Knowing which of the vertices + * are "targets" (see is_target), we can collect for which other + * vertices do we need to calculate the shortest paths. We reuse + * is_target for that; is_target = 0 means that we don't need the + * vertex, is_target = 1 means that the vertex is a target (hence + * we need it), is_target = 2 means that the vertex is not a target + * but it stands between a shortest path between the root and one + * of the targets + */ + if (igraph_vs_is_all(&to)) { + memset(is_target, 1, sizeof(unsigned char) * (size_t) no_of_nodes); + } else { + memset(is_target, 0, sizeof(unsigned char) * (size_t) no_of_nodes); + + IGRAPH_CHECK(igraph_stack_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_destroy, &stack); + + /* Add the target vertices to the queue */ + IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + i = (long int) IGRAPH_VIT_GET(vit); + if (!is_target[i]) { + is_target[i] = 1; + IGRAPH_CHECK(igraph_stack_push(&stack, i)); + } + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + while (!igraph_stack_empty(&stack)) { + /* For each parent of node i, get its parents */ + igraph_real_t el = igraph_stack_pop(&stack); + parent_vec = (igraph_vector_t*)VECTOR(parents)[(long int) el]; + i = igraph_vector_size(parent_vec); + + for (j = 0; j < i; j++) { + /* For each parent, check if it's already in the stack. + * If not, push it and mark it in is_target */ + n = (long int) VECTOR(*parent_vec)[j]; + if (!is_target[n]) { + is_target[n] = 2; + IGRAPH_CHECK(igraph_stack_push(&stack, n)); + } + } + } + igraph_stack_destroy(&stack); + IGRAPH_FINALLY_CLEAN(1); + } + + /* now, reconstruct the shortest paths from the parent list in the + * order we've found the nodes during the traversal. + * dists is being re-used as a vector where element i tells the + * index in res where the shortest paths leading to vertex i + * start, plus one (so that zero means that there are no paths + * for a given vertex). + */ + paths_index = &dists; + n = igraph_vector_size(&order); + igraph_vector_null(paths_index); + + /* clear the paths vector */ + igraph_vector_ptr_clear(res); + res_item_destructor = igraph_vector_ptr_get_item_destructor(res); + igraph_vector_ptr_set_item_destructor(res, + (igraph_finally_func_t*)igraph_vector_destroy); + + /* by definition, the shortest path leading to the starting vertex + * consists of the vertex itself only */ + path = IGRAPH_CALLOC(1, igraph_vector_t); + if (path == 0) + IGRAPH_ERROR("cannot run igraph_get_all_shortest_paths_dijkstra", + IGRAPH_ENOMEM); + IGRAPH_FINALLY(igraph_free, path); + IGRAPH_CHECK(igraph_vector_init(path, 1)); + IGRAPH_CHECK(igraph_vector_ptr_push_back(res, path)); + IGRAPH_FINALLY_CLEAN(1); /* ownership of path passed to res */ + VECTOR(*path)[0] = from; + VECTOR(*paths_index)[(long int)from] = 1; + + for (i = 1; i < n; i++) { + long int m, path_count; + igraph_vector_t *parent_path; + + node = (long int) VECTOR(order)[i]; + + /* if we don't need the shortest paths for this node (because + * it is not standing in a shortest path between the source + * node and any of the target nodes), skip it */ + if (!is_target[node]) { + continue; + } + + IGRAPH_ALLOW_INTERRUPTION(); + + /* we are calculating the shortest paths of node now. */ + /* first, we update the paths_index */ + path_count = igraph_vector_ptr_size(res); + VECTOR(*paths_index)[node] = path_count + 1; + /* res_end = (igraph_vector_t*)&(VECTOR(*res)[path_count]); */ + + /* now, take the parent vertices */ + parent_vec = (igraph_vector_t*)VECTOR(parents)[node]; + m = igraph_vector_size(parent_vec); + + /* + printf("Calculating shortest paths to vertex %ld\n", node); + printf("Parents are: "); + igraph_vector_print(parent_vec); + */ + + for (j = 0; j < m; j++) { + /* for each parent, copy the shortest paths leading to that parent + * and add the current vertex in the end */ + long int parent_node = (long int) VECTOR(*parent_vec)[j]; + long int parent_path_idx = (long int) VECTOR(*paths_index)[parent_node] - 1; + /* + printf(" Considering parent: %ld\n", parent_node); + printf(" Paths to parent start at index %ld in res\n", parent_path_idx); + */ + IGRAPH_ASSERT(parent_path_idx >= 0); + for (; parent_path_idx < path_count; parent_path_idx++) { + parent_path = (igraph_vector_t*)VECTOR(*res)[parent_path_idx]; + if (igraph_vector_tail(parent_path) != parent_node) { + break; + } + + path = IGRAPH_CALLOC(1, igraph_vector_t); + if (path == 0) + IGRAPH_ERROR("cannot run igraph_get_all_shortest_paths_dijkstra", + IGRAPH_ENOMEM); + IGRAPH_FINALLY(igraph_free, path); + IGRAPH_CHECK(igraph_vector_copy(path, parent_path)); + IGRAPH_CHECK(igraph_vector_ptr_push_back(res, path)); + IGRAPH_FINALLY_CLEAN(1); /* ownership of path passed to res */ + IGRAPH_CHECK(igraph_vector_push_back(path, node)); + } + } + } + + /* remove the path vector's original item destructor */ + igraph_vector_ptr_set_item_destructor(res, res_item_destructor); + + /* free those paths from the result vector which we won't need */ + n = igraph_vector_ptr_size(res); + j = 0; + for (i = 0; i < n; i++) { + igraph_real_t tmp; + path = (igraph_vector_t*)VECTOR(*res)[i]; + tmp = igraph_vector_tail(path); + if (is_target[(long int)tmp] == 1) { + /* we need this path, keep it */ + VECTOR(*res)[j] = path; + j++; + } else { + /* we don't need this path, free it */ + igraph_vector_destroy(path); free(path); + } + } + IGRAPH_CHECK(igraph_vector_ptr_resize(res, j)); + + /* sort the paths by the target vertices */ + igraph_vector_ptr_sort(res, igraph_i_vector_tail_cmp); + } + + /* free the allocated memory */ + igraph_vector_destroy(&order); + IGRAPH_FREE(is_target); + igraph_vector_destroy(&dists); + igraph_vector_ptr_destroy_all(&parents); + IGRAPH_FINALLY_CLEAN(4); + + return 0; +} diff --git a/src/rigraph/core/paths/distances.c b/src/rigraph/core/paths/distances.c new file mode 100644 index 0000000..7aae4a7 --- /dev/null +++ b/src/rigraph/core/paths/distances.c @@ -0,0 +1,214 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_paths.h" + +#include "igraph_datatype.h" +#include "igraph_dqueue.h" +#include "igraph_iterators.h" +#include "igraph_vector.h" +#include "igraph_interface.h" +#include "igraph_adjlist.h" + +#include "../core/interruption.h" + +static int igraph_i_eccentricity(const igraph_t *graph, + igraph_vector_t *res, + igraph_vs_t vids, + igraph_neimode_t mode, + const igraph_adjlist_t *adjlist) { + + int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_long_t q; + igraph_vit_t vit; + igraph_vector_int_t counted; + int i, mark = 1; + igraph_vector_t vneis; + igraph_vector_int_t *neis; + + IGRAPH_CHECK(igraph_dqueue_long_init(&q, 100)); + IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q); + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + IGRAPH_CHECK(igraph_vector_int_init(&counted, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &counted); + + if (!adjlist) { + IGRAPH_VECTOR_INIT_FINALLY(&vneis, 0); + } + + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_VIT_SIZE(vit))); + igraph_vector_fill(res, -1); + + for (i = 0, IGRAPH_VIT_RESET(vit); + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), mark++, i++) { + + long int source; + source = IGRAPH_VIT_GET(vit); + IGRAPH_CHECK(igraph_dqueue_long_push(&q, source)); + IGRAPH_CHECK(igraph_dqueue_long_push(&q, 0)); + VECTOR(counted)[source] = mark; + + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_long_empty(&q)) { + long int act = igraph_dqueue_long_pop(&q); + long int dist = igraph_dqueue_long_pop(&q); + int j, n; + + if (dist > VECTOR(*res)[i]) { + VECTOR(*res)[i] = dist; + } + + if (adjlist) { + neis = igraph_adjlist_get(adjlist, act); + n = (int) igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + int nei = (int) VECTOR(*neis)[j]; + if (VECTOR(counted)[nei] != mark) { + VECTOR(counted)[nei] = mark; + IGRAPH_CHECK(igraph_dqueue_long_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_long_push(&q, dist + 1)); + } + } + } else { + IGRAPH_CHECK(igraph_neighbors(graph, &vneis, + (igraph_integer_t) act, mode)); + n = (int) igraph_vector_size(&vneis); + for (j = 0; j < n; j++) { + int nei = (int) VECTOR(vneis)[j]; + if (VECTOR(counted)[nei] != mark) { + VECTOR(counted)[nei] = mark; + IGRAPH_CHECK(igraph_dqueue_long_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_long_push(&q, dist + 1)); + } + } + } + } /* while !igraph_dqueue_long_empty(dqueue) */ + + } /* for IGRAPH_VIT_NEXT(vit) */ + + if (!adjlist) { + igraph_vector_destroy(&vneis); + IGRAPH_FINALLY_CLEAN(1); + } + igraph_vector_int_destroy(&counted); + igraph_vit_destroy(&vit); + igraph_dqueue_long_destroy(&q); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \function igraph_eccentricity + * \brief Eccentricity of some vertices. + * + * The eccentricity of a vertex is calculated by measuring the shortest + * distance from (or to) the vertex, to (or from) all vertices in the + * graph, and taking the maximum. + * + * + * This implementation ignores vertex pairs that are in different + * components. Isolated vertices have eccentricity zero. + * + * \param graph The input graph, it can be directed or undirected. + * \param res Pointer to an initialized vector, the result is stored + * here. + * \param vids The vertices for which the eccentricity is calculated. + * \param mode What kind of paths to consider for the calculation: + * \c IGRAPH_OUT, paths that follow edge directions; + * \c IGRAPH_IN, paths that follow the opposite directions; and + * \c IGRAPH_ALL, paths that ignore edge directions. This argument + * is ignored for undirected graphs. + * \return Error code. + * + * Time complexity: O(v*(|V|+|E|)), where |V| is the number of + * vertices, |E| is the number of edges and v is the number of + * vertices for which eccentricity is calculated. + * + * \sa \ref igraph_radius(). + * + * \example examples/simple/igraph_eccentricity.c + */ + +int igraph_eccentricity(const igraph_t *graph, + igraph_vector_t *res, + igraph_vs_t vids, + igraph_neimode_t mode) { + + return igraph_i_eccentricity(graph, res, vids, mode, /*adjlist=*/ 0); +} + +/** + * \function igraph_radius + * \brief Radius of a graph. + * + * The radius of a graph is the defined as the minimum eccentricity of + * its vertices, see \ref igraph_eccentricity(). + * + * \param graph The input graph, it can be directed or undirected. + * \param radius Pointer to a real variable, the result is stored + * here. + * \param mode What kind of paths to consider for the calculation: + * \c IGRAPH_OUT, paths that follow edge directions; + * \c IGRAPH_IN, paths that follow the opposite directions; and + * \c IGRAPH_ALL, paths that ignore edge directions. This argument + * is ignored for undirected graphs. + * \return Error code. + * + * Time complexity: O(|V|(|V|+|E|)), where |V| is the number of + * vertices and |E| is the number of edges. + * + * \sa \ref igraph_eccentricity(). + * + * \example examples/simple/igraph_radius.c + */ + +int igraph_radius(const igraph_t *graph, igraph_real_t *radius, + igraph_neimode_t mode) { + + int no_of_nodes = igraph_vcount(graph); + + if (no_of_nodes == 0) { + *radius = IGRAPH_NAN; + } else { + igraph_adjlist_t adjlist; + igraph_vector_t ecc; + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + IGRAPH_VECTOR_INIT_FINALLY(&ecc, igraph_vcount(graph)); + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc, igraph_vss_all(), + mode, &adjlist)); + *radius = igraph_vector_min(&ecc); + igraph_vector_destroy(&ecc); + igraph_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(2); + } + + return 0; +} diff --git a/src/rigraph/core/paths/eulerian.c b/src/rigraph/core/paths/eulerian.c new file mode 100644 index 0000000..f1d15e0 --- /dev/null +++ b/src/rigraph/core/paths/eulerian.c @@ -0,0 +1,676 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "igraph_eulerian.h" + +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_components.h" +#include "igraph_stack.h" + +/** + * \section about_eulerian + * + * These functions calculate whether an Eulerian path or cycle exists + * and if so, can find them. + */ + + +/* solution adapted from https://www.geeksforgeeks.org/eulerian-path-and-circuit/ +The function returns one of the following values +has_path is set to 1 if a path exists, 0 otherwise +has_cycle is set to 1 if a cycle exists, 0 otherwise +*/ +static int igraph_i_is_eulerian_undirected(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle, igraph_integer_t *start_of_path) { + igraph_integer_t odd; + igraph_vector_t degree, csize; + /* boolean vector to mark singletons: */ + igraph_vector_t nonsingleton; + long int i, n, vsize; + long int cluster_count; + /* number of self-looping singletons: */ + long int es; + /* will be set to 1 if there are non-isolated vertices, otherwise 0: */ + long int ens; + + n = igraph_vcount(graph); + + if (igraph_ecount(graph) == 0 || n <= 1) { + start_of_path = 0; /* in case the graph has one vertex with self-loops */ + *has_path = 1; + *has_cycle = 1; + return IGRAPH_SUCCESS; + } + + /* check for connectedness, but singletons are special since they affect + * the Eulerian nature only if there is a self-loop AND another edge + * somewhere else in the graph */ + IGRAPH_VECTOR_INIT_FINALLY(&csize, 0); + IGRAPH_CHECK(igraph_clusters(graph, NULL, &csize, NULL, IGRAPH_WEAK)); + cluster_count = 0; + vsize = igraph_vector_size(&csize); + for (i = 0; i < vsize; i++) { + if (VECTOR(csize)[i] > 1) { + cluster_count++; + if (cluster_count > 1) { + /* disconnected edges, they'll never reach each other */ + *has_path = 0; + *has_cycle = 0; + igraph_vector_destroy(&csize); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; + } + } + } + + igraph_vector_destroy(&csize); + IGRAPH_FINALLY_CLEAN(1); + + /* the graph is connected except for singletons */ + /* find singletons (including those with self-loops) */ + IGRAPH_VECTOR_INIT_FINALLY(&nonsingleton, 0); + IGRAPH_CHECK(igraph_degree(graph, &nonsingleton, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS)); + + /* check the degrees for odd/even: + * - >= 2 odd means no cycle (1 odd is impossible) + * - > 2 odd means no path + * plus there are a few corner cases with singletons + */ + IGRAPH_VECTOR_INIT_FINALLY(°ree, 0); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); + odd = 0; + es = 0; + ens = 0; + for (i = 0; i < n; i++) { + long int deg = (long int) VECTOR(degree)[i]; + /* Eulerian is about edges, so skip free vertices */ + if (deg == 0) continue; + + if (!VECTOR(nonsingleton)[i]) { + /* singleton with self loops */ + es++; + } else { + /* at least one non-singleton */ + ens = 1; + /* note: self-loops count for two (in and out) */ + if (deg % 2) odd++; + } + + if (es + ens > 1) { + /* 2+ singletons with self loops or singleton with self-loops and + * 1+ edges in the non-singleton part of the graph. */ + *has_path = 0; + *has_cycle = 0; + igraph_vector_destroy(&nonsingleton); + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; + } + } + + igraph_vector_destroy(&nonsingleton); + IGRAPH_FINALLY_CLEAN(1); + + /* this is the usual algorithm on the connected part of the graph */ + if (odd > 2) { + *has_path = 0; + *has_cycle = 0; + } else if (odd == 2) { + *has_path = 1; + *has_cycle = 0; + } else { + *has_path = 1; + *has_cycle = 1; + } + + /* set start of path if there is one but there is no cycle */ + /* note: we cannot do this in the previous loop because at that time we are + * not sure yet if a path exists */ + for (i = 0; i < n; i++) { + if ((*has_cycle && ((long int) VECTOR(degree)[i]) > 0) || (!*has_cycle && ((long int) VECTOR(degree)[i]) %2 == 1)) { + *start_of_path = i; + break; + } + } + + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +static int igraph_i_is_eulerian_directed(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle, igraph_integer_t *start_of_path) { + igraph_integer_t incoming_excess, outgoing_excess, n; + long int i, vsize; + long int cluster_count; + igraph_vector_t out_degree, in_degree, csize; + /* boolean vector to mark singletons: */ + igraph_vector_t nonsingleton; + /* number of self-looping singletons: */ + long int es; + /* will be set to 1 if there are non-isolated vertices, otherwise 0: */ + long int ens; + + n = igraph_vcount(graph); + + if (igraph_ecount(graph) == 0 || n <= 1) { + start_of_path = 0; /* in case the graph has one vertex with self-loops */ + *has_path = 1; + *has_cycle = 1; + return IGRAPH_SUCCESS; + } + + incoming_excess = 0; + outgoing_excess = 0; + + /* check for weak connectedness, but singletons are special since they affect + * the Eulerian nature only if there is a self-loop AND another edge + * somewhere else in the graph */ + IGRAPH_VECTOR_INIT_FINALLY(&csize, 0); + + IGRAPH_CHECK(igraph_clusters(graph, NULL, &csize, NULL, IGRAPH_WEAK)); + cluster_count = 0; + vsize = igraph_vector_size(&csize); + for (i = 0; i < vsize; i++) { + if (VECTOR(csize)[i] > 1) { + cluster_count++; + if (cluster_count > 1) { + /* weakly disconnected edges, they'll never reach each other */ + *has_path = 0; + *has_cycle = 0; + igraph_vector_destroy(&csize); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; + } + } + } + + igraph_vector_destroy(&csize); + IGRAPH_FINALLY_CLEAN(1); + + /* the graph is weakly connected except for singletons */ + /* find the singletons (including those with self-loops) */ + IGRAPH_VECTOR_INIT_FINALLY(&nonsingleton, 0); + IGRAPH_CHECK(igraph_degree(graph, &nonsingleton, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS)); + + + /* checking if no. of incoming edges == outgoing edges + * plus there are a few corner cases with singletons */ + IGRAPH_VECTOR_INIT_FINALLY(&out_degree, 0); + IGRAPH_CHECK(igraph_degree(graph, &out_degree, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); + + IGRAPH_VECTOR_INIT_FINALLY(&in_degree, 0); + IGRAPH_CHECK(igraph_degree(graph, &in_degree, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS)); + es = 0; + ens = 0; + *start_of_path = -1; + for (i = 0; i < n; i++) { + long int degin = VECTOR(in_degree)[i]; + long int degout = VECTOR(out_degree)[i]; + + /* Eulerian is about edges, so skip free vertices */ + if (degin + degout == 0) continue; + + if (!VECTOR(nonsingleton)[i]) { + /* singleton with self loops */ + es++; + /* if we ever want a path, it has to be this self-loop */ + *start_of_path = i; + } else { + /* at least one non-singleton */ + ens = 1; + } + + if (es + ens > 1) { + /* 2+ singletons with self loops or singleton with self-loops and + * 1+ edges in the non-singleton part of the graph. */ + *has_path = 0; + *has_cycle = 0; + igraph_vector_destroy(&nonsingleton); + igraph_vector_destroy(&in_degree); + igraph_vector_destroy(&out_degree); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; + } + + /* as long as we have perfect balance, you can start + * anywhere with an edge */ + if (*start_of_path == -1 && incoming_excess == 0 && outgoing_excess == 0) { + *start_of_path = i; + } + + /* same in and out (including self-loops, even in singletons) */ + if (degin == degout) { + continue; + } + + /* non-singleton, in != out */ + if (degin > degout) { + incoming_excess += degin - degout; + } else { + outgoing_excess += degout - degin; + if (outgoing_excess == 1) { + *start_of_path = i; + } + } + + /* too much imbalance, either of the following: + * 1. 1+ vertices have 2+ in/out + * 2. 2+ nodes have 1+ in/out */ + if (incoming_excess > 1 || outgoing_excess > 1) { + *has_path = 0; + *has_cycle = 0; + igraph_vector_destroy(&nonsingleton); + igraph_vector_destroy(&in_degree); + igraph_vector_destroy(&out_degree); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; + } + } + + *has_path = 1; + /* perfect edge balance -> strong connectivity */ + *has_cycle = (incoming_excess == 0) && (outgoing_excess == 0); + /* either way, the start was set already */ + + igraph_vector_destroy(&nonsingleton); + igraph_vector_destroy(&in_degree); + igraph_vector_destroy(&out_degree); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup Eulerian + * \function igraph_is_eulerian + * \brief Checks whether an Eulerian path or cycle exists + * + * An Eulerian path traverses each edge of the graph precisely once. A closed + * Eulerian path is referred to as an Eulerian cycle. + * + * \param graph The graph object. + * \param has_path Pointer to a Boolean, will be set to true if an Eulerian path exists. + * \param has_cycle Pointer to a Boolean, will be set to true if an Eulerian cycle exists. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. + * + */ + +int igraph_is_eulerian(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle) { + igraph_integer_t start_of_path = 0; + + if (igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_is_eulerian_directed(graph, has_path, has_cycle, &start_of_path)); + } else { + IGRAPH_CHECK(igraph_i_is_eulerian_undirected(graph, has_path, has_cycle, &start_of_path)); + } + return IGRAPH_SUCCESS; +} + + +static int igraph_i_eulerian_path_undirected(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res, igraph_integer_t start_of_path) { + long int curr; + igraph_integer_t n, m; + igraph_inclist_t il; + igraph_stack_t path, tracker, edge_tracker, edge_path; + igraph_vector_bool_t visited_list; + igraph_vector_t degree; + + n = igraph_vcount(graph); + m = igraph_ecount(graph); + + if (edge_res) { + igraph_vector_clear(edge_res); + } + + if (vertex_res) { + igraph_vector_clear(vertex_res); + } + + if (m == 0 || n == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(°ree, 0); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); + + IGRAPH_CHECK(igraph_stack_init(&path, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &path); + + IGRAPH_CHECK(igraph_stack_init(&tracker, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &tracker); + + IGRAPH_CHECK(igraph_stack_init(&edge_path, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &edge_path); + + IGRAPH_CHECK(igraph_stack_init(&edge_tracker, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &edge_tracker); + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited_list, m); + + IGRAPH_CHECK(igraph_stack_push(&tracker, start_of_path)); + + IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + + curr = start_of_path; + + while (!igraph_stack_empty(&tracker)) { + + if (VECTOR(degree)[curr] != 0) { + igraph_vector_int_t *incedges; + long nc, edge = -1; + long int j, next; + IGRAPH_CHECK(igraph_stack_push(&tracker, curr)); + + incedges = igraph_inclist_get(&il, curr); + nc = igraph_vector_int_size(incedges); + IGRAPH_ASSERT(nc > 0); + + for (j = 0; j < nc; j++) { + edge = (long) VECTOR(*incedges)[j]; + if (!VECTOR(visited_list)[edge]) { + break; + } + } + + next = IGRAPH_OTHER(graph, edge, curr); + + IGRAPH_CHECK(igraph_stack_push(&edge_tracker, edge)); + + /* remove edge here */ + VECTOR(degree)[curr]--; + VECTOR(degree)[next]--; + VECTOR(visited_list)[edge] = 1; + + curr = next; + } else { /* back track to find remaining circuit */ + igraph_integer_t curr_e; + IGRAPH_CHECK(igraph_stack_push(&path, curr)); + curr = igraph_stack_pop(&tracker); + if (!igraph_stack_empty(&edge_tracker)) { + curr_e = igraph_stack_pop(&edge_tracker); + IGRAPH_CHECK(igraph_stack_push(&edge_path, curr_e)); + } + } + } + + if (edge_res) { + IGRAPH_CHECK(igraph_vector_reserve(edge_res, m)); + while (!igraph_stack_empty(&edge_path)) { + IGRAPH_CHECK(igraph_vector_push_back(edge_res, igraph_stack_pop(&edge_path))); + } + } + if (vertex_res) { + IGRAPH_CHECK(igraph_vector_reserve(vertex_res, m+1)); + while (!igraph_stack_empty(&path)) { + IGRAPH_CHECK(igraph_vector_push_back(vertex_res, igraph_stack_pop(&path))); + } + } + + igraph_stack_destroy(&path); + igraph_stack_destroy(&tracker); + igraph_stack_destroy(&edge_path); + igraph_stack_destroy(&edge_tracker); + igraph_vector_bool_destroy(&visited_list); + igraph_inclist_destroy(&il); + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} + +/* solution adapted from https://www.geeksforgeeks.org/hierholzers-algorithm-directed-graph/ */ +static int igraph_i_eulerian_path_directed(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res, igraph_integer_t start_of_path) { + long int curr; + igraph_integer_t n, m; + igraph_inclist_t il; + igraph_stack_t path, tracker, edge_tracker, edge_path; + igraph_vector_bool_t visited_list; + igraph_vector_t remaining_out_edges; + + n = igraph_vcount(graph); + m = igraph_ecount(graph); + + if (edge_res) { + igraph_vector_clear(edge_res); + } + + if (vertex_res) { + igraph_vector_clear(vertex_res); + } + + if (m == 0 || n == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_stack_init(&path, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &path); + + IGRAPH_CHECK(igraph_stack_init(&tracker, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &tracker); + + IGRAPH_CHECK(igraph_stack_init(&edge_path, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &edge_path); + + IGRAPH_CHECK(igraph_stack_init(&edge_tracker, n)); + IGRAPH_FINALLY(igraph_stack_destroy, &edge_tracker); + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited_list, m); + + IGRAPH_CHECK(igraph_stack_push(&tracker, start_of_path)); + + IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + + IGRAPH_VECTOR_INIT_FINALLY(&remaining_out_edges, 0); + IGRAPH_CHECK(igraph_degree(graph, &remaining_out_edges, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); + + curr = start_of_path; + + while (!igraph_stack_empty(&tracker)) { + + if (VECTOR(remaining_out_edges)[curr] != 0) { + igraph_vector_int_t *incedges; + long nc, edge = -1; + long int j, next; + IGRAPH_CHECK(igraph_stack_push(&tracker, curr)); + + incedges = igraph_inclist_get(&il, curr); + nc = igraph_vector_int_size(incedges); + IGRAPH_ASSERT(nc > 0); + + for (j = 0; j < nc; j++) { + edge = (long) VECTOR(*incedges)[j]; + if (!VECTOR(visited_list)[edge]) { + break; + } + } + + next = IGRAPH_TO(graph, edge); + + IGRAPH_CHECK(igraph_stack_push(&edge_tracker, edge)); + + /* remove edge here */ + VECTOR(remaining_out_edges)[curr]--; + VECTOR(visited_list)[edge] = 1; + + curr = next; + } else { /* back track to find remaining circuit */ + igraph_integer_t curr_e; + IGRAPH_CHECK(igraph_stack_push(&path, curr)); + curr = igraph_stack_pop(&tracker); + if (!igraph_stack_empty(&edge_tracker)) { + curr_e = igraph_stack_pop(&edge_tracker); + IGRAPH_CHECK(igraph_stack_push(&edge_path, curr_e)); + } + } + } + + if (edge_res) { + IGRAPH_CHECK(igraph_vector_reserve(edge_res, m)); + while (!igraph_stack_empty(&edge_path)) { + IGRAPH_CHECK(igraph_vector_push_back(edge_res, igraph_stack_pop(&edge_path))); + } + } + if (vertex_res) { + IGRAPH_CHECK(igraph_vector_reserve(vertex_res, m+1)); + while (!igraph_stack_empty(&path)) { + IGRAPH_CHECK(igraph_vector_push_back(vertex_res, igraph_stack_pop(&path))); + } + } + + igraph_stack_destroy(&path); + igraph_stack_destroy(&tracker); + igraph_stack_destroy(&edge_path); + igraph_stack_destroy(&edge_tracker); + igraph_vector_bool_destroy(&visited_list); + igraph_inclist_destroy(&il); + igraph_vector_destroy(&remaining_out_edges); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup Eulerian + * \function igraph_eulerian_cycle + * \brief Finds an Eulerian cycle + * + * Finds an Eulerian cycle, if it exists. An Eulerian cycle is a closed path + * that traverses each edge precisely once. + * + * + * This function uses Hierholzer's algorithm. + * + * \param graph The graph object. + * \param edge_res Pointer to an initialised vector. The indices of edges + * belonging to the cycle will be stored here. May be \c NULL + * if it is not needed by the caller. + * \param vertex_res Pointer to an initialised vector. The indices of vertices + * belonging to the cycle will be stored here. May be \c NULL + * if it is not needed by the caller. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * graph does not have an Eulerian cycle. + * \endclist + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. + * + */ + +int igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res) { + igraph_bool_t has_cycle; + igraph_bool_t has_path; + igraph_integer_t start_of_path = 0; + + if (igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_is_eulerian_directed(graph, &has_path, &has_cycle, &start_of_path)); + + if (!has_cycle) { + IGRAPH_ERROR("The graph does not have an Eulerian cycle.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_i_eulerian_path_directed(graph, edge_res, vertex_res, start_of_path)); + } else { + IGRAPH_CHECK(igraph_i_is_eulerian_undirected(graph, &has_path, &has_cycle, &start_of_path)); + + if (!has_cycle) { + IGRAPH_ERROR("The graph does not have an Eulerian cycle.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_i_eulerian_path_undirected(graph, edge_res, vertex_res, start_of_path)); + } + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup Eulerian + * \function igraph_eulerian_path + * \brief Finds an Eulerian path + * + * Finds an Eulerian path, if it exists. An Eulerian path traverses + * each edge precisely once. + * + * + * This function uses Hierholzer's algorithm. + * + * \param graph The graph object. + * \param edge_res Pointer to an initialised vector. The indices of edges + * belonging to the path will be stored here. May be \c NULL + * if it is not needed by the caller. + * \param vertex_res Pointer to an initialised vector. The indices of vertices + * belonging to the path will be stored here. May be \c NULL + * if it is not needed by the caller. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * graph does not have an Eulerian path. + * \endclist + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. + * + */ + +int igraph_eulerian_path(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res) { + igraph_bool_t has_cycle; + igraph_bool_t has_path; + igraph_integer_t start_of_path = 0; + + if (igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_is_eulerian_directed(graph, &has_path, &has_cycle, &start_of_path)); + + if (!has_path) { + IGRAPH_ERROR("The graph does not have an Eulerian path.", IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_i_eulerian_path_directed(graph, edge_res, vertex_res, start_of_path)); + } else { + IGRAPH_CHECK(igraph_i_is_eulerian_undirected(graph, &has_path, &has_cycle, &start_of_path)); + + if (!has_path) { + IGRAPH_ERROR("The graph does not have an Eulerian path.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_i_eulerian_path_undirected(graph, edge_res, vertex_res, start_of_path)); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/paths/histogram.c b/src/rigraph/core/paths/histogram.c new file mode 100644 index 0000000..76128f3 --- /dev/null +++ b/src/rigraph/core/paths/histogram.c @@ -0,0 +1,149 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_progress.h" + +#include "../core/interruption.h" + +/** + * \function igraph_path_length_hist + * Create a histogram of all shortest path lengths. + * + * This function calculates a histogram, by calculating the + * shortest path length between each pair of vertices. For directed + * graphs both directions might be considered and then every pair of vertices + * appears twice in the histogram. + * \param graph The input graph. + * \param res Pointer to an initialized vector, the result is stored + * here. The first (i.e. zeroth) element contains the number of + * shortest paths of length 1, etc. The supplied vector is resized + * as needed. + * \param unconnected Pointer to a real number, the number of + * pairs for which the second vertex is not reachable from the + * first is stored here. + * \param directed Whether to consider directed paths in a directed + * graph (if not zero). This argument is ignored for undirected + * graphs. + * \return Error code. + * + * Time complexity: O(|V||E|), the number of vertices times the number + * of edges. + * + * \sa \ref igraph_average_path_length() and \ref igraph_shortest_paths() + */ + +int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, + igraph_real_t *unconnected, igraph_bool_t directed) { + + long int no_of_nodes = igraph_vcount(graph); + long int i, j, n; + igraph_vector_long_t already_added; + long int nodes_reached; + + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_vector_int_t *neis; + igraph_neimode_t dirmode; + igraph_adjlist_t allneis; + igraph_real_t unconn = 0; + long int ressize; + + if (directed) { + dirmode = IGRAPH_OUT; + } else { + dirmode = IGRAPH_ALL; + } + + IGRAPH_CHECK(igraph_vector_long_init(&already_added, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &already_added); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, dirmode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + + IGRAPH_CHECK(igraph_vector_resize(res, 0)); + ressize = 0; + + for (i = 0; i < no_of_nodes; i++) { + nodes_reached = 1; /* itself */ + IGRAPH_CHECK(igraph_dqueue_push(&q, i)); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + VECTOR(already_added)[i] = i + 1; + + IGRAPH_PROGRESS("Path length histogram: ", 100.0 * i / no_of_nodes, NULL); + + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + long int actdist = (long int) igraph_dqueue_pop(&q); + + neis = igraph_adjlist_get(&allneis, actnode); + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + long int neighbor = (long int) VECTOR(*neis)[j]; + if (VECTOR(already_added)[neighbor] == i + 1) { + continue; + } + VECTOR(already_added)[neighbor] = i + 1; + nodes_reached++; + if (actdist + 1 > ressize) { + IGRAPH_CHECK(igraph_vector_resize(res, actdist + 1)); + for (; ressize < actdist + 1; ressize++) { + VECTOR(*res)[ressize] = 0; + } + } + VECTOR(*res)[actdist] += 1; + + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + } + } /* while !igraph_dqueue_empty */ + + unconn += (no_of_nodes - nodes_reached); + + } /* for i + * If no edge weights are supplied, then the unweighted + * version, \ref igraph_shortest_paths() is called. + * + * + * If all the supplied edge weights are non-negative, + * then Dijkstra's algorithm is used by calling + * \ref igraph_shortest_paths_dijkstra(). + * + * \param graph The input graph. If negative weights are present, it + * should be directed. + * \param res Pointer to an initialized matrix, the result will be + * stored here, one line for each source vertex, one column for each + * target vertex. + * \param from The source vertices. + * \param to The target vertices. It is not allowed to include a + * vertex twice or more. + * \param weights Optional edge weights. If it is a null-pointer, then + * the unweighted breadth-first search based \ref + * igraph_shortest_paths() will be called. + * \return Error code. + * + * Time complexity: O(s|V|log|V|+|V||E|), |V| and |E| are the number + * of vertices and edges, s is the number of source vertices. + * + * \sa \ref igraph_shortest_paths() for a faster unweighted version + * or \ref igraph_shortest_paths_dijkstra() if you do not have negative + * edge weights, \ref igraph_shortest_paths_bellman_ford() if you only + * need to calculate shortest paths from a couple of sources. + */ +int igraph_shortest_paths_johnson(const igraph_t *graph, + igraph_matrix_t *res, + const igraph_vs_t from, + const igraph_vs_t to, + const igraph_vector_t *weights) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_t newgraph; + igraph_vector_t edges, newweights; + igraph_matrix_t bfres; + long int i, ptr; + long int nr, nc; + igraph_vit_t fromvit; + + /* If no weights, then we can just run the unweighted version */ + if (!weights) { + return igraph_shortest_paths(graph, res, from, to, IGRAPH_OUT); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); + } + + /* If no edges, then we can just run the unweighted version */ + if (no_of_edges == 0) { + return igraph_shortest_paths(graph, res, from, to, IGRAPH_OUT); + } + + /* If no negative weights, then we can run Dijkstra's algorithm */ + { + igraph_real_t min_weight = igraph_vector_min(weights); + if (igraph_is_nan(min_weight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + } + if (min_weight >= 0) { + return igraph_shortest_paths_dijkstra(graph, res, from, to, + weights, IGRAPH_OUT); + } + } + + if (!igraph_is_directed(graph)) { + IGRAPH_ERROR("Johnson's shortest path: undirected graph and negative weight", + IGRAPH_EINVAL); + } + + /* ------------------------------------------------------------ */ + /* -------------------- Otherwise proceed --------------------- */ + + IGRAPH_MATRIX_INIT_FINALLY(&bfres, 0, 0); + IGRAPH_VECTOR_INIT_FINALLY(&newweights, 0); + + IGRAPH_CHECK(igraph_empty(&newgraph, (igraph_integer_t) no_of_nodes + 1, + igraph_is_directed(graph))); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + + /* Add a new node to the graph, plus edges from it to all the others. */ + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2 + no_of_nodes * 2); + igraph_get_edgelist(graph, &edges, /*bycol=*/ 0); + igraph_vector_resize(&edges, no_of_edges * 2 + no_of_nodes * 2); + for (i = 0, ptr = no_of_edges * 2; i < no_of_nodes; i++) { + VECTOR(edges)[ptr++] = no_of_nodes; + VECTOR(edges)[ptr++] = i; + } + IGRAPH_CHECK(igraph_add_edges(&newgraph, &edges, 0)); + igraph_vector_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_vector_reserve(&newweights, no_of_edges + no_of_nodes)); + igraph_vector_update(&newweights, weights); + igraph_vector_resize(&newweights, no_of_edges + no_of_nodes); + for (i = no_of_edges; i < no_of_edges + no_of_nodes; i++) { + VECTOR(newweights)[i] = 0; + } + + /* Run Bellmann-Ford algorithm on the new graph, starting from the + new vertex. */ + + IGRAPH_CHECK(igraph_shortest_paths_bellman_ford(&newgraph, &bfres, + igraph_vss_1((igraph_integer_t) no_of_nodes), + igraph_vss_all(), &newweights, IGRAPH_OUT)); + + igraph_destroy(&newgraph); + IGRAPH_FINALLY_CLEAN(1); + + /* Now the edges of the original graph are reweighted, using the + values from the BF algorithm. Instead of w(u,v) we will have + w(u,v) + h(u) - h(v) */ + + igraph_vector_resize(&newweights, no_of_edges); + for (i = 0; i < no_of_edges; i++) { + long int ffrom = IGRAPH_FROM(graph, i); + long int tto = IGRAPH_TO(graph, i); + VECTOR(newweights)[i] += MATRIX(bfres, 0, ffrom) - MATRIX(bfres, 0, tto); + } + + /* Run Dijkstra's algorithm on the new weights */ + IGRAPH_CHECK(igraph_shortest_paths_dijkstra(graph, res, from, + to, &newweights, + IGRAPH_OUT)); + + igraph_vector_destroy(&newweights); + IGRAPH_FINALLY_CLEAN(1); + + /* Reweight the shortest paths */ + nr = igraph_matrix_nrow(res); + nc = igraph_matrix_ncol(res); + + IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit)); + IGRAPH_FINALLY(igraph_vit_destroy, &fromvit); + + for (i = 0; i < nr; i++, IGRAPH_VIT_NEXT(fromvit)) { + long int v1 = IGRAPH_VIT_GET(fromvit); + if (igraph_vs_is_all(&to)) { + long int v2; + for (v2 = 0; v2 < nc; v2++) { + igraph_real_t sub = MATRIX(bfres, 0, v1) - MATRIX(bfres, 0, v2); + MATRIX(*res, i, v2) -= sub; + } + } else { + long int j; + igraph_vit_t tovit; + IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit)); + IGRAPH_FINALLY(igraph_vit_destroy, &tovit); + for (j = 0, IGRAPH_VIT_RESET(tovit); j < nc; j++, IGRAPH_VIT_NEXT(tovit)) { + long int v2 = IGRAPH_VIT_GET(tovit); + igraph_real_t sub = MATRIX(bfres, 0, v1) - MATRIX(bfres, 0, v2); + MATRIX(*res, i, j) -= sub; + } + igraph_vit_destroy(&tovit); + IGRAPH_FINALLY_CLEAN(1); + } + } + + igraph_vit_destroy(&fromvit); + igraph_matrix_destroy(&bfres); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/paths/random_walk.c b/src/rigraph/core/paths/random_walk.c new file mode 100644 index 0000000..d301677 --- /dev/null +++ b/src/rigraph/core/paths/random_walk.c @@ -0,0 +1,294 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2014 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_random.h" +#include "igraph_memory.h" + +#include "../core/interruption.h" + +/** + * \function igraph_random_walk + * Perform a random walk on a graph + * + * Performs a random walk with a given length on a graph, from the given + * start vertex. Edge directions are (potentially) considered, depending on + * the \p mode argument. + * + * \param graph The input graph, it can be directed or undirected. + * Multiple edges are respected, so are loop edges. + * \param walk An allocated vector, the result is stored here. + * It will be resized as needed. + * \param start The start vertex for the walk. + * \param steps The number of steps to take. If the random walk gets + * stuck, then the \p stuck argument specifies what happens. + * \param mode How to walk along the edges in directed graphs. + * \c IGRAPH_OUT means following edge directions, \c IGRAPH_IN means + * going opposite the edge directions, \c IGRAPH_ALL means ignoring + * edge directions. This argument is ignored for undirected graphs. + * \param stuck What to do if the random walk gets stuck. + * \c IGRAPH_RANDOM_WALK_STUCK_RETURN means that the function returns + * with a shorter walk; \c IGRAPH_RANDOM_WALK_STUCK_ERROR means + * that an error is reported. In both cases \p walk is truncated + * to contain the actual interrupted walk. + * \return Error code. + * + * Time complexity: O(l + d), where \c l is the length of the + * walk, and \c d is the total degree of the visited nodes. + */ + + +int igraph_random_walk(const igraph_t *graph, igraph_vector_t *walk, + igraph_integer_t start, igraph_neimode_t mode, + igraph_integer_t steps, + igraph_random_walk_stuck_t stuck) { + + /* TODO: + - multiple walks potentially from multiple start vertices + - weights + */ + + igraph_lazy_adjlist_t adj; + igraph_integer_t vc = igraph_vcount(graph); + igraph_integer_t i; + + if (start < 0 || start >= vc) { + IGRAPH_ERROR("Invalid start vertex.", IGRAPH_EINVAL); + } + if (steps < 0) { + IGRAPH_ERROR("Invalid number of steps.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adj, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adj); + + IGRAPH_CHECK(igraph_vector_resize(walk, steps)); + + RNG_BEGIN(); + + VECTOR(*walk)[0] = start; + for (i = 1; i < steps; i++) { + igraph_vector_int_t *neis; + igraph_integer_t nn; + neis = igraph_lazy_adjlist_get(&adj, start); + nn = igraph_vector_int_size(neis); + + if (IGRAPH_UNLIKELY(nn == 0)) { + igraph_vector_resize(walk, i); + if (stuck == IGRAPH_RANDOM_WALK_STUCK_RETURN) { + break; + } else { + IGRAPH_ERROR("Random walk got stuck.", IGRAPH_ERWSTUCK); + } + } + start = VECTOR(*walk)[i] = VECTOR(*neis)[ RNG_INTEGER(0, nn - 1) ]; + } + + RNG_END(); + + igraph_lazy_adjlist_destroy(&adj); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/* Used as item destructor for 'cdfs' in igraph_random_edge_walk(). */ +static void vec_destr(igraph_vector_t *vec) { + if (vec != NULL) { + igraph_vector_destroy(vec); + } +} + + +/** + * \function igraph_random_edge_walk + * \brief Perform a random walk on a graph and return the traversed edges + * + * Performs a random walk with a given length on a graph, from the given + * start vertex. Edge directions are (potentially) considered, depending on + * the \p mode argument. + * + * \param graph The input graph, it can be directed or undirected. + * Multiple edges are respected, so are loop edges. + * \param weights A vector of non-negative edge weights. It is assumed + * that at least one strictly positive weight is found among the + * outgoing edges of each vertex. Additionally, no edge weight may + * be NaN. If either case does not hold, an error is returned. If it + * is a NULL pointer, all edges are considered to have equal weight. + * \param edgewalk An initialized vector; the indices of traversed + * edges are stored here. It will be resized as needed. + * \param start The start vertex for the walk. + * \param steps The number of steps to take. If the random walk gets + * stuck, then the \p stuck argument specifies what happens. + * \param mode How to walk along the edges in directed graphs. + * \c IGRAPH_OUT means following edge directions, \c IGRAPH_IN means + * going opposite the edge directions, \c IGRAPH_ALL means ignoring + * edge directions. This argument is ignored for undirected graphs. + * \param stuck What to do if the random walk gets stuck. + * \c IGRAPH_RANDOM_WALK_STUCK_RETURN means that the function returns + * with a shorter walk; \c IGRAPH_RANDOM_WALK_STUCK_ERROR means + * that an error is reported. In both cases, \p edgewalk is truncated + * to contain the actual interrupted walk. + * + * \return Error code. + * + */ +int igraph_random_edge_walk(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_t *edgewalk, + igraph_integer_t start, igraph_neimode_t mode, + igraph_integer_t steps, + igraph_random_walk_stuck_t stuck) { + igraph_integer_t vc = igraph_vcount(graph); + igraph_integer_t ec = igraph_ecount(graph); + igraph_integer_t i; + igraph_inclist_t il; + igraph_vector_t weight_temp; + igraph_vector_ptr_t cdfs; /* cumulative distribution vectors for each node, used for weighted choice */ + + if (! (mode == IGRAPH_ALL || mode == IGRAPH_IN || mode == IGRAPH_OUT)) { + IGRAPH_ERROR("Invalid mode parameter.", IGRAPH_EINVMODE); + } + + /* ref switch statement at end of main loop */ + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (start < 0 || start >= vc) { + IGRAPH_ERROR("Invalid start vertex.", IGRAPH_EINVAL); + } + + if (steps < 0) { + IGRAPH_ERROR("Invalid number of steps.", IGRAPH_EINVAL); + } + + if (weights) { + if (igraph_vector_size(weights) != ec) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + if (ec > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERROR("Weights must be non-negative.", IGRAPH_EINVAL); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weights must not contain NaN values.", IGRAPH_EINVAL); + } + } + } + + IGRAPH_CHECK(igraph_vector_resize(edgewalk, steps)); + + IGRAPH_CHECK(igraph_inclist_init(graph, &il, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + + IGRAPH_VECTOR_INIT_FINALLY(&weight_temp, 0); + + /* cdf vectors will be computed lazily */ + IGRAPH_CHECK(igraph_vector_ptr_init(&cdfs, vc)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &cdfs); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&cdfs, vec_destr); + for (i = 0; i < vc; ++i) { + VECTOR(cdfs)[i] = NULL; + } + + RNG_BEGIN(); + + for (i = 0; i < steps; ++i) { + long degree, edge, idx; + igraph_vector_int_t *edges = igraph_inclist_get(&il, start); + + degree = igraph_vector_int_size(edges); + + /* are we stuck? */ + if (IGRAPH_UNLIKELY(degree == 0)) { + igraph_vector_resize(edgewalk, i); /* can't fail since size is reduced, skip IGRAPH_CHECK */ + if (stuck == IGRAPH_RANDOM_WALK_STUCK_RETURN) { + break; + } else { + IGRAPH_ERROR("Random walk got stuck.", IGRAPH_ERWSTUCK); + } + } + + if (weights) { /* weighted: choose an out-edge with probability proportional to its weight */ + igraph_real_t r; + igraph_vector_t **cd = (igraph_vector_t **) & (VECTOR(cdfs)[start]); + + /* compute out-edge cdf for this node if not already done */ + if (IGRAPH_UNLIKELY(! *cd)) { + long j; + + *cd = IGRAPH_CALLOC(1, igraph_vector_t); + if (*cd == NULL) { + IGRAPH_ERROR("Random edge walk failed.", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(igraph_vector_init(*cd, degree)); + + IGRAPH_CHECK(igraph_vector_resize(&weight_temp, degree)); + for (j = 0; j < degree; ++j) { + VECTOR(weight_temp)[j] = VECTOR(*weights)[ VECTOR(*edges)[j] ]; + } + + IGRAPH_CHECK(igraph_vector_cumsum(*cd, &weight_temp)); + } + + r = RNG_UNIF(0, VECTOR( **cd )[degree - 1]); + igraph_vector_binsearch(*cd, r, &idx); + } else { /* unweighted: choose an out-edge at random */ + idx = RNG_INTEGER(0, degree - 1); + } + + edge = VECTOR(*edges)[idx]; + VECTOR(*edgewalk)[i] = edge; + + /* travel along edge in a direction specified by 'mode' */ + /* note: 'mode' is always set to IGRAPH_ALL for undirected graphs */ + switch (mode) { + case IGRAPH_OUT: + start = IGRAPH_TO(graph, edge); + break; + case IGRAPH_IN: + start = IGRAPH_FROM(graph, edge); + break; + case IGRAPH_ALL: + start = IGRAPH_OTHER(graph, edge, start); + break; + } + + IGRAPH_ALLOW_INTERRUPTION(); + } + + RNG_END(); + + igraph_vector_ptr_destroy_all(&cdfs); + igraph_vector_destroy(&weight_temp); + igraph_inclist_destroy(&il); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/paths/shortest_paths.c b/src/rigraph/core/paths/shortest_paths.c new file mode 100644 index 0000000..34b4202 --- /dev/null +++ b/src/rigraph/core/paths/shortest_paths.c @@ -0,0 +1,1270 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2020 The igraph development team + + 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 2 of the License, or + (at your option) 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 . +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "igraph_dqueue.h" +#include "igraph_memory.h" +#include "igraph_progress.h" + +#include "../core/indheap.h" +#include "../core/interruption.h" + +#include + +/*****************************************************/ +/***** Average path length and global efficiency *****/ +/*****************************************************/ + +/* Computes the average of pairwise distances (used for igraph_average_path_length), + * or of inverse pairwise distances (used for igraph_global_efficiency), in an unweighted graph. */ +static int igraph_i_average_path_length_unweighted( + const igraph_t *graph, + igraph_real_t *res, + igraph_real_t *unconnected_pairs, /* if not NULL, will be set to the no. of non-connected ordered vertex pairs */ + const igraph_bool_t directed, + const igraph_bool_t invert, /* average inverse distances instead of distances */ + const igraph_bool_t unconn /* average over connected pairs instead of all pairs */) +{ + long int no_of_nodes = igraph_vcount(graph); + long int source, j, n; + long int *already_added; + igraph_real_t no_of_pairs = no_of_nodes > 0 ? no_of_nodes * (no_of_nodes - 1.0) : 0.0; /* no. of ordered vertex pairs */ + igraph_real_t no_of_conn_pairs = 0.0; /* no. of ordered pairs between which there is a path */ + + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_vector_int_t *neis; + igraph_adjlist_t allneis; + + *res = 0; + already_added = IGRAPH_CALLOC(no_of_nodes, long int); + if (already_added == 0) { + IGRAPH_ERROR("Average path length calculation failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, already_added); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_adjlist_init( + graph, &allneis, + directed ? IGRAPH_OUT : IGRAPH_ALL, + IGRAPH_LOOPS, IGRAPH_MULTIPLE + )); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + + for (source = 0; source < no_of_nodes; source++) { + IGRAPH_CHECK(igraph_dqueue_push(&q, source)); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + already_added[source] = source + 1; + + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + long int actdist = (long int) igraph_dqueue_pop(&q); + + neis = igraph_adjlist_get(&allneis, actnode); + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + long int neighbor = (long int) VECTOR(*neis)[j]; + if (already_added[neighbor] == source + 1) { + continue; + } + already_added[neighbor] = source + 1; + if (invert) { + *res += 1.0/(actdist + 1.0); + } else { + *res += actdist + 1.0; + } + no_of_conn_pairs += 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + } + } /* while !igraph_dqueue_empty */ + } /* for source < no_of_nodes */ + + + if (no_of_pairs == 0) { + *res = IGRAPH_NAN; /* can't average zero items */ + } else { + if (unconn) { /* average over connected pairs */ + if (no_of_conn_pairs == 0) { + *res = IGRAPH_NAN; /* can't average zero items */ + } else { + *res /= no_of_conn_pairs; + } + } else { /* average over all pairs */ + /* no_of_conn_pairs < no_of_pairs implies that the graph is disconnected */ + if (no_of_conn_pairs < no_of_pairs && ! invert) { + /* When invert=false, assume the distance between non-connected pairs to be infinity */ + *res = IGRAPH_INFINITY; + } else { + /* When invert=true, assume the inverse distance between non-connected pairs + * to be zero. Therefore, no special treatment is needed for disconnected graphs. */ + *res /= no_of_pairs; + } + } + } + + if (unconnected_pairs) + *unconnected_pairs = no_of_pairs - no_of_conn_pairs; + + /* clean */ + IGRAPH_FREE(already_added); + igraph_dqueue_destroy(&q); + igraph_adjlist_destroy(&allneis); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/* Computes the average of pairwise distances (used for igraph_average_path_length_dijkstra), + * or of inverse pairwise distances (used for igraph_global_efficiency), in an unweighted graph. + * Uses Dijkstra's algorithm, therefore all weights must be non-negative. + */ +static int igraph_i_average_path_length_dijkstra( + const igraph_t *graph, + igraph_real_t *res, + igraph_real_t *unconnected_pairs, + const igraph_vector_t *weights, + const igraph_bool_t directed, + const igraph_bool_t invert, /* average inverse distances instead of distances */ + const igraph_bool_t unconn /* average over connected pairs instead of all pairs */) +{ + + /* Implementation details. This is the basic Dijkstra algorithm, + with a binary heap. The heap is indexed, i.e. it stores not only + the distances, but also which vertex they belong to. + + From now on we use a 2-way heap, so the distances can be queried + directly from the heap. + + Dirty tricks: + - the opposite of the distance is stored in the heap, as it is a + maximum heap and we need a minimum heap. + - we don't use IGRAPH_INFINITY in the res matrix during the + computation, as IGRAPH_FINITE() might involve a function call + and we want to spare that. -1 will denote infinity instead. + */ + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_2wheap_t Q; + igraph_lazy_inclist_t inclist; + long int source, j; + igraph_real_t no_of_pairs; + igraph_real_t no_of_conn_pairs = 0.0; /* no. of ordered pairs between which there is a path */ + + if (!weights) { + return igraph_i_average_path_length_unweighted(graph, res, unconnected_pairs, directed, invert, unconn); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%ld) does not match the number of edges (%ld).", + IGRAPH_EINVAL, igraph_vector_size(weights), no_of_edges); + } + if (no_of_edges > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERRORF("Weight vector must be non-negative, got %g.", IGRAPH_EINVAL, min); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + /* Avoid returning a negative zero, which would be printed as -0 in tests. */ + if (no_of_nodes > 0) { + no_of_pairs = no_of_nodes * (no_of_nodes - 1.0); + } else { + no_of_pairs = 0; + } + + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + IGRAPH_CHECK(igraph_lazy_inclist_init( + graph, &inclist, directed ? IGRAPH_OUT : IGRAPH_ALL, IGRAPH_LOOPS + )); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + *res = 0.0; + + for (source = 0; source < no_of_nodes; ++source) { + + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_2wheap_clear(&Q); + igraph_2wheap_push_with_index(&Q, source, -1.0); + + while (!igraph_2wheap_empty(&Q)) { + long int minnei = igraph_2wheap_max_index(&Q); + igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); + igraph_vector_int_t *neis; + long int nlen; + + if (minnei != source) { + if (invert) { + *res += 1.0/(mindist - 1.0); + } else { + *res += mindist - 1.0; + } + no_of_conn_pairs += 1; + } + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); + nlen = igraph_vector_int_size(neis); + for (j = 0; j < nlen; j++) { + long int edge = (long int) VECTOR(*neis)[j]; + long int tto = IGRAPH_OTHER(graph, edge, minnei); + igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; + igraph_bool_t active = igraph_2wheap_has_active(&Q, tto); + igraph_bool_t has = igraph_2wheap_has_elem(&Q, tto); + igraph_real_t curdist = active ? -igraph_2wheap_get(&Q, tto) : 0.0; + if (!has) { + /* This is the first non-infinite distance */ + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist)); + } else if (altdist < curdist) { + /* This is a shorter path */ + IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist)); + } + } + } /* !igraph_2wheap_empty(&Q) */ + } /* for source < no_of_nodes */ + + if (no_of_pairs == 0) { + *res = IGRAPH_NAN; /* can't average zero items */ + } else { + if (unconn) { /* average over connected pairs */ + if (no_of_conn_pairs == 0) { + *res = IGRAPH_NAN; /* can't average zero items */ + } else { + *res /= no_of_conn_pairs; + } + } else { /* average over all pairs */ + /* no_of_conn_pairs < no_of_pairs implies that the graph is disconnected */ + if (no_of_conn_pairs < no_of_pairs && ! invert) { + *res = IGRAPH_INFINITY; + } else { + *res /= no_of_pairs; + } + } + } + + if (unconnected_pairs) + *unconnected_pairs = no_of_pairs - no_of_conn_pairs; + + igraph_lazy_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup structural + * \function igraph_average_path_length + * \brief Calculates the average unweighted shortest path length between all vertex pairs. + * + * + * If no vertex pairs can be included in the calculation, for example because the graph + * has fewer than two vertices, or if the graph has no edges and \c unconn is set to \c TRUE, + * NaN is returned. + * + * \param graph The graph object. + * \param res Pointer to a real number, this will contain the result. + * \param unconn_pairs Pointer to a real number. If not a null pointer, the number of + * ordered vertex pairs where the second vertex is unreachable from the first one + * will be stored here. + * \param directed Boolean, whether to consider directed + * paths. Ignored for undirected graphs. + * \param unconn What to do if the graph is not connected. If + * \c TRUE, only those vertex pairs will be included in the calculation + * between which there is a path. If \c FALSE, \c IGRAPH_INFINITY is returned + * for disconnected graphs. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for data structures + * + * Time complexity: O(|V| |E|), the number of vertices times the number of edges. + * + * \sa \ref igraph_average_path_length_dijkstra() for the weighted version. + * + * \example examples/simple/igraph_average_path_length.c + */ + +int igraph_average_path_length(const igraph_t *graph, + igraph_real_t *res, igraph_real_t *unconn_pairs, + igraph_bool_t directed, igraph_bool_t unconn) +{ + return igraph_i_average_path_length_unweighted(graph, res, unconn_pairs, directed, /* invert= */ 0, unconn); +} + + +/** + * \ingroup structural + * \function igraph_average_path_length_dijkstra + * \brief Calculates the average weighted shortest path length between all vertex pairs. + * + * + * If no vertex pairs can be included in the calculation, for example because the graph + * has fewer than two vertices, or if the graph has no edges and \c unconn is set to \c TRUE, + * NaN is returned. + * + * + * All distinct ordered vertex pairs are taken into account. + * + * \param graph The graph object. + * \param res Pointer to a real number, this will contain the result. + * \param unconn_pairs Pointer to a real number. If not a null pointer, the number of + * ordered vertex pairs where the second vertex is unreachable from the first one + * will be stored here. + * \param weights The edge weights. All edge weights must be + * non-negative for Dijkstra's algorithm to work. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. If this is a null pointer, then the unweighted + * version, \ref igraph_average_path_length() is called. + * \param directed Boolean, whether to consider directed paths. + * Ignored for undirected graphs. + * \param unconn If \c TRUE, only those pairs are considered for the calculation + * between which there is a path. If \c FALSE, \c IGRAPH_INFINITY is returned + * for disconnected graphs. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for data structures + * \cli IGRAPH_EINVAL + * invalid weight vector + * \endclist + * + * Time complexity: O(|V| |E| log|E| + |V|), where |V| is the number of + * vertices and |E| is the number of edges. + * + * \sa \ref igraph_average_path_length() for a slightly faster unweighted version. + * + * \example examples/simple/igraph_grg_game.c + */ + +int igraph_average_path_length_dijkstra(const igraph_t *graph, + igraph_real_t *res, igraph_real_t *unconn_pairs, + const igraph_vector_t *weights, + igraph_bool_t directed, igraph_bool_t unconn) +{ + return igraph_i_average_path_length_dijkstra(graph, res, unconn_pairs, weights, directed, /* invert= */ 0, unconn); +} + + +/** + * \ingroup structural + * \function igraph_global_efficiency + * \brief Calculates the global efficiency of a network. + * + * + * The global efficiency of a network is defined as the average of inverse distances + * between all pairs of vertices: E_g = 1/(N*(N-1)) sum_{i!=j} 1/d_ij, + * where N is the number of vertices. + * The inverse distance between pairs that are not reachable from each other is considered + * to be zero. For graphs with fewer than 2 vertices, NaN is returned. + * + * + * Reference: + * V. Latora and M. Marchiori, + * Efficient Behavior of Small-World Networks, + * Phys. Rev. Lett. 87, 198701 (2001). + * https://dx.doi.org/10.1103/PhysRevLett.87.198701 + * + * \param graph The graph object. + * \param res Pointer to a real number, this will contain the result. + * \param weights The edge weights. All edge weights must be + * non-negative for Dijkstra's algorithm to work. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. If this is a null pointer, then the unweighted + * version, \ref igraph_average_path_length() is used in calculating + * the global efficiency. + * \param directed Boolean, whether to consider directed paths. + * Ignored for undirected graphs. + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for data structures + * \cli IGRAPH_EINVAL + * invalid weight vector + * \endclist + * + * Time complexity: O(|V| |E| log|E| + |V|) for weighted graphs and + * O(|V| |E|) for unweighted ones. |V| denotes the number of + * vertices and |E| denotes the number of edges. + * + */ + +int igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res, + const igraph_vector_t *weights, + igraph_bool_t directed) +{ + return igraph_i_average_path_length_dijkstra(graph, res, NULL, weights, directed, /* invert= */ 1, /* unconn= */ 0); +} + + +/****************************/ +/***** Local efficiency *****/ +/****************************/ + +static int igraph_i_local_efficiency_unweighted( + const igraph_t *graph, + const igraph_adjlist_t *adjlist, + igraph_dqueue_t *q, + long int *already_counted, + igraph_vector_t *vertex_neis, + igraph_vector_char_t *nei_mask, + igraph_real_t *res, + igraph_integer_t vertex, + igraph_neimode_t mode) +{ + + long int no_of_nodes = igraph_vcount(graph); + long int vertex_neis_size; + long int neighbor_count; /* unlike 'vertex_neis_size', 'neighbor_count' does not count self-loops and multi-edges */ + long int i, j; + + igraph_dqueue_clear(q); + + /* already_counted[i] is 0 iff vertex i was not reached so far, otherwise + * it is the index of the source vertex in vertex_neis that it was reached + * from, plus 1 */ + memset(already_counted, 0, no_of_nodes * sizeof(long int)); + + IGRAPH_CHECK(igraph_neighbors(graph, vertex_neis, vertex, mode)); + vertex_neis_size = igraph_vector_size(vertex_neis); + + igraph_vector_char_fill(nei_mask, 0); + neighbor_count = 0; + for (i=0; i < vertex_neis_size; ++i) { + long int v = VECTOR(*vertex_neis)[i]; + if (v != vertex && ! VECTOR(*nei_mask)[v]) { + VECTOR(*nei_mask)[v] = 1; /* mark as unprocessed neighbour */ + neighbor_count++; + } + } + + *res = 0.0; + + /* when the neighbor count is smaller than 2, we return 0.0 */ + if (neighbor_count < 2) { + return IGRAPH_SUCCESS; + } + + for (i=0; i < vertex_neis_size; ++i) { + long int source = VECTOR(*vertex_neis)[i]; + long int reached = 0; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (source == vertex) + continue; + + if (VECTOR(*nei_mask)[source] == 2) + continue; + + VECTOR(*nei_mask)[source] = 2; /* mark neighbour as already processed */ + + IGRAPH_CHECK(igraph_dqueue_push(q, source)); + IGRAPH_CHECK(igraph_dqueue_push(q, 0)); + already_counted[source] = i + 1; + + while (!igraph_dqueue_empty(q)) { + igraph_vector_int_t *act_neis; + long int act_neis_size; + long int act = (long int) igraph_dqueue_pop(q); + long int actdist = (long int) igraph_dqueue_pop(q); + + if (act != source && VECTOR(*nei_mask)[act]) { + *res += 1.0 / actdist; + reached++; + if (reached == neighbor_count) { + igraph_dqueue_clear(q); + break; + } + } + + act_neis = igraph_adjlist_get(adjlist, act); + act_neis_size = igraph_vector_int_size(act_neis); + for (j = 0; j < act_neis_size; j++) { + long int neighbor = (long int) VECTOR(*act_neis)[j]; + + if (neighbor == vertex || already_counted[neighbor] == i + 1) + continue; + + already_counted[neighbor] = i + 1; + IGRAPH_CHECK(igraph_dqueue_push(q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(q, actdist + 1)); + } + } + } + + *res /= neighbor_count * (neighbor_count - 1.0); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_local_efficiency_dijkstra( + const igraph_t *graph, + igraph_lazy_inclist_t *inclist, + igraph_2wheap_t *Q, + igraph_vector_t *vertex_neis, + igraph_vector_char_t *nei_mask, /* true if the corresponding node is a neighbour of 'vertex' */ + igraph_real_t *res, + igraph_integer_t vertex, + igraph_neimode_t mode, + const igraph_vector_t *weights) +{ + + /* Implementation details. This is the basic Dijkstra algorithm, + with a binary heap. The heap is indexed, i.e. it stores not only + the distances, but also which vertex they belong to. + + From now on we use a 2-way heap, so the distances can be queried + directly from the heap. + + Dirty tricks: + - the opposite of the distance is stored in the heap, as it is a + maximum heap and we need a minimum heap. + - we don't use IGRAPH_INFINITY in the res matrix during the + computation, as IGRAPH_FINITE() might involve a function call + and we want to spare that. -1 will denote infinity instead. + */ + + long int i, j; + long int vertex_neis_size; + long int neighbor_count; /* unlike 'inc_edges_size', 'neighbor_count' does not count self-loops or multi-edges */ + + IGRAPH_CHECK(igraph_neighbors(graph, vertex_neis, vertex, mode)); + vertex_neis_size = igraph_vector_size(vertex_neis); + + igraph_vector_char_fill(nei_mask, 0); + neighbor_count = 0; + for (i=0; i < vertex_neis_size; ++i) { + long int v = VECTOR(*vertex_neis)[i]; + if (v != vertex && ! VECTOR(*nei_mask)[v]) { + VECTOR(*nei_mask)[v] = 1; /* mark as unprocessed neighbour */ + neighbor_count++; + } + } + + *res = 0.0; + + /* when the neighbor count is smaller than 2, we return 0.0 */ + if (neighbor_count < 2) { + return IGRAPH_SUCCESS; + } + + for (i=0; i < vertex_neis_size; ++i) { + long int source = VECTOR(*vertex_neis)[i]; + long int reached = 0; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (source == vertex) + continue; + + /* avoid processing a neighbour twice in multigraphs */ + if (VECTOR(*nei_mask)[source] == 2) + continue; + VECTOR(*nei_mask)[source] = 2; /* mark as already processed */ + + igraph_2wheap_clear(Q); + igraph_2wheap_push_with_index(Q, source, -1.0); + + while (!igraph_2wheap_empty(Q)) { + long int minnei = igraph_2wheap_max_index(Q); + igraph_real_t mindist = -igraph_2wheap_deactivate_max(Q); + igraph_vector_int_t *neis; + long int nlen; + + if (minnei != source && VECTOR(*nei_mask)[minnei]) { + *res += 1.0/(mindist - 1.0); + reached++; + if (reached == neighbor_count) { + igraph_2wheap_clear(Q); + break; + } + } + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_lazy_inclist_get(inclist, (igraph_integer_t) minnei); + nlen = igraph_vector_int_size(neis); + for (j = 0; j < nlen; j++) { + igraph_real_t altdist, curdist; + igraph_bool_t active, has; + long int edge = (long int) VECTOR(*neis)[j]; + long int tto = IGRAPH_OTHER(graph, edge, minnei); + + if (tto == vertex) + continue; + + altdist = mindist + VECTOR(*weights)[edge]; + active = igraph_2wheap_has_active(Q, tto); + has = igraph_2wheap_has_elem(Q, tto); + curdist = active ? -igraph_2wheap_get(Q, tto) : 0.0; + if (!has) { + /* This is the first non-infinite distance */ + IGRAPH_CHECK(igraph_2wheap_push_with_index(Q, tto, -altdist)); + } else if (altdist < curdist) { + /* This is a shorter path */ + IGRAPH_CHECK(igraph_2wheap_modify(Q, tto, -altdist)); + } + } + + } /* !igraph_2wheap_empty(&Q) */ + + } + + *res /= neighbor_count * (neighbor_count - 1.0); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup structural + * \function igraph_local_efficiency + * \brief Calculates the local efficiency around each vertex in a network. + * + * + * The local efficiency of a network around a vertex is defined as follows: + * We remove the vertex and compute the distances (shortest path lengths) between + * its neighbours through the rest of the network. The local efficiency around the + * removed vertex is the average of the inverse of these distances. + * + * + * The inverse distance between two vertices which are not reachable from each other + * is considered to be zero. The local efficiency around a vertex with fewer than two + * neighbours is taken to be zero by convention. + * + * + * Reference: + * I. Vragović, E. Louis, and A. Díaz-Guilera, + * Efficiency of informational transfer in regular and complex networks, + * Phys. Rev. E 71, 1 (2005). + * http://dx.doi.org/10.1103/PhysRevE.71.036122 + * + * \param graph The graph object. + * \param res Pointer to an initialized vector, this will contain the result. + * \param vids The vertices around which the local efficiency will be calculated. + * \param weights The edge weights. All edge weights must be + * non-negative. Additionally, no edge weight may be NaN. If either + * case does not hold, an error is returned. If this is a null + * pointer, then the unweighted version, + * \ref igraph_average_path_length() is called. + * \param directed Boolean, whether to consider directed paths. + * Ignored for undirected graphs. + * \param mode How to determine the local neighborhood of each vertex + * in directed graphs. Ignored in undirected graphs. + * \clist + * \cli IGRAPH_ALL + * take both in- and out-neighbours; + * this is a reasonable default for high-level interfaces. + * \cli IGRAPH_OUT + * take only out-neighbours + * \cli IGRAPH_IN + * take only in-neighbours + * \endclist + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for data structures + * \cli IGRAPH_EINVAL + * invalid weight vector + * \endclist + * + * Time complexity: O(|E|^2 log|E|) for weighted graphs and + * O(|E|^2) for unweighted ones. |E| denotes the number of edges. + * + * \sa \ref igraph_average_local_efficiency() + * + */ + +int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, + const igraph_vector_t *weights, + igraph_bool_t directed, igraph_neimode_t mode) +{ + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int nodes_to_calc; /* no. of vertices includes in computation */ + igraph_vit_t vit; + igraph_vector_t vertex_neis; + igraph_vector_char_t nei_mask; + long int i; + + /* 'nei_mask' is a vector indexed by vertices. The meaning of its values is as follows: + * 0: not a neighbour of 'vertex' + * 1: a not-yet-processed neighbour of 'vertex' + * 2: an already processed neighbour of 'vertex' + * + * Marking neighbours of already processed is necessary to avoid processing them more + * than once in multigraphs. + */ + IGRAPH_CHECK(igraph_vector_char_init(&nei_mask, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_char_destroy, &nei_mask); + IGRAPH_VECTOR_INIT_FINALLY(&vertex_neis, 0); + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + nodes_to_calc = IGRAPH_VIT_SIZE(vit); + + IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); + + if (! weights) /* unweighted case */ + { + long int *already_counted; + igraph_adjlist_t adjlist; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + + already_counted = IGRAPH_CALLOC(no_of_nodes, long int); + if (already_counted == 0) { + IGRAPH_ERROR("Local efficiency calculation failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, already_counted); + + IGRAPH_CHECK(igraph_adjlist_init( + graph, &adjlist, + directed ? IGRAPH_OUT : IGRAPH_ALL, + IGRAPH_LOOPS, IGRAPH_MULTIPLE + )); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + for (IGRAPH_VIT_RESET(vit), i=0; + ! IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) + { + IGRAPH_CHECK(igraph_i_local_efficiency_unweighted( + graph, &adjlist, + &q, already_counted, &vertex_neis, &nei_mask, + &(VECTOR(*res)[i]), IGRAPH_VIT_GET(vit), mode)); + } + + igraph_dqueue_destroy(&q); + igraph_adjlist_destroy(&adjlist); + IGRAPH_FREE(already_counted); + IGRAPH_FINALLY_CLEAN(3); + } + else /* weighted case */ + { + igraph_lazy_inclist_t inclist; + igraph_2wheap_t Q; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length does not match the number of edges", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_lazy_inclist_init( + graph, &inclist, directed ? IGRAPH_OUT : IGRAPH_ALL, IGRAPH_LOOPS + )); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + + for (IGRAPH_VIT_RESET(vit), i=0; + ! IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) + { + IGRAPH_CHECK(igraph_i_local_efficiency_dijkstra( + graph, &inclist, + &Q, &vertex_neis, &nei_mask, + &(VECTOR(*res)[i]), IGRAPH_VIT_GET(vit), mode, weights)); + } + + igraph_2wheap_destroy(&Q); + igraph_lazy_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_vit_destroy(&vit); + igraph_vector_destroy(&vertex_neis); + igraph_vector_char_destroy(&nei_mask); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup structural + * \function igraph_average_local_efficiency + * \brief Calculates the average local efficiency in a network. + * + * For the null graph, zero is returned by convention. + * + * \param graph The graph object. + * \param res Pointer to a real number, this will contain the result. + * \param weights The edge weights. They must be all non-negative. + * If a null pointer is given, all weights are assumed to be 1. + * \param directed Boolean, whether to consider directed paths. + * Ignored for undirected graphs. + * \param mode How to determine the local neighborhood of each vertex + * in directed graphs. Ignored in undirected graphs. + * \clist + * \cli IGRAPH_ALL + * take both in- and out-neighbours; + * this is a reasonable default for high-level interfaces. + * \cli IGRAPH_OUT + * take only out-neighbours + * \cli IGRAPH_IN + * take only in-neighbours + * \endclist + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for data structures + * \cli IGRAPH_EINVAL + * invalid weight vector + * \endclist + * + * Time complexity: O(|E|^2 log|E|) for weighted graphs and + * O(|E|^2) for unweighted ones. |E| denotes the number of edges. + * + * \sa \ref igraph_local_efficiency() + * + */ + +int igraph_average_local_efficiency(const igraph_t *graph, igraph_real_t *res, + const igraph_vector_t *weights, + igraph_bool_t directed, igraph_neimode_t mode) +{ + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t local_eff; + + /* If there are fewer than 3 vertices, no vertex has more than one neighbour, thus all + local efficiencies are zero. For the null graph, we return zero by convention. */ + if (no_of_nodes < 3) { + *res = 0; + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(&local_eff, no_of_nodes); + + IGRAPH_CHECK(igraph_local_efficiency(graph, &local_eff, igraph_vss_all(), weights, directed, mode)); + + *res = igraph_vector_sum(&local_eff); + *res /= no_of_nodes; + + igraph_vector_destroy(&local_eff); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/***************************/ +/***** Graph diameter ******/ +/***************************/ + +/** + * \ingroup structural + * \function igraph_diameter + * \brief Calculates the diameter of a graph (longest geodesic). + * + * The diameter of a graph is the length of the longest shortest path it has. + * This function computes both the diameter, as well as the corresponding path. + * The diameter of the null graph is considered be infinity by convention. + * + * If the graph has no vertices, \c IGRAPH_NAN is returned. + * + * \param graph The graph object. + * \param pres Pointer to a real number, if not \c NULL then it will contain + * the diameter (the actual distance). + * \param pfrom Pointer to an integer, if not \c NULL it will be set to the + * source vertex of the diameter path. If the graph has no diameter path, + * it will be set to -1. + * \param pto Pointer to an integer, if not \c NULL it will be set to the + * target vertex of the diameter path. If the graph has no diameter path, + * it will be set to -1. + * \param path Pointer to an initialized vector. If not \c NULL the actual + * longest geodesic path will be stored here. The vector will be + * resized as needed. + * \param directed Boolean, whether to consider directed + * paths. Ignored for undirected graphs. + * \param unconn What to do if the graph is not connected. If + * \c TRUE the longest geodesic within a component + * will be returned, otherwise \c IGRAPH_INFINITY is returned. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. + * + * Time complexity: O(|V||E|), the + * number of vertices times the number of edges. + * + * \sa \ref igraph_diameter_dijkstra() + * + * \example examples/simple/igraph_diameter.c + */ + +int igraph_diameter(const igraph_t *graph, igraph_real_t *pres, + igraph_integer_t *pfrom, igraph_integer_t *pto, + igraph_vector_t *path, + igraph_bool_t directed, igraph_bool_t unconn) { + + long int no_of_nodes = igraph_vcount(graph); + long int i, j, n; + long int *already_added; + long int nodes_reached; + long int from = 0, to = 0; + igraph_real_t res = 0; + + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_vector_int_t *neis; + igraph_neimode_t dirmode; + igraph_adjlist_t allneis; + + /* See https://github.com/igraph/igraph/issues/1538#issuecomment-724071857 + * for why we return NaN for the null graph. */ + if (no_of_nodes == 0) { + if (pres) { + *pres = IGRAPH_NAN; + } + if (path) { + igraph_vector_clear(path); + } + if (pfrom) { + *pfrom = -1; + } + if (pto) { + *pto = -1; + } + return IGRAPH_SUCCESS; + } + + if (directed) { + dirmode = IGRAPH_OUT; + } else { + dirmode = IGRAPH_ALL; + } + already_added = IGRAPH_CALLOC(no_of_nodes, long int); + if (already_added == 0) { + IGRAPH_ERROR("diameter failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, already_added); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, dirmode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + + for (i = 0; i < no_of_nodes; i++) { + nodes_reached = 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, i)); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + already_added[i] = i + 1; + + IGRAPH_PROGRESS("Diameter: ", 100.0 * i / no_of_nodes, NULL); + + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + long int actdist = (long int) igraph_dqueue_pop(&q); + if (actdist > res) { + res = actdist; + from = i; + to = actnode; + } + + neis = igraph_adjlist_get(&allneis, actnode); + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + long int neighbor = (long int) VECTOR(*neis)[j]; + if (already_added[neighbor] == i + 1) { + continue; + } + already_added[neighbor] = i + 1; + nodes_reached++; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + } + } /* while !igraph_dqueue_empty */ + + /* not connected, return IGRAPH_INFINITY */ + if (nodes_reached != no_of_nodes && !unconn) { + res = IGRAPH_INFINITY; + from = -1; + to = -1; + break; + } + } /* for i 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); + } + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, dirmode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + + for (source = 0; source < no_of_nodes; source++) { + + IGRAPH_PROGRESS("Weighted diameter: ", source * 100.0 / no_of_nodes, NULL); + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_2wheap_clear(&Q); + igraph_2wheap_push_with_index(&Q, source, -1.0); + + nodes_reached = 0.0; + + while (!igraph_2wheap_empty(&Q)) { + long int minnei = igraph_2wheap_max_index(&Q); + igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); + igraph_vector_int_t *neis; + long int nlen; + + if (mindist > res) { + res = mindist; from = source; to = minnei; + } + nodes_reached++; + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_inclist_get(&inclist, minnei); + nlen = igraph_vector_int_size(neis); + for (j = 0; j < nlen; j++) { + long int edge = (long int) VECTOR(*neis)[j]; + long int tto = IGRAPH_OTHER(graph, edge, minnei); + igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; + igraph_bool_t active = igraph_2wheap_has_active(&Q, tto); + igraph_bool_t has = igraph_2wheap_has_elem(&Q, tto); + igraph_real_t curdist = active ? -igraph_2wheap_get(&Q, tto) : 0.0; + + if (!has) { + /* First finite distance */ + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist)); + } else if (altdist < curdist) { + /* A shorter path */ + IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist)); + } + } + + } /* !igraph_2wheap_empty(&Q) */ + + /* not connected, return infinity */ + if (nodes_reached != no_of_nodes && !unconn) { + res = IGRAPH_INFINITY; + from = to = -1; + break; + } + + } /* source < no_of_nodes */ + + /* Compensate for the +1 that we have added to distances */ + res -= 1; + + igraph_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_PROGRESS("Weighted diameter: ", 100.0, NULL); + + if (pres) { + *pres = res; + } + if (pfrom) { + *pfrom = (igraph_integer_t) from; + } + if (pto) { + *pto = (igraph_integer_t) to; + } + if (path) { + if (!igraph_finite(res)) { + igraph_vector_clear(path); + } else { + igraph_vector_ptr_t tmpptr; + igraph_vector_ptr_init(&tmpptr, 1); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &tmpptr); + VECTOR(tmpptr)[0] = path; + IGRAPH_CHECK(igraph_get_shortest_paths_dijkstra(graph, + /*vertices=*/ &tmpptr, /*edges=*/ 0, + (igraph_integer_t) from, + igraph_vss_1((igraph_integer_t) to), + weights, dirmode, /*predecessors=*/ 0, + /*inbound_edges=*/ 0)); + igraph_vector_ptr_destroy(&tmpptr); + IGRAPH_FINALLY_CLEAN(1); + } + } + + return 0; +} diff --git a/src/rigraph/core/paths/simple_paths.c b/src/rigraph/core/paths/simple_paths.c new file mode 100644 index 0000000..e5bfba0 --- /dev/null +++ b/src/rigraph/core/paths/simple_paths.c @@ -0,0 +1,179 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2014 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_paths.h" + +#include "igraph_interface.h" +#include "igraph_vector_ptr.h" +#include "igraph_iterators.h" +#include "igraph_adjlist.h" +#include "igraph_stack.h" + +#include "../core/interruption.h" + +/** + * \function igraph_get_all_simple_paths + * \brief List all simple paths from one source. + * + * A path is simple if its vertices are unique, i.e. no vertex + * is visited more than once. + * + * + * Note that potentially there are exponentially many + * paths between two vertices of a graph, and you may + * run out of memory when using this function, if your + * graph is lattice-like. + * + * + * This function currently ignored multiple and loop edges. + * \param graph The input graph. + * \param res Initialized integer vector, all paths are + * returned here, separated by -1 markers. The paths + * are included in arbitrary order, as they are found. + * \param from The start vertex. + * \param to The target vertices. + * \param cutoff Maximum length of path that is considered. If + * negative, paths of all lengths are considered. + * \param mode The type of the paths to consider, it is ignored + * for undirected graphs. + * \return Error code. + * + * Time complexity: O(n!) in the worst case, n is the number of + * vertices. + */ + +int igraph_get_all_simple_paths(const igraph_t *graph, + igraph_vector_int_t *res, + igraph_integer_t from, + const igraph_vs_t to, + igraph_integer_t cutoff, + igraph_neimode_t mode) { + + igraph_integer_t no_nodes = igraph_vcount(graph); + igraph_vit_t vit; + igraph_bool_t toall = igraph_vs_is_all(&to); + igraph_vector_char_t markto; + igraph_lazy_adjlist_t adjlist; + igraph_vector_int_t stack, dist; + igraph_vector_char_t added; + igraph_vector_int_t nptr; + int iteration = 0; + + if (from < 0 || from >= no_nodes) { + IGRAPH_ERROR("Invalid starting vertex", IGRAPH_EINVAL); + } + + if (!toall) { + igraph_vector_char_init(&markto, no_nodes); + IGRAPH_FINALLY(igraph_vector_char_destroy, &markto); + IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + for (; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + VECTOR(markto)[ IGRAPH_VIT_GET(vit) ] = 1; + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + } + + IGRAPH_CHECK(igraph_vector_char_init(&added, no_nodes)); + IGRAPH_FINALLY(igraph_vector_char_destroy, &added); + IGRAPH_CHECK(igraph_vector_int_init(&stack, 100)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &stack); + IGRAPH_CHECK(igraph_vector_int_init(&dist, 100)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &dist); + IGRAPH_CHECK(igraph_lazy_adjlist_init( + graph, &adjlist, mode, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE + )); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + IGRAPH_CHECK(igraph_vector_int_init(&nptr, no_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &nptr); + + igraph_vector_int_clear(res); + + igraph_vector_int_clear(&stack); + igraph_vector_int_clear(&dist); + igraph_vector_int_push_back(&stack, from); + igraph_vector_int_push_back(&dist, 0); + VECTOR(added)[from] = 1; + while (!igraph_vector_int_empty(&stack)) { + int act = igraph_vector_int_tail(&stack); + int curdist = igraph_vector_int_tail(&dist); + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, act); + int n = igraph_vector_int_size(neis); + int *ptr = igraph_vector_int_e_ptr(&nptr, act); + igraph_bool_t any; + igraph_bool_t within_dist; + int nei; + + if (iteration == 0) { + IGRAPH_ALLOW_INTERRUPTION(); + } + + within_dist = (curdist < cutoff || cutoff < 0); + if (within_dist) { + /* Search for a neighbor that was not yet visited */ + any = 0; + while (!any && (*ptr) < n) { + nei = (int) VECTOR(*neis)[(*ptr)]; + any = !VECTOR(added)[nei]; + (*ptr) ++; + } + } + if (within_dist && any) { + /* There is such a neighbor, add it */ + IGRAPH_CHECK(igraph_vector_int_push_back(&stack, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&dist, curdist + 1)); + VECTOR(added)[nei] = 1; + /* Add to results */ + if (toall || VECTOR(markto)[nei]) { + IGRAPH_CHECK(igraph_vector_int_append(res, &stack)); + IGRAPH_CHECK(igraph_vector_int_push_back(res, -1)); + } + } else { + /* There is no such neighbor, finished with the subtree */ + int up = igraph_vector_int_pop_back(&stack); + igraph_vector_int_pop_back(&dist); + VECTOR(added)[up] = 0; + VECTOR(nptr)[up] = 0; + } + + iteration++; + if (iteration >= 10000) { + iteration = 0; + } + } + + igraph_vector_int_destroy(&nptr); + igraph_lazy_adjlist_destroy(&adjlist); + igraph_vector_int_destroy(&dist); + igraph_vector_int_destroy(&stack); + igraph_vector_char_destroy(&added); + IGRAPH_FINALLY_CLEAN(5); + + if (!toall) { + igraph_vector_char_destroy(&markto); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} diff --git a/src/rigraph/core/paths/unweighted.c b/src/rigraph/core/paths/unweighted.c new file mode 100644 index 0000000..46f40ea --- /dev/null +++ b/src/rigraph/core/paths/unweighted.c @@ -0,0 +1,540 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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 . +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "../core/interruption.h" + +/** + * \ingroup structural + * \function igraph_shortest_paths + * \brief The length of the shortest paths between vertices. + * + * \param graph The graph object. + * \param res The result of the calculation, a matrix. A pointer to an + * initialized matrix, to be more precise. The matrix will be + * resized if needed. It will have the same + * number of rows as the length of the \c from + * argument, and its number of columns is the number of + * vertices in the \c to argument. One row of the matrix shows the + * distances from/to a given vertex to the ones in \c to. + * For the unreachable vertices IGRAPH_INFINITY is returned. + * \param from The source vertices. + * \param to The target vertices. It is not allowed to include a + * vertex twice or more. + * \param mode The type of shortest paths to be used for the + * calculation in directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the lengths of the outgoing paths are calculated. + * \cli IGRAPH_IN + * the lengths of the incoming paths are calculated. + * \cli IGRAPH_ALL + * the directed graph is considered as an undirected one for + * the computation. + * \endclist + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary + * data. + * \cli IGRAPH_EINVVID + * invalid vertex id passed. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(n(|V|+|E|)), + * n is the + * number of vertices to calculate, |V| and + * |E| are the number of vertices and + * edges in the graph. + * + * \sa \ref igraph_get_shortest_paths() to get the paths themselves, + * \ref igraph_shortest_paths_dijkstra() for the weighted version. + */ +int igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t from, const igraph_vs_t to, + igraph_neimode_t mode) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_from, no_of_to; + long int *already_counted; + igraph_adjlist_t adjlist; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_vector_int_t *neis; + igraph_bool_t all_to; + + long int i, j; + igraph_vit_t fromvit, tovit; + igraph_real_t my_infinity = IGRAPH_INFINITY; + igraph_vector_t indexv; + + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && + mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); + } + + IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit)); + IGRAPH_FINALLY(igraph_vit_destroy, &fromvit); + no_of_from = IGRAPH_VIT_SIZE(fromvit); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + already_counted = IGRAPH_CALLOC(no_of_nodes, long int); + if (already_counted == 0) { + IGRAPH_ERROR("shortest paths failed", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, already_counted); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + all_to = igraph_vs_is_all(&to); + if (all_to) { + no_of_to = no_of_nodes; + } else { + IGRAPH_VECTOR_INIT_FINALLY(&indexv, no_of_nodes); + IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit)); + IGRAPH_FINALLY(igraph_vit_destroy, &tovit); + no_of_to = IGRAPH_VIT_SIZE(tovit); + for (i = 0; !IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit)) { + long int v = IGRAPH_VIT_GET(tovit); + if (VECTOR(indexv)[v]) { + IGRAPH_ERROR("Duplicate vertices in `to', this is not allowed", + IGRAPH_EINVAL); + } + VECTOR(indexv)[v] = ++i; + } + } + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to)); + igraph_matrix_fill(res, my_infinity); + + for (IGRAPH_VIT_RESET(fromvit), i = 0; + !IGRAPH_VIT_END(fromvit); + IGRAPH_VIT_NEXT(fromvit), i++) { + long int reached = 0; + IGRAPH_CHECK(igraph_dqueue_push(&q, IGRAPH_VIT_GET(fromvit))); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + already_counted[ (long int) IGRAPH_VIT_GET(fromvit) ] = i + 1; + + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_empty(&q)) { + long int act = (long int) igraph_dqueue_pop(&q); + long int actdist = (long int) igraph_dqueue_pop(&q); + + if (all_to) { + MATRIX(*res, i, act) = actdist; + } else { + if (VECTOR(indexv)[act]) { + MATRIX(*res, i, (long int)(VECTOR(indexv)[act] - 1)) = actdist; + reached++; + if (reached == no_of_to) { + igraph_dqueue_clear(&q); + break; + } + } + } + + neis = igraph_adjlist_get(&adjlist, act); + long int nei_count = igraph_vector_int_size(neis); + for (j = 0; j < nei_count; j++) { + long int neighbor = (long int) VECTOR(*neis)[j]; + if (already_counted[neighbor] == i + 1) { + continue; + } + already_counted[neighbor] = i + 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + } + } + } + + /* Clean */ + if (!all_to) { + igraph_vit_destroy(&tovit); + igraph_vector_destroy(&indexv); + IGRAPH_FINALLY_CLEAN(2); + } + + IGRAPH_FREE(already_counted); + igraph_dqueue_destroy(&q); + igraph_vit_destroy(&fromvit); + igraph_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(4); + + return 0; +} + +/** + * \ingroup structural + * \function igraph_get_shortest_paths + * \brief Shortest paths from a vertex. + * + * + * If there is more than one geodesic between two vertices, this + * function gives only one of them. + * \param graph The graph object. + * \param vertices The result, the ids of the vertices along the paths. + * This is a pointer vector, each element points to a vector + * object. These should be initialized before passing them to + * the function, which will properly clear and/or resize them + * and fill the ids of the vertices along the geodesics from/to + * the vertices. Supply a null pointer here if you don't need + * these vectors. + * \param edges The result, the ids of the edges along the paths. + * This is a pointer vector, each element points to a vector + * object. These should be initialized before passing them to + * the function, which will properly clear and/or resize them + * and fill the ids of the vertices along the geodesics from/to + * the vertices. Supply a null pointer here if you don't need + * these vectors. + * \param from The id of the vertex from/to which the geodesics are + * calculated. + * \param to Vertex sequence with the ids of the vertices to/from which the + * shortest paths will be calculated. A vertex might be given multiple + * times. + * \param mode The type of shortest paths to be used for the + * calculation in directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * the outgoing paths are calculated. + * \cli IGRAPH_IN + * the incoming paths are calculated. + * \cli IGRAPH_ALL + * the directed graph is considered as an + * undirected one for the computation. + * \endclist + * \param predecessors A pointer to an initialized igraph vector or null. + * If not null, a vector containing the predecessor of each vertex in + * the single source shortest path tree is returned here. The + * predecessor of vertex i in the tree is the vertex from which vertex i + * was reached. The predecessor of the start vertex (in the \c from + * argument) is itself by definition. If the predecessor is -1, it means + * that the given vertex was not reached from the source during the + * search. Note that the search terminates if all the vertices in + * \c to are reached. + * \param inbound_edges A pointer to an initialized igraph vector or null. + * If not null, a vector containing the inbound edge of each vertex in + * the single source shortest path tree is returned here. The + * inbound edge of vertex i in the tree is the edge via which vertex i + * was reached. The start vertex and vertices that were not reached + * during the search will have -1 in the corresponding entry of the + * vector. Note that the search terminates if all the vertices in + * \c to are reached. + * + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \cli IGRAPH_EINVVID + * \p from is invalid vertex id, or the length of \p to is + * not the same as the length of \p res. + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(|V|+|E|), + * |V| is the number of vertices, + * |E| the number of edges in the + * graph. + * + * \sa \ref igraph_shortest_paths() if you only need the path length but + * not the paths themselves. + * + * \example examples/simple/igraph_get_shortest_paths.c + */ +int igraph_get_shortest_paths(const igraph_t *graph, + igraph_vector_ptr_t *vertices, + igraph_vector_ptr_t *edges, + igraph_integer_t from, const igraph_vs_t to, + igraph_neimode_t mode, + igraph_vector_long_t *predecessors, + igraph_vector_long_t *inbound_edges) { + + /* TODO: use inclist_t if to is long (longer than 1?) */ + + long int no_of_nodes = igraph_vcount(graph); + long int *father; + + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + + long int i, j, vsize; + igraph_vector_t tmp = IGRAPH_VECTOR_NULL; + + igraph_vit_t vit; + + long int to_reach; + long int reached = 0; + + if (from < 0 || from >= no_of_nodes) { + IGRAPH_ERROR("cannot get shortest paths", IGRAPH_EINVVID); + } + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && + mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); + } + + IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + if (vertices && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(vertices)) { + IGRAPH_ERROR("Size of the `vertices' and the `to' should match", IGRAPH_EINVAL); + } + if (edges && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(edges)) { + IGRAPH_ERROR("Size of the `edges' and the `to' should match", IGRAPH_EINVAL); + } + + father = IGRAPH_CALLOC(no_of_nodes, long int); + if (father == 0) { + IGRAPH_ERROR("cannot get shortest paths", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, father); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + /* Mark the vertices we need to reach */ + to_reach = IGRAPH_VIT_SIZE(vit); + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + if (father[ (long int) IGRAPH_VIT_GET(vit) ] == 0) { + father[ (long int) IGRAPH_VIT_GET(vit) ] = -1; + } else { + to_reach--; /* this node was given multiple times */ + } + } + + /* Meaning of father[i]: + * + * - If father[i] < 0, it means that vertex i has to be reached and has not + * been reached yet. + * + * - If father[i] = 0, it means that vertex i does not have to be reached and + * it has not been reached yet. + * + * - If father[i] = 1, it means that vertex i is the start vertex. + * + * - Otherwise, father[i] is the ID of the edge from which vertex i was + * reached plus 2. + */ + + IGRAPH_CHECK(igraph_dqueue_push(&q, from + 1)); + if (father[ (long int) from ] < 0) { + reached++; + } + father[ (long int)from ] = 1; + + while (!igraph_dqueue_empty(&q) && reached < to_reach) { + long int act = (long int) igraph_dqueue_pop(&q) - 1; + + IGRAPH_CHECK(igraph_incident(graph, &tmp, (igraph_integer_t) act, mode)); + vsize = igraph_vector_size(&tmp); + for (j = 0; j < vsize; j++) { + long int edge = (long int) VECTOR(tmp)[j]; + long int neighbor = IGRAPH_OTHER(graph, edge, act); + if (father[neighbor] > 0) { + continue; + } else if (father[neighbor] < 0) { + reached++; + } + father[neighbor] = edge + 2; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor + 1)); + } + } + + if (reached < to_reach) { + IGRAPH_WARNING("Couldn't reach some vertices"); + } + + /* Create `predecessors' if needed */ + if (predecessors) { + IGRAPH_CHECK(igraph_vector_long_resize(predecessors, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (father[i] <= 0) { + /* i was not reached */ + VECTOR(*predecessors)[i] = -1; + } else if (father[i] == 1) { + /* i is the start vertex */ + VECTOR(*predecessors)[i] = i; + } else { + /* i was reached via the edge with ID = father[i] - 2 */ + VECTOR(*predecessors)[i] = IGRAPH_OTHER(graph, father[i] - 2, i); + } + } + } + + /* Create `inbound_edges' if needed */ + if (inbound_edges) { + IGRAPH_CHECK(igraph_vector_long_resize(inbound_edges, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (father[i] <= 1) { + /* i was not reached or i is the start vertex */ + VECTOR(*inbound_edges)[i] = -1; + } else { + /* i was reached via the edge with ID = father[i] - 2 */ + VECTOR(*inbound_edges)[i] = father[i] - 2; + } + } + } + + /* Create `vertices' and `edges' if needed */ + if (vertices || edges) { + for (IGRAPH_VIT_RESET(vit), j = 0; + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), j++) { + long int node = IGRAPH_VIT_GET(vit); + igraph_vector_t *vvec = 0, *evec = 0; + if (vertices) { + vvec = VECTOR(*vertices)[j]; + igraph_vector_clear(vvec); + } + if (edges) { + evec = VECTOR(*edges)[j]; + igraph_vector_clear(evec); + } + + IGRAPH_ALLOW_INTERRUPTION(); + + if (father[node] > 0) { + long int act = node; + long int size = 0; + long int edge; + while (father[act] > 1) { + size++; + edge = father[act] - 2; + act = IGRAPH_OTHER(graph, edge, act); + } + if (vvec) { + IGRAPH_CHECK(igraph_vector_resize(vvec, size + 1)); + VECTOR(*vvec)[size] = node; + } + if (evec) { + IGRAPH_CHECK(igraph_vector_resize(evec, size)); + } + act = node; + while (father[act] > 1) { + size--; + edge = father[act] - 2; + act = IGRAPH_OTHER(graph, edge, act); + if (vvec) { + VECTOR(*vvec)[size] = act; + } + if (evec) { + VECTOR(*evec)[size] = edge; + } + } + } + } + } + + /* Clean */ + IGRAPH_FREE(father); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&tmp); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(4); + + return 0; +} + +/** + * \function igraph_get_shortest_path + * \brief Shortest path from one vertex to another one. + * + * Calculates and returns a single unweighted shortest path from a + * given vertex to another one. If there are more than one shortest + * paths between the two vertices, then an arbitrary one is returned. + * + * This function is a wrapper to \ref + * igraph_get_shortest_paths(), for the special case when only one + * target vertex is considered. + * \param graph The input graph, it can be directed or + * undirected. Directed paths are considered in directed + * graphs. + * \param vertices Pointer to an initialized vector or a null + * pointer. If not a null pointer, then the vertex ids along + * the path are stored here, including the source and target + * vertices. + * \param edges Pointer to an uninitialized vector or a null + * pointer. If not a null pointer, then the edge ids along the + * path are stored here. + * \param from The id of the source vertex. + * \param to The id of the target vertex. + * \param mode A constant specifying how edge directions are + * considered in directed graphs. Valid modes are: + * \c IGRAPH_OUT, follows edge directions; + * \c IGRAPH_IN, follows the opposite directions; and + * \c IGRAPH_ALL, ignores edge directions. This argument is + * ignored for undirected graphs. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges in the graph. + * + * \sa \ref igraph_get_shortest_paths() for the version with more target + * vertices. + */ + +int igraph_get_shortest_path(const igraph_t *graph, + igraph_vector_t *vertices, + igraph_vector_t *edges, + igraph_integer_t from, + igraph_integer_t to, + igraph_neimode_t mode) { + + igraph_vector_ptr_t vertices2, *vp = &vertices2; + igraph_vector_ptr_t edges2, *ep = &edges2; + + if (vertices) { + IGRAPH_CHECK(igraph_vector_ptr_init(&vertices2, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vertices2); + VECTOR(vertices2)[0] = vertices; + } else { + vp = 0; + } + if (edges) { + IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2); + VECTOR(edges2)[0] = edges; + } else { + ep = 0; + } + + IGRAPH_CHECK(igraph_get_shortest_paths(graph, vp, ep, from, + igraph_vss_1(to), mode, 0, 0)); + + if (edges) { + igraph_vector_ptr_destroy(&edges2); + IGRAPH_FINALLY_CLEAN(1); + } + if (vertices) { + igraph_vector_ptr_destroy(&vertices2); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} diff --git a/src/rigraph/core/properties/basic_properties.c b/src/rigraph/core/properties/basic_properties.c new file mode 100644 index 0000000..58bed34 --- /dev/null +++ b/src/rigraph/core/properties/basic_properties.c @@ -0,0 +1,331 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_structural.h" + +#include "igraph_interface.h" + +/** + * \section about_structural + * + * These functions usually calculate some structural property + * of a graph, like its diameter, the degree of the nodes, etc. + */ + +/** + * \function igraph_density + * \brief Calculate the density of a graph. + * + * The density of a graph is simply the ratio of the actual number of its + * edges and the largest possible number of edges it could have. + * The maximum number of edges depends on interpretation: are vertices + * allowed to have a connected to themselves? This is controlled by the + * \p loops parameter. + * + * + * Note that density is ill-defined for graphs which have multiple edges + * between some pairs of vertices. Consider calling \ref igraph_simplify() + * on such graphs. + * + * \param graph The input graph object. + * \param res Pointer to a real number, the result will be stored + * here. + * \param loops Logical constant, whether to include self-loops in the + * calculation. If this constant is TRUE then + * loop edges are thought to be possible in the graph (this does not + * necessarily mean that the graph really contains any loops). If + * this is FALSE then the result is only correct if the graph does not + * contain loops. + * \return Error code. + * + * Time complexity: O(1). + */ +int igraph_density(const igraph_t *graph, igraph_real_t *res, + igraph_bool_t loops) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_real_t no_of_edges = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + + if (no_of_nodes == 0) { + *res = IGRAPH_NAN; + return 0; + } + + if (!loops) { + if (no_of_nodes == 1) { + *res = IGRAPH_NAN; + } else if (directed) { + *res = no_of_edges / no_of_nodes / (no_of_nodes - 1); + } else { + *res = no_of_edges / no_of_nodes * 2.0 / (no_of_nodes - 1); + } + } else { + if (directed) { + *res = no_of_edges / no_of_nodes / no_of_nodes; + } else { + *res = no_of_edges / no_of_nodes * 2.0 / (no_of_nodes + 1); + } + } + + return 0; +} + +/** + * \function igraph_diversity + * Structural diversity index of the vertices + * + * This measure was defined in Nathan Eagle, Michael Macy and Rob + * Claxton: Network Diversity and Economic Development, Science 328, + * 1029--1031, 2010. + * + * + * It is simply the (normalized) Shannon entropy of the + * incident edges' weights. D(i)=H(i)/log(k[i]), and + * H(i) = -sum(p[i,j] log(p[i,j]), j=1..k[i]), + * where p[i,j]=w[i,j]/sum(w[i,l], l=1..k[i]), k[i] is the (total) + * degree of vertex i, and w[i,j] is the weight of the edge(s) between + * vertex i and j. The diversity of isolated vertices will be NaN + * (not-a-number), while that of vertices with a single connection + * will be zero. + * + * + * The measure works only if the graph is undirected and has no multiple edges. + * If the graph has multiple edges, simplify it first using \ref + * igraph_simplify(). If the graph is directed, convert it into an undirected + * graph with \ref igraph_to_undirected() . + * + * \param graph The undirected input graph. + * \param weights The edge weights, in the order of the edge ids, must + * have appropriate length. Weights must be non-negative. + * \param res An initialized vector, the results are stored here. + * \param vids Vertex selector that specifies the vertices which to calculate + * the measure. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear. + * + */ +int igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, + igraph_vector_t *res, const igraph_vs_t vids) { + + long int no_of_edges = igraph_ecount(graph); + long int k, i; + igraph_vector_t incident; + igraph_bool_t has_multiple; + igraph_vit_t vit; + + if (igraph_is_directed(graph)) { + IGRAPH_ERROR("Diversity measure works with undirected graphs only.", IGRAPH_EINVAL); + } + + if (!weights) { + IGRAPH_ERROR("Edge weights must be given.", IGRAPH_EINVAL); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid edge weight vector length.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_has_multiple(graph, &has_multiple)); + if (has_multiple) { + IGRAPH_ERROR("Diversity measure works only if the graph has no multiple edges.", IGRAPH_EINVAL); + } + + if (no_of_edges > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight < 0) { + IGRAPH_ERROR("Weight vector must be non-negative.", IGRAPH_EINVAL); + } else if (igraph_is_nan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&incident, 10); + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + igraph_vector_clear(res); + IGRAPH_CHECK(igraph_vector_reserve(res, IGRAPH_VIT_SIZE(vit))); + + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + igraph_real_t d; + long int v = IGRAPH_VIT_GET(vit); + + IGRAPH_CHECK(igraph_incident(graph, &incident, v, /*mode=*/ IGRAPH_ALL)); + k = igraph_vector_size(&incident); /* degree */ + + /* + * Non-normalized diversity is defined as + * d = -sum_i w_i/s log (w_i/s) + * where s = sum_i w_i. In order to avoid two passes through the w vector, + * we use the equivalent formulation of + * d = log s - (sum_i w_i log w_i) / s + * However, this formulation may not give an exact 0.0 for some w when k=1, + * due to roundoff errors (examples: w=3 or w=7). For this reason, we + * special-case the computation for k=1 even for the unnormalized diversity + * insted of just setting the normalization factor to 1 for this case. + */ + if (k == 0) { + d = IGRAPH_NAN; + } else if (k == 1) { + if (VECTOR(*weights)[0] > 0) d = 0.0; /* s > 0 */ + else d = IGRAPH_NAN; /* s == 0 */ + } else { + igraph_real_t s = 0.0, ent = 0.0; + for (i = 0; i < k; i++) { + igraph_real_t w = VECTOR(*weights)[(long int)VECTOR(incident)[i]]; + if (w == 0) continue; + s += w; + ent += (w * log(w)); + } + d = (log(s) - ent / s) / log(k); + } + + igraph_vector_push_back(res, d); /* reserved */ + } + + igraph_vit_destroy(&vit); + igraph_vector_destroy(&incident); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_reciprocity + * \brief Calculates the reciprocity of a directed graph. + * + * + * The measure of reciprocity defines the proportion of mutual + * connections, in a directed graph. It is most commonly defined as + * the probability that the opposite counterpart of a directed edge is + * also included in the graph. In adjacency matrix notation: + * sum(i, j, (A.*A')ij) / sum(i, j, Aij), where + * A.*A' is the element-wise product of matrix + * A and its transpose. This measure is + * calculated if the \p mode argument is \c + * IGRAPH_RECIPROCITY_DEFAULT. + * + * + * Prior to igraph version 0.6, another measure was implemented, + * defined as the probability of mutual connection between a vertex + * pair if we know that there is a (possibly non-mutual) connection + * between them. In other words, (unordered) vertex pairs are + * classified into three groups: (1) disconnected, (2) + * non-reciprocally connected, (3) reciprocally connected. + * The result is the size of group (3), divided by the sum of group + * sizes (2)+(3). This measure is calculated if \p mode is \c + * IGRAPH_RECIPROCITY_RATIO. + * + * \param graph The graph object. + * \param res Pointer to an \c igraph_real_t which will contain the result. + * \param ignore_loops Whether to ignore loop edges. + * \param mode Type of reciprocity to calculate, possible values are + * \c IGRAPH_RECIPROCITY_DEFAULT and \c IGRAPH_RECIPROCITY_RATIO, + * please see their description above. + * \return Error code: + * \c IGRAPH_EINVAL: graph has no edges + * \c IGRAPH_ENOMEM: not enough memory for + * temporary data. + * + * Time complexity: O(|V|+|E|), |V| is the number of vertices, + * |E| is the number of edges. + * + * \example examples/simple/igraph_reciprocity.c + */ +int igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, + igraph_bool_t ignore_loops, + igraph_reciprocity_t mode) { + + igraph_integer_t nonrec = 0, rec = 0, loops = 0; + igraph_vector_t inneis, outneis; + long int i; + long int no_of_nodes = igraph_vcount(graph); + + if (mode != IGRAPH_RECIPROCITY_DEFAULT && + mode != IGRAPH_RECIPROCITY_RATIO) { + IGRAPH_ERROR("Invalid reciprocity type", IGRAPH_EINVAL); + } + + /* THIS IS AN EXIT HERE !!!!!!!!!!!!!! */ + if (!igraph_is_directed(graph)) { + *res = 1.0; + return 0; + } + + IGRAPH_VECTOR_INIT_FINALLY(&inneis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outneis, 0); + + for (i = 0; i < no_of_nodes; i++) { + long int ip, op; + igraph_neighbors(graph, &inneis, (igraph_integer_t) i, IGRAPH_IN); + igraph_neighbors(graph, &outneis, (igraph_integer_t) i, IGRAPH_OUT); + + ip = op = 0; + while (ip < igraph_vector_size(&inneis) && + op < igraph_vector_size(&outneis)) { + if (VECTOR(inneis)[ip] < VECTOR(outneis)[op]) { + nonrec += 1; + ip++; + } else if (VECTOR(inneis)[ip] > VECTOR(outneis)[op]) { + nonrec += 1; + op++; + } else { + + /* loop edge? */ + if (VECTOR(inneis)[ip] == i) { + loops += 1; + if (!ignore_loops) { + rec += 1; + } + } else { + rec += 1; + } + + ip++; + op++; + } + } + nonrec += (igraph_vector_size(&inneis) - ip) + + (igraph_vector_size(&outneis) - op); + } + + if (mode == IGRAPH_RECIPROCITY_DEFAULT) { + if (ignore_loops) { + *res = (igraph_real_t) rec / (igraph_ecount(graph) - loops); + } else { + *res = (igraph_real_t) rec / (igraph_ecount(graph)); + } + } else if (mode == IGRAPH_RECIPROCITY_RATIO) { + *res = (igraph_real_t) rec / (rec + nonrec); + } + + igraph_vector_destroy(&inneis); + igraph_vector_destroy(&outneis); + IGRAPH_FINALLY_CLEAN(2); + return 0; +} diff --git a/src/rigraph/core/properties/constraint.c b/src/rigraph/core/properties/constraint.c new file mode 100644 index 0000000..15e5e95 --- /dev/null +++ b/src/rigraph/core/properties/constraint.c @@ -0,0 +1,306 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_centrality.h" + +#include "igraph_interface.h" + +/** + * \function igraph_constraint + * \brief Burt's constraint scores. + * + * + * This function calculates Burt's constraint scores for the given + * vertices, also known as structural holes. + * + * + * Burt's constraint is higher if ego has less, or mutually stronger + * related (i.e. more redundant) contacts. Burt's measure of + * constraint, C[i], of vertex i's ego network V[i], is defined for + * directed and valued graphs, + *
+ * C[i] = sum( sum( (p[i,q] p[q,j])^2, q in V[i], q != i,j ), j in + * V[], j != i) + *
+ * for a graph of order (i.e. number of vertices) N, where proportional + * tie strengths are defined as + *
+ * p[i,j]=(a[i,j]+a[j,i]) / sum(a[i,k]+a[k,i], k in V[i], k != i), + *
+ * a[i,j] are elements of A and + * the latter being the graph adjacency matrix. For isolated vertices, + * constraint is undefined. + * + *
+ * Burt, R.S. (2004). Structural holes and good ideas. American + * Journal of Sociology 110, 349-399. + * + * + * The first R version of this function was contributed by Jeroen + * Bruggeman. + * \param graph A graph object. + * \param res Pointer to an initialized vector, the result will be + * stored here. The vector will be resized to have the + * appropriate size for holding the result. + * \param vids Vertex selector containing the vertices for which the + * constraint should be calculated. + * \param weights Vector giving the weights of the edges. If it is + * \c NULL then each edge is supposed to have the same weight. + * \return Error code. + * + * Time complexity: O(|V|+E|+n*d^2), n is the number of vertices for + * which the constraint is calculated and d is the average degree, |V| + * is the number of vertices, |E| the number of edges in the + * graph. If the weights argument is \c NULL then the time complexity + * is O(|V|+n*d^2). + */ +int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, + igraph_vs_t vids, const igraph_vector_t *weights) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_vit_t vit; + long int nodes_to_calc; + long int a, b, c, i, j, q, vsize, vsize2; + igraph_integer_t edge, from, to, edge2; + + igraph_vector_t contrib; + igraph_vector_t degree; + igraph_vector_t ineis_in, ineis_out, jneis_in, jneis_out; + + if (weights != 0 && igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid length of weight vector", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&contrib, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&ineis_in, 0); + IGRAPH_VECTOR_INIT_FINALLY(&ineis_out, 0); + IGRAPH_VECTOR_INIT_FINALLY(&jneis_in, 0); + IGRAPH_VECTOR_INIT_FINALLY(&jneis_out, 0); + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + nodes_to_calc = IGRAPH_VIT_SIZE(vit); + + if (weights == 0) { + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_ALL, IGRAPH_NO_LOOPS)); + } else { + for (a = 0; a < no_of_edges; a++) { + igraph_edge(graph, (igraph_integer_t) a, &from, &to); + if (from != to) { + VECTOR(degree)[(long int) from] += VECTOR(*weights)[a]; + VECTOR(degree)[(long int) to ] += VECTOR(*weights)[a]; + } + } + } + + IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); + igraph_vector_null(res); + + for (a = 0; a < nodes_to_calc; a++, IGRAPH_VIT_NEXT(vit)) { + i = IGRAPH_VIT_GET(vit); + + /* get neighbors of i */ + IGRAPH_CHECK(igraph_incident(graph, &ineis_in, (igraph_integer_t) i, + IGRAPH_IN)); + IGRAPH_CHECK(igraph_incident(graph, &ineis_out, (igraph_integer_t) i, + IGRAPH_OUT)); + + /* NaN for isolates */ + if (igraph_vector_size(&ineis_in) == 0 && + igraph_vector_size(&ineis_out) == 0) { + VECTOR(*res)[a] = IGRAPH_NAN; + } + + /* zero their contribution */ + vsize = igraph_vector_size(&ineis_in); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_in)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + VECTOR(contrib)[j] = 0.0; + } + vsize = igraph_vector_size(&ineis_out); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_out)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + VECTOR(contrib)[j] = 0.0; + } + + /* add the direct contributions, in-neighbors and out-neighbors */ + vsize = igraph_vector_size(&ineis_in); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_in)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + if (i != j) { /* excluding loops */ + if (weights) { + VECTOR(contrib)[j] += + VECTOR(*weights)[(long int)edge] / VECTOR(degree)[i]; + } else { + VECTOR(contrib)[j] += 1.0 / VECTOR(degree)[i]; + } + } + } + if (igraph_is_directed(graph)) { + vsize = igraph_vector_size(&ineis_out); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_out)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + if (i != j) { + if (weights) { + VECTOR(contrib)[j] += + VECTOR(*weights)[(long int)edge] / VECTOR(degree)[i]; + } else { + VECTOR(contrib)[j] += 1.0 / VECTOR(degree)[i]; + } + } + } + } + + /* add the indirect contributions, in-in, in-out, out-in, out-out */ + vsize = igraph_vector_size(&ineis_in); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_in)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + if (i == j) { + continue; + } + IGRAPH_CHECK(igraph_incident(graph, &jneis_in, (igraph_integer_t) j, + IGRAPH_IN)); + IGRAPH_CHECK(igraph_incident(graph, &jneis_out, (igraph_integer_t) j, + IGRAPH_OUT)); + vsize2 = igraph_vector_size(&jneis_in); + for (c = 0; c < vsize2; c++) { + edge2 = (igraph_integer_t) VECTOR(jneis_in)[c]; + q = (long int) IGRAPH_OTHER(graph, edge2, j); + if (j != q) { + if (weights) { + VECTOR(contrib)[q] += + VECTOR(*weights)[(long int)edge] * + VECTOR(*weights)[(long int)edge2] / + VECTOR(degree)[i] / VECTOR(degree)[j]; + } else { + VECTOR(contrib)[q] += 1 / VECTOR(degree)[i] / VECTOR(degree)[j]; + } + } + } + if (igraph_is_directed(graph)) { + vsize2 = igraph_vector_size(&jneis_out); + for (c = 0; c < vsize2; c++) { + edge2 = (igraph_integer_t) VECTOR(jneis_out)[c]; + q = (long int) IGRAPH_OTHER(graph, edge2, j); + if (j != q) { + if (weights) { + VECTOR(contrib)[q] += + VECTOR(*weights)[(long int)edge] * + VECTOR(*weights)[(long int)edge2] / + VECTOR(degree)[i] / VECTOR(degree)[j]; + } else { + VECTOR(contrib)[q] += 1 / VECTOR(degree)[i] / VECTOR(degree)[j]; + } + } + } + } + } + if (igraph_is_directed(graph)) { + vsize = igraph_vector_size(&ineis_out); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_out)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + if (i == j) { + continue; + } + IGRAPH_CHECK(igraph_incident(graph, &jneis_in, (igraph_integer_t) j, + IGRAPH_IN)); + IGRAPH_CHECK(igraph_incident(graph, &jneis_out, (igraph_integer_t) j, + IGRAPH_OUT)); + vsize2 = igraph_vector_size(&jneis_in); + for (c = 0; c < vsize2; c++) { + edge2 = (igraph_integer_t) VECTOR(jneis_in)[c]; + q = (long int) IGRAPH_OTHER(graph, edge2, j); + if (j != q) { + if (weights) { + VECTOR(contrib)[q] += + VECTOR(*weights)[(long int)edge] * + VECTOR(*weights)[(long int)edge2] / + VECTOR(degree)[i] / VECTOR(degree)[j]; + } else { + VECTOR(contrib)[q] += 1 / VECTOR(degree)[i] / VECTOR(degree)[j]; + } + } + } + vsize2 = igraph_vector_size(&jneis_out); + for (c = 0; c < vsize2; c++) { + edge2 = (igraph_integer_t) VECTOR(jneis_out)[c]; + q = (long int) IGRAPH_OTHER(graph, edge2, j); + if (j != q) { + if (weights) { + VECTOR(contrib)[q] += + VECTOR(*weights)[(long int)edge] * + VECTOR(*weights)[(long int)edge2] / + VECTOR(degree)[i] / VECTOR(degree)[j]; + } else { + VECTOR(contrib)[q] += 1 / VECTOR(degree)[i] / VECTOR(degree)[j]; + } + } + } + } + } + + /* squared sum of the contributions */ + vsize = igraph_vector_size(&ineis_in); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_in)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + if (i == j) { + continue; + } + VECTOR(*res)[a] += VECTOR(contrib)[j] * VECTOR(contrib)[j]; + VECTOR(contrib)[j] = 0.0; + } + if (igraph_is_directed(graph)) { + vsize = igraph_vector_size(&ineis_out); + for (b = 0; b < vsize; b++) { + edge = (igraph_integer_t) VECTOR(ineis_out)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); + if (i == j) { + continue; + } + VECTOR(*res)[a] += VECTOR(contrib)[j] * VECTOR(contrib)[j]; + VECTOR(contrib)[j] = 0.0; + } + } + } + + igraph_vit_destroy(&vit); + igraph_vector_destroy(&jneis_out); + igraph_vector_destroy(&jneis_in); + igraph_vector_destroy(&ineis_out); + igraph_vector_destroy(&ineis_in); + igraph_vector_destroy(°ree); + igraph_vector_destroy(&contrib); + IGRAPH_FINALLY_CLEAN(7); + + return 0; +} diff --git a/src/rigraph/core/properties/convergence_degree.c b/src/rigraph/core/properties/convergence_degree.c new file mode 100644 index 0000000..948d5b0 --- /dev/null +++ b/src/rigraph/core/properties/convergence_degree.c @@ -0,0 +1,208 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_centrality.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "../core/interruption.h" + +#include + +/** + * \function igraph_convergence_degree + * \brief Calculates the convergence degree of each edge in a graph. + * + * Let us define the input set of an edge (i, j) as the set of vertices where + * the shortest paths passing through (i, j) originate, and similarly, let us + * defined the output set of an edge (i, j) as the set of vertices where the + * shortest paths passing through (i, j) terminate. The convergence degree of + * an edge is defined as the normalized value of the difference between the + * size of the input set and the output set, i.e. the difference of them + * divided by the sum of them. Convergence degrees are in the range (-1, 1); a + * positive value indicates that the edge is \em convergent since the shortest + * paths passing through it originate from a larger set and terminate in a + * smaller set, while a negative value indicates that the edge is \em divergent + * since the paths originate from a small set and terminate in a larger set. + * + * + * Note that the convergence degree as defined above does not make sense in + * undirected graphs as there is no distinction between the input and output + * set. Therefore, for undirected graphs, the input and output sets of an edge + * are determined by orienting the edge arbitrarily while keeping the remaining + * edges undirected, and then taking the absolute value of the convergence + * degree. + * + * \param graph The input graph, it can be either directed or undirected. + * \param result Pointer to an initialized vector; the convergence degrees of + * each edge will be stored here. May be \c NULL if we are not interested in + * the exact convergence degrees. + * \param ins Pointer to an initialized vector; the size of the input set of + * each edge will be stored here. May be \c NULL if we are not interested in + * the sizes of the input sets. + * \param outs Pointer to an initialized vector; the size of the output set of + * each edge will be stored here. May be \c NULL if we are not interested in + * the sizes of the output sets. + * \return Error code. + * + * Time complexity: O(|V||E|), the number of vertices times the number of edges. + */ +int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, + igraph_vector_t *ins, igraph_vector_t *outs) { + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int i, j, k, n; + long int *geodist; + igraph_vector_int_t *eids; + igraph_vector_t *ins_p, *outs_p, ins_v, outs_v; + igraph_dqueue_t q; + igraph_inclist_t inclist; + igraph_bool_t directed = igraph_is_directed(graph); + + if (result != 0) { + IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges)); + } + IGRAPH_CHECK(igraph_dqueue_init(&q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &q); + + if (ins == 0) { + ins_p = &ins_v; + IGRAPH_VECTOR_INIT_FINALLY(ins_p, no_of_edges); + } else { + ins_p = ins; + IGRAPH_CHECK(igraph_vector_resize(ins_p, no_of_edges)); + igraph_vector_null(ins_p); + } + + if (outs == 0) { + outs_p = &outs_v; + IGRAPH_VECTOR_INIT_FINALLY(outs_p, no_of_edges); + } else { + outs_p = outs; + IGRAPH_CHECK(igraph_vector_resize(outs_p, no_of_edges)); + igraph_vector_null(outs_p); + } + + geodist = IGRAPH_CALLOC(no_of_nodes, long int); + if (geodist == 0) { + IGRAPH_ERROR("Cannot calculate convergence degrees", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, geodist); + + /* Collect shortest paths originating from/to every node to correctly + * determine input and output field sizes */ + for (k = 0; k < (directed ? 2 : 1); k++) { + igraph_neimode_t neimode = (k == 0) ? IGRAPH_OUT : IGRAPH_IN; + igraph_real_t *vec; + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, neimode, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + vec = (k == 0) ? VECTOR(*ins_p) : VECTOR(*outs_p); + for (i = 0; i < no_of_nodes; i++) { + igraph_dqueue_clear(&q); + memset(geodist, 0, sizeof(long int) * (size_t) no_of_nodes); + geodist[i] = 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, i)); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0.0)); + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + long int actdist = (long int) igraph_dqueue_pop(&q); + IGRAPH_ALLOW_INTERRUPTION(); + eids = igraph_inclist_get(&inclist, actnode); + n = igraph_vector_int_size(eids); + for (j = 0; j < n; j++) { + long int neighbor = IGRAPH_OTHER(graph, VECTOR(*eids)[j], actnode); + if (geodist[neighbor] != 0) { + /* we've already seen this node, another shortest path? */ + if (geodist[neighbor] - 1 == actdist + 1) { + /* Since this edge is in the BFS tree rooted at i, we must + * increase either the size of the infield or the outfield */ + if (!directed) { + if (actnode < neighbor) { + VECTOR(*ins_p)[(long int)VECTOR(*eids)[j]] += 1; + } else { + VECTOR(*outs_p)[(long int)VECTOR(*eids)[j]] += 1; + } + } else { + vec[(long int)VECTOR(*eids)[j]] += 1; + } + } else if (geodist[neighbor] - 1 < actdist + 1) { + continue; + } + } else { + /* we haven't seen this node yet */ + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + /* Since this edge is in the BFS tree rooted at i, we must + * increase either the size of the infield or the outfield */ + if (!directed) { + if (actnode < neighbor) { + VECTOR(*ins_p)[(long int)VECTOR(*eids)[j]] += 1; + } else { + VECTOR(*outs_p)[(long int)VECTOR(*eids)[j]] += 1; + } + } else { + vec[(long int)VECTOR(*eids)[j]] += 1; + } + geodist[neighbor] = actdist + 2; + } + } + } + } + + igraph_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(1); + } + + if (result != 0) { + for (i = 0; i < no_of_edges; i++) { + VECTOR(*result)[i] = (VECTOR(*ins_p)[i] - VECTOR(*outs_p)[i]) / + (VECTOR(*ins_p)[i] + VECTOR(*outs_p)[i]); + } + + if (!directed) { + for (i = 0; i < no_of_edges; i++) { + if (VECTOR(*result)[i] < 0) { + VECTOR(*result)[i] = -VECTOR(*result)[i]; + } + } + } + } + + if (ins == 0) { + igraph_vector_destroy(ins_p); + IGRAPH_FINALLY_CLEAN(1); + } + if (outs == 0) { + igraph_vector_destroy(outs_p); + IGRAPH_FINALLY_CLEAN(1); + } + + IGRAPH_FREE(geodist); + igraph_dqueue_destroy(&q); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} diff --git a/src/rigraph/core/properties/dag.c b/src/rigraph/core/properties/dag.c new file mode 100644 index 0000000..6963209 --- /dev/null +++ b/src/rigraph/core/properties/dag.c @@ -0,0 +1,296 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_topology.h" + +#include "igraph_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_stack.h" + +/** + * \function igraph_topological_sorting + * \brief Calculate a possible topological sorting of the graph. + * + * + * A topological sorting of a directed acyclic graph (DAG) is a linear ordering + * of its vertices where each vertex comes before all nodes to which it has + * edges. Every DAG has at least one topological sort, and may have many. + * This function returns one possible topological sort among them. If the + * graph contains any cycles that are not self-loops, an error is raised. + * + * \param graph The input graph. + * \param res Pointer to a vector, the result will be stored here. + * It will be resized if needed. + * \param mode Specifies how to use the direction of the edges. + * For \c IGRAPH_OUT, the sorting order ensures that each vertex comes + * before all vertices to which it has edges, so vertices with no incoming + * edges go first. For \c IGRAPH_IN, it is quite the opposite: each + * vertex comes before all vertices from which it receives edges. Vertices + * with no outgoing edges go first. + * \return Error code. + * + * Time complexity: O(|V|+|E|), where |V| and |E| are the number of + * vertices and edges in the original input graph. + * + * \sa \ref igraph_is_dag() if you are only interested in whether a given + * graph is a DAG or not, or \ref igraph_feedback_arc_set() to find a + * set of edges whose removal makes the graph acyclic. + * + * \example examples/simple/igraph_topological_sorting.c + */ +int igraph_topological_sorting(const igraph_t* graph, igraph_vector_t *res, + igraph_neimode_t mode) { + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t degrees, neis; + igraph_dqueue_t sources; + igraph_neimode_t deg_mode; + long int node, i, j; + + if (mode == IGRAPH_ALL || !igraph_is_directed(graph)) { + IGRAPH_ERROR("Topological sorting does not make sense for undirected graphs", IGRAPH_EINVAL); + } else if (mode == IGRAPH_OUT) { + deg_mode = IGRAPH_IN; + } else if (mode == IGRAPH_IN) { + deg_mode = IGRAPH_OUT; + } else { + IGRAPH_ERROR("Invalid mode", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_init(&sources, 0)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &sources); + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), deg_mode, 0)); + + igraph_vector_clear(res); + + /* Do we have nodes with no incoming vertices? */ + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(degrees)[i] == 0) { + IGRAPH_CHECK(igraph_dqueue_push(&sources, i)); + } + } + + /* Take all nodes with no incoming vertices and remove them */ + while (!igraph_dqueue_empty(&sources)) { + igraph_real_t tmp = igraph_dqueue_pop(&sources); node = (long) tmp; + /* Add the node to the result vector */ + igraph_vector_push_back(res, node); + /* Exclude the node from further source searches */ + VECTOR(degrees)[node] = -1; + /* Get the neighbors and decrease their degrees by one */ + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) node, mode)); + j = igraph_vector_size(&neis); + for (i = 0; i < j; i++) { + VECTOR(degrees)[(long)VECTOR(neis)[i]]--; + if (VECTOR(degrees)[(long)VECTOR(neis)[i]] == 0) { + IGRAPH_CHECK(igraph_dqueue_push(&sources, VECTOR(neis)[i])); + } + } + } + + if (igraph_vector_size(res) < no_of_nodes) { + IGRAPH_ERROR("The graph has cycles; topological sorting is only possible in acyclic graphs", IGRAPH_EINVAL); + } + + igraph_vector_destroy(°rees); + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&sources); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} + +/** + * \function igraph_is_dag + * \brief Checks whether a graph is a directed acyclic graph (DAG). + * + * + * A directed acyclic graph (DAG) is a directed graph with no cycles. + * + * \param graph The input graph. + * \param res Pointer to a boolean constant, the result + * is stored here. + * \return Error code. + * + * Time complexity: O(|V|+|E|), where |V| and |E| are the number of + * vertices and edges in the original input graph. + * + * \sa \ref igraph_topological_sorting() to get a possible topological + * sorting of a DAG. + */ +int igraph_is_dag(const igraph_t* graph, igraph_bool_t *res) { + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t degrees, neis; + igraph_dqueue_t sources; + long int node, i, j, nei, vertices_left; + + if (!igraph_is_directed(graph)) { + *res = 0; + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_init(&sources, 0)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &sources); + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_OUT, 1)); + + vertices_left = no_of_nodes; + + /* Do we have nodes with no incoming edges? */ + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(degrees)[i] == 0) { + IGRAPH_CHECK(igraph_dqueue_push(&sources, i)); + } + } + + /* Take all nodes with no incoming edges and remove them */ + while (!igraph_dqueue_empty(&sources)) { + igraph_real_t tmp = igraph_dqueue_pop(&sources); node = (long) tmp; + /* Exclude the node from further source searches */ + VECTOR(degrees)[node] = -1; + vertices_left--; + /* Get the neighbors and decrease their degrees by one */ + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) node, + IGRAPH_IN)); + j = igraph_vector_size(&neis); + for (i = 0; i < j; i++) { + nei = (long)VECTOR(neis)[i]; + if (nei == node) { + continue; + } + VECTOR(degrees)[nei]--; + if (VECTOR(degrees)[nei] == 0) { + IGRAPH_CHECK(igraph_dqueue_push(&sources, nei)); + } + } + } + + *res = (vertices_left == 0); + if (vertices_left < 0) { + IGRAPH_WARNING("vertices_left < 0 in igraph_is_dag, possible bug"); + } + + igraph_vector_destroy(°rees); + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&sources); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/* Create the transitive closure of a tree graph. + This is fairly simple, we just collect all ancestors of a vertex + using a depth-first search. + */ +int igraph_transitive_closure_dag(const igraph_t *graph, + igraph_t *closure) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t deg; + igraph_vector_t new_edges; + igraph_vector_t ancestors; + long int root; + igraph_vector_t neighbors; + igraph_stack_t path; + igraph_vector_bool_t done; + + if (!igraph_is_directed(graph)) { + IGRAPH_ERROR("Tree transitive closure of a directed graph", + IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&new_edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&ancestors, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neighbors, 0); + IGRAPH_CHECK(igraph_stack_init(&path, 0)); + IGRAPH_FINALLY(igraph_stack_destroy, &path); + IGRAPH_CHECK(igraph_vector_bool_init(&done, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &done); + + IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), + IGRAPH_OUT, IGRAPH_LOOPS)); + +#define STAR (-1) + + for (root = 0; root < no_of_nodes; root++) { + if (VECTOR(deg)[root] != 0) { + continue; + } + IGRAPH_CHECK(igraph_stack_push(&path, root)); + + while (!igraph_stack_empty(&path)) { + long int node = (long int) igraph_stack_top(&path); + if (node == STAR) { + /* Leaving a node */ + long int j, n; + igraph_stack_pop(&path); + node = (long int) igraph_stack_pop(&path); + if (!VECTOR(done)[node]) { + igraph_vector_pop_back(&ancestors); + VECTOR(done)[node] = 1; + } + n = igraph_vector_size(&ancestors); + for (j = 0; j < n; j++) { + IGRAPH_CHECK(igraph_vector_push_back(&new_edges, node)); + IGRAPH_CHECK(igraph_vector_push_back(&new_edges, + VECTOR(ancestors)[j])); + } + } else { + /* Getting into a node */ + long int n, j; + if (!VECTOR(done)[node]) { + IGRAPH_CHECK(igraph_vector_push_back(&ancestors, node)); + } + IGRAPH_CHECK(igraph_neighbors(graph, &neighbors, + (igraph_integer_t) node, IGRAPH_IN)); + n = igraph_vector_size(&neighbors); + IGRAPH_CHECK(igraph_stack_push(&path, STAR)); + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(neighbors)[j]; + IGRAPH_CHECK(igraph_stack_push(&path, nei)); + } + } + } + } + +#undef STAR + + igraph_vector_bool_destroy(&done); + igraph_stack_destroy(&path); + igraph_vector_destroy(&neighbors); + igraph_vector_destroy(&ancestors); + igraph_vector_destroy(°); + IGRAPH_FINALLY_CLEAN(5); + + IGRAPH_CHECK(igraph_create(closure, &new_edges, (igraph_integer_t)no_of_nodes, + IGRAPH_DIRECTED)); + + igraph_vector_destroy(&new_edges); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} diff --git a/src/rigraph/core/properties/degrees.c b/src/rigraph/core/properties/degrees.c new file mode 100644 index 0000000..1bfa21f --- /dev/null +++ b/src/rigraph/core/properties/degrees.c @@ -0,0 +1,500 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_structural.h" + +#include "igraph_interface.h" + +/** + * \function igraph_maxdegree + * \brief The maximum degree in a graph (or set of vertices). + * + * + * The largest in-, out- or total degree of the specified vertices is + * calculated. If the graph has no vertices, or \p vids is empty, + * 0 is returned, as this is the smallest possible value for degrees. + * + * \param graph The input graph. + * \param res Pointer to an integer (\c igraph_integer_t), the result + * will be stored here. + * \param vids Vector giving the vertex IDs for which the maximum degree will + * be calculated. + * \param mode Defines the type of the degree. + * \c IGRAPH_OUT, out-degree, + * \c IGRAPH_IN, in-degree, + * \c IGRAPH_ALL, total degree (sum of the + * in- and out-degree). + * This parameter is ignored for undirected graphs. + * \param loops Boolean, gives whether the self-loops should be + * counted. + * \return Error code: + * \c IGRAPH_EINVVID: invalid vertex id. + * \c IGRAPH_EINVMODE: invalid mode argument. + * + * Time complexity: O(v) if loops is TRUE, and O(v*d) otherwise. v is the number + * of vertices for which the degree will be calculated, and d is their + * (average) degree. + */ +int igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res, + igraph_vs_t vids, igraph_neimode_t mode, + igraph_bool_t loops) { + + igraph_vector_t tmp; + + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + + IGRAPH_CHECK(igraph_degree(graph, &tmp, vids, mode, loops)); + if (igraph_vector_size(&tmp) == 0) { + *res = 0; + } else { + *res = (igraph_integer_t) igraph_vector_max(&tmp); + } + + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph, + igraph_vs_t vids, + igraph_neimode_t mode, + igraph_neimode_t neighbor_degree_mode, + igraph_vector_t *knn, + igraph_vector_t *knnk, + const igraph_vector_t *weights) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t neis, edge_neis; + long int i, j, no_vids; + igraph_vit_t vit; + igraph_vector_t my_knn_v, *my_knn = knn; + igraph_vector_t strength, deg; + igraph_integer_t maxdeg; + igraph_vector_t deghist; + igraph_real_t mynan = IGRAPH_NAN; + + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector size", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + no_vids = IGRAPH_VIT_SIZE(vit); + + if (!knn) { + IGRAPH_VECTOR_INIT_FINALLY(&my_knn_v, no_vids); + my_knn = &my_knn_v; + } else { + IGRAPH_CHECK(igraph_vector_resize(knn, no_vids)); + } + + /* Get degree of neighbours */ + IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), + neighbor_degree_mode, IGRAPH_LOOPS)); + IGRAPH_VECTOR_INIT_FINALLY(&strength, no_of_nodes); + + /* Get strength of all nodes */ + IGRAPH_CHECK(igraph_strength(graph, &strength, igraph_vss_all(), + mode, IGRAPH_LOOPS, weights)); + + /* Get maximum degree for initialization */ + IGRAPH_CHECK(igraph_maxdegree(graph, &maxdeg, igraph_vss_all(), + mode, IGRAPH_LOOPS)); + IGRAPH_VECTOR_INIT_FINALLY(&neis, (long int)maxdeg); + IGRAPH_VECTOR_INIT_FINALLY(&edge_neis, (long int)maxdeg); + igraph_vector_resize(&neis, 0); + igraph_vector_resize(&edge_neis, 0); + + if (knnk) { + IGRAPH_CHECK(igraph_vector_resize(knnk, (long int)maxdeg)); + igraph_vector_null(knnk); + IGRAPH_VECTOR_INIT_FINALLY(°hist, (long int)maxdeg); + } + + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_real_t sum = 0.0; + long int v = IGRAPH_VIT_GET(vit); + long int nv; + igraph_real_t str = VECTOR(strength)[v]; + /* Get neighbours and incident edges */ + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, mode)); + IGRAPH_CHECK(igraph_incident(graph, &edge_neis, (igraph_integer_t) v, mode)); + nv = igraph_vector_size(&neis); + for (j = 0; j < nv; j++) { + long int nei = (long int) VECTOR(neis)[j]; + long int e = (long int) VECTOR(edge_neis)[j]; + double w = VECTOR(*weights)[e]; + sum += w * VECTOR(deg)[nei]; + } + if (str != 0.0) { + VECTOR(*my_knn)[i] = sum / str; + } else { + VECTOR(*my_knn)[i] = mynan; + } + if (knnk && nv > 0) { + VECTOR(*knnk)[nv - 1] += VECTOR(*my_knn)[i]; + VECTOR(deghist)[nv - 1] += 1; + } + } + + igraph_vector_destroy(&edge_neis); + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(2); + + if (knnk) { + for (i = 0; i < maxdeg; i++) { + igraph_real_t dh = VECTOR(deghist)[i]; + if (dh != 0) { + VECTOR(*knnk)[i] /= dh; + } else { + VECTOR(*knnk)[i] = mynan; + } + } + + igraph_vector_destroy(°hist); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_destroy(&strength); + igraph_vector_destroy(°); + IGRAPH_FINALLY_CLEAN(2); + + if (!knn) { + igraph_vector_destroy(&my_knn_v); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +/** + * \function igraph_avg_nearest_neighbor_degree + * Average neighbor degree. + * + * Calculates the average degree of the neighbors for each vertex (\p knn), and + * optionally, the same quantity as a function of the vertex degree (\p knnk). + * + * + * For isolated vertices \p knn is set to NaN. + * The same is done in \p knnk for vertex degrees that + * don't appear in the graph. + * + * + * The weighted version computes a weighted average of the neighbor degrees as + * + * k_nn_u = 1/s_u sum_v w_uv k_v, + * + * where s_u = sum_v w_uv is the sum of the incident edge weights + * of vertex \c u, i.e. its strength. + * The sum runs over the neighbors \c v of vertex \c u + * as indicated by \p mode. w_uv denotes the weighted adjacency matrix + * and k_v is the neighbors' degree, specified by \p neighbor_degree_mode. + * + * + * Reference: + * A. Barrat, M. Barthélemy, R. Pastor-Satorras, and A. Vespignani, + * The architecture of complex weighted networks, + * Proc. Natl. Acad. Sci. USA 101, 3747 (2004). + * https://dx.doi.org/10.1073/pnas.0400087101 + * + * \param graph The input graph. It may be directed. + * \param vids The vertices for which the calculation is performed. + * \param mode The type of neighbors to consider in directed graphs. + * \c IGRAPH_OUT considers out-neighbors, \c IGRAPH_IN in-neighbors + * and \c IGRAPH_ALL ignores edge directions. + * \param neighbor_degree_mode The type of degree to average in directed graphs. + * \c IGRAPH_OUT averages out-degrees, \c IGRAPH_IN averages in-degrees + * and \c IGRAPH_ALL ignores edge directions for the degree calculation. + * \param vids The vertices for which the calculation is performed. + * \param knn Pointer to an initialized vector, the result will be + * stored here. It will be resized as needed. Supply a \c NULL pointer + * here, if you only want to calculate \c knnk. + * \param knnk Pointer to an initialized vector, the average + * neighbor degree as a function of the vertex degree is stored + * here. The first (zeroth) element is for degree one vertices, + * etc. Supply a \c NULL pointer here if you don't want to calculate + * this. + * \param weights Optional edge weights. Supply a null pointer here + * for the non-weighted version. + * + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + * + * \example examples/simple/igraph_knn.c + */ +int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, + igraph_vs_t vids, + igraph_neimode_t mode, + igraph_neimode_t neighbor_degree_mode, + igraph_vector_t *knn, + igraph_vector_t *knnk, + const igraph_vector_t *weights) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t neis; + long int i, j, no_vids; + igraph_vit_t vit; + igraph_vector_t my_knn_v, *my_knn = knn; + igraph_vector_t deg; + igraph_integer_t maxdeg; + igraph_vector_t deghist; + igraph_real_t mynan = IGRAPH_NAN; + igraph_bool_t simple; + + IGRAPH_CHECK(igraph_is_simple(graph, &simple)); + if (!simple) { + IGRAPH_ERROR("Average nearest neighbor degree works only with " + "simple graphs", IGRAPH_EINVAL); + } + + if (weights) { + return igraph_i_avg_nearest_neighbor_degree_weighted(graph, vids, + mode, neighbor_degree_mode, knn, knnk, weights); + } + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + no_vids = IGRAPH_VIT_SIZE(vit); + + if (!knn) { + IGRAPH_VECTOR_INIT_FINALLY(&my_knn_v, no_vids); + my_knn = &my_knn_v; + } else { + IGRAPH_CHECK(igraph_vector_resize(knn, no_vids)); + } + + IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), + neighbor_degree_mode, IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_maxdegree(graph, &maxdeg, igraph_vss_all(), mode, IGRAPH_LOOPS)); + IGRAPH_VECTOR_INIT_FINALLY(&neis, maxdeg); + igraph_vector_resize(&neis, 0); + + if (knnk) { + IGRAPH_CHECK(igraph_vector_resize(knnk, (long int)maxdeg)); + igraph_vector_null(knnk); + IGRAPH_VECTOR_INIT_FINALLY(°hist, (long int)maxdeg); + } + + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_real_t sum = 0.0; + long int v = IGRAPH_VIT_GET(vit); + long int nv; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, mode)); + nv = igraph_vector_size(&neis); + for (j = 0; j < nv; j++) { + long int nei = (long int) VECTOR(neis)[j]; + sum += VECTOR(deg)[nei]; + } + if (nv != 0) { + VECTOR(*my_knn)[i] = sum / nv; + } else { + VECTOR(*my_knn)[i] = mynan; + } + if (knnk && nv > 0) { + VECTOR(*knnk)[nv - 1] += VECTOR(*my_knn)[i]; + VECTOR(deghist)[nv - 1] += 1; + } + } + + if (knnk) { + for (i = 0; i < maxdeg; i++) { + long int dh = (long int) VECTOR(deghist)[i]; + if (dh != 0) { + VECTOR(*knnk)[i] /= dh; + } else { + VECTOR(*knnk)[i] = mynan; + } + } + igraph_vector_destroy(°hist); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_destroy(&neis); + igraph_vector_destroy(°); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(3); + + if (!knn) { + igraph_vector_destroy(&my_knn_v); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_strength + * Strength of the vertices, weighted vertex degree in other words. + * + * In a weighted network the strength of a vertex is the sum of the + * weights of all incident edges. In a non-weighted network this is + * exactly the vertex degree. + * \param graph The input graph. + * \param res Pointer to an initialized vector, the result is stored + * here. It will be resized as needed. + * \param vids The vertices for which the calculation is performed. + * \param mode Gives whether to count only outgoing (\c IGRAPH_OUT), + * incoming (\c IGRAPH_IN) edges or both (\c IGRAPH_ALL). + * \param loops A logical scalar, whether to count loop edges as well. + * \param weights A vector giving the edge weights. If this is a NULL + * pointer, then \ref igraph_degree() is called to perform the + * calculation. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number vertices and + * edges. + * + * \sa \ref igraph_degree() for the traditional, non-weighted version. + */ +int igraph_strength(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_neimode_t mode, + igraph_bool_t loops, const igraph_vector_t *weights) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vit_t vit; + long int no_vids; + igraph_vector_t neis; + long int i; + + if (!weights) { + return igraph_degree(graph, res, vids, mode, loops); + } + + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + no_vids = IGRAPH_VIT_SIZE(vit); + + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_vector_reserve(&neis, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(res, no_vids)); + igraph_vector_null(res); + + if (loops) { + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + long int vid = IGRAPH_VIT_GET(vit); + long int j, n; + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) vid, mode)); + n = igraph_vector_size(&neis); + for (j = 0; j < n; j++) { + long int edge = (long int) VECTOR(neis)[j]; + VECTOR(*res)[i] += VECTOR(*weights)[edge]; + } + } + } else { + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + long int vid = IGRAPH_VIT_GET(vit); + long int j, n; + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) vid, mode)); + n = igraph_vector_size(&neis); + for (j = 0; j < n; j++) { + long int edge = (long int) VECTOR(neis)[j]; + long int from = IGRAPH_FROM(graph, edge); + long int to = IGRAPH_TO(graph, edge); + if (from != to) { + VECTOR(*res)[i] += VECTOR(*weights)[edge]; + } + } + } + } + + igraph_vit_destroy(&vit); + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + + +/** + * \function igraph_sort_vertex_ids_by_degree + * \brief Calculate a list of vertex ids sorted by degree of the corresponding vertex. + * + * The list of vertex ids is returned in a vector that is sorted + * in ascending or descending order of vertex degree. + * + * \param graph The input graph. + * \param outvids Pointer to an initialized vector that will be + * resized and will contain the ordered vertex ids. + * \param vids Input vertex selector of vertex ids to include in + * calculation. + * \param mode Defines the type of the degree. + * \c IGRAPH_OUT, out-degree, + * \c IGRAPH_IN, in-degree, + * \c IGRAPH_ALL, total degree (sum of the + * in- and out-degree). + * This parameter is ignored for undirected graphs. + * \param loops Boolean, gives whether the self-loops should be + * counted. + * \param order Specifies whether the ordering should be ascending + * (\c IGRAPH_ASCENDING) or descending (\c IGRAPH_DESCENDING). + * \param only_indices If true, then return a sorted list of indices + * into a vector corresponding to \c vids, rather than a list + * of vertex ids. This parameter is ignored if \c vids is set + * to all vertices via igraph_vs_all() or igraph_vss_all(), + * because in this case the indices and vertex ids are the + * same. + * \return Error code: + * \c IGRAPH_EINVVID: invalid vertex id. + * \c IGRAPH_EINVMODE: invalid mode argument. + * + */ +int igraph_sort_vertex_ids_by_degree(const igraph_t *graph, + igraph_vector_t *outvids, + igraph_vs_t vids, + igraph_neimode_t mode, + igraph_bool_t loops, + igraph_order_t order, + igraph_bool_t only_indices) { + long int i; + igraph_vector_t degrees, vs_vec; + IGRAPH_VECTOR_INIT_FINALLY(°rees, 0); + IGRAPH_CHECK(igraph_degree(graph, °rees, vids, mode, loops)); + IGRAPH_CHECK((int) igraph_vector_qsort_ind(°rees, outvids, + order == IGRAPH_DESCENDING)); + if (only_indices || igraph_vs_is_all(&vids) ) { + igraph_vector_destroy(°rees); + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_VECTOR_INIT_FINALLY(&vs_vec, 0); + IGRAPH_CHECK(igraph_vs_as_vector(graph, vids, &vs_vec)); + for (i = 0; i < igraph_vector_size(outvids); i++) { + VECTOR(*outvids)[i] = VECTOR(vs_vec)[(long int)VECTOR(*outvids)[i]]; + } + igraph_vector_destroy(&vs_vec); + igraph_vector_destroy(°rees); + IGRAPH_FINALLY_CLEAN(2); + } + return 0; +} diff --git a/src/rigraph/core/properties/girth.c b/src/rigraph/core/properties/girth.c new file mode 100644 index 0000000..4a732a0 --- /dev/null +++ b/src/rigraph/core/properties/girth.c @@ -0,0 +1,208 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_structural.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" + +#include "../core/interruption.h" + +#include + +/** + * \function igraph_girth + * \brief The girth of a graph is the length of the shortest cycle in it. + * + * + * The current implementation works for undirected graphs only, + * directed graphs are treated as undirected graphs. Self-loops and + * multiple edges are ignored. + * + * + * For graphs that contain no cycles, and only for such graphs, + * zero is returned. Note that in some applications, it is customary + * to define the girth of acyclic graphs to be infinity. However, infinity + * is not representable as an \c igraph_integer_t, therefore zero is used + * for this case. + * + * + * This implementation is based on Alon Itai and Michael Rodeh: + * Finding a minimum circuit in a graph + * \emb Proceedings of the ninth annual ACM symposium on Theory of + * computing \eme, 1-10, 1977. The first implementation of this + * function was done by Keith Briggs, thanks Keith. + * \param graph The input graph. + * \param girth Pointer to an integer, if not \c NULL then the result + * will be stored here. + * \param circle Pointer to an initialized vector, the vertex ids in + * the shortest circle will be stored here. If \c NULL then it is + * ignored. + * \return Error code. + * + * Time complexity: O((|V|+|E|)^2), |V| is the number of vertices, |E| + * is the number of edges in the general case. If the graph has no + * cycles at all then the function needs O(|V|+|E|) time to realize + * this and then it stops. + * + * \example examples/simple/igraph_girth.c + */ +int igraph_girth(const igraph_t *graph, igraph_integer_t *girth, + igraph_vector_t *circle) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_t q; + igraph_lazy_adjlist_t adjlist; + long int mincirc = LONG_MAX, minvertex = 0; + long int node; + igraph_bool_t triangle = 0; + igraph_vector_int_t *neis; + igraph_vector_long_t level; + long int stoplevel = no_of_nodes + 1; + igraph_bool_t anycircle = 0; + long int t1 = 0, t2 = 0; + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + IGRAPH_CHECK(igraph_vector_long_init(&level, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &level); + + for (node = 0; !triangle && node < no_of_nodes; node++) { + + /* Are there circles in this graph at all? */ + if (node == 1 && anycircle == 0) { + igraph_bool_t conn; + IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); + if (conn) { + /* No, there are none */ + break; + } + } + + anycircle = 0; + igraph_dqueue_clear(&q); + igraph_vector_long_null(&level); + IGRAPH_CHECK(igraph_dqueue_push(&q, node)); + VECTOR(level)[node] = 1; + + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + long int actlevel = VECTOR(level)[actnode]; + long int i, n; + + if (actlevel >= stoplevel) { + break; + } + + neis = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) actnode); + n = igraph_vector_int_size(neis); + for (i = 0; i < n; i++) { + long int nei = (long int) VECTOR(*neis)[i]; + long int neilevel = VECTOR(level)[nei]; + if (neilevel != 0) { + if (neilevel == actlevel - 1) { + continue; + } else { + /* found circle */ + stoplevel = neilevel; + anycircle = 1; + if (actlevel < mincirc) { + /* Is it a minimum circle? */ + mincirc = actlevel + neilevel - 1; + minvertex = node; + t1 = actnode; t2 = nei; + if (neilevel == 2) { + /* Is it a triangle? */ + triangle = 1; + } + } + if (neilevel == actlevel) { + break; + } + } + } else { + igraph_dqueue_push(&q, nei); + VECTOR(level)[nei] = actlevel + 1; + } + } + + } /* while q !empty */ + } /* node */ + + if (girth) { + if (mincirc == LONG_MAX) { + *girth = mincirc = 0; + } else { + *girth = (igraph_integer_t) mincirc; + } + } + + /* Store the actual circle, if needed */ + if (circle) { + IGRAPH_CHECK(igraph_vector_resize(circle, mincirc)); + if (mincirc != 0) { + long int i, n, idx = 0; + igraph_dqueue_clear(&q); + igraph_vector_long_null(&level); /* used for father pointers */ +#define FATHER(x) (VECTOR(level)[(x)]) + IGRAPH_CHECK(igraph_dqueue_push(&q, minvertex)); + FATHER(minvertex) = minvertex; + while (FATHER(t1) == 0 || FATHER(t2) == 0) { + long int actnode = (long int) igraph_dqueue_pop(&q); + neis = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) actnode); + n = igraph_vector_int_size(neis); + for (i = 0; i < n; i++) { + long int nei = (long int) VECTOR(*neis)[i]; + if (FATHER(nei) == 0) { + FATHER(nei) = actnode + 1; + igraph_dqueue_push(&q, nei); + } + } + } /* while q !empty */ + /* Ok, now use FATHER to create the path */ + while (t1 != minvertex) { + VECTOR(*circle)[idx++] = t1; + t1 = FATHER(t1) - 1; + } + VECTOR(*circle)[idx] = minvertex; + idx = mincirc - 1; + while (t2 != minvertex) { + VECTOR(*circle)[idx--] = t2; + t2 = FATHER(t2) - 1; + } + } /* anycircle */ + } /* circle */ +#undef FATHER + + igraph_vector_long_destroy(&level); + igraph_dqueue_destroy(&q); + igraph_lazy_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(3); + + return 0; +} diff --git a/src/rigraph/core/properties/loops.c b/src/rigraph/core/properties/loops.c new file mode 100644 index 0000000..7728d57 --- /dev/null +++ b/src/rigraph/core/properties/loops.c @@ -0,0 +1,95 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_structural.h" + +#include "igraph_interface.h" + +/** + * \function igraph_has_loop + * \brief Returns whether the graph has at least one loop edge. + * + * + * A loop edge is an edge from a vertex to itself. + * \param graph The input graph. + * \param res Pointer to an initialized boolean vector for storing the result. + * + * \sa \ref igraph_simplify() to get rid of loop edges. + * + * Time complexity: O(e), the number of edges to check. + * + * \example examples/simple/igraph_has_loop.c + */ +int igraph_has_loop(const igraph_t *graph, igraph_bool_t *res) { + long int i, m = igraph_ecount(graph); + + *res = 0; + + for (i = 0; i < m; i++) { + if (IGRAPH_FROM(graph, i) == IGRAPH_TO(graph, i)) { + *res = 1; + break; + } + } + + return 0; +} + +/** + * \function igraph_is_loop + * \brief Find the loop edges in a graph. + * + * + * A loop edge is an edge from a vertex to itself. + * \param graph The input graph. + * \param res Pointer to an initialized boolean vector for storing the result, + * it will be resized as needed. + * \param es The edges to check, for all edges supply \ref igraph_ess_all() here. + * \return Error code. + * + * \sa \ref igraph_simplify() to get rid of loop edges. + * + * Time complexity: O(e), the number of edges to check. + * + * \example examples/simple/igraph_is_loop.c + */ +int igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, + igraph_es_t es) { + igraph_eit_t eit; + long int i; + + IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_CHECK(igraph_vector_bool_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (i = 0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { + long int e = IGRAPH_EIT_GET(eit); + VECTOR(*res)[i] = (IGRAPH_FROM(graph, e) == IGRAPH_TO(graph, e)) ? 1 : 0; + } + + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} diff --git a/src/rigraph/core/properties/multiplicity.c b/src/rigraph/core/properties/multiplicity.c new file mode 100644 index 0000000..20c8304 --- /dev/null +++ b/src/rigraph/core/properties/multiplicity.c @@ -0,0 +1,356 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_structural.h" + +#include "igraph_adjlist.h" +#include "igraph_interface.h" + +/** + * \function igraph_is_simple + * \brief Decides whether the input graph is a simple graph. + * + * + * A graph is a simple graph if it does not contain loop edges and + * multiple edges. + * + * \param graph The input graph. + * \param res Pointer to a boolean constant, the result + * is stored here. + * \return Error code. + * + * \sa \ref igraph_is_loop() and \ref igraph_is_multiple() to + * find the loops and multiple edges, \ref igraph_simplify() to + * get rid of them, or \ref igraph_has_multiple() to decide whether + * there is at least one multiple edge. + * + * Time complexity: O(|V|+|E|). + */ +int igraph_is_simple(const igraph_t *graph, igraph_bool_t *res) { + long int vc = igraph_vcount(graph); + long int ec = igraph_ecount(graph); + + if (vc == 0 || ec == 0) { + *res = 1; + } else { + igraph_vector_t neis; + long int i, j, n; + igraph_bool_t found = 0; + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + for (i = 0; i < vc; i++) { + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, IGRAPH_OUT)); + n = igraph_vector_size(&neis); + for (j = 0; j < n; j++) { + if (VECTOR(neis)[j] == i) { + found = 1; break; + } + if (j > 0 && VECTOR(neis)[j - 1] == VECTOR(neis)[j]) { + found = 1; break; + } + } + } + *res = !found; + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + + +/** + * \function igraph_has_multiple + * \brief Check whether the graph has at least one multiple edge. + * + * + * An edge is a multiple edge if there is another + * edge with the same head and tail vertices in the graph. + * + * \param graph The input graph. + * \param res Pointer to a boolean variable, the result will be stored here. + * \return Error code. + * + * \sa \ref igraph_count_multiple(), \ref igraph_is_multiple() and \ref igraph_simplify(). + * + * Time complexity: O(e*d), e is the number of edges to check and d is the + * average degree (out-degree in directed graphs) of the vertices at the + * tail of the edges. + * + * \example examples/simple/igraph_has_multiple.c + */ +int igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res) { + long int vc = igraph_vcount(graph); + long int ec = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + + if (vc == 0 || ec == 0) { + *res = 0; + } else { + igraph_vector_t neis; + long int i, j, n; + igraph_bool_t found = 0; + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + for (i = 0; i < vc && !found; i++) { + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, + IGRAPH_OUT)); + n = igraph_vector_size(&neis); + for (j = 1; j < n; j++) { + if (VECTOR(neis)[j - 1] == VECTOR(neis)[j]) { + /* If the graph is undirected, loop edges appear twice in the neighbor + * list, so check the next item as well */ + if (directed) { + /* Directed, so this is a real multiple edge */ + found = 1; break; + } else if (VECTOR(neis)[j - 1] != i) { + /* Undirected, but not a loop edge */ + found = 1; break; + } else if (j < n - 1 && VECTOR(neis)[j] == VECTOR(neis)[j + 1]) { + /* Undirected, loop edge, multiple times */ + found = 1; break; + } + } + } + } + *res = found; + igraph_vector_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_is_multiple + * \brief Find the multiple edges in a graph. + * + * + * An edge is a multiple edge if there is another + * edge with the same head and tail vertices in the graph. + * + * + * Note that this function returns true only for the second or more + * appearances of the multiple edges. + * \param graph The input graph. + * \param res Pointer to a boolean vector, the result will be stored + * here. It will be resized as needed. + * \param es The edges to check. Supply \ref igraph_ess_all() if you want + * to check all edges. + * \return Error code. + * + * \sa \ref igraph_count_multiple(), \ref igraph_has_multiple() and \ref igraph_simplify(). + * + * Time complexity: O(e*d), e is the number of edges to check and d is the + * average degree (out-degree in directed graphs) of the vertices at the + * tail of the edges. + * + * \example examples/simple/igraph_is_multiple.c + */ +int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, + igraph_es_t es) { + igraph_eit_t eit; + long int i, j, n; + igraph_lazy_inclist_t inclist; + + IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + IGRAPH_CHECK(igraph_vector_bool_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (i = 0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { + long int e = IGRAPH_EIT_GET(eit); + long int from = IGRAPH_FROM(graph, e); + long int to = IGRAPH_TO(graph, e); + igraph_vector_int_t *neis = + igraph_lazy_inclist_get(&inclist, (igraph_integer_t) from); + + if (neis == 0) { + /* Most likely out of memory */ + IGRAPH_ERROR("Out of memory while building lazy incidence list", IGRAPH_ENOMEM); + } + + VECTOR(*res)[i] = 0; + + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + long int e2 = (long int) VECTOR(*neis)[j]; + long int to2 = IGRAPH_OTHER(graph, e2, from); + if (to2 == to && e2 < e) { + VECTOR(*res)[i] = 1; + } + } + } + + igraph_lazy_inclist_destroy(&inclist); + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + + +/** + * \function igraph_count_multiple + * \brief Count the number of appearances of the edges in a graph. + * + * + * If the graph has no multiple edges then the result vector will be + * filled with ones. + * (An edge is a multiple edge if there is another + * edge with the same head and tail vertices in the graph.) + * + * + * \param graph The input graph. + * \param res Pointer to a vector, the result will be stored + * here. It will be resized as needed. + * \param es The edges to check. Supply \ref igraph_ess_all() if you want + * to check all edges. + * \return Error code. + * + * \sa \ref igraph_is_multiple() and \ref igraph_simplify(). + * + * Time complexity: O(E d), E is the number of edges to check and d is the + * average degree (out-degree in directed graphs) of the vertices at the + * tail of the edges. + */ +int igraph_count_multiple(const igraph_t *graph, igraph_vector_t *res, igraph_es_t es) { + igraph_eit_t eit; + long int i, j, n; + igraph_lazy_inclist_t inclist; + + IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (i = 0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { + long int e = IGRAPH_EIT_GET(eit); + long int from = IGRAPH_FROM(graph, e); + long int to = IGRAPH_TO(graph, e); + igraph_vector_int_t *neis = + igraph_lazy_inclist_get(&inclist, (igraph_integer_t) from); + + if (neis == 0) { + /* Most likely out of memory */ + IGRAPH_ERROR("Out of memory while building lazy incidence list", IGRAPH_ENOMEM); + } + + VECTOR(*res)[i] = 0; + + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + long int e2 = (long int) VECTOR(*neis)[j]; + long int to2 = IGRAPH_OTHER(graph, e2, from); + if (to2 == to) { + VECTOR(*res)[i] += 1; + } + } + } + + igraph_lazy_inclist_destroy(&inclist); + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_is_mutual + * Check whether the edges of a directed graph are mutual. + * + * An (A,B) edge is mutual if the graph contains the (B,A) edge, too. + * + * + * An undirected graph only has mutual edges, by definition. + * + * + * Edge multiplicity is not considered here, e.g. if there are two + * (A,B) edges and one (B,A) edge, then all three are considered to be + * mutual. + * + * + * Loops are always mutual. + * + * \param graph The input graph. + * \param res Pointer to an initialized vector, the result is stored + * here. + * \param es The sequence of edges to check. Supply + * igraph_ess_all() for all edges, see \ref + * igraph_ess_all(). + * \return Error code. + * + * Time complexity: O(n log(d)), n is the number of edges supplied, d + * is the maximum in-degree of the vertices that are targets of the + * supplied edges. An upper limit of the time complexity is O(n log(|E|)), + * |E| is the number of edges in the graph. + */ +int igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es) { + + igraph_eit_t eit; + igraph_lazy_adjlist_t adjlist; + long int i; + + /* How many edges do we have? */ + IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + IGRAPH_CHECK(igraph_vector_bool_resize(res, IGRAPH_EIT_SIZE(eit))); + + /* An undirected graph has mutual edges by definition, + res is already properly resized */ + if (! igraph_is_directed(graph)) { + igraph_vector_bool_fill(res, 1); + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + + for (i = 0; ! IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { + long int edge = IGRAPH_EIT_GET(eit); + long int from = IGRAPH_FROM(graph, edge); + long int to = IGRAPH_TO(graph, edge); + + /* Check whether there is a to->from edge, search for from in the + out-list of to. We don't search an empty vector, because + vector_binsearch seems to have a bug with this. */ + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, + (igraph_integer_t) to); + if (igraph_vector_int_empty(neis)) { + VECTOR(*res)[i] = 0; + } else { + VECTOR(*res)[i] = igraph_vector_int_binsearch2(neis, from); + } + } + + igraph_lazy_adjlist_destroy(&adjlist); + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/properties/neighborhood.c b/src/rigraph/core/properties/neighborhood.c new file mode 100644 index 0000000..5a65415 --- /dev/null +++ b/src/rigraph/core/properties/neighborhood.c @@ -0,0 +1,454 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_neighborhood.h" + +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" + +/** + * \function igraph_neighborhood_size + * \brief Calculates the size of the neighborhood of a given vertex. + * + * The neighborhood of a given order of a vertex includes all vertices + * which are closer to the vertex than the order. I.e., order 0 is + * always the vertex itself, order 1 is the vertex plus its immediate + * neighbors, order 2 is order 1 plus the immediate neighbors of the + * vertices in order 1, etc. + * + * + * This function calculates the size of the neighborhood + * of the given order for the given vertices. + * + * \param graph The input graph. + * \param res Pointer to an initialized vector, the result will be + * stored here. It will be resized as needed. + * \param vids The vertices for which the calculation is performed. + * \param order Integer giving the order of the neighborhood. + * \param mode Specifies how to use the direction of the edges if a + * directed graph is analyzed. For \c IGRAPH_OUT only the outgoing + * edges are followed, so all vertices reachable from the source + * vertex in at most \c order steps are counted. For \c IGRAPH_IN + * all vertices from which the source vertex is reachable in at most + * \c order steps are counted. \c IGRAPH_ALL ignores the direction + * of the edges. This argument is ignored for undirected graphs. + * \param mindist The minimum distance to include a vertex in the counting. + * Vertices reachable with a path shorter than this value are excluded. + * If this is one, then the starting vertex is not counted. If this is + * two, then its neighbors are not counted either, etc. + * \return Error code. + * + * \sa \ref igraph_neighborhood() for calculating the actual neighborhood, + * \ref igraph_neighborhood_graphs() for creating separate graphs from + * the neighborhoods. + * + * Time complexity: O(n*d*o), where n is the number vertices for which + * the calculation is performed, d is the average degree, o is the order. + */ +int igraph_neighborhood_size(const igraph_t *graph, igraph_vector_t *res, + igraph_vs_t vids, igraph_integer_t order, + igraph_neimode_t mode, + igraph_integer_t mindist) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_t q; + igraph_vit_t vit; + long int i, j; + long int *added; + igraph_vector_t neis; + + if (order < 0) { + IGRAPH_ERRORF("Negative order in neighborhood size: %" IGRAPH_PRId ".", + IGRAPH_EINVAL, order); + } + + if (mindist < 0 || mindist > order) { + IGRAPH_ERRORF("Minimum distance should be between 0 and the neighborhood order (%" IGRAPH_PRId "), got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, order, mindist); + } + + added = IGRAPH_CALLOC(no_of_nodes, long int); + if (added == 0) { + IGRAPH_ERROR("Cannot calculate neighborhood size.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_VIT_SIZE(vit))); + + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + long int node = IGRAPH_VIT_GET(vit); + long int size = mindist == 0 ? 1 : 0; + added[node] = i + 1; + igraph_dqueue_clear(&q); + if (order > 0) { + igraph_dqueue_push(&q, node); + igraph_dqueue_push(&q, 0); + } + + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + long int actdist = (long int) igraph_dqueue_pop(&q); + long int n; + igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, mode); + n = igraph_vector_size(&neis); + + if (actdist < order - 1) { + /* we add them to the q */ + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + if (actdist + 1 >= mindist) { + size++; + } + } + } + } else { + /* we just count them, but don't add them */ + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + if (actdist + 1 >= mindist) { + size++; + } + } + } + } + + } /* while q not empty */ + + VECTOR(*res)[i] = size; + } /* for VIT, i */ + + igraph_vector_destroy(&neis); + igraph_vit_destroy(&vit); + igraph_dqueue_destroy(&q); + IGRAPH_FREE(added); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_neighborhood + * \brief Calculate the neighborhood of vertices. + * + * The neighborhood of a given order of a vertex includes all vertices + * which are closer to the vertex than the order. I.e., order 0 is + * always the vertex itself, order 1 is the vertex plus its immediate + * neighbors, order 2 is order 1 plus the immediate neighbors of the + * vertices in order 1, etc. + * + * + * This function calculates the vertices within the + * neighborhood of the specified vertices. + * + * \param graph The input graph. + * \param res An initialized pointer vector. Note that the objects + * (pointers) in the vector will \em not be freed, but the pointer + * vector will be resized as needed. The result of the calculation + * will be stored here in \ref igraph_vector_t objects. + * \param vids The vertices for which the calculation is performed. + * \param order Integer giving the order of the neighborhood. + * \param mode Specifies how to use the direction of the edges if a + * directed graph is analyzed. For \c IGRAPH_OUT only the outgoing + * edges are followed, so all vertices reachable from the source + * vertex in at most \p order steps are included. For \c IGRAPH_IN + * all vertices from which the source vertex is reachable in at most + * \p order steps are included. \c IGRAPH_ALL ignores the direction + * of the edges. This argument is ignored for undirected graphs. + * \param mindist The minimum distance to include a vertex in the counting. + * Vertices reachable with a path shorter than this value are excluded. + * If this is one, then the starting vertex is not counted. If this is + * two, then its neighbors are not counted either, etc. + * \return Error code. + * + * \sa \ref igraph_neighborhood_size() to calculate the size of the + * neighborhood, \ref igraph_neighborhood_graphs() for creating + * graphs from the neighborhoods. + * + * Time complexity: O(n*d*o), n is the number of vertices for which + * the calculation is performed, d is the average degree, o is the + * order. + */ +int igraph_neighborhood(const igraph_t *graph, igraph_vector_ptr_t *res, + igraph_vs_t vids, igraph_integer_t order, + igraph_neimode_t mode, igraph_integer_t mindist) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_t q; + igraph_vit_t vit; + long int i, j; + long int *added; + igraph_vector_t neis; + igraph_vector_t tmp; + igraph_vector_t *newv; + + if (order < 0) { + IGRAPH_ERROR("Negative order in neighborhood size", IGRAPH_EINVAL); + } + + if (mindist < 0 || mindist > order) { + IGRAPH_ERROR("Minimum distance should be between zero and order", + IGRAPH_EINVAL); + } + + added = IGRAPH_CALLOC(no_of_nodes, long int); + if (added == 0) { + IGRAPH_ERROR("Cannot calculate neighborhood size", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + IGRAPH_CHECK(igraph_vector_ptr_resize(res, IGRAPH_VIT_SIZE(vit))); + + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + long int node = IGRAPH_VIT_GET(vit); + added[node] = i + 1; + igraph_vector_clear(&tmp); + if (mindist == 0) { + IGRAPH_CHECK(igraph_vector_push_back(&tmp, node)); + } + if (order > 0) { + igraph_dqueue_push(&q, node); + igraph_dqueue_push(&q, 0); + } + + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + long int actdist = (long int) igraph_dqueue_pop(&q); + long int n; + igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, mode); + n = igraph_vector_size(&neis); + + if (actdist < order - 1) { + /* we add them to the q */ + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei)); + } + } + } + } else { + /* we just count them but don't add them to q */ + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei)); + } + } + } + } + + } /* while q not empty */ + + newv = IGRAPH_CALLOC(1, igraph_vector_t); + if (newv == 0) { + IGRAPH_ERROR("Cannot calculate neighborhood", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_CHECK(igraph_vector_copy(newv, &tmp)); + VECTOR(*res)[i] = newv; + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_destroy(&tmp); + igraph_vector_destroy(&neis); + igraph_vit_destroy(&vit); + igraph_dqueue_destroy(&q); + IGRAPH_FREE(added); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_neighborhood_graphs + * \brief Create graphs from the neighborhood(s) of some vertex/vertices. + * + * The neighborhood of a given order of a vertex includes all vertices + * which are closer to the vertex than the order. Ie. order 0 is + * always the vertex itself, order 1 is the vertex plus its immediate + * neighbors, order 2 is order 1 plus the immediate neighbors of the + * vertices in order 1, etc. + * + * + * This function finds every vertex in the neighborhood + * of a given parameter vertex and creates the induced subgraph from these + * vertices. + * + * + * The first version of this function was written by + * Vincent Matossian, thanks Vincent. + * \param graph The input graph. + * \param res Pointer to a pointer vector, the result will be stored + * here, ie. \p res will contain pointers to \c igraph_t + * objects. It will be resized if needed but note that the + * objects in the pointer vector will not be freed. + * \param vids The vertices for which the calculation is performed. + * \param order Integer giving the order of the neighborhood. + * \param mode Specifies how to use the direction of the edges if a + * directed graph is analyzed. For \c IGRAPH_OUT only the outgoing + * edges are followed, so all vertices reachable from the source + * vertex in at most \p order steps are counted. For \c IGRAPH_IN + * all vertices from which the source vertex is reachable in at most + * \p order steps are counted. \c IGRAPH_ALL ignores the direction + * of the edges. This argument is ignored for undirected graphs. + * \param mindist The minimum distance to include a vertex in the counting. + * Vertices reachable with a path shorter than this value are excluded. + * If this is one, then the starting vertex is not counted. If this is + * two, then its neighbors are not counted either, etc. + * \return Error code. + * + * \sa \ref igraph_neighborhood_size() for calculating the neighborhood + * sizes only, \ref igraph_neighborhood() for calculating the + * neighborhoods (but not creating graphs). + * + * Time complexity: O(n*(|V|+|E|)), where n is the number vertices for + * which the calculation is performed, |V| and |E| are the number of + * vertices and edges in the original input graph. + */ +int igraph_neighborhood_graphs(const igraph_t *graph, igraph_vector_ptr_t *res, + igraph_vs_t vids, igraph_integer_t order, + igraph_neimode_t mode, + igraph_integer_t mindist) { + long int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_t q; + igraph_vit_t vit; + long int i, j; + long int *added; + igraph_vector_t neis; + igraph_vector_t tmp; + igraph_t *newg; + + if (order < 0) { + IGRAPH_ERROR("Negative order in neighborhood size", IGRAPH_EINVAL); + } + + if (mindist < 0 || mindist > order) { + IGRAPH_ERROR("Minimum distance should be between zero and order", + IGRAPH_EINVAL); + } + + added = IGRAPH_CALLOC(no_of_nodes, long int); + if (added == 0) { + IGRAPH_ERROR("Cannot calculate neighborhood size", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + IGRAPH_CHECK(igraph_vector_ptr_resize(res, IGRAPH_VIT_SIZE(vit))); + + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + long int node = IGRAPH_VIT_GET(vit); + added[node] = i + 1; + igraph_vector_clear(&tmp); + if (mindist == 0) { + IGRAPH_CHECK(igraph_vector_push_back(&tmp, node)); + } + if (order > 0) { + igraph_dqueue_push(&q, node); + igraph_dqueue_push(&q, 0); + } + + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); + long int actdist = (long int) igraph_dqueue_pop(&q); + long int n; + igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, mode); + n = igraph_vector_size(&neis); + + if (actdist < order - 1) { + /* we add them to the q */ + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei)); + } + } + } + } else { + /* we just count them but don't add them to q */ + for (j = 0; j < n; j++) { + long int nei = (long int) VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei)); + } + } + } + } + + } /* while q not empty */ + + newg = IGRAPH_CALLOC(1, igraph_t); + if (newg == 0) { + IGRAPH_ERROR("Cannot create neighborhood graph", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newg); + if (igraph_vector_size(&tmp) < no_of_nodes) { + IGRAPH_CHECK(igraph_induced_subgraph(graph, newg, + igraph_vss_vector(&tmp), + IGRAPH_SUBGRAPH_AUTO)); + } else { + IGRAPH_CHECK(igraph_copy(newg, graph)); + } + VECTOR(*res)[i] = newg; + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_destroy(&tmp); + igraph_vector_destroy(&neis); + igraph_vit_destroy(&vit); + igraph_dqueue_destroy(&q); + IGRAPH_FREE(added); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/properties/properties_internal.h b/src/rigraph/core/properties/properties_internal.h new file mode 100644 index 0000000..f3daab8 --- /dev/null +++ b/src/rigraph/core/properties/properties_internal.h @@ -0,0 +1,31 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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 IGRAPH_PROPERTIES_INTERNAL_H +#define IGRAPH_PROPERTIES_INTERNAL_H + +#include "igraph_adjlist.h" +#include "igraph_constants.h" +#include "igraph_iterators.h" +#include "igraph_types.h" + +int igraph_i_trans4_al_simplify(igraph_adjlist_t *al, + const igraph_vector_int_t *rank); + +#endif diff --git a/src/rigraph/core/properties/spectral.c b/src/rigraph/core/properties/spectral.c new file mode 100644 index 0000000..8bd78b3 --- /dev/null +++ b/src/rigraph/core/properties/spectral.c @@ -0,0 +1,436 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2006-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_structural.h" +#include "igraph_interface.h" + +#include + +static int igraph_i_weighted_laplacian(const igraph_t *graph, igraph_matrix_t *res, + igraph_sparsemat_t *sparseres, + igraph_bool_t normalized, + const igraph_vector_t *weights) { + + igraph_eit_t edgeit; + int no_of_nodes = (int) igraph_vcount(graph); + int no_of_edges = (int) igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + igraph_vector_t degree; + long int i; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid edge weight vector length", IGRAPH_EINVAL); + } + + if (res) { + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); + igraph_matrix_null(res); + } + if (sparseres) { + int nz = directed ? no_of_edges + no_of_nodes : + no_of_edges * 2 + no_of_nodes; + igraph_sparsemat_resize(sparseres, no_of_nodes, no_of_nodes, nz); + } + + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); + IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); + + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + + if (directed) { + + if (!normalized) { + + while (!IGRAPH_EIT_END(edgeit)) { + long int edge = IGRAPH_EIT_GET(edgeit); + long int from = IGRAPH_FROM(graph, edge); + long int to = IGRAPH_TO (graph, edge); + igraph_real_t weight = VECTOR(*weights)[edge]; + if (from != to) { + if (res) { + MATRIX(*res, from, to) -= weight; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) from, (int)to, + -weight)); + } + VECTOR(degree)[from] += weight; + } + IGRAPH_EIT_NEXT(edgeit); + } + + /* And the diagonal */ + for (i = 0; i < no_of_nodes; i++) { + if (res) { + MATRIX(*res, i, i) = VECTOR(degree)[i]; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) i, (int) i, + VECTOR(degree)[i])); + } + } + + } else { /* normalized */ + + while (!IGRAPH_EIT_END(edgeit)) { + long int edge = IGRAPH_EIT_GET(edgeit); + long int from = IGRAPH_FROM(graph, edge); + long int to = IGRAPH_TO (graph, edge); + igraph_real_t weight = VECTOR(*weights)[edge]; + if (from != to) { + VECTOR(degree)[from] += weight; + } + IGRAPH_EIT_NEXT(edgeit); + } + + for (i = 0; i < no_of_nodes; i++) { + int t = VECTOR(degree)[i] > 0 ? 1 : 0; + if (res) { + MATRIX(*res, i, i) = t; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) i, (int) i, t)); + } + } + + IGRAPH_EIT_RESET(edgeit); + while (!IGRAPH_EIT_END(edgeit)) { + long int edge = IGRAPH_EIT_GET(edgeit); + long int from = IGRAPH_FROM(graph, edge); + long int to = IGRAPH_TO (graph, edge); + igraph_real_t weight = VECTOR(*weights)[edge]; + if (from != to) { + igraph_real_t t = weight / VECTOR(degree)[from]; + if (res) { + MATRIX(*res, from, to) -= t; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) from, (int) to, + -t)); + } + } + IGRAPH_EIT_NEXT(edgeit); + } + + } + + } else { /* undirected */ + + if (!normalized) { + + while (!IGRAPH_EIT_END(edgeit)) { + long int edge = IGRAPH_EIT_GET(edgeit); + long int from = IGRAPH_FROM(graph, edge); + long int to = IGRAPH_TO (graph, edge); + igraph_real_t weight = VECTOR(*weights)[edge]; + if (from != to) { + if (res) { + MATRIX(*res, from, to) -= weight; + MATRIX(*res, to, from) -= weight; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) from, (int) to, + -weight)); + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) to, (int) from, + -weight)); + } + VECTOR(degree)[from] += weight; + VECTOR(degree)[to] += weight; + } + IGRAPH_EIT_NEXT(edgeit); + } + + /* And the diagonal */ + for (i = 0; i < no_of_nodes; i++) { + if (res) { + MATRIX(*res, i, i) = VECTOR(degree)[i]; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) i, (int) i, + VECTOR(degree)[i])); + } + } + + } else { /* normalized */ + + while (!IGRAPH_EIT_END(edgeit)) { + long int edge = IGRAPH_EIT_GET(edgeit); + long int from = IGRAPH_FROM(graph, edge); + long int to = IGRAPH_TO (graph, edge); + igraph_real_t weight = VECTOR(*weights)[edge]; + if (from != to) { + VECTOR(degree)[from] += weight; + VECTOR(degree)[to] += weight; + } + IGRAPH_EIT_NEXT(edgeit); + } + + for (i = 0; i < no_of_nodes; i++) { + int t = VECTOR(degree)[i] > 0 ? 1 : 0; + if (res) { + MATRIX(*res, i, i) = t; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) i, (int) i, t)); + } + VECTOR(degree)[i] = sqrt(VECTOR(degree)[i]); + } + + IGRAPH_EIT_RESET(edgeit); + while (!IGRAPH_EIT_END(edgeit)) { + long int edge = IGRAPH_EIT_GET(edgeit); + long int from = IGRAPH_FROM(graph, edge); + long int to = IGRAPH_TO (graph, edge); + igraph_real_t weight = VECTOR(*weights)[edge]; + if (from != to) { + double diff = weight / (VECTOR(degree)[from] * VECTOR(degree)[to]); + if (res) { + MATRIX(*res, from, to) -= diff; + MATRIX(*res, to, from) -= diff; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) from, (int) to, + -diff)); + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) to, (int) from, + -diff)); + } + } + IGRAPH_EIT_NEXT(edgeit); + } + + } + + } + + igraph_vector_destroy(°ree); + igraph_eit_destroy(&edgeit); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \function igraph_laplacian + * \brief Returns the Laplacian matrix of a graph + * + * + * The graph Laplacian matrix is similar to an adjacency matrix but + * contains -1's instead of 1's and the vertex degrees are included in + * the diagonal. So the result for edge i--j is -1 if i!=j and is equal + * to the degree of vertex i if i==j. igraph_laplacian will work on a + * directed graph; in this case, the diagonal will contain the out-degrees. + * Loop edges will be ignored. + * + * + * The normalized version of the Laplacian matrix has 1 in the diagonal and + * -1/sqrt(d[i]d[j]) if there is an edge from i to j. + * + * + * The first version of this function was written by Vincent Matossian. + * \param graph Pointer to the graph to convert. + * \param res Pointer to an initialized matrix object, the result is + * stored here. It will be resized if needed. + * If it is a null pointer, then it is ignored. + * At least one of \p res and \p sparseres must be a non-null pointer. + * \param sparseres Pointer to an initialized sparse matrix object, the + * result is stored here, if it is not a null pointer. + * At least one of \p res and \p sparseres must be a non-null pointer. + * \param normalized Whether to create a normalized Laplacian matrix. + * \param weights An optional vector containing edge weights, to calculate + * the weighted Laplacian matrix. Set it to a null pointer to + * calculate the unweighted Laplacian. + * \return Error code. + * + * Time complexity: O(|V||V|), + * |V| is the + * number of vertices in the graph. + * + * \example examples/simple/igraph_laplacian.c + */ + +int igraph_laplacian(const igraph_t *graph, igraph_matrix_t *res, + igraph_sparsemat_t *sparseres, + igraph_bool_t normalized, + const igraph_vector_t *weights) { + + igraph_eit_t edgeit; + int no_of_nodes = (int) igraph_vcount(graph); + int no_of_edges = (int) igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + int from, to; + igraph_integer_t ffrom, fto; + igraph_vector_t degree; + int i; + + if (!res && !sparseres) { + IGRAPH_ERROR("Laplacian: give at least one of `res' or `sparseres'", + IGRAPH_EINVAL); + } + + if (weights) { + return igraph_i_weighted_laplacian(graph, res, sparseres, normalized, + weights); + } + + if (res) { + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); + igraph_matrix_null(res); + } + if (sparseres) { + int nz = directed ? no_of_edges + no_of_nodes : + no_of_edges * 2 + no_of_nodes; + IGRAPH_CHECK(igraph_sparsemat_resize(sparseres, no_of_nodes, + no_of_nodes, nz)); + } + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); + IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); + + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_OUT, IGRAPH_NO_LOOPS)); + + if (directed) { + if (!normalized) { + for (i = 0; i < no_of_nodes; i++) { + if (res) { + MATRIX(*res, i, i) = VECTOR(degree)[i]; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, + VECTOR(degree)[i])); + } + } + while (!IGRAPH_EIT_END(edgeit)) { + igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); + from = ffrom; + to = fto; + if (from != to) { + if (res) { + MATRIX(*res, from, to) -= 1; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -1.0)); + } + } + IGRAPH_EIT_NEXT(edgeit); + } + } else { + for (i = 0; i < no_of_nodes; i++) { + int t = VECTOR(degree)[i] > 0 ? 1 : 0; + if (res) { + MATRIX(*res, i, i) = t; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, t)); + } + if (VECTOR(degree)[i] > 0) { + VECTOR(degree)[i] = 1.0 / VECTOR(degree)[i]; + } + } + + while (!IGRAPH_EIT_END(edgeit)) { + igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); + from = ffrom; to = fto; + if (from != to) { + if (res) { + MATRIX(*res, from, to) -= VECTOR(degree)[from]; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, + -VECTOR(degree)[from])); + } + } + IGRAPH_EIT_NEXT(edgeit); + } + } + + } else { + + if (!normalized) { + for (i = 0; i < no_of_nodes; i++) { + if (res) { + MATRIX(*res, i, i) = VECTOR(degree)[i]; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, + VECTOR(degree)[i])); + } + } + + while (!IGRAPH_EIT_END(edgeit)) { + igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); + from = ffrom; + to = fto; + + if (from != to) { + if (res) { + MATRIX(*res, to, from) -= 1; + MATRIX(*res, from, to) -= 1; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -1.0)); + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -1.0)); + } + } + + IGRAPH_EIT_NEXT(edgeit); + } + } else { + for (i = 0; i < no_of_nodes; i++) { + int t = VECTOR(degree)[i] > 0 ? 1 : 0; + if (res) { + MATRIX(*res, i, i) = t; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, t)); + } + VECTOR(degree)[i] = sqrt(VECTOR(degree)[i]); + } + + while (!IGRAPH_EIT_END(edgeit)) { + igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); + from = ffrom; to = fto; + if (from != to) { + double diff = 1.0 / (VECTOR(degree)[from] * VECTOR(degree)[to]); + if (res) { + MATRIX(*res, from, to) -= diff; + MATRIX(*res, to, from) -= diff; + } + if (sparseres) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -diff)); + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -diff)); + } + } + IGRAPH_EIT_NEXT(edgeit); + } + } + + } + + igraph_vector_destroy(°ree); + igraph_eit_destroy(&edgeit); + IGRAPH_FINALLY_CLEAN(2); + return 0; +} diff --git a/src/rigraph/core/properties/trees.c b/src/rigraph/core/properties/trees.c new file mode 100644 index 0000000..e2bc8ea --- /dev/null +++ b/src/rigraph/core/properties/trees.c @@ -0,0 +1,351 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2021 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_structural.h" + +#include "igraph_adjlist.h" +#include "igraph_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_stack.h" + +/** + * \function igraph_unfold_tree + * Unfolding a graph into a tree, by possibly multiplicating its vertices. + * + * A graph is converted into a tree (or forest, if it is unconnected), + * by performing a breadth-first search on it, and replicating + * vertices that were found a second, third, etc. time. + * \param graph The input graph, it can be either directed or + * undirected. + * \param tree Pointer to an uninitialized graph object, the result is + * stored here. + * \param mode For directed graphs; whether to follow paths along edge + * directions (\c IGRAPH_OUT), or the opposite (\c IGRAPH_IN), or + * ignore edge directions completely (\c IGRAPH_ALL). It is ignored + * for undirected graphs. + * \param roots A numeric vector giving the root vertex, or vertices + * (if the graph is not connected), to start from. + * \param vertex_index Pointer to an initialized vector, or a null + * pointer. If not a null pointer, then a mapping from the vertices + * in the new graph to the ones in the original is created here. + * \return Error code. + * + * Time complexity: O(n+m), linear in the number vertices and edges. + * + */ +int igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, + igraph_neimode_t mode, const igraph_vector_t *roots, + igraph_vector_t *vertex_index) { + + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int no_of_roots = igraph_vector_size(roots); + long int tree_vertex_count = no_of_nodes; + + igraph_vector_t edges; + igraph_vector_bool_t seen_vertices; + igraph_vector_bool_t seen_edges; + + igraph_dqueue_t Q; + igraph_vector_t neis; + + long int i, n, r, v_ptr = no_of_nodes; + + /* TODO: handle not-connected graphs, multiple root vertices */ + + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + igraph_vector_reserve(&edges, no_of_edges * 2); + IGRAPH_DQUEUE_INIT_FINALLY(&Q, 100); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&seen_vertices, no_of_nodes); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&seen_edges, no_of_edges); + + if (vertex_index) { + IGRAPH_CHECK(igraph_vector_resize(vertex_index, no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*vertex_index)[i] = i; + } + } + + for (r = 0; r < no_of_roots; r++) { + + long int root = (long int) VECTOR(*roots)[r]; + VECTOR(seen_vertices)[root] = 1; + igraph_dqueue_push(&Q, root); + + while (!igraph_dqueue_empty(&Q)) { + long int actnode = (long int) igraph_dqueue_pop(&Q); + + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) actnode, mode)); + n = igraph_vector_size(&neis); + for (i = 0; i < n; i++) { + + long int edge = (long int) VECTOR(neis)[i]; + long int from = IGRAPH_FROM(graph, edge); + long int to = IGRAPH_TO(graph, edge); + long int nei = IGRAPH_OTHER(graph, edge, actnode); + + if (! VECTOR(seen_edges)[edge]) { + + VECTOR(seen_edges)[edge] = 1; + + if (! VECTOR(seen_vertices)[nei]) { + + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + + VECTOR(seen_vertices)[nei] = 1; + IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); + + } else { + + tree_vertex_count++; + if (vertex_index) { + IGRAPH_CHECK(igraph_vector_push_back(vertex_index, nei)); + } + + if (from == nei) { + igraph_vector_push_back(&edges, v_ptr++); + igraph_vector_push_back(&edges, to); + } else { + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, v_ptr++); + } + } + } + + } /* for i + * + * In the directed case, a possible additional requirement is that all + * edges are oriented away from a root (out-tree or arborescence) or all edges + * are oriented towards a root (in-tree or anti-arborescence). + * This test can be controlled using the \p mode parameter. + * + * + * By convention, the null graph (i.e. the graph with no vertices) is considered not to be a tree. + * + * \param graph The graph object to analyze. + * \param res Pointer to a logical variable, the result will be stored + * here. + * \param root If not \c NULL, the root node will be stored here. When \p mode + * is \c IGRAPH_ALL or the graph is undirected, any vertex can be the root + * and \p root is set to 0 (the first vertex). When \p mode is \c IGRAPH_OUT + * or \c IGRAPH_IN, the root is set to the vertex with zero in- or out-degree, + * respectively. + * \param mode For a directed graph this specifies whether to test for an + * out-tree, an in-tree or ignore edge directions. The respective + * possible values are: + * \c IGRAPH_OUT, \c IGRAPH_IN, \c IGRAPH_ALL. This argument is + * ignored for undirected graphs. + * \return Error code: + * \c IGRAPH_EINVAL: invalid mode argument. + * + * Time complexity: At most O(|V|+|E|), the + * number of vertices plus the number of edges in the graph. + * + * \sa igraph_is_weakly_connected() + * + * \example examples/simple/igraph_tree.c + */ +int igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode) { + igraph_integer_t iroot = 0; + igraph_integer_t visited_count; + igraph_integer_t vcount, ecount; + + vcount = igraph_vcount(graph); + ecount = igraph_ecount(graph); + + /* A tree must have precisely vcount-1 edges. */ + /* By convention, the zero-vertex graph will not be considered a tree. */ + if (ecount != vcount - 1) { + *res = 0; + return IGRAPH_SUCCESS; + } + + /* The single-vertex graph is a tree, provided it has no edges (checked in the previous if (..)) */ + if (vcount == 1) { + *res = 1; + if (root) { + *root = 0; + } + return IGRAPH_SUCCESS; + } + + /* For higher vertex counts we cannot short-circuit due to the possibility + * of loops or multi-edges even when the edge count is correct. */ + + /* Ignore mode for undirected graphs. */ + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + /* The main algorithm: + * We find a root and check that all other vertices are reachable from it. + * We have already checked the number of edges, so with the additional + * reachability condition we can verify if the graph is a tree. + * + * For directed graphs, the root is the node with no incoming/outgoing + * connections, depending on 'mode'. For undirected, it is arbitrary, so + * we choose 0. + */ + + *res = 1; /* assume success */ + + switch (mode) { + case IGRAPH_ALL: + iroot = 0; + break; + + case IGRAPH_IN: + case IGRAPH_OUT: { + igraph_vector_t degree; + igraph_integer_t i; + + IGRAPH_CHECK(igraph_vector_init(°ree, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, °ree); + + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), mode == IGRAPH_IN ? IGRAPH_OUT : IGRAPH_IN, /* loops = */ 1)); + + for (i = 0; i < vcount; ++i) { + if (VECTOR(degree)[i] == 0) { + break; + } + if (VECTOR(degree)[i] > 1) { + /* In an out-tree, all vertices have in-degree 1, except for the root, + * which has in-degree 0. Thus, if we encounter a larger in-degree, + * the graph cannot be an out-tree. + * We could perform this check for all degrees, but that would not + * improve performance when the graph is indeed a tree, persumably + * the most common case. Thus we only check until finding the root. + */ + *res = 0; + break; + } + } + + /* If no suitable root is found, the graph is not a tree. */ + if (*res && i == vcount) { + *res = 0; + } else { + iroot = i; + } + + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + } + + break; + default: + IGRAPH_ERROR("Invalid mode,", IGRAPH_EINVMODE); + } + + /* if no suitable root was found, skip visiting vertices */ + if (*res) { + IGRAPH_CHECK(igraph_i_is_tree_visitor(graph, iroot, mode, &visited_count)); + *res = visited_count == vcount; + } + + if (root) { + *root = iroot; + } + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/properties/triangles.c b/src/rigraph/core/properties/triangles.c new file mode 100644 index 0000000..8957cc9 --- /dev/null +++ b/src/rigraph/core/properties/triangles.c @@ -0,0 +1,925 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_transitivity.h" + +#include "igraph_interface.h" +#include "igraph_adjlist.h" +#include "igraph_memory.h" +#include "igraph_motifs.h" +#include "igraph_structural.h" + +#include "../core/interruption.h" +#include "properties_internal.h" + +/** + * \function igraph_transitivity_avglocal_undirected + * \brief Average local transitivity (clustering coefficient). + * + * The transitivity measures the probability that two neighbors of a + * vertex are connected. In case of the average local transitivity, + * this probability is calculated for each vertex and then the average + * is taken. Vertices with less than two neighbors require special treatment, + * they will either be left out from the calculation or they will be considered + * as having zero transitivity, depending on the \c mode argument. + * Edge directions and edge multiplicities are ignored. + * + * + * Note that this measure is different from the global transitivity measure + * (see \ref igraph_transitivity_undirected() ) as it simply takes the + * average local transitivity across the whole network. + * + * + * Clustering coefficient is an alternative name for transitivity. + * + * + * References: + * + * + * D. J. Watts and S. Strogatz: Collective dynamics of small-world networks. + * Nature 393(6684):440-442 (1998). + * + * \param graph The input graph. Edge directions and multiplicites are ignored. + * \param res Pointer to a real variable, the result will be stored here. + * \param mode Defines how to treat vertices with degree less than two. + * \c IGRAPH_TRANSITIVITY_NAN leaves them out from averaging, + * \c IGRAPH_TRANSITIVITY_ZERO includes them with zero transitivity. + * The result will be \c NaN if the mode is \c IGRAPH_TRANSITIVITY_NAN + * and there are no vertices with more than one neighbor. + * + * \return Error code. + * + * \sa \ref igraph_transitivity_undirected(), \ref + * igraph_transitivity_local_undirected(). + * + * Time complexity: O(|V|*d^2), |V| is the number of vertices in the + * graph and d is the average degree. + */ + +int igraph_transitivity_avglocal_undirected(const igraph_t *graph, + igraph_real_t *res, + igraph_transitivity_mode_t mode) { + + igraph_integer_t i, no_of_nodes = igraph_vcount(graph), nans = 0; + igraph_real_t sum = 0.0; + igraph_vector_t vec; + + if (no_of_nodes == 0) { + if (mode == IGRAPH_TRANSITIVITY_ZERO) { + *res = 0; + } else { + *res = IGRAPH_NAN; + } + } else { + IGRAPH_VECTOR_INIT_FINALLY(&vec, no_of_nodes); + + IGRAPH_CHECK(igraph_transitivity_local_undirected(graph, &vec, igraph_vss_all(), mode)); + + for (i = 0, nans = 0; i < no_of_nodes; i++) { + if (!igraph_is_nan(VECTOR(vec)[i])) { + sum += VECTOR(vec)[i]; + } else { + nans++; + } + } + + igraph_vector_destroy(&vec); + IGRAPH_FINALLY_CLEAN(1); + + *res = sum / (no_of_nodes - nans); + } + + return IGRAPH_SUCCESS; +} + +int igraph_transitivity_local_undirected1(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + igraph_transitivity_mode_t mode) { + +#define TRANSIT +#include "triangles_template1.h" +#undef TRANSIT + + return IGRAPH_SUCCESS; +} + +int igraph_transitivity_local_undirected2(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + igraph_transitivity_mode_t mode) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vit_t vit; + long int nodes_to_calc, affected_nodes; + long int maxdegree = 0; + long int i, j, k, nn; + igraph_lazy_adjlist_t adjlist; + igraph_vector_t indexv, avids, rank, order, triangles, degree; + long int *neis; + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + nodes_to_calc = IGRAPH_VIT_SIZE(vit); + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + + IGRAPH_VECTOR_INIT_FINALLY(&indexv, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&avids, 0); + IGRAPH_CHECK(igraph_vector_reserve(&avids, nodes_to_calc)); + k = 0; + for (i = 0; i < nodes_to_calc; IGRAPH_VIT_NEXT(vit), i++) { + long int v = IGRAPH_VIT_GET(vit); + igraph_vector_int_t *neis2; + long int neilen; + if (VECTOR(indexv)[v] == 0) { + VECTOR(indexv)[v] = k + 1; k++; + IGRAPH_CHECK(igraph_vector_push_back(&avids, v)); + } + + neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) v); + neilen = igraph_vector_int_size(neis2); + for (j = 0; j < neilen; j++) { + long int nei = (long int) VECTOR(*neis2)[j]; + if (VECTOR(indexv)[nei] == 0) { + VECTOR(indexv)[nei] = k + 1; k++; + IGRAPH_CHECK(igraph_vector_push_back(&avids, nei)); + } + } + } + + /* Degree, ordering, ranking */ + affected_nodes = igraph_vector_size(&avids); + IGRAPH_VECTOR_INIT_FINALLY(&order, 0); + IGRAPH_VECTOR_INIT_FINALLY(°ree, affected_nodes); + for (i = 0; i < affected_nodes; i++) { + long int v = (long int) VECTOR(avids)[i]; + igraph_vector_int_t *neis2; + long int deg; + neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) v); + VECTOR(degree)[i] = deg = igraph_vector_int_size(neis2); + if (deg > maxdegree) { + maxdegree = deg; + } + } + igraph_vector_order1(°ree, &order, maxdegree + 1); + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_VECTOR_INIT_FINALLY(&rank, affected_nodes); + for (i = 0; i < affected_nodes; i++) { + VECTOR(rank)[ (long int) VECTOR(order)[i] ] = affected_nodes - i - 1; + } + + neis = IGRAPH_CALLOC(no_of_nodes, long int); + if (neis == 0) { + IGRAPH_ERROR("Insufficient memory for local transitivity calculation.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, neis); + + IGRAPH_VECTOR_INIT_FINALLY(&triangles, affected_nodes); + for (nn = affected_nodes - 1; nn >= 0; nn--) { + long int node = (long int) VECTOR(avids) [ (long int) VECTOR(order)[nn] ]; + igraph_vector_int_t *neis1, *neis2; + long int neilen1, neilen2; + long int nodeindex = (long int) VECTOR(indexv)[node]; + long int noderank = (long int) VECTOR(rank) [nodeindex - 1]; + + /* fprintf(stderr, "node %li (indexv %li, rank %li)\n", node, */ + /* (long int)VECTOR(indexv)[node]-1, noderank); */ + + IGRAPH_ALLOW_INTERRUPTION(); + + neis1 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) node); + neilen1 = igraph_vector_int_size(neis1); + for (i = 0; i < neilen1; i++) { + long int nei = (long int) VECTOR(*neis1)[i]; + neis[nei] = node + 1; + } + for (i = 0; i < neilen1; i++) { + long int nei = (long int) VECTOR(*neis1)[i]; + long int neiindex = (long int) VECTOR(indexv)[nei]; + long int neirank = (long int) VECTOR(rank)[neiindex - 1]; + + /* fprintf(stderr, " nei %li (indexv %li, rank %li)\n", nei, */ + /* neiindex, neirank); */ + if (neirank > noderank) { + neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) nei); + neilen2 = igraph_vector_int_size(neis2); + for (j = 0; j < neilen2; j++) { + long int nei2 = (long int) VECTOR(*neis2)[j]; + long int nei2index = (long int) VECTOR(indexv)[nei2]; + long int nei2rank = (long int) VECTOR(rank)[nei2index - 1]; + /* fprintf(stderr, " triple %li %li %li\n", node, nei, nei2); */ + if (nei2rank < neirank) { + continue; + } + if (neis[nei2] == node + 1) { + /* fprintf(stderr, " triangle\n"); */ + VECTOR(triangles) [ nei2index - 1 ] += 1; + VECTOR(triangles) [ neiindex - 1 ] += 1; + VECTOR(triangles) [ nodeindex - 1 ] += 1; + } + } + } + } + } + + /* Ok, for all affected vertices the number of triangles were counted */ + + IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); + IGRAPH_VIT_RESET(vit); + for (i = 0; i < nodes_to_calc; i++, IGRAPH_VIT_NEXT(vit)) { + long int node = IGRAPH_VIT_GET(vit); + long int idx = (long int) VECTOR(indexv)[node] - 1; + igraph_vector_int_t *neis2 = + igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) node); + long int deg = igraph_vector_int_size(neis2); + if (mode == IGRAPH_TRANSITIVITY_ZERO && deg < 2) { + VECTOR(*res)[i] = 0.0; + } else { + VECTOR(*res)[i] = VECTOR(triangles)[idx] / deg / (deg - 1) * 2.0; + } + /* fprintf(stderr, "%f %f\n", VECTOR(triangles)[idx], triples); */ + } + + igraph_vector_destroy(&triangles); + igraph_free(neis); + igraph_vector_destroy(&rank); + igraph_vector_destroy(&order); + igraph_vector_destroy(&avids); + igraph_vector_destroy(&indexv); + igraph_lazy_adjlist_destroy(&adjlist); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(8); + + return 0; +} + +/* We don't use this, it is theoretically good, but practically not. + */ + +/* int igraph_transitivity_local_undirected3(const igraph_t *graph, */ +/* igraph_vector_t *res, */ +/* const igraph_vs_t vids) { */ + +/* igraph_vit_t vit; */ +/* long int nodes_to_calc; */ +/* igraph_lazy_adjlist_t adjlist; */ +/* long int i, j; */ + +/* IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); */ +/* IGRAPH_FINALLY(igraph_vit_destroy, &vit); */ +/* nodes_to_calc=IGRAPH_VIT_SIZE(vit); */ + +/* IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, */ +/* IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); */ +/* IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); */ + +/* IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); */ +/* for (i=0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); */ +/* i++, IGRAPH_VIT_NEXT(vit)) { */ +/* long int node=IGRAPH_VIT_GET(vit); */ +/* igraph_vector_t *neis=igraph_lazy_adjlist_get(&adjlist, node); */ +/* long int n1=igraph_vector_size(neis); */ +/* igraph_real_t triangles=0; */ +/* igraph_real_t triples=(double)n1*(n1-1); */ +/* IGRAPH_ALLOW_INTERRUPTION(); */ +/* for (j=0; j nei2) { */ +/* l2++; */ +/* } else { */ +/* triangles+=1; */ +/* l1++; l2++; */ +/* } */ +/* } */ +/* } */ +/* /\* We're done with 'node' *\/ */ +/* VECTOR(*res)[i] = triangles / triples; */ +/* } */ + +/* igraph_lazy_adjlist_destroy(&adjlist); */ +/* igraph_vit_destroy(&vit); */ +/* IGRAPH_FINALLY_CLEAN(2); */ + +/* return 0; */ +/* } */ + +/* This removes loop, multiple edges and edges that point + "backwards" according to the rank vector. */ +/* Note: Also used in scan.c */ +int igraph_i_trans4_al_simplify(igraph_adjlist_t *al, + const igraph_vector_int_t *rank) { + long int i; + long int n = al->length; + igraph_vector_int_t mark; + igraph_vector_int_init(&mark, n); + IGRAPH_FINALLY(igraph_vector_int_destroy, &mark); + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = &al->adjs[i]; + int j, l = igraph_vector_int_size(v); + int irank = VECTOR(*rank)[i]; + VECTOR(mark)[i] = i + 1; + for (j = 0; j < l; /* nothing */) { + long int e = (long int) VECTOR(*v)[j]; + if (VECTOR(*rank)[e] > irank && VECTOR(mark)[e] != i + 1) { + VECTOR(mark)[e] = i + 1; + j++; + } else { + VECTOR(*v)[j] = igraph_vector_int_tail(v); + igraph_vector_int_pop_back(v); + l--; + } + } + } + + igraph_vector_int_destroy(&mark); + IGRAPH_FINALLY_CLEAN(1); + return 0; + +} + +int igraph_transitivity_local_undirected4(const igraph_t *graph, + igraph_vector_t *res, + igraph_transitivity_mode_t mode) { + +#define TRANSIT 1 +#include "triangles_template.h" +#undef TRANSIT + + return 0; +} + +/** + * \function igraph_transitivity_local_undirected + * \brief Calculates the local transitivity (clustering coefficient) of a graph. + * + * The transitivity measures the probability that two neighbors of a + * vertex are connected. In case of the local transitivity, this + * probability is calculated separately for each vertex. + * + * + * Note that this measure is different from the global transitivity measure + * (see \ref igraph_transitivity_undirected() ) as it calculates a transitivity + * value for each vertex individually. + * + * + * Clustering coefficient is an alternative name for transitivity. + * + * + * References: + * + * + * D. J. Watts and S. Strogatz: Collective dynamics of small-world networks. + * Nature 393(6684):440-442 (1998). + * + * \param graph The input graph. Edge directions and multiplicities are ignored. + * \param res Pointer to an initialized vector, the result will be + * stored here. It will be resized as needed. + * \param vids Vertex set, the vertices for which the local + * transitivity will be calculated. + * \param mode Defines how to treat vertices with degree less than two. + * \c IGRAPH_TRANSITIVITY_NAN returns \c NaN for these vertices, + * \c IGRAPH_TRANSITIVITY_ZERO returns zero. + * \return Error code. + * + * \sa \ref igraph_transitivity_undirected(), \ref + * igraph_transitivity_avglocal_undirected(). + * + * Time complexity: O(n*d^2), n is the number of vertices for which + * the transitivity is calculated, d is the average vertex degree. + */ + +int igraph_transitivity_local_undirected(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + igraph_transitivity_mode_t mode) { + + if (igraph_vs_is_all(&vids)) { + return igraph_transitivity_local_undirected4(graph, res, mode); + } else { + igraph_vit_t vit; + long int size; + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + size = IGRAPH_VIT_SIZE(vit); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + if (size < 100) { + return igraph_transitivity_local_undirected1(graph, res, vids, mode); + } else { + return igraph_transitivity_local_undirected2(graph, res, vids, mode); + } + } +} + +static int igraph_adjacent_triangles1(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids) { +# include "triangles_template1.h" + return 0; +} + +static int igraph_adjacent_triangles4(const igraph_t *graph, + igraph_vector_t *res) { +# include "triangles_template.h" + return 0; +} + +/** + * \function igraph_adjacent_triangles + * \brief Count the number of triangles a vertex is part of. + * + * \param graph The input graph. Edge directions and multiplicities are ignored. + * \param res Initiliazed vector, the results are stored here. + * \param vids The vertices to perform the calculation for. + * \return Error mode. + * + * \sa \ref igraph_list_triangles() to list them. + * + * Time complexity: O(d^2 n), d is the average vertex degree of the + * queried vertices, n is their number. + */ + +int igraph_adjacent_triangles(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids) { + if (igraph_vs_is_all(&vids)) { + return igraph_adjacent_triangles4(graph, res); + } else { + return igraph_adjacent_triangles1(graph, res, vids); + } +} + +/** + * \function igraph_list_triangles + * \brief Find all triangles in a graph. + * + * \param graph The input graph, edge directions are ignored. + * Multiple edges are ignored. + * \param res Pointer to an initialized integer vector, the result + * is stored here, in a long list of triples of vertex ids. + * Each triple is a triangle in the graph. Each triangle is + * listed exactly once. + * \return Error code. + * + * \sa \ref igraph_transitivity_undirected() to count the triangles, + * \ref igraph_adjacent_triangles() to count the triangles a vertex + * participates in. + * + * Time complexity: O(d^2 n), d is the average degree, n is the number + * of vertices. + */ + +int igraph_list_triangles(const igraph_t *graph, + igraph_vector_int_t *res) { +# define TRIANGLES +# include "triangles_template.h" +# undef TRIANGLES + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_transitivity_undirected + * \brief Calculates the transitivity (clustering coefficient) of a graph. + * + * + * The transitivity measures the probability that two neighbors of a + * vertex are connected. More precisely, this is the ratio of the + * triangles and connected triples in the graph, the result is a + * single real number. Directed graphs are considered as undirected ones + * and multi-edges are ignored. + * + * + * Note that this measure is different from the local transitivity measure + * (see \ref igraph_transitivity_local_undirected() ) as it calculates a single + * value for the whole graph. + * + * + * Clustering coefficient is an alternative name for transitivity. + * + * + * References: + * + * + * S. Wasserman and K. Faust: Social Network Analysis: Methods and + * Applications. Cambridge: Cambridge University Press, 1994. + * + * \param graph The graph object. Edge directions and multiplicites are ignored. + * \param res Pointer to a real variable, the result will be stored here. + * \param mode Defines how to treat graphs with no connected triples. + * \c IGRAPH_TRANSITIVITY_NAN returns \c NaN in this case, + * \c IGRAPH_TRANSITIVITY_ZERO returns zero. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory for + * temporary data. + * + * \sa \ref igraph_transitivity_local_undirected(), + * \ref igraph_transitivity_avglocal_undirected(). + * + * Time complexity: O(|V|*d^2), |V| is the number of vertices in + * the graph, d is the average node degree. + * + * \example examples/simple/igraph_transitivity.c + */ + +int igraph_transitivity_undirected(const igraph_t *graph, + igraph_real_t *res, + igraph_transitivity_mode_t mode) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_real_t triples = 0, triangles = 0; + long int node, nn; + long int maxdegree; + long int *neis; + igraph_vector_t order; + igraph_vector_t rank; + igraph_vector_t degree; + + igraph_adjlist_t allneis; + igraph_vector_int_t *neis1, *neis2; + long int i, j, neilen1, neilen2; + + if (no_of_nodes == 0) { + *res = mode == IGRAPH_TRANSITIVITY_ZERO ? 0.0 : IGRAPH_NAN; + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS)); + maxdegree = (long int) igraph_vector_max(°ree) + 1; + IGRAPH_CHECK(igraph_vector_order1(°ree, &order, maxdegree)); + + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_VECTOR_INIT_FINALLY(&rank, no_of_nodes); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(rank)[ (long int) VECTOR(order)[i] ] = no_of_nodes - i - 1; + } + + IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + + neis = IGRAPH_CALLOC(no_of_nodes, long int); + if (! neis) { + IGRAPH_ERROR("Insufficient memory for undirected global transitivity.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, neis); + + for (nn = no_of_nodes - 1; nn >= 0; nn--) { + node = (long int) VECTOR(order)[nn]; + + IGRAPH_ALLOW_INTERRUPTION(); + + neis1 = igraph_adjlist_get(&allneis, node); + neilen1 = igraph_vector_int_size(neis1); + triples += (double)neilen1 * (neilen1 - 1); + /* Mark the neighbors of 'node' */ + for (i = 0; i < neilen1; i++) { + long int nei = (long int) VECTOR(*neis1)[i]; + neis[nei] = node + 1; + } + for (i = 0; i < neilen1; i++) { + long int nei = (long int) VECTOR(*neis1)[i]; + /* If 'nei' is not ready yet */ + if (VECTOR(rank)[nei] > VECTOR(rank)[node]) { + neis2 = igraph_adjlist_get(&allneis, nei); + neilen2 = igraph_vector_int_size(neis2); + for (j = 0; j < neilen2; j++) { + long int nei2 = (long int) VECTOR(*neis2)[j]; + if (neis[nei2] == node + 1) { + triangles += 1.0; + } + } + } + } + } + + IGRAPH_FREE(neis); + igraph_adjlist_destroy(&allneis); + igraph_vector_destroy(&rank); + igraph_vector_destroy(&order); + IGRAPH_FINALLY_CLEAN(4); + + if (triples == 0 && mode == IGRAPH_TRANSITIVITY_ZERO) { + *res = 0; + } else { + *res = triangles / triples * 2.0; + } + + return 0; +} + +static int igraph_i_transitivity_barrat1(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + const igraph_vector_t *weights, + igraph_transitivity_mode_t mode) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vit_t vit; + long int nodes_to_calc; + igraph_vector_int_t *adj1, *adj2; + igraph_vector_long_t neis; + igraph_vector_t actw; + igraph_lazy_inclist_t incident; + long int i; + igraph_vector_t strength; + + /* Precondition: weight vector is not null, its length equals the number of + * edges, and the graph has at least one vertex. The graph must not have + * multi-edges. These must be ensured by the caller. + */ + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + nodes_to_calc = IGRAPH_VIT_SIZE(vit); + + IGRAPH_CHECK(igraph_vector_long_init(&neis, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &neis); + + IGRAPH_VECTOR_INIT_FINALLY(&actw, no_of_nodes); + + IGRAPH_VECTOR_INIT_FINALLY(&strength, 0); + IGRAPH_CHECK(igraph_strength(graph, &strength, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS, weights)); + + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &incident, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &incident); + + IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); + + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + long int node = IGRAPH_VIT_GET(vit); + long int adjlen1, adjlen2, j, k; + igraph_real_t triples, triangles; + + IGRAPH_ALLOW_INTERRUPTION(); + + adj1 = igraph_lazy_inclist_get(&incident, (igraph_integer_t) node); + adjlen1 = igraph_vector_int_size(adj1); + /* Mark the neighbors of the node */ + for (j = 0; j < adjlen1; j++) { + long int edge = (long int) VECTOR(*adj1)[j]; + long int nei = IGRAPH_OTHER(graph, edge, node); + VECTOR(neis)[nei] = i + 1; + VECTOR(actw)[nei] = VECTOR(*weights)[edge]; + } + triples = VECTOR(strength)[node] * (adjlen1 - 1); + triangles = 0.0; + + for (j = 0; j < adjlen1; j++) { + long int edge1 = (long int) VECTOR(*adj1)[j]; + igraph_real_t weight1 = VECTOR(*weights)[edge1]; + long int v = IGRAPH_OTHER(graph, edge1, node); + adj2 = igraph_lazy_inclist_get(&incident, (igraph_integer_t) v); + adjlen2 = igraph_vector_int_size(adj2); + for (k = 0; k < adjlen2; k++) { + long int edge2 = (long int) VECTOR(*adj2)[k]; + long int v2 = IGRAPH_OTHER(graph, edge2, v); + if (VECTOR(neis)[v2] == i + 1) { + triangles += (VECTOR(actw)[v2] + weight1) / 2.0; + } + } + } + if (mode == IGRAPH_TRANSITIVITY_ZERO && triples == 0) { + VECTOR(*res)[i] = 0.0; + } else { + VECTOR(*res)[i] = triangles / triples; + } + } + + igraph_lazy_inclist_destroy(&incident); + igraph_vector_destroy(&strength); + igraph_vector_destroy(&actw); + igraph_vector_long_destroy(&neis); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +static int igraph_i_transitivity_barrat4(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + const igraph_vector_t *weights, + igraph_transitivity_mode_t mode) { + + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t order, degree, rank; + long int maxdegree; + igraph_inclist_t incident; + igraph_vector_long_t neis; + igraph_vector_int_t *adj1, *adj2; + igraph_vector_t actw; + long int i, nn; + + /* Precondition: weight vector is not null, its length equals the number of + * edges, and the graph has at least one vertex. The graph must not have + * multi-edges. These must be ensured by the caller. + */ + + IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS)); + maxdegree = (long int) igraph_vector_max(°ree) + 1; + IGRAPH_CHECK(igraph_vector_order1(°ree, &order, maxdegree)); + + IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS, weights)); + + IGRAPH_VECTOR_INIT_FINALLY(&rank, no_of_nodes); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(rank)[ (long int)VECTOR(order)[i] ] = no_of_nodes - i - 1; + } + + IGRAPH_CHECK(igraph_inclist_init(graph, &incident, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &incident); + + IGRAPH_CHECK(igraph_vector_long_init(&neis, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &neis); + + IGRAPH_VECTOR_INIT_FINALLY(&actw, no_of_nodes); + + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + igraph_vector_null(res); + + for (nn = no_of_nodes - 1; nn >= 0; nn--) { + long int adjlen1, adjlen2; + igraph_real_t triples; + long int node = (long int) VECTOR(order)[nn]; + + IGRAPH_ALLOW_INTERRUPTION(); + + adj1 = igraph_inclist_get(&incident, node); + adjlen1 = igraph_vector_int_size(adj1); + triples = VECTOR(degree)[node] * (adjlen1 - 1) / 2.0; + /* Mark the neighbors of the node */ + for (i = 0; i < adjlen1; i++) { + long int edge = (long int) VECTOR(*adj1)[i]; + long int nei = IGRAPH_OTHER(graph, edge, node); + VECTOR(neis)[nei] = node + 1; + VECTOR(actw)[nei] = VECTOR(*weights)[edge]; + } + + for (i = 0; i < adjlen1; i++) { + long int edge1 = (long int) VECTOR(*adj1)[i]; + igraph_real_t weight1 = VECTOR(*weights)[edge1]; + long int nei = IGRAPH_OTHER(graph, edge1, node); + long int j; + if (VECTOR(rank)[nei] > VECTOR(rank)[node]) { + adj2 = igraph_inclist_get(&incident, nei); + adjlen2 = igraph_vector_int_size(adj2); + for (j = 0; j < adjlen2; j++) { + long int edge2 = (long int) VECTOR(*adj2)[j]; + igraph_real_t weight2 = VECTOR(*weights)[edge2]; + long int nei2 = IGRAPH_OTHER(graph, edge2, nei); + if (VECTOR(rank)[nei2] < VECTOR(rank)[nei]) { + continue; + } + if (VECTOR(neis)[nei2] == node + 1) { + VECTOR(*res)[nei2] += (VECTOR(actw)[nei2] + weight2) / 2.0; + VECTOR(*res)[nei] += (weight1 + weight2) / 2.0; + VECTOR(*res)[node] += (VECTOR(actw)[nei2] + weight1) / 2.0; + } + } + } + } + + if (mode == IGRAPH_TRANSITIVITY_ZERO && triples == 0) { + VECTOR(*res)[node] = 0.0; + } else { + VECTOR(*res)[node] /= triples; + } + } + + igraph_vector_destroy(&actw); + igraph_vector_long_destroy(&neis); + igraph_inclist_destroy(&incident); + igraph_vector_destroy(&rank); + igraph_vector_destroy(°ree); + igraph_vector_destroy(&order); + IGRAPH_FINALLY_CLEAN(6); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_transitivity_barrat + * Weighted transitivity, as defined by A. Barrat. + * + * This is a local transitivity, i.e. a vertex-level index. For a + * given vertex \c i, from all triangles in which it participates we + * consider the weight of the edges incident on \c i. The transitivity + * is the sum of these weights divided by twice the strength of the + * vertex (see \ref igraph_strength()) and the degree of the vertex + * minus one. See Alain Barrat, Marc Barthelemy, Romualdo + * Pastor-Satorras, Alessandro Vespignani: The architecture of complex + * weighted networks, Proc. Natl. Acad. Sci. USA 101, 3747 (2004) at + * http://arxiv.org/abs/cond-mat/0311416 for the exact formula. + * + * \param graph The input graph. Edge directions are ignored for + * directed graphs. Note that the function does \em not work for + * non-simple graphs. + * \param res Pointer to an initialized vector, the result will be + * stored here. It will be resized as needed. + * \param vids The vertices for which the calculation is performed. + * \param weights Edge weights. If this is a null pointer, then a + * warning is given and \ref igraph_transitivity_local_undirected() + * is called. + * \param mode Defines how to treat vertices with zero strength. + * \c IGRAPH_TRANSITIVITY_NAN says that the transitivity of these + * vertices is \c NaN, \c IGRAPH_TRANSITIVITY_ZERO says it is zero. + * + * \return Error code. + * + * Time complexity: O(|V|*d^2), |V| is the number of vertices in + * the graph, d is the average node degree. + * + * \sa \ref igraph_transitivity_undirected(), \ref + * igraph_transitivity_local_undirected() and \ref + * igraph_transitivity_avglocal_undirected() for other kinds of + * (non-weighted) transitivity. + */ + +int igraph_transitivity_barrat(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + const igraph_vector_t *weights, + igraph_transitivity_mode_t mode) { + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_bool_t has_multiple; + + /* Handle fallback to unweighted version and common cases */ + if (!weights) { + if (no_of_edges != 0) { + IGRAPH_WARNING("No weights given for Barrat's transitivity, unweighted version is used."); + } + return igraph_transitivity_local_undirected(graph, res, vids, mode); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Edge weight vector length (%ld) not equal to " + "number of edges (%ld).", IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); + } + + if (no_of_nodes == 0) { + igraph_vector_clear(res); + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_has_multiple(graph, &has_multiple)); + if (has_multiple) { + IGRAPH_ERROR( + "Barrat's weighted transitivity measure works only if the graph " + "has no multiple edges.", IGRAPH_EINVAL + ); + } + + /* Preconditions validated, now we can call the real implementation */ + + if (igraph_vs_is_all(&vids)) { + return igraph_i_transitivity_barrat4(graph, res, vids, weights, mode); + } else { + return igraph_i_transitivity_barrat1(graph, res, vids, weights, mode); + } +} diff --git a/src/rigraph/core/properties/triangles_template.h b/src/rigraph/core/properties/triangles_template.h new file mode 100644 index 0000000..61418fd --- /dev/null +++ b/src/rigraph/core/properties/triangles_template.h @@ -0,0 +1,143 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifdef TRANSIT +#define TRANSIT_TRIEDGES +#endif + +long int no_of_nodes = igraph_vcount(graph); +long int node, i, j, nn; +igraph_adjlist_t allneis; +igraph_vector_int_t *neis1, *neis2; +long int neilen1, neilen2; +long int *neis; +long int maxdegree; + +#ifdef TRANSIT_TRIEDGES +long int deg1; +#endif + +igraph_vector_int_t order; +igraph_vector_int_t rank; +igraph_vector_t degree; + +if (no_of_nodes == 0) { +#ifndef TRIANGLES + igraph_vector_clear(res); +#else + igraph_vector_int_clear(res); +#endif + return IGRAPH_SUCCESS; +} + +igraph_vector_int_init(&order, no_of_nodes); +IGRAPH_FINALLY(igraph_vector_int_destroy, &order); +IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + +IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); +IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + +for (i = 0; i < no_of_nodes; i++) { + VECTOR(degree)[i] = igraph_vector_int_size(igraph_adjlist_get(&allneis, i)); +} + +maxdegree = (long int) igraph_vector_max(°ree) + 1; +igraph_vector_order1_int(°ree, &order, maxdegree); +igraph_vector_int_init(&rank, no_of_nodes); +IGRAPH_FINALLY(igraph_vector_int_destroy, &rank); +for (i = 0; i < no_of_nodes; i++) { + VECTOR(rank)[ VECTOR(order)[i] ] = no_of_nodes - i - 1; +} + +IGRAPH_CHECK(igraph_i_trans4_al_simplify(&allneis, &rank)); + +neis = IGRAPH_CALLOC(no_of_nodes, long int); +if (neis == 0) { + IGRAPH_ERROR("undirected local transitivity failed", IGRAPH_ENOMEM); +} +IGRAPH_FINALLY(igraph_free, neis); + +#ifndef TRIANGLES + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + igraph_vector_null(res); +#else + igraph_vector_int_clear(res); +#endif + +for (nn = no_of_nodes - 1; nn >= 0; nn--) { + node = VECTOR(order)[nn]; + + IGRAPH_ALLOW_INTERRUPTION(); + + neis1 = igraph_adjlist_get(&allneis, node); + neilen1 = igraph_vector_int_size(neis1); + +#ifdef TRANSIT_TRIEDGES + deg1 = (long int) VECTOR(degree)[node]; +#endif + + /* Mark the neighbors of the node */ + for (i = 0; i < neilen1; i++) { + neis[ (long int) VECTOR(*neis1)[i] ] = node + 1; + } + + for (i = 0; i < neilen1; i++) { + long int nei = (long int) VECTOR(*neis1)[i]; + neis2 = igraph_adjlist_get(&allneis, nei); + neilen2 = igraph_vector_int_size(neis2); + for (j = 0; j < neilen2; j++) { + long int nei2 = (long int) VECTOR(*neis2)[j]; + if (neis[nei2] == node + 1) { +#ifndef TRIANGLES + VECTOR(*res)[nei2] += 1; + VECTOR(*res)[nei] += 1; + VECTOR(*res)[node] += 1; +#else + IGRAPH_CHECK(igraph_vector_int_push_back(res, node)); + IGRAPH_CHECK(igraph_vector_int_push_back(res, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(res, nei2)); +#endif + } + } + } + +#ifdef TRANSIT + if (mode == IGRAPH_TRANSITIVITY_ZERO && deg1 < 2) { + VECTOR(*res)[node] = 0.0; + } else { + VECTOR(*res)[node] = VECTOR(*res)[node] / deg1 / (deg1 - 1) * 2.0; + } +#endif +} + +igraph_free(neis); +igraph_adjlist_destroy(&allneis); +igraph_vector_int_destroy(&rank); +igraph_vector_destroy(°ree); +igraph_vector_int_destroy(&order); +IGRAPH_FINALLY_CLEAN(5); + +#ifdef TRANSIT_TRIEDGES +#undef TRANSIT_TRIEDGES +#endif diff --git a/src/rigraph/core/properties/triangles_template1.h b/src/rigraph/core/properties/triangles_template1.h new file mode 100644 index 0000000..3ab191f --- /dev/null +++ b/src/rigraph/core/properties/triangles_template1.h @@ -0,0 +1,95 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +long int no_of_nodes = igraph_vcount(graph); +igraph_vit_t vit; +long int nodes_to_calc; +igraph_vector_int_t *neis1, *neis2; +igraph_real_t triangles; +long int i, j, k; +long int neilen1, neilen2; +long int *neis; +igraph_lazy_adjlist_t adjlist; + +IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); +IGRAPH_FINALLY(igraph_vit_destroy, &vit); +nodes_to_calc = IGRAPH_VIT_SIZE(vit); + +if (nodes_to_calc == 0) { + igraph_vector_clear(res); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +neis = IGRAPH_CALLOC(no_of_nodes, long int); +if (neis == 0) { + IGRAPH_ERROR("local undirected transitivity failed", IGRAPH_ENOMEM); +} +IGRAPH_FINALLY(igraph_free, neis); + +IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); + +IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); +IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + +for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + long int node = IGRAPH_VIT_GET(vit); + + IGRAPH_ALLOW_INTERRUPTION(); + + neis1 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) node); + neilen1 = igraph_vector_int_size(neis1); + for (j = 0; j < neilen1; j++) { + neis[ (long int)VECTOR(*neis1)[j] ] = i + 1; + } + triangles = 0; + + for (j = 0; j < neilen1; j++) { + long int v = (long int) VECTOR(*neis1)[j]; + neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) v); + neilen2 = igraph_vector_int_size(neis2); + for (k = 0; k < neilen2; k++) { + long int v2 = (long int) VECTOR(*neis2)[k]; + if (neis[v2] == i + 1) { + triangles += 1.0; + } + } + } + +#ifdef TRANSIT + if (mode == IGRAPH_TRANSITIVITY_ZERO && neilen1 < 2) { + VECTOR(*res)[i] = 0.0; + } else { + VECTOR(*res)[i] = triangles / neilen1 / (neilen1 - 1); + } +#else + VECTOR(*res)[i] = triangles / 2; +#endif +} + +igraph_lazy_adjlist_destroy(&adjlist); +IGRAPH_FREE(neis); +igraph_vit_destroy(&vit); +IGRAPH_FINALLY_CLEAN(3); diff --git a/src/rigraph/core/random/random.c b/src/rigraph/core/random/random.c new file mode 100644 index 0000000..0cb0497 --- /dev/null +++ b/src/rigraph/core/random/random.c @@ -0,0 +1,2284 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_random.h" + +#include "igraph_nongraph.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_memory.h" + +#include "../core/math.h" + +#include "config.h" +#include +#include + +/** + * \section about_rngs + * + *
+ * About random numbers in igraph, use cases + * + * + * Some algorithms in igraph, e.g. the generation of random graphs, + * require random number generators (RNGs). Prior to version 0.6 + * igraph did not have a sophisticated way to deal with random number + * generators at the C level, but this has changed. From version 0.6 + * different and multiple random number generators are supported. + * + *
+ * + */ + +/** + * \section rng_use_cases + * + *
Use cases + * + *
Normal (default) use + * + * If the user does not use any of the RNG functions explicitly, but calls + * some of the randomized igraph functions, then a default RNG is set + * up the first time an igraph function needs random numbers. The + * seed of this RNG is the output of the time(0) function + * call, using the time function from the standard C + * library. This ensures that igraph creates a different random graph, + * each time the C program is called. + * + * + * + * The created default generator is stored internally and can be + * queried with the \ref igraph_rng_default() function. + * + *
+ * + *
Reproducible simulations + * + * If reproducible results are needed, then the user should set the + * seed of the default random number generator explicitly, using the + * \ref igraph_rng_seed() function on the default generator, \ref + * igraph_rng_default(). When setting the seed to the same number, + * igraph generates exactly the same random graph (or series of random + * graphs). + * + *
+ * + *
Changing the default generator + * + * By default igraph uses the \ref igraph_rng_default() random number + * generator. This can be changed any time by calling \ref + * igraph_rng_set_default(), with an already initialized random number + * generator. Note that the old (replaced) generator is not + * destroyed, so no memory is deallocated. + * + *
+ * + *
Using multiple generators + * + * igraph also provides functions to set up multiple random number + * generators, using the \ref igraph_rng_init() function, and then + * generating random numbers from them, e.g. with \ref igraph_rng_get_integer() + * and/or \ref igraph_rng_get_unif() calls. + * + * + * + * Note that initializing a new random number generator is + * independent of the generator that the igraph functions themselves + * use. If you want to replace that, then please use \ref + * igraph_rng_set_default(). + * + *
+ * + *
Example + * + * \example examples/simple/random_seed.c + * + *
+ * + *
+ */ + +/* ------------------------------------ */ + +typedef struct { + int i, j; + long int x[31]; +} igraph_i_rng_glibc2_state_t; + +static unsigned long int igraph_i_rng_glibc2_get(int *i, int *j, int n, long int *x) { + unsigned long int k; + + x[*i] += x[*j]; + k = (x[*i] >> 1) & 0x7FFFFFFF; + + (*i)++; + if (*i == n) { + *i = 0; + } + + (*j)++ ; + if (*j == n) { + *j = 0; + } + + return k; +} + +static unsigned long int igraph_rng_glibc2_get(void *vstate) { + igraph_i_rng_glibc2_state_t *state = + (igraph_i_rng_glibc2_state_t*) vstate; + return igraph_i_rng_glibc2_get(&state->i, &state->j, 31, state->x); +} + +static igraph_real_t igraph_rng_glibc2_get_real(void *state) { + return igraph_rng_glibc2_get(state) / 2147483648.0; +} + +/* this function is independent of the bit size */ + +static void igraph_i_rng_glibc2_init(long int *x, int n, + unsigned long int s) { + int i; + + if (s == 0) { + s = 1; + } + + x[0] = (long) s; + for (i = 1 ; i < n ; i++) { + const long int h = s / 127773; + const long int t = 16807 * ((long) s - h * 127773) - h * 2836; + if (t < 0) { + s = (unsigned long) t + 2147483647 ; + } else { + s = (unsigned long) t ; + } + + x[i] = (long int) s ; + } +} + +static int igraph_rng_glibc2_seed(void *vstate, unsigned long int seed) { + igraph_i_rng_glibc2_state_t *state = + (igraph_i_rng_glibc2_state_t*) vstate; + int i; + + igraph_i_rng_glibc2_init(state->x, 31, seed); + + state->i = 3; + state->j = 0; + + for (i = 0; i < 10 * 31; i++) { + igraph_rng_glibc2_get(state); + } + + return IGRAPH_SUCCESS; +} + +static int igraph_rng_glibc2_init(void **state) { + igraph_i_rng_glibc2_state_t *st; + + st = IGRAPH_CALLOC(1, igraph_i_rng_glibc2_state_t); + if (!st) { + IGRAPH_ERROR("Cannot initialize RNG", IGRAPH_ENOMEM); + } + (*state) = st; + + igraph_rng_glibc2_seed(st, 0); + + return IGRAPH_SUCCESS; +} + +static void igraph_rng_glibc2_destroy(void *vstate) { + igraph_i_rng_glibc2_state_t *state = + (igraph_i_rng_glibc2_state_t*) vstate; + IGRAPH_FREE(state); +} + +/** + * \var igraph_rngtype_glibc2 + * \brief The random number generator introduced in GNU libc 2. + * + * This is a linear feedback shift register generator with a 128-byte + * buffer. This generator was the default prior to igraph version 0.6, + * at least on systems relying on GNU libc. + * + * This generator was ported from the GNU Scientific Library. It is a + * reimplementation and does not call the system glibc generator. + */ + +const igraph_rng_type_t igraph_rngtype_glibc2 = { + /* name= */ "LIBC", + /* min= */ 0, + /* max= */ 0x7fffffffUL, + /* init= */ igraph_rng_glibc2_init, + /* destroy= */ igraph_rng_glibc2_destroy, + /* seed= */ igraph_rng_glibc2_seed, + /* get= */ igraph_rng_glibc2_get, + /* get_real= */ igraph_rng_glibc2_get_real, + /* get_norm= */ 0, + /* get_geom= */ 0, + /* get_binom= */ 0, + /* get_exp= */ 0, + /* get_gamma= */ 0 +}; + +/* ------------------------------------ */ + +typedef struct { + unsigned long int x; +} igraph_i_rng_rand_state_t; + +static unsigned long int igraph_rng_rand_get(void *vstate) { + igraph_i_rng_rand_state_t *state = vstate; + state->x = (1103515245 * state->x + 12345) & 0x7fffffffUL; + return state->x; +} + +static igraph_real_t igraph_rng_rand_get_real(void *vstate) { + return igraph_rng_rand_get (vstate) / 2147483648.0 ; +} + +static int igraph_rng_rand_seed(void *vstate, unsigned long int seed) { + igraph_i_rng_rand_state_t *state = vstate; + state->x = seed; + return IGRAPH_SUCCESS; +} + +static int igraph_rng_rand_init(void **state) { + igraph_i_rng_rand_state_t *st; + + st = IGRAPH_CALLOC(1, igraph_i_rng_rand_state_t); + if (!st) { + IGRAPH_ERROR("Cannot initialize RNG", IGRAPH_ENOMEM); + } + (*state) = st; + + igraph_rng_rand_seed(st, 0); + + return IGRAPH_SUCCESS; +} + +static void igraph_rng_rand_destroy(void *vstate) { + igraph_i_rng_rand_state_t *state = + (igraph_i_rng_rand_state_t*) vstate; + IGRAPH_FREE(state); +} + +/** + * \var igraph_rngtype_rand + * \brief The old BSD rand/srand random number generator. + * + * The sequence is + * x_{n+1} = (a x_n + c) mod m + * with a = 1103515245, c = 12345 and + * m = 2^31 = 2147483648. + * The seed specifies the initial value, x_1. + * + *
+ * The theoretical value of x_{10001} is 1910041713. + * + * + * The period of this generator is 2^31. + * + * + * This generator is not very good—the low bits of successive + * numbers are correlated. + * + * + * This generator was ported from the GNU Scientific Library. + */ + +const igraph_rng_type_t igraph_rngtype_rand = { + /* name= */ "RAND", + /* min= */ 0, + /* max= */ 0x7fffffffUL, + /* init= */ igraph_rng_rand_init, + /* destroy= */ igraph_rng_rand_destroy, + /* seed= */ igraph_rng_rand_seed, + /* get= */ igraph_rng_rand_get, + /* get_real= */ igraph_rng_rand_get_real, + /* get_norm= */ 0, + /* get_geom= */ 0, + /* get_binom= */ 0, + /* get_exp= */ 0, + /* get_gamma= */ 0 +}; + +/* ------------------------------------ */ + +#define N 624 /* Period parameters */ +#define M 397 + +/* most significant w-r bits */ +static const unsigned long UPPER_MASK = 0x80000000UL; + +/* least significant r bits */ +static const unsigned long LOWER_MASK = 0x7fffffffUL; + +typedef struct { + unsigned long mt[N]; + int mti; +} igraph_i_rng_mt19937_state_t; + +static unsigned long int igraph_rng_mt19937_get(void *vstate) { + igraph_i_rng_mt19937_state_t *state = vstate; + + unsigned long k ; + unsigned long int *const mt = state->mt; + +#define MAGIC(y) (((y)&0x1) ? 0x9908b0dfUL : 0) + + if (state->mti >= N) { + /* generate N words at one time */ + int kk; + + for (kk = 0; kk < N - M; kk++) { + unsigned long y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + M] ^ (y >> 1) ^ MAGIC(y); + } + for (; kk < N - 1; kk++) { + unsigned long y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ MAGIC(y); + } + + { + unsigned long y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK); + mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ MAGIC(y); + } + + state->mti = 0; + } + +#undef MAGIC + + /* Tempering */ + + k = mt[state->mti]; + k ^= (k >> 11); + k ^= (k << 7) & 0x9d2c5680UL; + k ^= (k << 15) & 0xefc60000UL; + k ^= (k >> 18); + + state->mti++; + + return k; +} + +static igraph_real_t igraph_rng_mt19937_get_real(void *vstate) { + return igraph_rng_mt19937_get (vstate) / 4294967296.0 ; +} + +static int igraph_rng_mt19937_seed(void *vstate, unsigned long int seed) { + igraph_i_rng_mt19937_state_t *state = vstate; + int i; + + memset(state, 0, sizeof(igraph_i_rng_mt19937_state_t)); + + if (seed == 0) { + seed = 4357; /* the default seed is 4357 */ + } + state->mt[0] = seed & 0xffffffffUL; + + for (i = 1; i < N; i++) { + /* See Knuth's "Art of Computer Programming" Vol. 2, 3rd + Ed. p.106 for multiplier. */ + state->mt[i] = + (1812433253UL * (state->mt[i - 1] ^ (state->mt[i - 1] >> 30)) + + (unsigned long) i); + state->mt[i] &= 0xffffffffUL; + } + + state->mti = i; + return IGRAPH_SUCCESS; +} + +static int igraph_rng_mt19937_init(void **state) { + igraph_i_rng_mt19937_state_t *st; + + st = IGRAPH_CALLOC(1, igraph_i_rng_mt19937_state_t); + if (!st) { + IGRAPH_ERROR("Cannot initialize RNG", IGRAPH_ENOMEM); + } + (*state) = st; + + igraph_rng_mt19937_seed(st, 0); + + return IGRAPH_SUCCESS; +} + +static void igraph_rng_mt19937_destroy(void *vstate) { + igraph_i_rng_mt19937_state_t *state = + (igraph_i_rng_mt19937_state_t*) vstate; + IGRAPH_FREE(state); +} + +/** + * \var igraph_rngtype_mt19937 + * \brief The MT19937 random number generator. + * + * The MT19937 generator of Makoto Matsumoto and Takuji Nishimura is a + * variant of the twisted generalized feedback shift-register + * algorithm, and is known as the “Mersenne Twister” generator. It has + * a Mersenne prime period of 2^19937 - 1 (about 10^6000) and is + * equi-distributed in 623 dimensions. It has passed the diehard + * statistical tests. It uses 624 words of state per generator and is + * comparable in speed to the other generators. The original generator + * used a default seed of 4357 and choosing \c s equal to zero in + * \c gsl_rng_set reproduces this. Later versions switched to 5489 as the + * default seed, you can choose this explicitly via \ref igraph_rng_seed() + * instead if you require it. + * + * + * For more information see, + * Makoto Matsumoto and Takuji Nishimura, “Mersenne Twister: A + * 623-dimensionally equidistributed uniform pseudorandom number + * generator”. ACM Transactions on Modeling and Computer Simulation, + * Vol. 8, No. 1 (Jan. 1998), Pages 3–30 + * + * + * The generator \c igraph_rngtype_mt19937 uses the second revision of the + * seeding procedure published by the two authors above in 2002. The + * original seeding procedures could cause spurious artifacts for some + * seed values. + * + * + * This generator was ported from the GNU Scientific Library. + */ + +const igraph_rng_type_t igraph_rngtype_mt19937 = { + /* name= */ "MT19937", + /* min= */ 0, + /* max= */ 0xffffffffUL, + /* init= */ igraph_rng_mt19937_init, + /* destroy= */ igraph_rng_mt19937_destroy, + /* seed= */ igraph_rng_mt19937_seed, + /* get= */ igraph_rng_mt19937_get, + /* get_real= */ igraph_rng_mt19937_get_real, + /* get_norm= */ 0, + /* get_geom= */ 0, + /* get_binom= */ 0, + /* get_exp= */ 0, + /* get_gamma= */ 0 +}; + +#undef N +#undef M + +/* ------------------------------------ */ + +igraph_i_rng_mt19937_state_t igraph_i_rng_default_state; + +#define addr(a) (&a) + +/** + * \var igraph_i_rng_default + * The default igraph random number generator + * + * This generator is used by all builtin igraph functions that need to + * generate random numbers; e.g. all random graph generators. + * + * You can use \ref igraph_i_rng_default with \ref igraph_rng_seed() + * to set its seed. + * + * You can change the default generator using the \ref + * igraph_rng_set_default() function. + */ + +igraph_rng_t igraph_i_rng_default = { + addr(igraph_rngtype_mt19937), + addr(igraph_i_rng_default_state), + /* def= */ 1 +}; + +#undef addr + +/** + * \function igraph_rng_set_default + * \brief Set the default igraph random number generator. + * + * \param rng The random number generator to use as default from now + * on. Calling \ref igraph_rng_destroy() on it, while it is still + * being used as the default will result in crashes and/or + * unpredictable results. + * + * Time complexity: O(1). + */ + +void igraph_rng_set_default(igraph_rng_t *rng) { + igraph_i_rng_default = (*rng); +} + + +/* ------------------------------------ */ + +/** + * \function igraph_rng_default + * \brief Query the default random number generator. + * + * \return A pointer to the default random number generator. + * + * \sa igraph_rng_set_default() + */ + +igraph_rng_t *igraph_rng_default() { + return &igraph_i_rng_default; +} + +/* ------------------------------------ */ + +static double igraph_norm_rand(igraph_rng_t *rng); +static double igraph_rgeom(igraph_rng_t *rng, double p); +static double igraph_rbinom(igraph_rng_t *rng, double nin, double pp); +static double igraph_rexp(igraph_rng_t *rng, double rate); +static double igraph_rgamma(igraph_rng_t *rng, double shape, double scale); + +/** + * \function igraph_rng_init + * \brief Initialize a random number generator. + * + * This function allocates memory for a random number generator, with + * the given type, and sets its seed to the default. + * + * \param rng Pointer to an uninitialized RNG. + * \param type The type of the RNG, like \ref igraph_rngtype_glibc2, + * \ref igraph_rngtype_mt19937 or \ref igraph_rngtype_rand. + * \return Error code. + * + * Time complexity: depends on the type of the generator, but usually + * it should be O(1). + */ + +int igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type) { + rng->type = type; + IGRAPH_CHECK(rng->type->init(&rng->state)); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_rng_destroy + * \brief Deallocate memory associated with a random number generator. + * + * \param rng The RNG to destroy. Do not destroy an RNG that is used + * as the default igraph RNG. + * + * Time complexity: O(1). + */ + +void igraph_rng_destroy(igraph_rng_t *rng) { + rng->type->destroy(rng->state); +} + +/** + * \function igraph_rng_seed + * \brief Set the seed of a random number generator. + * + * \param rng The RNG. + * \param seed The new seed. + * \return Error code. + * + * Time complexity: usually O(1), but may depend on the type of the + * RNG. + */ +int igraph_rng_seed(igraph_rng_t *rng, unsigned long int seed) { + const igraph_rng_type_t *type = rng->type; + rng->def = 0; + IGRAPH_CHECK(type->seed(rng->state, seed)); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_rng_max + * \brief Query the maximum possible integer for a random number generator. + * + * \param rng The RNG. + * \return The largest possible integer that can be generated by + * calling \ref igraph_rng_get_integer() on the RNG. + * + * Time complexity: O(1). + */ + +unsigned long int igraph_rng_max(igraph_rng_t *rng) { + const igraph_rng_type_t *type = rng->type; + return type->max; +} + +/** + * \function igraph_rng_min + * \brief Query the minimum possible integer for a random number generator. + * + * This function will be removed in a future version. Assume zero + * as the retun value. + * + * \param rng The RNG. + * \return The smallest possible integer that can be generated by + * calling \ref igraph_rng_get_integer() on the RNG. + * + * Time complexity: O(1). + * + * \deprecated 0.9.3 + */ + +unsigned long int igraph_rng_min(igraph_rng_t *rng) { + const igraph_rng_type_t *type = rng->type; + IGRAPH_WARNING("igraph_rng_min() is deprecated; assume 0 as the return value."); + return type->min; +} + +/** + * \function igraph_rng_name + * \brief Query the type of a random number generator. + * + * \param rng The RNG. + * \return The name of the type of the generator. Do not deallocate or + * change the returned string pointer. + * + * Time complexity: O(1). + */ + +const char *igraph_rng_name(igraph_rng_t *rng) { + const igraph_rng_type_t *type = rng->type; + return type->name; +} + +/** + * \function igraph_rng_get_integer + * \brief Generate an integer random number from an interval. + * + * \param rng Pointer to the RNG to use for the generation. Use \ref + * igraph_rng_default() here to use the default igraph RNG. + * \param l Lower limit, inclusive, it can be negative as well. + * \param h Upper limit, inclusive, it can be negative as well, but it + * should be at least l. + * \return The generated random integer. + * + * Time complexity: depends on the generator, but should be usually + * O(1). + */ + +long int igraph_rng_get_integer(igraph_rng_t *rng, + long int l, long int h) { + const igraph_rng_type_t *type = rng->type; + /* We require the random integer to be in the range [l, h]. We do so by + * first casting (truncate toward zero) to the range [0, h - l] and then add + * l to arrive at the range [l, h]. That is, we calculate + * (long)( r * (h - l + 1) ) + l + * instead of + * (long)( r * (h - l + 1) + l), + * please note the difference in the parentheses. + * + * In the latter formulation, if l is negative, this would incorrectly lead + * to the range [l + 1, h] instead of the desired [l, h] because negative + * numbers are truncated towards zero when cast. For example, if l = -5, any + * real in the range (-5, -4] would get cast to -4, not to -5. + */ + if (type->get_real) { + return (long int)(type->get_real(rng->state) * (h - l + 1)) + l; + } else if (type->get) { + unsigned long int max = type->max; + return (long int)(type->get(rng->state) / ((double)max + 1) * (h - l + 1)) + l; + } + IGRAPH_FATAL("Internal random generator error"); +} + +/** + * \function igraph_rng_get_normal + * \brief Normally distributed random numbers. + * + * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() + * here to use the default igraph RNG. + * \param m The mean. + * \param s Standard deviation. + * \return The generated normally distributed random number. + * + * Time complexity: depends on the type of the RNG. + */ + +igraph_real_t igraph_rng_get_normal(igraph_rng_t *rng, + igraph_real_t m, igraph_real_t s) { + const igraph_rng_type_t *type = rng->type; + if (type->get_norm) { + return type->get_norm(rng->state) * s + m; + } else { + return igraph_norm_rand(rng) * s + m; + } +} + +/** + * \function igraph_rng_get_unif + * \brief Generate real, uniform random numbers from an interval. + * + * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() + * here to use the default igraph RNG. + * \param l The lower bound, it can be negative. + * \param h The upper bound, it can be negative, but it has to be + * larger than the lower bound. + * \return The generated uniformly distributed random number. + * + * Time complexity: depends on the type of the RNG. + */ + +igraph_real_t igraph_rng_get_unif(igraph_rng_t *rng, + igraph_real_t l, igraph_real_t h) { + const igraph_rng_type_t *type = rng->type; + if (type->get_real) { + return type->get_real(rng->state) * (h - l) + l; + } else if (type->get) { + unsigned long int max = type->max; + return type->get(rng->state) / ((double)max + 1) * (double)(h - l) + l; + } + IGRAPH_FATAL("Internal random generator error"); +} + +/** + * \function igraph_rng_get_unif01 + * \brief Generate real, uniform random number from the unit interval. + * + * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() + * here to use the default igraph RNG. + * \return The generated uniformly distributed random number. + * + * Time complexity: depends on the type of the RNG. + */ + +igraph_real_t igraph_rng_get_unif01(igraph_rng_t *rng) { + const igraph_rng_type_t *type = rng->type; + if (type->get_real) { + return type->get_real(rng->state); + } else if (type->get) { + unsigned long int max = type->max; + return type->get(rng->state) / ((double)max + 1); + } + IGRAPH_FATAL("Internal random generator error"); +} + +/** + * \function igraph_rng_get_geom + * \brief Generate geometrically distributed random numbers. + * + * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() + * here to use the default igraph RNG. + * \param p The probability of success in each trial. Must be larger + * than zero and smaller or equal to 1. + * \return The generated geometrically distributed random number. + * + * Time complexity: depends on the type of the RNG. + */ + +igraph_real_t igraph_rng_get_geom(igraph_rng_t *rng, igraph_real_t p) { + const igraph_rng_type_t *type = rng->type; + if (type->get_geom) { + return type->get_geom(rng->state, p); + } else { + return igraph_rgeom(rng, p); + } +} + +/** + * \function igraph_rng_get_binom + * \brief Generate binomially distributed random numbers. + * + * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() + * here to use the default igraph RNG. + * \param n Number of observations. + * \param p Probability of an event. + * \return The generated binomially distributed random number. + * + * Time complexity: depends on the type of the RNG. + */ + +igraph_real_t igraph_rng_get_binom(igraph_rng_t *rng, long int n, + igraph_real_t p) { + const igraph_rng_type_t *type = rng->type; + if (type->get_binom) { + return type->get_binom(rng->state, n, p); + } else { + return igraph_rbinom(rng, n, p); + } +} + +/** + * \function igraph_rng_get_gamma + * \brief Generate sample from a Gamma distribution. + * + * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() + * here to use the default igraph RNG. + * \param shape Shape parameter. + * \param scale Scale parameter. + * \return The generated sample + * + * Time complexity: depends on RNG. + */ + +igraph_real_t igraph_rng_get_gamma(igraph_rng_t *rng, igraph_real_t shape, + igraph_real_t scale) { + const igraph_rng_type_t *type = rng->type; + if (type->get_gamma) { + return type->get_gamma(rng->state, shape, scale); + } else { + return igraph_rgamma(rng, shape, scale); + } +} + +unsigned long int igraph_rng_get_int31(igraph_rng_t *rng) { + const igraph_rng_type_t *type = rng->type; + unsigned long int max = type->max; + if (type->get && max == 0x7FFFFFFFUL) { + return type->get(rng->state); + } else if (type->get_real) { + return (unsigned long int) (type->get_real(rng->state) * 0x7FFFFFFFUL); + } else { + return (unsigned long int) (igraph_rng_get_unif01(rng) * 0x7FFFFFFFUL); + } +} + +igraph_real_t igraph_rng_get_exp(igraph_rng_t *rng, igraph_real_t rate) { + const igraph_rng_type_t *type = rng->type; + if (type->get_exp) { + return type->get_exp(rng->state, rate); + } else { + return igraph_rexp(rng, rate); + } +} + + +/* + * \ingroup internal + * + * This function appends the rest of the needed random number to the + * result vector. + */ + +static int igraph_i_random_sample_alga(igraph_vector_t *res, + igraph_integer_t l, igraph_integer_t h, + igraph_integer_t length) { + igraph_real_t N = h - l + 1; + igraph_real_t n = length; + + igraph_real_t top = N - n; + igraph_real_t Nreal = N; + igraph_real_t S = 0; + igraph_real_t V, quot; + + l = l - 1; + + while (n >= 2) { + V = RNG_UNIF01(); + S = 1; + quot = top / Nreal; + while (quot > V) { + S += 1; + top = -1.0 + top; + Nreal = -1.0 + Nreal; + quot = (quot * top) / Nreal; + } + l += S; + igraph_vector_push_back(res, l); /* allocated */ + Nreal = -1.0 + Nreal; n = -1 + n; + } + + S = floor(round(Nreal) * RNG_UNIF01()); + l += S + 1; + igraph_vector_push_back(res, l); /* allocated */ + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup nongraph + * \function igraph_random_sample + * \brief Generates an increasing random sequence of integers. + * + * + * This function generates an increasing sequence of random integer + * numbers from a given interval. The algorithm is taken literally + * from (Vitter 1987). This method can be used for generating numbers from a + * \em very large interval. It is primarily created for randomly + * selecting some edges from the sometimes huge set of possible edges + * in a large graph. + * + * Note that the type of the lower and the upper limit is \c igraph_real_t, + * not \c igraph_integer_t. This does not mean that you can pass fractional + * numbers there; these values must still be integral, but we need the + * longer range of \c igraph_real_t in several places in the library + * (for instance, when generating Erdos-Renyi graphs). + * \param res Pointer to an initialized vector. This will hold the + * result. It will be resized to the proper size. + * \param l The lower limit of the generation interval (inclusive). This must + * be less than or equal to the upper limit, and it must be integral. + * Passing a fractional number here results in undefined behaviour. + * \param h The upper limit of the generation interval (inclusive). This must + * be greater than or equal to the lower limit, and it must be integral. + * Passing a fractional number here results in undefined behaviour. + * \param length The number of random integers to generate. + * \return The error code \c IGRAPH_EINVAL is returned in each of the + * following cases: (1) The given lower limit is greater than the + * given upper limit, i.e. \c l > \c h. (2) Assuming that + * \c l < \c h and N is the sample size, the above error code is + * returned if N > |\c h - \c l|, i.e. the sample size exceeds the + * size of the candidate pool. + * + * Time complexity: according to (Vitter 1987), the expected + * running time is O(length). + * + * + * Reference: + * \clist + * \cli (Vitter 1987) + * J. S. Vitter. An efficient algorithm for sequential random sampling. + * \emb ACM Transactions on Mathematical Software, \eme 13(1):58--67, 1987. + * \endclist + * + * \example examples/simple/igraph_random_sample.c + */ + +int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h, + igraph_integer_t length) { + igraph_real_t N = h - l + 1; + igraph_real_t n = length; + int retval; + + igraph_real_t nreal = length; + igraph_real_t ninv = (nreal != 0) ? 1.0 / nreal : 0.0; + igraph_real_t Nreal = N; + igraph_real_t Vprime; + igraph_real_t qu1 = -n + 1 + N; + igraph_real_t qu1real = -nreal + 1.0 + Nreal; + igraph_real_t negalphainv = -13; + igraph_real_t threshold = -negalphainv * n; + igraph_real_t S; + + /* getting back some sense of sanity */ + if (l > h) { + IGRAPH_ERROR("Lower limit is greater than upper limit", IGRAPH_EINVAL); + } + /* now we know that l <= h */ + if (length > N) { + IGRAPH_ERROR("Sample size exceeds size of candidate pool", IGRAPH_EINVAL); + } + + /* treat rare cases quickly */ + if (l == h) { + IGRAPH_CHECK(igraph_vector_resize(res, 1)); + VECTOR(*res)[0] = l; + return IGRAPH_SUCCESS; + } + if (length == 0) { + igraph_vector_clear(res); + return IGRAPH_SUCCESS; + } + if (length == N) { + long int i = 0; + IGRAPH_CHECK(igraph_vector_resize(res, length)); + for (i = 0; i < length; i++) { + VECTOR(*res)[i] = l++; + } + return IGRAPH_SUCCESS; + } + + igraph_vector_clear(res); + IGRAPH_CHECK(igraph_vector_reserve(res, length)); + + RNG_BEGIN(); + + Vprime = exp(log(RNG_UNIF01()) * ninv); + l = l - 1; + + while (n > 1 && threshold < N) { + igraph_real_t X, U; + igraph_real_t limit, t; + igraph_real_t negSreal, y1, y2, top, bottom; + igraph_real_t nmin1inv = 1.0 / (-1.0 + nreal); + while (1) { + while (1) { + X = Nreal * (-Vprime + 1.0); + S = floor(X); + /* if (S==0) { S=1; } */ + if (S < qu1) { + break; + } + Vprime = exp(log(RNG_UNIF01()) * ninv); + } + U = RNG_UNIF01(); + negSreal = -S; + + y1 = exp(log(U * Nreal / qu1real) * nmin1inv); + Vprime = y1 * (-X / Nreal + 1.0) * (qu1real / (negSreal + qu1real)); + if (Vprime <= 1.0) { + break; + } + + y2 = 1.0; + top = -1.0 + Nreal; + if (-1 + n > S) { + bottom = -nreal + Nreal; + limit = -S + N; + } else { + bottom = -1.0 + negSreal + Nreal; + limit = qu1; + } + for (t = -1 + N; t >= limit; t--) { + y2 = (y2 * top) / bottom; + top = -1.0 + top; + bottom = -1.0 + bottom; + } + if (Nreal / (-X + Nreal) >= y1 * exp(log(y2)*nmin1inv)) { + Vprime = exp(log(RNG_UNIF01()) * nmin1inv); + break; + } + Vprime = exp(log(RNG_UNIF01()) * ninv); + } + + l += S + 1; + igraph_vector_push_back(res, l); /* allocated */ + N = -S + (-1 + N); Nreal = negSreal + (-1.0 + Nreal); + n = -1 + n; nreal = -1.0 + nreal; ninv = nmin1inv; + qu1 = -S + qu1; qu1real = negSreal + qu1real; + threshold = threshold + negalphainv; + } + + if (n > 1) { + retval = igraph_i_random_sample_alga(res, (igraph_integer_t) l + 1, + (igraph_integer_t) h, + (igraph_integer_t) n); + } else { + retval = 0; + S = floor(N * Vprime); + l += S + 1; + igraph_vector_push_back(res, l); /* allocated */ + } + + RNG_END(); + + return retval; +} + +/* + * Mathlib : A C Library of Special Functions + * Copyright (C) 1998 Ross Ihaka + * Copyright (C) 2000 The R Development Core Team + * based on AS 111 (C) 1977 Royal Statistical Society + * and on AS 241 (C) 1988 Royal Statistical Society + * + * 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 2 of the License, or + * (at your option) 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * SYNOPSIS + * + * double qnorm5(double p, double mu, double sigma, + * int lower_tail, int log_p) + * {qnorm (..) is synonymous and preferred inside R} + * + * DESCRIPTION + * + * Compute the quantile function for the normal distribution. + * + * For small to moderate probabilities, algorithm referenced + * below is used to obtain an initial approximation which is + * polished with a final Newton step. + * + * For very large arguments, an algorithm of Wichura is used. + * + * REFERENCE + * + * Beasley, J. D. and S. G. Springer (1977). + * Algorithm AS 111: The percentage points of the normal distribution, + * Applied Statistics, 26, 118-121. + * + * Wichura, M.J. (1988). + * Algorithm AS 241: The Percentage Points of the Normal Distribution. + * Applied Statistics, 37, 477-484. + */ + +/* + * Mathlib : A C Library of Special Functions + * Copyright (C) 1998-2004 The R Development Core Team + * + * 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 2 of the License, or + * (at your option) 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Private header file for use during compilation of Mathlib */ +#ifndef MATHLIB_PRIVATE_H +#define MATHLIB_PRIVATE_H + +#define ML_POSINF IGRAPH_INFINITY +#define ML_NEGINF -IGRAPH_INFINITY +#define ML_NAN IGRAPH_NAN + +#define ML_ERROR(x) /* nothing */ +#define ML_UNDERFLOW (DBL_MIN * DBL_MIN) +#define ML_VALID(x) (!ISNAN(x)) + +#define ME_NONE 0 +/* no error */ +#define ME_DOMAIN 1 +/* argument out of domain */ +#define ME_RANGE 2 +/* value out of range */ +#define ME_NOCONV 4 +/* process did not converge */ +#define ME_PRECISION 8 +/* does not have "full" precision */ +#define ME_UNDERFLOW 16 +/* and underflow occurred (important for IEEE)*/ + +#define ML_ERR_return_NAN { ML_ERROR(ME_DOMAIN); return ML_NAN; } + +/* Wilcoxon Rank Sum Distribution */ + +#define WILCOX_MAX 50 + +/* Wilcoxon Signed Rank Distribution */ + +#define SIGNRANK_MAX 50 + +/* Formerly private part of Mathlib.h */ + +/* always remap internal functions */ +#define bd0 Rf_bd0 +#define chebyshev_eval Rf_chebyshev_eval +#define chebyshev_init Rf_chebyshev_init +#define i1mach Rf_i1mach +#define gammalims Rf_gammalims +#define lfastchoose Rf_lfastchoose +#define lgammacor Rf_lgammacor +#define stirlerr Rf_stirlerr + +/* Chebyshev Series */ + +int chebyshev_init(double*, int, double); +double chebyshev_eval(double, const double *, const int); + +/* Gamma and Related Functions */ + +void gammalims(double*, double*); +double lgammacor(double); /* log(gamma) correction */ +double stirlerr(double); /* Stirling expansion "error" */ + +double lfastchoose(double, double); + +double bd0(double, double); + +/* Consider adding these two to the API (Rmath.h): */ +double dbinom_raw(double, double, double, double, int); +double dpois_raw (double, double, int); +double pnchisq_raw(double, double, double, double, double, int); + +int i1mach(int); + +/* From toms708.c */ +void bratio(double a, double b, double x, double y, + double *w, double *w1, int *ierr); + + +#endif /* MATHLIB_PRIVATE_H */ + + +/* Utilities for `dpq' handling (density/probability/quantile) */ + +/* give_log in "d"; log_p in "p" & "q" : */ +#define give_log log_p +/* "DEFAULT" */ +/* --------- */ +#define R_D__0 (log_p ? ML_NEGINF : 0.) /* 0 */ +#define R_D__1 (log_p ? 0. : 1.) /* 1 */ +#define R_DT_0 (lower_tail ? R_D__0 : R_D__1) /* 0 */ +#define R_DT_1 (lower_tail ? R_D__1 : R_D__0) /* 1 */ + +#define R_D_Lval(p) (lower_tail ? (p) : (1 - (p))) /* p */ +#define R_D_Cval(p) (lower_tail ? (1 - (p)) : (p)) /* 1 - p */ + +#define R_D_val(x) (log_p ? log(x) : (x)) /* x in pF(x,..) */ +#define R_D_qIv(p) (log_p ? exp(p) : (p)) /* p in qF(p,..) */ +#define R_D_exp(x) (log_p ? (x) : exp(x)) /* exp(x) */ +#define R_D_log(p) (log_p ? (p) : log(p)) /* log(p) */ +#define R_D_Clog(p) (log_p ? log1p(-(p)) : (1 - (p)))/* [log](1-p) */ + +/* log(1-exp(x)): R_D_LExp(x) == (log1p(- R_D_qIv(x))) but even more stable:*/ +#define R_D_LExp(x) (log_p ? R_Log1_Exp(x) : log1p(-x)) + +/*till 1.8.x: + * #define R_DT_val(x) R_D_val(R_D_Lval(x)) + * #define R_DT_Cval(x) R_D_val(R_D_Cval(x)) */ +#define R_DT_val(x) (lower_tail ? R_D_val(x) : R_D_Clog(x)) +#define R_DT_Cval(x) (lower_tail ? R_D_Clog(x) : R_D_val(x)) + +/*#define R_DT_qIv(p) R_D_Lval(R_D_qIv(p)) * p in qF ! */ +#define R_DT_qIv(p) (log_p ? (lower_tail ? exp(p) : - expm1(p)) \ + : R_D_Lval(p)) + +/*#define R_DT_CIv(p) R_D_Cval(R_D_qIv(p)) * 1 - p in qF */ +#define R_DT_CIv(p) (log_p ? (lower_tail ? -expm1(p) : exp(p)) \ + : R_D_Cval(p)) + +#define R_DT_exp(x) R_D_exp(R_D_Lval(x)) /* exp(x) */ +#define R_DT_Cexp(x) R_D_exp(R_D_Cval(x)) /* exp(1 - x) */ + +#define R_DT_log(p) (lower_tail? R_D_log(p) : R_D_LExp(p))/* log(p) in qF */ +#define R_DT_Clog(p) (lower_tail? R_D_LExp(p): R_D_log(p))/* log(1-p) in qF*/ +#define R_DT_Log(p) (lower_tail? (p) : R_Log1_Exp(p)) +/* == R_DT_log when we already "know" log_p == TRUE :*/ + +#define R_Q_P01_check(p) \ + if ((log_p && p > 0) || \ + (!log_p && (p < 0 || p > 1)) ) \ + ML_ERR_return_NAN + +/* additions for density functions (C.Loader) */ +#define R_D_fexp(f,x) (give_log ? -0.5*log(f)+(x) : exp(x)/sqrt(f)) +#define R_D_forceint(x) floor((x) + 0.5) +#define R_D_nonint(x) (fabs((x) - floor((x)+0.5)) > 1e-7) +/* [neg]ative or [non int]eger : */ +#define R_D_negInonint(x) (x < 0. || R_D_nonint(x)) + +#define R_D_nonint_check(x) \ + if(R_D_nonint(x)) { \ + MATHLIB_WARNING("non-integer x = %f", x); \ + return R_D__0; \ + } + +double igraph_qnorm5(double p, double mu, double sigma, int lower_tail, int log_p) { + double p_, q, r, val; + +#ifdef IEEE_754 + if (ISNAN(p) || ISNAN(mu) || ISNAN(sigma)) { + return p + mu + sigma; + } +#endif + if (p == R_DT_0) { + return ML_NEGINF; + } + if (p == R_DT_1) { + return ML_POSINF; + } + R_Q_P01_check(p); + + if (sigma < 0) { + ML_ERR_return_NAN; + } + if (sigma == 0) { + return mu; + } + + p_ = R_DT_qIv(p);/* real lower_tail prob. p */ + q = p_ - 0.5; + + /*-- use AS 241 --- */ + /* double ppnd16_(double *p, long *ifault)*/ + /* ALGORITHM AS241 APPL. STATIST. (1988) VOL. 37, NO. 3 + + Produces the normal deviate Z corresponding to a given lower + tail area of P; Z is accurate to about 1 part in 10**16. + + (original fortran code used PARAMETER(..) for the coefficients + and provided hash codes for checking them...) + */ + if (fabs(q) <= .425) {/* 0.075 <= p <= 0.925 */ + r = .180625 - q * q; + val = + q * (((((((r * 2509.0809287301226727 + + 33430.575583588128105) * r + 67265.770927008700853) * r + + 45921.953931549871457) * r + 13731.693765509461125) * r + + 1971.5909503065514427) * r + 133.14166789178437745) * r + + 3.387132872796366608) + / (((((((r * 5226.495278852854561 + + 28729.085735721942674) * r + 39307.89580009271061) * r + + 21213.794301586595867) * r + 5394.1960214247511077) * r + + 687.1870074920579083) * r + 42.313330701600911252) * r + 1.); + } else { /* closer than 0.075 from {0,1} boundary */ + + /* r = min(p, 1-p) < 0.075 */ + if (q > 0) { + r = R_DT_CIv(p); /* 1-p */ + } else { + r = p_; /* = R_DT_Iv(p) ^= p */ + } + + r = sqrt(- ((log_p && + ((lower_tail && q <= 0) || (!lower_tail && q > 0))) ? + p : /* else */ log(r))); + /* r = sqrt(-log(r)) <==> min(p, 1-p) = exp( - r^2 ) */ + + if (r <= 5.) { /* <==> min(p,1-p) >= exp(-25) ~= 1.3888e-11 */ + r += -1.6; + val = (((((((r * 7.7454501427834140764e-4 + + .0227238449892691845833) * r + .24178072517745061177) * + r + 1.27045825245236838258) * r + + 3.64784832476320460504) * r + 5.7694972214606914055) * + r + 4.6303378461565452959) * r + + 1.42343711074968357734) + / (((((((r * + 1.05075007164441684324e-9 + 5.475938084995344946e-4) * + r + .0151986665636164571966) * r + + .14810397642748007459) * r + .68976733498510000455) * + r + 1.6763848301838038494) * r + + 2.05319162663775882187) * r + 1.); + } else { /* very close to 0 or 1 */ + r += -5.; + val = (((((((r * 2.01033439929228813265e-7 + + 2.71155556874348757815e-5) * r + + .0012426609473880784386) * r + .026532189526576123093) * + r + .29656057182850489123) * r + + 1.7848265399172913358) * r + 5.4637849111641143699) * + r + 6.6579046435011037772) + / (((((((r * + 2.04426310338993978564e-15 + 1.4215117583164458887e-7) * + r + 1.8463183175100546818e-5) * r + + 7.868691311456132591e-4) * r + .0148753612908506148525) + * r + .13692988092273580531) * r + + .59983220655588793769) * r + 1.); + } + + if (q < 0.0) { + val = -val; + } + /* return (q >= 0.)? r : -r ;*/ + } + return mu + sigma * val; +} + +static double fsign(double x, double y) { +#ifdef IEEE_754 + if (ISNAN(x) || ISNAN(y)) { + return x + y; + } +#endif + return ((y >= 0) ? fabs(x) : -fabs(x)); +} + +static int imax2(int x, int y) { + return (x < y) ? y : x; +} + +static int imin2(int x, int y) { + return (x < y) ? x : y; +} + +static double igraph_norm_rand(igraph_rng_t *rng) { + + double u1; + +#define BIG 134217728 /* 2^27 */ + /* unif_rand() alone is not of high enough precision */ + u1 = igraph_rng_get_unif01(rng); + u1 = (int)(BIG * u1) + igraph_rng_get_unif01(rng); + return igraph_qnorm5(u1 / BIG, 0.0, 1.0, 1, 0); +} + +/* + * Mathlib : A C Library of Special Functions + * Copyright (C) 1998 Ross Ihaka + * Copyright (C) 2000-2002 the R Development Core Team + * + * 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 2 of the License, or + * (at your option) 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * SYNOPSIS + * + * #include + * double exp_rand(void); + * + * DESCRIPTION + * + * Random variates from the standard exponential distribution. + * + * REFERENCE + * + * Ahrens, J.H. and Dieter, U. (1972). + * Computer methods for sampling from the exponential and + * normal distributions. + * Comm. ACM, 15, 873-882. + */ + +double igraph_exp_rand(igraph_rng_t *rng) { + /* q[k-1] = sum(log(2)^k / k!) k=1,..,n, */ + /* The highest n (here 8) is determined by q[n-1] = 1.0 */ + /* within standard precision */ + const double q[] = { + 0.6931471805599453, + 0.9333736875190459, + 0.9888777961838675, + 0.9984959252914960, + 0.9998292811061389, + 0.9999833164100727, + 0.9999985691438767, + 0.9999998906925558, + 0.9999999924734159, + 0.9999999995283275, + 0.9999999999728814, + 0.9999999999985598, + 0.9999999999999289, + 0.9999999999999968, + 0.9999999999999999, + 1.0000000000000000 + }; + double a, u, ustar, umin; + int i; + + a = 0.; + /* precaution if u = 0 is ever returned */ + u = igraph_rng_get_unif01(rng); + while (u <= 0.0 || u >= 1.0) { + u = igraph_rng_get_unif01(rng); + } + for (;;) { + u += u; + if (u > 1.0) { + break; + } + a += q[0]; + } + u -= 1.; + + if (u <= q[0]) { + return a + u; + } + + i = 0; + ustar = igraph_rng_get_unif01(rng); + umin = ustar; + do { + ustar = igraph_rng_get_unif01(rng); + if (ustar < umin) { + umin = ustar; + } + i++; + } while (u > q[i]); + return a + umin * q[0]; +} + +/* + * Mathlib : A C Library of Special Functions + * Copyright (C) 1998 Ross Ihaka + * Copyright (C) 2000-2001 The R Development Core Team + * + * 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 2 of the License, or + * (at your option) 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * SYNOPSIS + * + * #include + * double rpois(double lambda) + * + * DESCRIPTION + * + * Random variates from the Poisson distribution. + * + * REFERENCE + * + * Ahrens, J.H. and Dieter, U. (1982). + * Computer generation of Poisson deviates + * from modified normal distributions. + * ACM Trans. Math. Software 8, 163-179. + */ + +#define a0 -0.5 +#define a1 0.3333333 +#define a2 -0.2500068 +#define a3 0.2000118 +#define a4 -0.1661269 +#define a5 0.1421878 +#define a6 -0.1384794 +#define a7 0.1250060 + +#define one_7 0.1428571428571428571 +#define one_12 0.0833333333333333333 +#define one_24 0.0416666666666666667 + +#define repeat for(;;) + +#define FALSE 0 +#define TRUE 1 +#define M_1_SQRT_2PI 0.398942280401432677939946059934 /* 1/sqrt(2pi) */ + +static double igraph_rpois(igraph_rng_t *rng, double mu) { + /* Factorial Table (0:9)! */ + const double fact[10] = { + 1., 1., 2., 6., 24., 120., 720., 5040., 40320., 362880. + }; + + /* These are static --- persistent between calls for same mu : */ + static int l, m; + + static double b1, b2, c, c0, c1, c2, c3; + static double pp[36], p0, p, q, s, d, omega; + static double big_l;/* integer "w/o overflow" */ + static double muprev = 0., muprev2 = 0.;/*, muold = 0.*/ + + /* Local Vars [initialize some for -Wall]: */ + double del, difmuk = 0., E = 0., fk = 0., fx, fy, g, px, py, t, u = 0., v, x; + double pois = -1.; + int k, kflag, big_mu, new_big_mu = FALSE; + + if (!igraph_finite(mu)) { + ML_ERR_return_NAN; + } + + if (mu <= 0.) { + return 0.; + } + + big_mu = mu >= 10.; + if (big_mu) { + new_big_mu = FALSE; + } + + if (!(big_mu && mu == muprev)) {/* maybe compute new persistent par.s */ + + if (big_mu) { + new_big_mu = TRUE; + /* Case A. (recalculation of s,d,l because mu has changed): + * The Poisson probabilities pk exceed the discrete normal + * probabilities fk whenever k >= m(mu). + */ + muprev = mu; + s = sqrt(mu); + d = 6. * mu * mu; + big_l = floor(mu - 1.1484); + /* = an upper bound to m(mu) for all mu >= 10.*/ + } else { /* Small mu ( < 10) -- not using normal approx. */ + + /* Case B. (start new table and calculate p0 if necessary) */ + + /*muprev = 0.;-* such that next time, mu != muprev ..*/ + if (mu != muprev) { + muprev = mu; + m = imax2(1, (int) mu); + l = 0; /* pp[] is already ok up to pp[l] */ + q = p0 = p = exp(-mu); + } + + repeat { + /* Step U. uniform sample for inversion method */ + u = igraph_rng_get_unif01(rng); + if (u <= p0) { + return 0.; + } + + /* Step T. table comparison until the end pp[l] of the + pp-table of cumulative Poisson probabilities + (0.458 > ~= pp[9](= 0.45792971447) for mu=10 ) */ + if (l != 0) { + for (k = (u <= 0.458) ? 1 : imin2(l, m); k <= l; k++) + if (u <= pp[k]) { + return (double)k; + } + if (l == 35) { /* u > pp[35] */ + continue; + } + } + /* Step C. creation of new Poisson + probabilities p[l..] and their cumulatives q =: pp[k] */ + l++; + for (k = l; k <= 35; k++) { + p *= mu / k; + q += p; + pp[k] = q; + if (u <= q) { + l = k; + return (double)k; + } + } + l = 35; + } /* end(repeat) */ + }/* mu < 10 */ + + } /* end {initialize persistent vars} */ + + /* Only if mu >= 10 : ----------------------- */ + + /* Step N. normal sample */ + g = mu + s * igraph_norm_rand(rng);/* norm_rand() ~ N(0,1), standard normal */ + + if (g >= 0.) { + pois = floor(g); + /* Step I. immediate acceptance if pois is large enough */ + if (pois >= big_l) { + return pois; + } + /* Step S. squeeze acceptance */ + fk = pois; + difmuk = mu - fk; + u = igraph_rng_get_unif01(rng); /* ~ U(0,1) - sample */ + if (d * u >= difmuk * difmuk * difmuk) { + return pois; + } + } + + /* Step P. preparations for steps Q and H. + (recalculations of parameters if necessary) */ + + if (new_big_mu || mu != muprev2) { + /* Careful! muprev2 is not always == muprev + because one might have exited in step I or S + */ + muprev2 = mu; + omega = M_1_SQRT_2PI / s; + /* The quantities b1, b2, c3, c2, c1, c0 are for the Hermite + * approximations to the discrete normal probabilities fk. */ + + b1 = one_24 / mu; + b2 = 0.3 * b1 * b1; + c3 = one_7 * b1 * b2; + c2 = b2 - 15. * c3; + c1 = b1 - 6. * b2 + 45. * c3; + c0 = 1. - b1 + 3. * b2 - 15. * c3; + c = 0.1069 / mu; /* guarantees majorization by the 'hat'-function. */ + } + + if (g >= 0.) { + /* 'Subroutine' F is called (kflag=0 for correct return) */ + kflag = 0; + goto Step_F; + } + + + repeat { + /* Step E. Exponential Sample */ + + E = igraph_exp_rand(rng);/* ~ Exp(1) (standard exponential) */ + + /* sample t from the laplace 'hat' + (if t <= -0.6744 then pk < fk for all mu >= 10.) */ + u = 2 * igraph_rng_get_unif01(rng) - 1.; + t = 1.8 + fsign(E, u); + if (t > -0.6744) { + pois = floor(mu + s * t); + fk = pois; + difmuk = mu - fk; + + /* 'subroutine' F is called (kflag=1 for correct return) */ + kflag = 1; + +Step_F: /* 'subroutine' F : calculation of px,py,fx,fy. */ + + if (pois < 10) { /* use factorials from table fact[] */ + px = -mu; + py = pow(mu, pois) / fact[(int)pois]; + } else { + /* Case pois >= 10 uses polynomial approximation + a0-a7 for accuracy when advisable */ + del = one_12 / fk; + del = del * (1. - 4.8 * del * del); + v = difmuk / fk; + if (fabs(v) <= 0.25) + px = fk * v * v * (((((((a7 * v + a6) * v + a5) * v + a4) * + v + a3) * v + a2) * v + a1) * v + a0) + - del; + else { /* |v| > 1/4 */ + px = fk * log(1. + v) - difmuk - del; + } + py = M_1_SQRT_2PI / sqrt(fk); + } + x = (0.5 - difmuk) / s; + x *= x;/* x^2 */ + fx = -0.5 * x; + fy = omega * (((c3 * x + c2) * x + c1) * x + c0); + if (kflag > 0) { + /* Step H. Hat acceptance (E is repeated on rejection) */ + if (c * fabs(u) <= py * exp(px + E) - fy * exp(fx + E)) { + break; + } + } else + /* Step Q. Quotient acceptance (rare case) */ + if (fy - u * fy <= py * exp(px - fx)) { + break; + } + }/* t > -.67.. */ + } + return pois; +} + +#undef a1 +#undef a2 +#undef a3 +#undef a4 +#undef a5 +#undef a6 +#undef a7 + +static double igraph_rgeom(igraph_rng_t *rng, double p) { + if (igraph_is_nan(p) || p <= 0 || p > 1) { + ML_ERR_return_NAN; + } + + return igraph_rpois(rng, igraph_exp_rand(rng) * ((1 - p) / p)); +} + +/* This is from nmath/rbinom.c */ + +#define repeat for(;;) + +static double igraph_rbinom(igraph_rng_t *rng, double nin, double pp) { + /* FIXME: These should become THREAD_specific globals : */ + + static double c, fm, npq, p1, p2, p3, p4, qn; + static double xl, xll, xlr, xm, xr; + + static double psave = -1.0; + static int nsave = -1; + static int m; + + double f, f1, f2, u, v, w, w2, x, x1, x2, z, z2; + double p, q, np, g, r, al, alv, amaxp, ffm, ynorm; + int i, ix, k, n; + + if (!igraph_finite(nin)) { + ML_ERR_return_NAN; + } + n = floor(nin + 0.5); + if (n != nin) { + ML_ERR_return_NAN; + } + + if (!igraph_finite(pp) || + /* n=0, p=0, p=1 are not errors */ + n < 0 || pp < 0. || pp > 1.) { + ML_ERR_return_NAN; + } + + if (n == 0 || pp == 0.) { + return 0; + } + if (pp == 1.) { + return n; + } + + p = fmin(pp, 1. - pp); + q = 1. - p; + np = n * p; + r = p / q; + g = r * (n + 1); + + /* Setup, perform only when parameters change [using static (globals): */ + + /* FIXING: Want this thread safe + -- use as little (thread globals) as possible + */ + if (pp != psave || n != nsave) { + psave = pp; + nsave = n; + if (np < 30.0) { + /* inverse cdf logic for mean less than 30 */ + qn = pow(q, (double) n); + goto L_np_small; + } else { + ffm = np + p; + m = ffm; + fm = m; + npq = np * q; + p1 = (int)(2.195 * sqrt(npq) - 4.6 * q) + 0.5; + xm = fm + 0.5; + xl = xm - p1; + xr = xm + p1; + c = 0.134 + 20.5 / (15.3 + fm); + al = (ffm - xl) / (ffm - xl * p); + xll = al * (1.0 + 0.5 * al); + al = (xr - ffm) / (xr * q); + xlr = al * (1.0 + 0.5 * al); + p2 = p1 * (1.0 + c + c); + p3 = p2 + c / xll; + p4 = p3 + c / xlr; + } + } else if (n == nsave) { + if (np < 30.0) { + goto L_np_small; + } + } + + /*-------------------------- np = n*p >= 30 : ------------------- */ + repeat { + u = igraph_rng_get_unif01(rng) * p4; + v = igraph_rng_get_unif01(rng); + /* triangular region */ + if (u <= p1) { + ix = xm - p1 * v + u; + goto finis; + } + /* parallelogram region */ + if (u <= p2) { + x = xl + (u - p1) / c; + v = v * c + 1.0 - fabs(xm - x) / p1; + if (v > 1.0 || v <= 0.) { + continue; + } + ix = x; + } else { + if (u > p3) { /* right tail */ + ix = xr - log(v) / xlr; + if (ix > n) { + continue; + } + v = v * (u - p3) * xlr; + } else {/* left tail */ + ix = xl + log(v) / xll; + if (ix < 0) { + continue; + } + v = v * (u - p2) * xll; + } + } + /* determine appropriate way to perform accept/reject test */ + k = abs(ix - m); + if (k <= 20 || k >= npq / 2 - 1) { + /* explicit evaluation */ + f = 1.0; + if (m < ix) { + for (i = m + 1; i <= ix; i++) { + f *= (g / i - r); + } + } else if (m != ix) { + for (i = ix + 1; i <= m; i++) { + f /= (g / i - r); + } + } + if (v <= f) { + goto finis; + } + } else { + /* squeezing using upper and lower bounds on log(f(x)) */ + amaxp = (k / npq) * ((k * (k / 3. + 0.625) + 0.1666666666666) / npq + 0.5); + ynorm = -k * k / (2.0 * npq); + alv = log(v); + if (alv < ynorm - amaxp) { + goto finis; + } + if (alv <= ynorm + amaxp) { + /* Stirling's formula to machine accuracy */ + /* for the final acceptance/rejection test */ + x1 = ix + 1; + f1 = fm + 1.0; + z = n + 1 - fm; + w = n - ix + 1.0; + z2 = z * z; + x2 = x1 * x1; + f2 = f1 * f1; + w2 = w * w; + if (alv <= xm * log(f1 / x1) + (n - m + 0.5) * log(z / w) + (ix - m) * log(w * p / (x1 * q)) + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / f2) / f2) / f2) / f2) / f1 / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / z2) / z2) / z2) / z2) / z / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / x2) / x2) / x2) / x2) / x1 / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / w2) / w2) / w2) / w2) / w / 166320.) { + goto finis; + } + } + } + } + +L_np_small: + /*---------------------- np = n*p < 30 : ------------------------- */ + + repeat { + ix = 0; + f = qn; + u = igraph_rng_get_unif01(rng); + repeat { + if (u < f) { + goto finis; + } + if (ix > 110) { + break; + } + u -= f; + ix++; + f *= (g / ix - r); + } + } +finis: + if (psave > 0.5) { + ix = n - ix; + } + return (double)ix; +} + +static igraph_real_t igraph_rexp(igraph_rng_t *rng, double rate) { + igraph_real_t scale = 1.0 / rate; + if (!IGRAPH_FINITE(scale) || scale <= 0.0) { + if (scale == 0.0) { + return 0.0; + } + return IGRAPH_NAN; + } + return scale * igraph_exp_rand(rng); +} + +/* + * Mathlib : A C Library of Special Functions + * Copyright (C) 1998 Ross Ihaka + * Copyright (C) 2000 The R Core Team + * Copyright (C) 2003 The R Foundation + * + * 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 2 of the License, or + * (at your option) 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, a copy is available at + * http://www.r-project.org/Licenses/ + * + * SYNOPSIS + * + * double dnorm4(double x, double mu, double sigma, int give_log) + * {dnorm (..) is synonymous and preferred inside R} + * + * DESCRIPTION + * + * Compute the density of the normal distribution. + */ + +double igraph_dnorm(double x, double mu, double sigma, int give_log) { +#ifdef IEEE_754 + if (ISNAN(x) || ISNAN(mu) || ISNAN(sigma)) { + return x + mu + sigma; + } +#endif + if (!igraph_finite(sigma)) { + return R_D__0; + } + if (!igraph_finite(x) && mu == x) { + return ML_NAN; /* x-mu is NaN */ + } + if (sigma <= 0) { + if (sigma < 0) { + ML_ERR_return_NAN; + } + /* sigma == 0 */ + return (x == mu) ? ML_POSINF : R_D__0; + } + x = (x - mu) / sigma; + + if (!igraph_finite(x)) { + return R_D__0; + } + return (give_log ? + -(M_LN_SQRT_2PI + 0.5 * x * x + log(sigma)) : + M_1_SQRT_2PI * exp(-0.5 * x * x) / sigma); + /* M_1_SQRT_2PI = 1 / sqrt(2 * pi) */ +} + +/* This is from nmath/rgamma.c */ + +/* + * Mathlib : A C Library of Special Functions + * Copyright (C) 1998 Ross Ihaka + * Copyright (C) 2000--2008 The R Core Team + * + * 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 2 of the License, or + * (at your option) 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, a copy is available at + * http://www.r-project.org/Licenses/ + * + * SYNOPSIS + * + * #include + * double rgamma(double a, double scale); + * + * DESCRIPTION + * + * Random variates from the gamma distribution. + * + * REFERENCES + * + * [1] Shape parameter a >= 1. Algorithm GD in: + * + * Ahrens, J.H. and Dieter, U. (1982). + * Generating gamma variates by a modified + * rejection technique. + * Comm. ACM, 25, 47-54. + * + * + * [2] Shape parameter 0 < a < 1. Algorithm GS in: + * + * Ahrens, J.H. and Dieter, U. (1974). + * Computer methods for sampling from gamma, beta, + * poisson and binomial distributions. + * Computing, 12, 223-246. + * + * Input: a = parameter (mean) of the standard gamma distribution. + * Output: a variate from the gamma(a)-distribution + */ + +static double igraph_rgamma(igraph_rng_t *rng, double a, double scale) { + /* Constants : */ + static const double sqrt32 = 5.656854; + static const double exp_m1 = 0.36787944117144232159;/* exp(-1) = 1/e */ + + /* Coefficients q[k] - for q0 = sum(q[k]*a^(-k)) + * Coefficients a[k] - for q = q0+(t*t/2)*sum(a[k]*v^k) + * Coefficients e[k] - for exp(q)-1 = sum(e[k]*q^k) + */ + static const double q1 = 0.04166669; + static const double q2 = 0.02083148; + static const double q3 = 0.00801191; + static const double q4 = 0.00144121; + static const double q5 = -7.388e-5; + static const double q6 = 2.4511e-4; + static const double q7 = 2.424e-4; + + static const double a1 = 0.3333333; + static const double a2 = -0.250003; + static const double a3 = 0.2000062; + static const double a4 = -0.1662921; + static const double a5 = 0.1423657; + static const double a6 = -0.1367177; + static const double a7 = 0.1233795; + + /* State variables [FIXME for threading!] :*/ + static double aa = 0.; + static double aaa = 0.; + static double s, s2, d; /* no. 1 (step 1) */ + static double q0, b, si, c;/* no. 2 (step 4) */ + + double e, p, q, r, t, u, v, w, x, ret_val; + + if (!igraph_finite(a) || !igraph_finite(scale) || a < 0.0 || scale <= 0.0) { + if (scale == 0.) { + return 0.; + } + ML_ERR_return_NAN; + } + + if (a < 1.) { /* GS algorithm for parameters a < 1 */ + if (a == 0) { + return 0.; + } + e = 1.0 + exp_m1 * a; + repeat { + p = e * igraph_rng_get_unif01(rng); + if (p >= 1.0) { + x = -log((e - p) / a); + if (igraph_exp_rand(rng) >= (1.0 - a) * log(x)) { + break; + } + } else { + x = exp(log(p) / a); + if (igraph_exp_rand(rng) >= x) { + break; + } + } + } + return scale * x; + } + + /* --- a >= 1 : GD algorithm --- */ + + /* Step 1: Recalculations of s2, s, d if a has changed */ + if (a != aa) { + aa = a; + s2 = a - 0.5; + s = sqrt(s2); + d = sqrt32 - s * 12.0; + } + /* Step 2: t = standard normal deviate, + x = (s,1/2) -normal deviate. */ + + /* immediate acceptance (i) */ + t = igraph_norm_rand(rng); + x = s + 0.5 * t; + ret_val = x * x; + if (t >= 0.0) { + return scale * ret_val; + } + + /* Step 3: u = 0,1 - uniform sample. squeeze acceptance (s) */ + u = igraph_rng_get_unif01(rng); + if (d * u <= t * t * t) { + return scale * ret_val; + } + + /* Step 4: recalculations of q0, b, si, c if necessary */ + + if (a != aaa) { + aaa = a; + r = 1.0 / a; + q0 = ((((((q7 * r + q6) * r + q5) * r + q4) * r + q3) * r + + q2) * r + q1) * r; + + /* Approximation depending on size of parameter a */ + /* The constants in the expressions for b, si and c */ + /* were established by numerical experiments */ + + if (a <= 3.686) { + b = 0.463 + s + 0.178 * s2; + si = 1.235; + c = 0.195 / s - 0.079 + 0.16 * s; + } else if (a <= 13.022) { + b = 1.654 + 0.0076 * s2; + si = 1.68 / s + 0.275; + c = 0.062 / s + 0.024; + } else { + b = 1.77; + si = 0.75; + c = 0.1515 / s; + } + } + /* Step 5: no quotient test if x not positive */ + + if (x > 0.0) { + /* Step 6: calculation of v and quotient q */ + v = t / (s + s); + if (fabs(v) <= 0.25) + q = q0 + 0.5 * t * t * ((((((a7 * v + a6) * v + a5) * v + a4) * v + + a3) * v + a2) * v + a1) * v; + else { + q = q0 - s * t + 0.25 * t * t + (s2 + s2) * log(1.0 + v); + } + + + /* Step 7: quotient acceptance (q) */ + if (log(1.0 - u) <= q) { + return scale * ret_val; + } + } + + repeat { + /* Step 8: e = standard exponential deviate + * u = 0,1 -uniform deviate + * t = (b,si)-double exponential (laplace) sample */ + e = igraph_exp_rand(rng); + u = igraph_rng_get_unif01(rng); + u = u + u - 1.0; + if (u < 0.0) { + t = b - si * e; + } else { + t = b + si * e; + } + /* Step 9: rejection if t < tau(1) = -0.71874483771719 */ + if (t >= -0.71874483771719) { + /* Step 10: calculation of v and quotient q */ + v = t / (s + s); + if (fabs(v) <= 0.25) + q = q0 + 0.5 * t * t * + ((((((a7 * v + a6) * v + a5) * v + a4) * v + a3) * v + + a2) * v + a1) * v; + else { + q = q0 - s * t + 0.25 * t * t + (s2 + s2) * log(1.0 + v); + } + /* Step 11: hat acceptance (h) */ + /* (if q not positive go to step 8) */ + if (q > 0.0) { + w = expm1(q); + /* ^^^^^ original code had approximation with rel.err < 2e-7 */ + /* if t is rejected sample again at step 8 */ + if (c * fabs(u) <= w * exp(e - 0.5 * t * t)) { + break; + } + } + } + } /* repeat .. until `t' is accepted */ + x = s + 0.5 * t; + return scale * x * x; +} + +int igraph_rng_get_dirichlet(igraph_rng_t *rng, + const igraph_vector_t *alpha, + igraph_vector_t *result) { + + igraph_integer_t len = igraph_vector_size(alpha); + igraph_integer_t j; + igraph_real_t sum = 0.0; + + if (len < 2) { + IGRAPH_ERROR("Dirichlet parameter vector too short, must " + "have at least two entries", IGRAPH_EINVAL); + } + if (igraph_vector_min(alpha) <= 0) { + IGRAPH_ERROR("Dirichlet concentration parameters must be positive", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_resize(result, len)); + + RNG_BEGIN(); + + for (j = 0; j < len; j++) { + VECTOR(*result)[j] = igraph_rng_get_gamma(rng, VECTOR(*alpha)[j], 1.0); + sum += VECTOR(*result)[j]; + } + for (j = 0; j < len; j++) { + VECTOR(*result)[j] /= sum; + } + + RNG_END(); + + return IGRAPH_SUCCESS; +} diff --git a/src/rigraph/core/scg/scg.c b/src/rigraph/core/scg/scg.c new file mode 100644 index 0000000..9510fff --- /dev/null +++ b/src/rigraph/core/scg/scg.c @@ -0,0 +1,2303 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-12 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* + * SCGlib : A C library for the spectral coarse graining of matrices + * as described in the paper: Shrinking Matrices while preserving their + * eigenpairs with Application to the Spectral Coarse Graining of Graphs. + * Preprint available at + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * 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 2 of the License, or + * (at your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * DESCRIPTION + * ----------- + * The grouping function takes as argument 'nev' eigenvectors and + * and tries to minimize the eigenpair shifts induced by the coarse + * graining (Section 5 of the above reference). The eigenvectors are + * stored in a 'nev'x'n' matrix 'v'. + * The 'algo' parameter can take the following values + * 1 -> Optimal method (sec. 5.3.1) + * 2 -> Intervals+k-means (sec. 5.3.3) + * 3 -> Intervals (sec. 5.3.2) + * 4 -> Exact SCG (sec. 5.4.1--last paragraph) + * 'nt' is a vector of length 'nev' giving either the size of the + * partitions (if algo = 1) or the number of intervals to cut the + * eigenvectors if algo = 2 or algo = 3. When algo = 4 this parameter + * is ignored. 'maxiter' fixes the maximum number of iterations of + * the k-means algorithm, and is only considered when algo = 2. + * All the algorithms try to find a minimizing partition of + * ||v_i-Pv_i|| where P is a problem-specific projector and v_i denotes + * the eigenvectors stored in v. The final partition is worked out + * as decribed in Method 1 of Section 5.4.2. + * 'matrix' provides the type of SCG (i.e. the form of P). So far, + * the options are those described in section 6, that is: + * 1 -> Symmetric (sec. 6.1) + * 2 -> Laplacian (sec. 6.2) + * 3 -> Stochastic (sec. 6.3) + * In the stochastic case, a valid distribution probability 'p' must be + * provided. In all other cases, 'p' is ignored and can be set to NULL. + * The group labels in the final partition are given in 'gr' as positive + * consecutive integers starting from 0. + */ + +#include "igraph_scg.h" + +#include "igraph_eigen.h" +#include "igraph_interface.h" +#include "igraph_structural.h" +#include "igraph_community.h" +#include "igraph_constructors.h" +#include "igraph_conversion.h" +#include "igraph_memory.h" +#include "igraph_qsort.h" + +#include "../misc/conversion_internal.h" + +#include "scg_headers.h" + +#include "math.h" + +/** + * \section about_scg + * + * + * The SCG functions provide a framework, called Spectral Coarse Graining + * (SCG), for reducing large graphs while preserving their + * spectral-related features, that is features + * closely related with the eigenvalues and eigenvectors of a graph + * matrix (which for now can be the adjacency, the stochastic, or the + * Laplacian matrix). + * + * + * + * Common examples of such features comprise the first-passage-time of + * random walkers on Markovian graphs, thermodynamic properties of + * lattice models in statistical physics (e.g. Ising model), and the + * epidemic threshold of epidemic network models (SIR and SIS models). + * + * + * + * SCG differs from traditional clustering schemes by producing a + * coarse-grained graph (not just a partition of + * the vertices), representative of the original one. As shown in [1], + * Principal Component Analysis can be viewed as a particular SCG, + * called exact SCG, where the matrix to be + * coarse-grained is the covariance matrix of some data set. + * + * + * + * SCG should be of interest to practitioners of various + * fields dealing with problems where matrix eigenpairs play an important + * role, as for instance is the case of dynamical processes on networks. + * + * + *
SCG in brief + * + * The main idea of SCG is to operate on a matrix a shrinkage operation + * specifically designed to preserve some of the matrix eigenpairs while + * not altering other important matrix features (such as its structure). + * Mathematically, this idea was expressed as follows. Consider a + * (complex) n x n matrix M and form the product + *
+ * M'=LMR*, + *
+ * where n' < n and L, R are from C[n'xn]} and are such + * that LR*=I[n'] (R* denotes the conjugate transpose of R). Under + * these assumptions, it can be shown that P=R*L is an n'-rank + * projector and that, if (lambda, v) is a (right) + * eigenpair of M (i.e. Mv=lambda v} and P is orthogonal, there exists + * an eigenvalue lambda' of M' such that + *
+ * |lambda-lambda'| <= const ||e[P](v)|| + * [1+O(||e[P](v)||2)], + *
+ * where ||e[P](v)||=||v-Pv||. Hence, if P (or equivalently + * L, R) is chosen so as to make ||e[P](v)|| as small as possible, one + * can preserve to any desired level the original eigenvalue + * lambda in the coarse-grained matrix M'; + * under extra assumptions on M, this result can be generalized to + * eigenvectors [1]. This leads to the following generic definition of a + * SCG problem. + *
+ * + * + * Given M (C[nxn]) and (lambda, v), a (right) eigenpair of M to be + * preserved by the coarse graining, the problem is to find a projector + * P' solving + *
+ * min(||e[P](v)||, p in Omega), + *
+ * where Omega is a set of projectors in C[nxn] described by some + * ad hoc constraints c[1], ..., c[r] + * (e.g. c[1]: P in R[nxn], c[2]: P=t(P), c[3]: P[i,j] >= 0}, etc). + *
+ * + * + * Choosing pertinent constraints to solve the SCG problem is of great + * importance in applications. For instance, in the absence of + * constraints the SCG problem is solved trivially by + * P'=vv* (v is assumed normalized). We have designed a particular + * constraint, called homogeneous mixing, which + * ensures that vertices belonging to the same group are merged + * consistently from a physical point of view (see [1] for + * details). Under this constraint the SCG problem reduces to finding + * the partition of 1, ..., n (labeling the original vertices) + * minimizing + *
+ * ||e[P](v)||2 = + * sum([v(i)-(Pv)(i)]2; + * alpha=1,...,n', i in alpha), + *
+ * where alpha denotes a group (i.e. a block) in a partition of + * {1, ..., n}, and |alpha| is the number of elements in alpha. + *
+ * + * + * If M is symmetric or stochastic, for instance, then it may be + * desirable (or mandatory) to choose L, R so that M' is symmetric or + * stochastic as well. This structural constraint + * has led to the construction of particular semi-projectors for + * symmetric [1], stochastic [3] and Laplacian [2] matrices, that are + * made available. + * + * + * + * In short, the coarse graining of matrices and graphs involves: + * \olist + * \oli Retrieving a matrix or a graph matrix M from the + * problem. + * \oli Computing the eigenpairs of M to be preserved in the + * coarse-grained graph or matrix. + * \oli Setting some problem-specific constraints (e.g. dimension of + * the coarse-grained object). + * \oli Solving the constrained SCG problem, that is finding P'. + * \oli Computing from P' two semi-projectors L' and R' + * (e.g. following the method proposed in [1]). + * \oli Working out the product M'=L'MR'* and, if needed, defining + * from M' a coarse-grained graph. + * \endolist + * + *
+ * + *
Functions for performing SCG + * + * The main functions are \ref igraph_scg_adjacency(), \ref + * igraph_scg_laplacian() and \ref igraph_scg_stochastic(). + * These functions handle all the steps involved in the + * Spectral Coarse Graining (SCG) of some particular matrices and graphs + * as described above and in reference [1]. In more details, + * they compute some prescribed eigenpairs of a matrix or a + * graph matrix, (for now adjacency, Laplacian and stochastic matrices are + * available), work out an optimal partition to preserve the eigenpairs, + * and finally output a coarse-grained matrix or graph along with other + * useful information. + * + * + * + * These steps can also be carried out independently: (1) Use + * \ref igraph_get_adjacency(), \ref igraph_get_sparsemat(), + * \ref igraph_laplacian(), \ref igraph_get_stochastic() or \ref + * igraph_get_stochastic_sparsemat() to compute a matrix M. + * (2) Work out some prescribed eigenpairs of M e.g. by + * means of \ref igraph_arpack_rssolve() or \ref + * igraph_arpack_rnsolve(). (3) Invoke one the four + * algorithms of the function \ref igraph_scg_grouping() to get a + * partition that will preserve the eigenpairs in the coarse-grained + * matrix. (4) Compute the semi-projectors L and R using + * \ref igraph_scg_semiprojectors() and from there the coarse-grained + * matrix M'=LMR*. If necessary, construct a coarse-grained graph from + * M' (e.g. as in [1]). + * + *
+ * + *
References + * + * [1] D. Morton de Lachapelle, D. Gfeller, and P. De Los Rios, + * Shrinking Matrices while Preserving their Eigenpairs with Application + * to the Spectral Coarse Graining of Graphs. Submitted to + * SIAM Journal on Matrix Analysis and + * Applications, 2008. + * http://people.epfl.ch/david.morton + * + * + * [2] D. Gfeller, and P. De Los Rios, Spectral Coarse Graining and + * Synchronization in Oscillator Networks. + * Physical Review Letters, + * 100(17), 2008. + * http://arxiv.org/abs/0708.2055 + * + * + * [3] D. Gfeller, and P. De Los Rios, Spectral Coarse Graining of Complex + * Networks, Physical Review Letters, + * 99(3), 2007. + * http://arxiv.org/abs/0706.0812 + * + *
+ */ + +/** + * \function igraph_scg_grouping + * \brief SCG problem solver. + * + * This function solves the Spectral Coarse Graining (SCG) problem; + * either exactly, or approximately but faster. + * + *
+ * The algorithm \c IGRAPH_SCG_OPTIMUM solves the SCG problem exactly + * for each eigenvector in \p V. The running time of this algorithm is + * O(max(nt) m^2) for the symmetric and Laplacian matrix problems. + * It is O(m^3) for the stochastic problem. Here m is the number + * of rows in \p V. In all three cases, the memory usage is O(m^2). + * + * + * The algorithms \c IGRAPH_SCG_INTERV and \c IGRAPH_SCG_INTERV_KM solve + * the SCG problem approximately by performing a (for now) constant + * binning of the components of the eigenvectors, that is nt_vec[i] + * constant-size bins are used to partition the ith eigenvector in \c V. + * When \p algo is \c IGRAPH_SCG_INTERV_KM, the (Lloyd) k-means algorithm is + * run on each partition obtained by \c IGRAPH_SCG_INTERV to improve + * accuracy. + * + * + * Once a minimizing partition (either exact or approximate) has been + * found for each eigenvector, the final grouping is worked out as + * follows: two vertices are grouped together in the final partition if + * they are grouped together in each minimizing partition. In general, the + * size of the final partition is not known in advance when the number + * of columns in \p V is larger than one. + * + * + * Finally, the algorithm \c IGRAPH_SCG_EXACT groups the vertices with + * equal components in each eigenvector. The last three algorithms + * essentially have linear running time and memory load. + * + * \param V The matrix of eigenvectors to be preserved by coarse + * graining, each column is an eigenvector. + * \param groups Pointer to an initialized vector; the result of the + * SCG is stored here. + * \param nt Positive integer. When \p algo is \c IGRAPH_SCG_OPTIMUM, + * it gives the number of groups to partition each eigenvector + * separately. When \p algo is \c IGRAPH_SCG_INTERV or \c + * IGRAPH_SCG_INTERV_KM, it gives the number of intervals to + * partition each eigenvector. This is ignored when \p algo is \c + * IGRAPH_SCG_EXACT. + * \param nt_vec May be (1) a numeric vector of length one, or + * (2) a vector of the same length as the number of eigenvectors given in \p V, or + * (3) a \c NULL pointer. + * If not \c NULL, then this argument gives the number of + * groups or intervals, and \p nt is ignored. Different number of + * groups or intervals can be specified for each eigenvector. + * \param mtype The type of semi-projectors used in the SCG. Possible + * values are \c IGRAPH_SCG_SYMMETRIC, \c IGRAPH_SCG_STOCHASTIC and + * \c IGRAPH_SCG_LAPLACIAN. + * \param algo The algorithm to solve the SCG problem. Possible + * values: \c IGRAPH_SCG_OPTIMUM, \c IGRAPH_SCG_INTERV_KM, \c + * IGRAPH_SCG_INTERV and \c IGRAPH_SCG_EXACT. Please see the + * details about them above. + * \param p A probability vector, or \c NULL. This argument must be + * given if \p mtype is \c IGRAPH_SCG_STOCHASTIC, but it is ignored + * otherwise. For the stochastic case it gives the stationary + * probability distribution of a Markov chain, the one specified by + * the graph/matrix under study. + * \param maxiter A positive integer giving the number of iterations + * of the k-means algorithm when \p algo is \c + * IGRAPH_SCG_INTERV_KM. It is ignored in other cases. A reasonable + * (initial) value for this argument is 100. + * \return Error code. + * + * Time complexity: see description above. + * + * \sa \ref igraph_scg_adjacency(), \ref igraph_scg_laplacian(), \ref + * igraph_scg_stochastic(). + * + * \example examples/simple/igraph_scg_grouping.c + * \example examples/simple/igraph_scg_grouping2.c + * \example examples/simple/igraph_scg_grouping3.c + * \example examples/simple/igraph_scg_grouping4.c + */ + +int igraph_scg_grouping(const igraph_matrix_t *V, + igraph_vector_t *groups, + igraph_integer_t nt, + const igraph_vector_t *nt_vec, + igraph_scg_matrix_t mtype, + igraph_scg_algorithm_t algo, + const igraph_vector_t *p, + igraph_integer_t maxiter) { + + int no_of_nodes = (int) igraph_matrix_nrow(V); + int nev = (int) igraph_matrix_ncol(V); + igraph_matrix_int_t gr_mat; + int i; + + if (nt_vec && igraph_vector_size(nt_vec) != 1 && + igraph_vector_size(nt_vec) != nev) { + IGRAPH_ERROR("Invalid length for interval specification", IGRAPH_EINVAL); + } + if (nt_vec && igraph_vector_size(nt_vec) == 1) { + nt = (igraph_integer_t) VECTOR(*nt_vec)[0]; + nt_vec = 0; + } + + if (!nt_vec && algo != IGRAPH_SCG_EXACT) { + if (nt <= 1 || nt >= no_of_nodes) { + IGRAPH_ERROR("Invalid interval specification", IGRAPH_EINVAL); + } + } else if (algo != IGRAPH_SCG_EXACT) { + igraph_real_t min, max; + igraph_vector_minmax(nt_vec, &min, &max); + if (min <= 1 || max >= no_of_nodes) { + IGRAPH_ERROR("Invalid interval specification", IGRAPH_EINVAL); + } + } + + if (mtype == IGRAPH_SCG_STOCHASTIC && !p) { + IGRAPH_ERROR("The p vector must be given for the stochastic matrix case", + IGRAPH_EINVAL); + } + + if (p) { + if (igraph_vector_size(p) != no_of_nodes) { + IGRAPH_ERROR("Invalid p vector size", IGRAPH_EINVAL); + } + + if (igraph_vector_min(p) < 0) { + IGRAPH_ERROR("The elements of the p vector must be non-negative", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_vector_resize(groups, no_of_nodes)); + +#define INVEC(i) (nt_vec ? VECTOR(*nt_vec)[i] : nt) + + IGRAPH_CHECK(igraph_matrix_int_init(&gr_mat, no_of_nodes, nev)); + IGRAPH_FINALLY(igraph_matrix_int_destroy, &gr_mat); + + switch (algo) { + case IGRAPH_SCG_OPTIMUM: + for (i = 0; i < nev; i++) { + IGRAPH_CHECK(igraph_i_optimal_partition(&MATRIX(*V, 0, i), + &MATRIX(gr_mat, 0, i), + no_of_nodes, (int) INVEC(i), + mtype, + p ? VECTOR(*p) : 0, 0)); + } + break; + case IGRAPH_SCG_INTERV_KM: + for (i = 0; i < nev; i++) { + igraph_vector_t tmpv; + igraph_vector_view(&tmpv, &MATRIX(*V, 0, i), no_of_nodes); + IGRAPH_CHECK(igraph_i_intervals_plus_kmeans(&tmpv, + &MATRIX(gr_mat, 0, i), + no_of_nodes, (int) INVEC(i), + maxiter)); + } + break; + case IGRAPH_SCG_INTERV: + for (i = 0; i < nev; i++) { + igraph_vector_t tmpv; + igraph_vector_view(&tmpv, &MATRIX(*V, 0, i), no_of_nodes); + IGRAPH_CHECK(igraph_i_intervals_method(&tmpv, + &MATRIX(gr_mat, 0, i), + no_of_nodes, (int) INVEC(i))); + } + break; + case IGRAPH_SCG_EXACT: + for (i = 0; i < nev; i++) { + IGRAPH_CHECK(igraph_i_exact_coarse_graining(&MATRIX(*V, 0, i), + &MATRIX(gr_mat, 0, i), + no_of_nodes)); + } + break; + } + +#undef INVEC + + if (nev == 1) { + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*groups)[i] = MATRIX(gr_mat, i, 0); + } + } else { + igraph_i_scg_groups_t *g; + int gr_nb = 0; + + g = IGRAPH_CALLOC(no_of_nodes, igraph_i_scg_groups_t); + IGRAPH_FINALLY(igraph_free, g); + + IGRAPH_CHECK(igraph_matrix_int_transpose(&gr_mat)); + for (i = 0; i < no_of_nodes; i++) { + g[i].ind = i; + g[i].n = nev; + g[i].gr = &MATRIX(gr_mat, 0, i); + } + + igraph_qsort(g, (size_t) no_of_nodes, sizeof(igraph_i_scg_groups_t), + igraph_i_compare_groups); + VECTOR(*groups)[g[0].ind] = gr_nb; + for (i = 1; i < no_of_nodes; i++) { + if (igraph_i_compare_groups(&g[i], &g[i - 1]) != 0) { + gr_nb++; + } + VECTOR(*groups)[g[i].ind] = gr_nb; + } + + IGRAPH_FREE(g); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_matrix_int_destroy(&gr_mat); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_reindex_membership(groups, 0, 0)); + + return 0; +} + +static int igraph_i_scg_semiprojectors_sym(const igraph_vector_t *groups, + igraph_matrix_t *L, + igraph_matrix_t *R, + igraph_sparsemat_t *Lsparse, + igraph_sparsemat_t *Rsparse, + int no_of_groups, + int no_of_nodes) { + + igraph_vector_t tab; + int i; + + IGRAPH_VECTOR_INIT_FINALLY(&tab, no_of_groups); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(tab)[ (int) VECTOR(*groups)[i] ] += 1; + } + for (i = 0; i < no_of_groups; i++) { + VECTOR(tab)[i] = sqrt(VECTOR(tab)[i]); + } + + if (L) { + IGRAPH_CHECK(igraph_matrix_resize(L, no_of_groups, no_of_nodes)); + igraph_matrix_null(L); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + MATRIX(*L, g, i) = 1 / VECTOR(tab)[g]; + } + } + + if (R) { + if (L) { + IGRAPH_CHECK(igraph_matrix_update(R, L)); + } else { + IGRAPH_CHECK(igraph_matrix_resize(R, no_of_groups, no_of_nodes)); + igraph_matrix_null(R); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + MATRIX(*R, g, i) = 1 / VECTOR(tab)[g]; + } + } + } + + if (Lsparse) { + IGRAPH_CHECK(igraph_sparsemat_init(Lsparse, no_of_groups, no_of_nodes, + /* nzmax= */ no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + IGRAPH_CHECK(igraph_sparsemat_entry(Lsparse, g, i, 1 / VECTOR(tab)[g])); + } + } + + if (Rsparse) { + IGRAPH_CHECK(igraph_sparsemat_init(Rsparse, no_of_groups, no_of_nodes, + /* nzmax= */ no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + IGRAPH_CHECK(igraph_sparsemat_entry(Rsparse, g, i, 1 / VECTOR(tab)[g])); + } + } + + igraph_vector_destroy(&tab); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_scg_semiprojectors_lap(const igraph_vector_t *groups, + igraph_matrix_t *L, + igraph_matrix_t *R, + igraph_sparsemat_t *Lsparse, + igraph_sparsemat_t *Rsparse, + int no_of_groups, + int no_of_nodes, + igraph_scg_norm_t norm) { + + igraph_vector_t tab; + int i; + + IGRAPH_VECTOR_INIT_FINALLY(&tab, no_of_groups); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(tab)[ (int) VECTOR(*groups)[i] ] += 1; + } + for (i = 0; i < no_of_groups; i++) { + VECTOR(tab)[i] = VECTOR(tab)[i]; + } + + if (norm == IGRAPH_SCG_NORM_ROW) { + if (L) { + IGRAPH_CHECK(igraph_matrix_resize(L, no_of_groups, no_of_nodes)); + igraph_matrix_null(L); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + MATRIX(*L, g, i) = 1.0 / VECTOR(tab)[g]; + } + } + if (R) { + IGRAPH_CHECK(igraph_matrix_resize(R, no_of_groups, no_of_nodes)); + igraph_matrix_null(R); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + MATRIX(*R, g, i) = 1.0; + } + } + if (Lsparse) { + IGRAPH_CHECK(igraph_sparsemat_init(Lsparse, no_of_groups, no_of_nodes, + /* nzmax= */ no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + IGRAPH_CHECK(igraph_sparsemat_entry(Lsparse, g, i, + 1.0 / VECTOR(tab)[g])); + } + } + if (Rsparse) { + IGRAPH_CHECK(igraph_sparsemat_init(Rsparse, no_of_groups, no_of_nodes, + /* nzmax= */ no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + IGRAPH_CHECK(igraph_sparsemat_entry(Rsparse, g, i, 1.0)); + } + } + } else { + if (L) { + IGRAPH_CHECK(igraph_matrix_resize(L, no_of_groups, no_of_nodes)); + igraph_matrix_null(L); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + MATRIX(*L, g, i) = 1.0; + } + } + if (R) { + IGRAPH_CHECK(igraph_matrix_resize(R, no_of_groups, no_of_nodes)); + igraph_matrix_null(R); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + MATRIX(*R, g, i) = 1.0 / VECTOR(tab)[g]; + } + } + if (Lsparse) { + IGRAPH_CHECK(igraph_sparsemat_init(Lsparse, no_of_groups, no_of_nodes, + /* nzmax= */ no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + IGRAPH_CHECK(igraph_sparsemat_entry(Lsparse, g, i, 1.0)); + } + } + if (Rsparse) { + IGRAPH_CHECK(igraph_sparsemat_init(Rsparse, no_of_groups, no_of_nodes, + /* nzmax= */ no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + IGRAPH_CHECK(igraph_sparsemat_entry(Rsparse, g, i, + 1.0 / VECTOR(tab)[g])); + } + } + + } + + igraph_vector_destroy(&tab); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_scg_semiprojectors_sto(const igraph_vector_t *groups, + igraph_matrix_t *L, + igraph_matrix_t *R, + igraph_sparsemat_t *Lsparse, + igraph_sparsemat_t *Rsparse, + int no_of_groups, + int no_of_nodes, + const igraph_vector_t *p, + igraph_scg_norm_t norm) { + + igraph_vector_t pgr, pnormed; + int i; + + IGRAPH_VECTOR_INIT_FINALLY(&pgr, no_of_groups); + IGRAPH_VECTOR_INIT_FINALLY(&pnormed, no_of_nodes); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + VECTOR(pgr)[g] += VECTOR(*p)[i]; + } + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + VECTOR(pnormed)[i] = VECTOR(*p)[i] / VECTOR(pgr)[g]; + } + + if (norm == IGRAPH_SCG_NORM_ROW) { + if (L) { + IGRAPH_CHECK(igraph_matrix_resize(L, no_of_groups, no_of_nodes)); + igraph_matrix_null(L); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + MATRIX(*L, g, i) = VECTOR(pnormed)[i]; + } + } + if (R) { + IGRAPH_CHECK(igraph_matrix_resize(R, no_of_groups, no_of_nodes)); + igraph_matrix_null(R); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + MATRIX(*R, g, i) = 1.0; + } + } + if (Lsparse) { + IGRAPH_CHECK(igraph_sparsemat_init(Lsparse, no_of_groups, no_of_nodes, + /* nzmax= */ no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + IGRAPH_CHECK(igraph_sparsemat_entry(Lsparse, g, i, + VECTOR(pnormed)[i])); + } + } + if (Rsparse) { + IGRAPH_CHECK(igraph_sparsemat_init(Rsparse, no_of_groups, no_of_nodes, + /* nzmax= */ no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + IGRAPH_CHECK(igraph_sparsemat_entry(Rsparse, g, i, 1.0)); + } + } + } else { + if (L) { + IGRAPH_CHECK(igraph_matrix_resize(L, no_of_groups, no_of_nodes)); + igraph_matrix_null(L); + for (i = 0; i < no_of_nodes; i++) { + int g = (int ) VECTOR(*groups)[i]; + MATRIX(*L, g, i) = 1.0; + } + } + if (R) { + IGRAPH_CHECK(igraph_matrix_resize(R, no_of_groups, no_of_nodes)); + igraph_matrix_null(R); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + MATRIX(*R, g, i) = VECTOR(pnormed)[i]; + } + } + if (Lsparse) { + IGRAPH_CHECK(igraph_sparsemat_init(Lsparse, no_of_groups, no_of_nodes, + /* nzmax= */ no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + IGRAPH_CHECK(igraph_sparsemat_entry(Lsparse, g, i, 1.0)); + } + } + if (Rsparse) { + IGRAPH_CHECK(igraph_sparsemat_init(Rsparse, no_of_groups, no_of_nodes, + /* nzmax= */ no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + int g = (int) VECTOR(*groups)[i]; + IGRAPH_CHECK(igraph_sparsemat_entry(Rsparse, g, i, + VECTOR(pnormed)[i])); + } + } + } + + + igraph_vector_destroy(&pnormed); + igraph_vector_destroy(&pgr); + IGRAPH_FINALLY_CLEAN(2); + + return 0; +} + +/** + * \function igraph_scg_semiprojectors + * \brief Compute SCG semi-projectors for a given partition. + * + * The three types of semi-projectors are defined as follows. + * Let gamma(j) label the group of vertex j in a partition of all the + * vertices. + * + * + * The symmetric semi-projectors are defined as + *
+ * L[alpha,j] = R[alpha,j] = 1/sqrt(|alpha|) delta[alpha,gamma(j)], + *
+ * the (row) Laplacian semi-projectors as + *
+ * L[alpha,j] = 1/|alpha| delta[alpha,gamma(j)] + *
+ * and + *
+ * R[alpha,j] = delta[alpha,gamma(j)], + *
+ * and the (row) stochastic semi-projectors as + *
+ * L[alpha,j] = p[1][j] / sum(p[1][k]; k in gamma(j)) + * delta[alpha,gamma(j)] + *
+ * and + *
+ * R[alpha,j] = delta[alpha,gamma(j)], + *
+ * where p[1] is the (left) eigenvector associated with the + * one-eigenvalue of the stochastic matrix. L and R are + * defined in a symmetric way when \p norm is \c + * IGRAPH_SCG_NORM_COL. All these semi-projectors verify various + * properties described in the reference. + * \param groups A vector of integers, giving the group label of every + * vertex in the partition. Group labels should start at zero and + * should be sequential. + * \param mtype The type of semi-projectors. For now \c + * IGRAPH_SCG_SYMMETRIC, \c IGRAPH_SCG_STOCHASTIC and \c + * IGRAP_SCG_LAPLACIAN are supported. + * \param L If not a \c NULL pointer, then it must be a pointer to + * an initialized matrix. The left semi-projector is stored here. + * \param R If not a \c NULL pointer, then it must be a pointer to + * an initialized matrix. The right semi-projector is stored here. + * \param Lsparse If not a \c NULL pointer, then it must be a pointer + * to an uninitialized sparse matrix. The left semi-projector is + * stored here. + * \param Rsparse If not a \c NULL pointer, then it must be a pointer + * to an uninitialized sparse matrix. The right semi-projector is + * stored here. + * \param p \c NULL, or a probability vector of the same length as \p + * groups. \p p is the stationary probability distribution of a + * Markov chain when \p mtype is \c IGRAPH_SCG_STOCHASTIC. This + * argument is ignored in all other cases. + * \param norm Either \c IGRAPH_SCG_NORM_ROW or \c IGRAPH_SCG_NORM_COL. + * Specifies whether the rows or the columns of the Laplacian + * matrix sum up to zero, or whether the rows or the columns of the + * stochastic matrix sum up to one. + * \return Error code. + * + * Time complexity: TODO. + * + * \sa \ref igraph_scg_adjacency(), \ref igraph_scg_stochastic() and + * \ref igraph_scg_laplacian(), \ref igraph_scg_grouping(). + * + * \example examples/simple/igraph_scg_semiprojectors.c + * \example examples/simple/igraph_scg_semiprojectors2.c + * \example examples/simple/igraph_scg_semiprojectors3.c + */ + +int igraph_scg_semiprojectors(const igraph_vector_t *groups, + igraph_scg_matrix_t mtype, + igraph_matrix_t *L, + igraph_matrix_t *R, + igraph_sparsemat_t *Lsparse, + igraph_sparsemat_t *Rsparse, + const igraph_vector_t *p, + igraph_scg_norm_t norm) { + + int no_of_nodes = (int) igraph_vector_size(groups); + int no_of_groups; + igraph_real_t min, max; + + igraph_vector_minmax(groups, &min, &max); + no_of_groups = (int) max + 1; + + if (min < 0 || max >= no_of_nodes) { + IGRAPH_ERROR("Invalid membership vector", IGRAPH_EINVAL); + } + + if (mtype == IGRAPH_SCG_STOCHASTIC && !p) { + IGRAPH_ERROR("`p' must be given for the stochastic matrix case", + IGRAPH_EINVAL); + } + + if (p && igraph_vector_size(p) != no_of_nodes) { + IGRAPH_ERROR("Invalid `p' vector length, should match number of vertices", + IGRAPH_EINVAL); + } + + switch (mtype) { + case IGRAPH_SCG_SYMMETRIC: + IGRAPH_CHECK(igraph_i_scg_semiprojectors_sym(groups, L, R, Lsparse, + Rsparse, no_of_groups, + no_of_nodes)); + break; + + case IGRAPH_SCG_LAPLACIAN: + IGRAPH_CHECK(igraph_i_scg_semiprojectors_lap(groups, L, R, Lsparse, + Rsparse, no_of_groups, + no_of_nodes, norm)); + break; + + case IGRAPH_SCG_STOCHASTIC: + IGRAPH_CHECK(igraph_i_scg_semiprojectors_sto(groups, L, R, Lsparse, + Rsparse, no_of_groups, + no_of_nodes, p, norm)); + break; + } + + return 0; +} + +/** + * \function igraph_scg_norm_eps + * \brief Calculate SCG residuals. + * + * Computes |v[i]-Pv[i]|, where v[i] is the i-th eigenvector in \p V + * and P is the projector corresponding to the \p mtype argument. + * + * \param V The matrix of eigenvectors to be preserved by coarse + * graining, each column is an eigenvector. + * \param groups A vector of integers, giving the group label of every + * vertex in the partition. Group labels should start at zero and + * should be sequential. + * \param eps Pointer to a real value, the result is stored here. + * \param mtype The type of semi-projectors. For now \c + * IGRAPH_SCG_SYMMETRIC, \c IGRAPH_SCG_STOCHASTIC and \c + * IGRAP_SCG_LAPLACIAN are supported. + * \param p \c NULL, or a probability vector of the same length as \p + * groups. \p p is the stationary probability distribution of a + * Markov chain when \p mtype is \c IGRAPH_SCG_STOCHASTIC. This + * argument is ignored in all other cases. + * \param norm Either \c IGRAPH_SCG_NORM_ROW or \c IGRAPH_SCG_NORM_COL. + * Specifies whether the rows or the columns of the Laplacian + * matrix sum up to zero, or whether the rows or the columns of the + * stochastic matrix sum up to one. + * \return Error code. + * + * Time complexity: TODO. + * + * \sa \ref igraph_scg_adjacency(), \ref igraph_scg_stochastic() and + * \ref igraph_scg_laplacian(), \ref igraph_scg_grouping(), \ref + * igraph_scg_semiprojectors(). + */ + +int igraph_scg_norm_eps(const igraph_matrix_t *V, + const igraph_vector_t *groups, + igraph_vector_t *eps, + igraph_scg_matrix_t mtype, + const igraph_vector_t *p, + igraph_scg_norm_t norm) { + + int no_of_nodes = (int) igraph_vector_size(groups); + int no_of_vectors = (int) igraph_matrix_ncol(V); + igraph_real_t min, max; + igraph_sparsemat_t Lsparse, Rsparse, Lsparse2, Rsparse2, Rsparse3, proj; + igraph_vector_t x, res; + int k, i; + + if (igraph_matrix_nrow(V) != no_of_nodes) { + IGRAPH_ERROR("Eigenvector length and group vector length do not match", + IGRAPH_EINVAL); + } + + igraph_vector_minmax(groups, &min, &max); + + if (min < 0 || max >= no_of_nodes) { + IGRAPH_ERROR("Invalid membership vector", IGRAPH_EINVAL); + } + + if (mtype == IGRAPH_SCG_STOCHASTIC && !p) { + IGRAPH_ERROR("`p' must be given for the stochastic matrix case", + IGRAPH_EINVAL); + } + + if (p && igraph_vector_size(p) != no_of_nodes) { + IGRAPH_ERROR("Invalid `p' vector length, should match number of vertices", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_scg_semiprojectors(groups, mtype, /* L= */ 0, + /* R= */ 0, &Lsparse, &Rsparse, p, + norm)); + + IGRAPH_FINALLY(igraph_sparsemat_destroy, &Lsparse); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse); + + IGRAPH_CHECK(igraph_sparsemat_compress(&Lsparse, &Lsparse2)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &Lsparse2); + IGRAPH_CHECK(igraph_sparsemat_compress(&Rsparse, &Rsparse2)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse2); + IGRAPH_CHECK(igraph_sparsemat_transpose(&Rsparse2, &Rsparse3, + /*values=*/ 1)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse3); + + IGRAPH_CHECK(igraph_sparsemat_multiply(&Rsparse3, &Lsparse2, &proj)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &proj); + + IGRAPH_VECTOR_INIT_FINALLY(&res, no_of_nodes); + IGRAPH_CHECK(igraph_vector_resize(eps, no_of_vectors)); + + for (k = 0; k < no_of_vectors; k++) { + igraph_vector_view(&x, &MATRIX(*V, 0, k), no_of_nodes); + igraph_vector_null(&res); + IGRAPH_CHECK(igraph_sparsemat_gaxpy(&proj, &x, &res)); + VECTOR(*eps)[k] = 0.0; + for (i = 0; i < no_of_nodes; i++) { + igraph_real_t di = MATRIX(*V, i, k) - VECTOR(res)[i]; + VECTOR(*eps)[k] += di * di; + } + VECTOR(*eps)[k] = sqrt(VECTOR(*eps)[k]); + } + + igraph_vector_destroy(&res); + igraph_sparsemat_destroy(&proj); + igraph_sparsemat_destroy(&Rsparse3); + igraph_sparsemat_destroy(&Rsparse2); + igraph_sparsemat_destroy(&Lsparse2); + igraph_sparsemat_destroy(&Rsparse); + igraph_sparsemat_destroy(&Lsparse); + IGRAPH_FINALLY_CLEAN(7); + + return 0; +} + +static int igraph_i_matrix_laplacian(const igraph_matrix_t *matrix, + igraph_matrix_t *mymatrix, + igraph_scg_norm_t norm) { + + igraph_vector_t degree; + int i, j, n = (int) igraph_matrix_nrow(matrix); + IGRAPH_CHECK(igraph_matrix_resize(mymatrix, n, n)); + + IGRAPH_VECTOR_INIT_FINALLY(°ree, n); + + if (norm == IGRAPH_SCG_NORM_ROW) { + IGRAPH_CHECK(igraph_matrix_rowsum(matrix, °ree)); + } else { + IGRAPH_CHECK(igraph_matrix_colsum(matrix, °ree)); + } + for (i = 0; i < n; i++) { + VECTOR(degree)[i] -= MATRIX(*matrix, i, i); + } + + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + MATRIX(*mymatrix, i, j) = - MATRIX(*matrix, i, j); + } + MATRIX(*mymatrix, i, i) = VECTOR(degree)[i]; + } + + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_sparsemat_laplacian(const igraph_sparsemat_t *sparse, + igraph_sparsemat_t *mysparse, + igraph_scg_norm_t norm) { + + igraph_vector_t degree; + int i, n = (int) igraph_sparsemat_nrow(sparse); + int nzmax = igraph_sparsemat_nzmax(sparse); + igraph_sparsemat_iterator_t it; + + IGRAPH_CHECK(igraph_sparsemat_init(mysparse, n, n, nzmax + n)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparse); + igraph_sparsemat_iterator_init(&it, (igraph_sparsemat_t *) sparse); + + IGRAPH_VECTOR_INIT_FINALLY(°ree, n); + for (igraph_sparsemat_iterator_reset(&it); + !igraph_sparsemat_iterator_end(&it); + igraph_sparsemat_iterator_next(&it)) { + int row = igraph_sparsemat_iterator_row(&it); + int col = igraph_sparsemat_iterator_col(&it); + if (row != col) { + igraph_real_t val = igraph_sparsemat_iterator_get(&it); + if (norm == IGRAPH_SCG_NORM_ROW) { + VECTOR(degree)[row] += val; + } else { + VECTOR(degree)[col] += val; + } + } + } + + /* Diagonal */ + for (i = 0; i < n; i++) { + igraph_sparsemat_entry(mysparse, i, i, VECTOR(degree)[i]); + } + + /* And the rest, filter out diagonal elements */ + for (igraph_sparsemat_iterator_reset(&it); + !igraph_sparsemat_iterator_end(&it); + igraph_sparsemat_iterator_next(&it)) { + int row = igraph_sparsemat_iterator_row(&it); + int col = igraph_sparsemat_iterator_col(&it); + if (row != col) { + igraph_real_t val = igraph_sparsemat_iterator_get(&it); + igraph_sparsemat_entry(mysparse, row, col, -val); + } + } + + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(2); /* + mysparse */ + + return 0; +} + +static int igraph_i_matrix_stochastic(const igraph_matrix_t *matrix, + igraph_matrix_t *mymatrix, + igraph_scg_norm_t norm) { + + int i, j, n = (int) igraph_matrix_nrow(matrix); + IGRAPH_CHECK(igraph_matrix_copy(mymatrix, matrix)); + + if (norm == IGRAPH_SCG_NORM_ROW) { + for (i = 0; i < n; i++) { + igraph_real_t sum = 0.0; + for (j = 0; j < n; j++) { + sum += MATRIX(*matrix, i, j); + } + if (sum == 0) { + IGRAPH_WARNING("Zero degree vertices"); + } + for (j = 0; j < n; j++) { + MATRIX(*mymatrix, i, j) = MATRIX(*matrix, i, j) / sum; + } + } + } else { + for (i = 0; i < n; i++) { + igraph_real_t sum = 0.0; + for (j = 0; j < n; j++) { + sum += MATRIX(*matrix, j, i); + } + if (sum == 0) { + IGRAPH_WARNING("Zero degree vertices"); + } + for (j = 0; j < n; j++) { + MATRIX(*mymatrix, j, i) = MATRIX(*matrix, j, i) / sum; + } + } + } + + return 0; +} + +static int igraph_i_sparsemat_stochastic(const igraph_sparsemat_t *sparse, + igraph_sparsemat_t *mysparse, + igraph_scg_norm_t norm) { + + IGRAPH_CHECK(igraph_sparsemat_copy(mysparse, sparse)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparse); + IGRAPH_CHECK(igraph_i_normalize_sparsemat(mysparse, + norm == IGRAPH_SCG_NORM_COL)); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +static int igraph_i_scg_get_result(igraph_scg_matrix_t type, + const igraph_matrix_t *matrix, + const igraph_sparsemat_t *sparsemat, + const igraph_sparsemat_t *Lsparse, + const igraph_sparsemat_t *Rsparse_t, + igraph_t *scg_graph, + igraph_matrix_t *scg_matrix, + igraph_sparsemat_t *scg_sparsemat, + igraph_bool_t directed) { + + /* We need to calculate either scg_matrix (if input is dense), or + scg_sparsemat (if input is sparse). For the latter we might need + to temporarily use another matrix. */ + + + if (matrix) { + igraph_matrix_t *my_scg_matrix = scg_matrix, v_scg_matrix; + igraph_matrix_t tmp; + igraph_sparsemat_t *myLsparse = (igraph_sparsemat_t *) Lsparse, v_Lsparse; + + if (!scg_matrix) { + my_scg_matrix = &v_scg_matrix; + IGRAPH_CHECK(igraph_matrix_init(my_scg_matrix, 0, 0)); + IGRAPH_FINALLY(igraph_matrix_destroy, my_scg_matrix); + } + + if (!igraph_sparsemat_is_cc(Lsparse)) { + myLsparse = &v_Lsparse; + IGRAPH_CHECK(igraph_sparsemat_compress(Lsparse, myLsparse)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, myLsparse); + } + + IGRAPH_CHECK(igraph_matrix_init(&tmp, 0, 0)); + IGRAPH_FINALLY(igraph_matrix_destroy, &tmp); + IGRAPH_CHECK(igraph_sparsemat_dense_multiply(matrix, Rsparse_t, &tmp)); + IGRAPH_CHECK(igraph_sparsemat_multiply_by_dense(myLsparse, &tmp, + my_scg_matrix)); + igraph_matrix_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + if (scg_sparsemat) { + IGRAPH_CHECK(igraph_matrix_as_sparsemat(scg_sparsemat, my_scg_matrix, + /* tol= */ 0)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, scg_sparsemat); + } + + if (scg_graph) { + if (type != IGRAPH_SCG_LAPLACIAN) { + IGRAPH_CHECK(igraph_weighted_adjacency(scg_graph, my_scg_matrix, + directed ? + IGRAPH_ADJ_DIRECTED : + IGRAPH_ADJ_UNDIRECTED, + "weight", /*loops=*/ 1)); + } else { + int i, j, n = (int) igraph_matrix_nrow(my_scg_matrix); + igraph_matrix_t tmp; + IGRAPH_MATRIX_INIT_FINALLY(&tmp, n, n); + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + MATRIX(tmp, i, j) = -MATRIX(*my_scg_matrix, i, j); + } + MATRIX(tmp, i, i) = 0; + } + IGRAPH_CHECK(igraph_weighted_adjacency(scg_graph, &tmp, directed ? + IGRAPH_ADJ_DIRECTED : + IGRAPH_ADJ_UNDIRECTED, + "weight", /*loops=*/ 0)); + igraph_matrix_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + } + IGRAPH_FINALLY(igraph_destroy, scg_graph); + } + + if (scg_graph) { + IGRAPH_FINALLY_CLEAN(1); + } + if (scg_sparsemat) { + IGRAPH_FINALLY_CLEAN(1); + } + + if (!igraph_sparsemat_is_cc(Lsparse)) { + igraph_sparsemat_destroy(myLsparse); + IGRAPH_FINALLY_CLEAN(1); + } + + if (!scg_matrix) { + igraph_matrix_destroy(my_scg_matrix); + IGRAPH_FINALLY_CLEAN(1); + } + + } else { /* sparsemat */ + igraph_sparsemat_t *my_scg_sparsemat = scg_sparsemat, v_scg_sparsemat; + igraph_sparsemat_t tmp, *mysparsemat = (igraph_sparsemat_t *) sparsemat, + v_sparsemat, *myLsparse = (igraph_sparsemat_t *) Lsparse, v_Lsparse; + if (!scg_sparsemat) { + my_scg_sparsemat = &v_scg_sparsemat; + } + if (!igraph_sparsemat_is_cc(sparsemat)) { + mysparsemat = &v_sparsemat; + IGRAPH_CHECK(igraph_sparsemat_compress(sparsemat, mysparsemat)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparsemat); + } + if (!igraph_sparsemat_is_cc(Lsparse)) { + myLsparse = &v_Lsparse; + IGRAPH_CHECK(igraph_sparsemat_compress(Lsparse, myLsparse)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, myLsparse); + } + IGRAPH_CHECK(igraph_sparsemat_multiply(mysparsemat, Rsparse_t, + &tmp)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); + IGRAPH_CHECK(igraph_sparsemat_multiply(myLsparse, &tmp, + my_scg_sparsemat)); + igraph_sparsemat_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_sparsemat_destroy, my_scg_sparsemat); + + if (scg_matrix) { + IGRAPH_CHECK(igraph_sparsemat_as_matrix(scg_matrix, my_scg_sparsemat)); + } + if (scg_graph) { + if (type != IGRAPH_SCG_LAPLACIAN) { + IGRAPH_CHECK(igraph_weighted_sparsemat(scg_graph, my_scg_sparsemat, + directed, "weight", + /*loops=*/ 1)); + } else { + igraph_sparsemat_t tmp; + IGRAPH_CHECK(igraph_sparsemat_copy(&tmp, my_scg_sparsemat)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); + IGRAPH_CHECK(igraph_sparsemat_neg(&tmp)); + IGRAPH_CHECK(igraph_weighted_sparsemat(scg_graph, &tmp, directed, + "weight", /*loops=*/ 0)); + igraph_sparsemat_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + } + IGRAPH_FINALLY(igraph_destroy, scg_graph); + } + + if (scg_graph) { + IGRAPH_FINALLY_CLEAN(1); + } + if (!scg_sparsemat) { + igraph_sparsemat_destroy(my_scg_sparsemat); + } + IGRAPH_FINALLY_CLEAN(1); /* my_scg_sparsemat */ + if (!igraph_sparsemat_is_cc(Lsparse)) { + igraph_sparsemat_destroy(myLsparse); + IGRAPH_FINALLY_CLEAN(1); + } + if (!igraph_sparsemat_is_cc(sparsemat)) { + igraph_sparsemat_destroy(mysparsemat); + IGRAPH_FINALLY_CLEAN(1); + } + } + + return 0; +} + +static int igraph_i_scg_common_checks(const igraph_t *graph, + const igraph_matrix_t *matrix, + const igraph_sparsemat_t *sparsemat, + const igraph_vector_t *ev, + igraph_integer_t nt, + const igraph_vector_t *nt_vec, + const igraph_matrix_t *vectors, + const igraph_matrix_complex_t *vectors_cmplx, + const igraph_vector_t *groups, + const igraph_t *scg_graph, + const igraph_matrix_t *scg_matrix, + const igraph_sparsemat_t *scg_sparsemat, + const igraph_vector_t *p, + igraph_real_t *evmin, igraph_real_t *evmax) { + + int no_of_nodes = -1; + igraph_real_t min, max; + int no_of_ev = (int) igraph_vector_size(ev); + + if ( (graph ? 1 : 0) + (matrix ? 1 : 0) + (sparsemat ? 1 : 0) != 1 ) { + IGRAPH_ERROR("Give exactly one of `graph', `matrix' and `sparsemat'", + IGRAPH_EINVAL); + } + + if (graph) { + no_of_nodes = igraph_vcount(graph); + } else if (matrix) { + no_of_nodes = (int) igraph_matrix_nrow(matrix); + } else if (sparsemat) { + no_of_nodes = (int) igraph_sparsemat_nrow(sparsemat); + } + + if ((matrix && igraph_matrix_ncol(matrix) != no_of_nodes) || + (sparsemat && igraph_sparsemat_ncol(sparsemat) != no_of_nodes)) { + IGRAPH_ERROR("Matrix must be square", IGRAPH_NONSQUARE); + } + + igraph_vector_minmax(ev, evmin, evmax); + if (*evmin < 0 || *evmax >= no_of_nodes) { + IGRAPH_ERROR("Invalid eigenvectors given", IGRAPH_EINVAL); + } + + if (!nt_vec && (nt <= 1 || nt >= no_of_nodes)) { + IGRAPH_ERROR("Invalid interval specification", IGRAPH_EINVAL); + } + + if (nt_vec) { + if (igraph_vector_size(nt_vec) != 1 && + igraph_vector_size(nt_vec) != no_of_ev) { + IGRAPH_ERROR("Invalid length for interval specification", + IGRAPH_EINVAL); + } + igraph_vector_minmax(nt_vec, &min, &max); + if (min <= 1 || max >= no_of_nodes) { + IGRAPH_ERROR("Invalid interval specification", IGRAPH_EINVAL); + } + } + + if (vectors && igraph_matrix_size(vectors) != 0 && + (igraph_matrix_ncol(vectors) != no_of_ev || + igraph_matrix_nrow(vectors) != no_of_nodes)) { + IGRAPH_ERROR("Invalid eigenvector matrix size", IGRAPH_EINVAL); + } + + if (vectors_cmplx && igraph_matrix_complex_size(vectors_cmplx) != 0 && + (igraph_matrix_complex_ncol(vectors_cmplx) != no_of_ev || + igraph_matrix_complex_nrow(vectors_cmplx) != no_of_nodes)) { + IGRAPH_ERROR("Invalid eigenvector matrix size", IGRAPH_EINVAL); + } + + if (groups && igraph_vector_size(groups) != 0 && + igraph_vector_size(groups) != no_of_nodes) { + IGRAPH_ERROR("Invalid `groups' vector size", IGRAPH_EINVAL); + } + + if ( (scg_graph != 0) + (scg_matrix != 0) + (scg_sparsemat != 0) == 0 ) { + IGRAPH_ERROR("No output is requested, please give at least one of " + "`scg_graph', `scg_matrix' and `scg_sparsemat'", + IGRAPH_EINVAL); + } + + if (p && igraph_vector_size(p) != 0 && + igraph_vector_size(p) != no_of_nodes) { + IGRAPH_ERROR("Invalid `p' vector size", IGRAPH_EINVAL); + } + + return 0; +} + +/** + * \function igraph_scg_adjacency + * Spectral coarse graining, symmetric case. + * + * This function handles all the steps involved in the Spectral Coarse + * Graining (SCG) of some matrices and graphs as described in the + * reference below. + * + * \param graph The input graph. Exactly one of \p graph, \p matrix + * and \p sparsemat must be given, the other two must be \c NULL + * pointers. + * \param matrix The input matrix. Exactly one of \p graph, \p matrix + * and \p sparsemat must be given, the other two must be \c NULL + * pointers. + * \param sparsemat The input sparse matrix. Exactly one of \p graph, + * \p matrix and \p sparsemat must be given, the other two must be + * \c NULL pointers. + * \param ev A vector of positive integers giving the indexes of the + * eigenpairs to be preserved. 1 designates the eigenvalue with + * largest algebraic value, 2 the one with second largest algebraic + * value, etc. + * \param nt Positive integer. When \p algo is \c IGRAPH_SCG_OPTIMUM, + * it gives the number of groups to partition each eigenvector + * separately. When \p algo is \c IGRAPH_SCG_INTERV or \c + * IGRAPH_SCG_INTERV_KM, it gives the number of intervals to + * partition each eigenvector. This is ignored when \p algo is \c + * IGRAPH_SCG_EXACT. + * \param nt_vec A numeric vector of length one or the length must + * match the number of eigenvectors given in \p V, or a \c NULL + * pointer. If not \c NULL, then this argument gives the number of + * groups or intervals, and \p nt is ignored. Different number of + * groups or intervals can be specified for each eigenvector. + * \param algo The algorithm to solve the SCG problem. Possible + * values: \c IGRAPH_SCG_OPTIMUM, \c IGRAPH_SCG_INTERV_KM, \c + * IGRAPH_SCG_INTERV and \c IGRAPH_SCG_EXACT. Please see the + * details about them above. + * \param values If this is not \c NULL and the eigenvectors are + * re-calculated, then the eigenvalues are stored here. + * \param vectors If this is not \c NULL, and not a zero-length + * matrix, then it is interpreted as the eigenvectors to use for + * the coarse-graining. Otherwise the eigenvectors are + * re-calculated, and they are stored here. (If this is not \c NULL.) + * \param groups If this is not \c NULL, and not a zero-length vector, + * then it is interpreted as the vector of group labels. (Group + * labels are integers from zero and are sequential.) Otherwise + * group labels are re-calculated and stored here, if this argument + * is not a null pointer. + * \param use_arpack Whether to use ARPACK for solving the + * eigenproblem. Currently ARPACK is not implemented. + * \param maxiter A positive integer giving the number of iterations + * of the k-means algorithm when \p algo is \c + * IGRAPH_SCG_INTERV_KM. It is ignored in other cases. A reasonable + * (initial) value for this argument is 100. + * \param scg_graph If not a \c NULL pointer, then the coarse-grained + * graph is returned here. + * \param scg_matrix If not a \c NULL pointer, then it must be an + * initialied matrix, and the coarse-grained matrix is returned + * here. + * \param scg_sparsemat If not a \c NULL pointer, then the coarse + * grained matrix is returned here, in sparse matrix form. + * \param L If not a \c NULL pointer, then it must be an initialized + * matrix and the left semi-projector is returned here. + * \param R If not a \c NULL pointer, then it must be an initialized + * matrix and the right semi-projector is returned here. + * \param Lsparse If not a \c NULL pointer, then the left + * semi-projector is returned here. + * \param Rsparse If not a \c NULL pointer, then the right + * semi-projector is returned here. + * \return Error code. + * + * Time complexity: TODO. + * + * \sa \ref igraph_scg_grouping(), \ref igraph_scg_semiprojectors(), + * \ref igraph_scg_stochastic() and \ref igraph_scg_laplacian(). + * + * \example examples/simple/scg.c + */ + +int igraph_scg_adjacency(const igraph_t *graph, + const igraph_matrix_t *matrix, + const igraph_sparsemat_t *sparsemat, + const igraph_vector_t *ev, + igraph_integer_t nt, + const igraph_vector_t *nt_vec, + igraph_scg_algorithm_t algo, + igraph_vector_t *values, + igraph_matrix_t *vectors, + igraph_vector_t *groups, + igraph_bool_t use_arpack, + igraph_integer_t maxiter, + igraph_t *scg_graph, + igraph_matrix_t *scg_matrix, + igraph_sparsemat_t *scg_sparsemat, + igraph_matrix_t *L, + igraph_matrix_t *R, + igraph_sparsemat_t *Lsparse, + igraph_sparsemat_t *Rsparse) { + + igraph_sparsemat_t *mysparsemat = (igraph_sparsemat_t*) sparsemat, + real_sparsemat; + int no_of_ev = (int) igraph_vector_size(ev); + /* eigenvectors are calculated and returned */ + igraph_bool_t do_vectors = vectors && igraph_matrix_size(vectors) == 0; + /* groups are calculated */ + igraph_bool_t do_groups = !groups || igraph_vector_size(groups) == 0; + /* eigenvectors are not returned but must be calculated for groups */ + igraph_bool_t tmp_vectors = !do_vectors && do_groups; + /* need temporary vector for groups */ + igraph_bool_t tmp_groups = !groups; + igraph_matrix_t myvectors; + igraph_vector_t mygroups; + igraph_bool_t tmp_lsparse = !Lsparse, tmp_rsparse = !Rsparse; + igraph_sparsemat_t myLsparse, myRsparse, tmpsparse, Rsparse_t; + int no_of_nodes; + igraph_real_t evmin, evmax; + igraph_bool_t directed; + + /* --------------------------------------------------------------------*/ + /* Argument checks */ + + IGRAPH_CHECK(igraph_i_scg_common_checks(graph, matrix, sparsemat, + ev, nt, nt_vec, + vectors, 0, groups, scg_graph, + scg_matrix, scg_sparsemat, + /*p=*/ 0, &evmin, &evmax)); + + if (graph) { + no_of_nodes = igraph_vcount(graph); + directed = igraph_is_directed(graph); + } else if (matrix) { + no_of_nodes = (int) igraph_matrix_nrow(matrix); + directed = !igraph_matrix_is_symmetric(matrix); + } else { + no_of_nodes = (int) igraph_sparsemat_nrow(sparsemat); + directed = !igraph_sparsemat_is_symmetric(sparsemat); + } + + /* -------------------------------------------------------------------- */ + /* Convert graph, if needed */ + + if (graph) { + mysparsemat = &real_sparsemat; + IGRAPH_CHECK(igraph_get_sparsemat(graph, mysparsemat)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparsemat); + } + + /* -------------------------------------------------------------------- */ + /* Compute eigenpairs, if needed */ + if (tmp_vectors) { + vectors = &myvectors; + IGRAPH_MATRIX_INIT_FINALLY(vectors, no_of_nodes, no_of_ev); + } + + if (do_vectors || tmp_vectors) { + igraph_arpack_options_t options; + igraph_eigen_which_t which; + igraph_matrix_t tmp; + igraph_vector_t tmpev; + igraph_vector_t tmpeval; + int i; + + which.pos = IGRAPH_EIGEN_SELECT; + which.il = (int) (no_of_nodes - evmax + 1); + which.iu = (int) (no_of_nodes - evmin + 1); + + if (values) { + IGRAPH_VECTOR_INIT_FINALLY(&tmpeval, 0); + } + IGRAPH_CHECK(igraph_matrix_init(&tmp, no_of_nodes, + which.iu - which.il + 1)); + IGRAPH_FINALLY(igraph_matrix_destroy, &tmp); + IGRAPH_CHECK(igraph_eigen_matrix_symmetric(matrix, mysparsemat, + /* fun= */ 0, no_of_nodes, + /* extra= */ 0, + /* algorithm= */ + use_arpack ? + IGRAPH_EIGEN_ARPACK : + IGRAPH_EIGEN_LAPACK, &which, + &options, /*storage=*/ 0, + values ? &tmpeval : 0, + &tmp)); + IGRAPH_VECTOR_INIT_FINALLY(&tmpev, no_of_ev); + for (i = 0; i < no_of_ev; i++) { + VECTOR(tmpev)[i] = evmax - VECTOR(*ev)[i]; + } + if (values) { + IGRAPH_CHECK(igraph_vector_index(&tmpeval, values, &tmpev)); + } + IGRAPH_CHECK(igraph_matrix_select_cols(&tmp, vectors, &tmpev)); + igraph_vector_destroy(&tmpev); + igraph_matrix_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); + if (values) { + igraph_vector_destroy(&tmpeval); + IGRAPH_FINALLY_CLEAN(1); + } + } + + /* -------------------------------------------------------------------- */ + /* Work out groups, if needed */ + if (tmp_groups) { + groups = &mygroups; + IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)groups, no_of_nodes); + } + if (do_groups) { + IGRAPH_CHECK(igraph_scg_grouping(vectors, (igraph_vector_t*)groups, + nt, nt_vec, + IGRAPH_SCG_SYMMETRIC, algo, + /*p=*/ 0, maxiter)); + } + + /* -------------------------------------------------------------------- */ + /* Perform coarse graining */ + if (tmp_lsparse) { + Lsparse = &myLsparse; + } + if (tmp_rsparse) { + Rsparse = &myRsparse; + } + IGRAPH_CHECK(igraph_scg_semiprojectors(groups, IGRAPH_SCG_SYMMETRIC, + L, R, Lsparse, Rsparse, /*p=*/ 0, + IGRAPH_SCG_NORM_ROW)); + if (tmp_groups) { + igraph_vector_destroy((igraph_vector_t*) groups); + IGRAPH_FINALLY_CLEAN(1); + } + if (tmp_vectors) { + igraph_matrix_destroy(vectors); + IGRAPH_FINALLY_CLEAN(1); + } + if (Rsparse) { + IGRAPH_FINALLY(igraph_sparsemat_destroy, Rsparse); + } + if (Lsparse) { + IGRAPH_FINALLY(igraph_sparsemat_destroy, Lsparse); + } + + /* -------------------------------------------------------------------- */ + /* Compute coarse grained matrix/graph/sparse matrix */ + IGRAPH_CHECK(igraph_sparsemat_compress(Rsparse, &tmpsparse)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmpsparse); + IGRAPH_CHECK(igraph_sparsemat_transpose(&tmpsparse, &Rsparse_t, + /*values=*/ 1)); + igraph_sparsemat_destroy(&tmpsparse); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse_t); + + IGRAPH_CHECK(igraph_i_scg_get_result(IGRAPH_SCG_SYMMETRIC, + matrix, mysparsemat, + Lsparse, &Rsparse_t, + scg_graph, scg_matrix, + scg_sparsemat, directed)); + + /* -------------------------------------------------------------------- */ + /* Clean up */ + + igraph_sparsemat_destroy(&Rsparse_t); + IGRAPH_FINALLY_CLEAN(1); + if (Lsparse) { + IGRAPH_FINALLY_CLEAN(1); + } + if (Rsparse) { + IGRAPH_FINALLY_CLEAN(1); + } + + if (graph) { + igraph_sparsemat_destroy(mysparsemat); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_scg_stochastic + * Spectral coarse graining, stochastic case. + * + * This function handles all the steps involved in the Spectral Coarse + * Graining (SCG) of some matrices and graphs as described in the + * reference below. + * + * \param graph The input graph. Exactly one of \p graph, \p matrix + * and \p sparsemat must be given, the other two must be \c NULL + * pointers. + * \param matrix The input matrix. Exactly one of \p graph, \p matrix + * and \p sparsemat must be given, the other two must be \c NULL + * pointers. + * \param sparsemat The input sparse matrix. Exactly one of \p graph, + * \p matrix and \p sparsemat must be given, the other two must be + * \c NULL pointers. + * \param ev A vector of positive integers giving the indexes of the + * eigenpairs to be preserved. 1 designates the eigenvalue with + * largest magnitude, 2 the one with second largest magnitude, etc. + * \param nt Positive integer. When \p algo is \c IGRAPH_SCG_OPTIMUM, + * it gives the number of groups to partition each eigenvector + * separately. When \p algo is \c IGRAPH_SCG_INTERV or \c + * IGRAPH_SCG_INTERV_KM, it gives the number of intervals to + * partition each eigenvector. This is ignored when \p algo is \c + * IGRAPH_SCG_EXACT. + * \param nt_vec A numeric vector of length one or the length must + * match the number of eigenvectors given in \p V, or a \c NULL + * pointer. If not \c NULL, then this argument gives the number of + * groups or intervals, and \p nt is ignored. Different number of + * groups or intervals can be specified for each eigenvector. + * \param algo The algorithm to solve the SCG problem. Possible + * values: \c IGRAPH_SCG_OPTIMUM, \c IGRAPH_SCG_INTERV_KM, \c + * IGRAPH_SCG_INTERV and \c IGRAPH_SCG_EXACT. Please see the + * details about them above. + * \param norm Either \c IGRAPH_SCG_NORM_ROW or \c IGRAPH_SCG_NORM_COL. + * Specifies whether the rows or the columns of the + * stochastic matrix sum up to one. + * \param values If this is not \c NULL and the eigenvectors are + * re-calculated, then the eigenvalues are stored here. + * \param vectors If this is not \c NULL, and not a zero-length + * matrix, then it is interpreted as the eigenvectors to use for + * the coarse-graining. Otherwise the eigenvectors are + * re-calculated, and they are stored here. (If this is not \c NULL.) + * \param groups If this is not \c NULL, and not a zero-length vector, + * then it is interpreted as the vector of group labels. (Group + * labels are integers from zero and are sequential.) Otherwise + * group labels are re-calculated and stored here, if this argument + * is not a null pointer. + * \param p If this is not \c NULL, and not zero length, then it is + * interpreted as the stationary probability distribution of the + * Markov chain corresponding to the input matrix/graph. Its length + * must match the number of vertices in the input graph (or number + * of rows in the input matrix). If not given, then the stationary + * distribution is calculated and stored here. (Unless this + * argument is a \c NULL pointer, in which case it is not stored.) + * \param use_arpack Whether to use ARPACK for solving the + * eigenproblem. Currently ARPACK is not implemented. + * \param maxiter A positive integer giving the number of iterations + * of the k-means algorithm when \p algo is \c + * IGRAPH_SCG_INTERV_KM. It is ignored in other cases. A reasonable + * (initial) value for this argument is 100. + * \param scg_graph If not a \c NULL pointer, then the coarse-grained + * graph is returned here. + * \param scg_matrix If not a \c NULL pointer, then it must be an + * initialied matrix, and the coarse-grained matrix is returned + * here. + * \param scg_sparsemat If not a \c NULL pointer, then the coarse + * grained matrix is returned here, in sparse matrix form. + * \param L If not a \c NULL pointer, then it must be an initialized + * matrix and the left semi-projector is returned here. + * \param R If not a \c NULL pointer, then it must be an initialized + * matrix and the right semi-projector is returned here. + * \param Lsparse If not a \c NULL pointer, then the left + * semi-projector is returned here. + * \param Rsparse If not a \c NULL pointer, then the right + * semi-projector is returned here. + * \return Error code. + * + * Time complexity: TODO. + * + * \sa \ref igraph_scg_grouping(), \ref igraph_scg_semiprojectors(), + * \ref igraph_scg_adjacency() and \ref igraph_scg_laplacian(). + */ + +int igraph_scg_stochastic(const igraph_t *graph, + const igraph_matrix_t *matrix, + const igraph_sparsemat_t *sparsemat, + const igraph_vector_t *ev, + igraph_integer_t nt, + const igraph_vector_t *nt_vec, + igraph_scg_algorithm_t algo, + igraph_scg_norm_t norm, + igraph_vector_complex_t *values, + igraph_matrix_complex_t *vectors, + igraph_vector_t *groups, + igraph_vector_t *p, + igraph_bool_t use_arpack, + igraph_integer_t maxiter, + igraph_t *scg_graph, + igraph_matrix_t *scg_matrix, + igraph_sparsemat_t *scg_sparsemat, + igraph_matrix_t *L, + igraph_matrix_t *R, + igraph_sparsemat_t *Lsparse, + igraph_sparsemat_t *Rsparse) { + + igraph_matrix_t *mymatrix = (igraph_matrix_t*) matrix, real_matrix; + igraph_sparsemat_t *mysparsemat = (igraph_sparsemat_t*) sparsemat, + real_sparsemat; + int no_of_nodes; + igraph_real_t evmin, evmax; + igraph_arpack_options_t options; + igraph_eigen_which_t which; + /* eigenvectors are calculated and returned */ + igraph_bool_t do_vectors = vectors && igraph_matrix_complex_size(vectors) == 0; + /* groups are calculated */ + igraph_bool_t do_groups = !groups || igraph_vector_size(groups) == 0; + igraph_bool_t tmp_groups = !groups; + /* eigenvectors are not returned but must be calculated for groups */ + igraph_bool_t tmp_vectors = !do_vectors && do_groups; + igraph_matrix_complex_t myvectors; + igraph_vector_t mygroups; + igraph_bool_t do_p = !p || igraph_vector_size(p) == 0; + igraph_vector_t *myp = (igraph_vector_t *) p, real_p; + int no_of_ev = (int) igraph_vector_size(ev); + igraph_bool_t tmp_lsparse = !Lsparse, tmp_rsparse = !Rsparse; + igraph_sparsemat_t myLsparse, myRsparse, tmpsparse, Rsparse_t; + + /* --------------------------------------------------------------------*/ + /* Argument checks */ + + IGRAPH_CHECK(igraph_i_scg_common_checks(graph, matrix, sparsemat, + ev, nt, nt_vec, + 0, vectors, groups, scg_graph, + scg_matrix, scg_sparsemat, p, + &evmin, &evmax)); + + if (graph) { + no_of_nodes = igraph_vcount(graph); + } else if (matrix) { + no_of_nodes = (int) igraph_matrix_nrow(matrix); + } else { + no_of_nodes = (int) igraph_sparsemat_nrow(sparsemat); + } + + /* -------------------------------------------------------------------- */ + /* Convert graph, if needed */ + + if (graph) { + mysparsemat = &real_sparsemat; + IGRAPH_CHECK(igraph_get_stochastic_sparsemat(graph, mysparsemat, + norm == IGRAPH_SCG_NORM_COL)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparsemat); + } else if (matrix) { + mymatrix = &real_matrix; + IGRAPH_CHECK(igraph_i_matrix_stochastic(matrix, mymatrix, norm)); + IGRAPH_FINALLY(igraph_matrix_destroy, mymatrix); + } else { /* sparsemat */ + mysparsemat = &real_sparsemat; + IGRAPH_CHECK(igraph_i_sparsemat_stochastic(sparsemat, mysparsemat, norm)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparsemat); + } + + /* -------------------------------------------------------------------- */ + /* Compute eigenpairs, if needed */ + + if (tmp_vectors) { + vectors = &myvectors; + IGRAPH_CHECK(igraph_matrix_complex_init(vectors, no_of_nodes, no_of_ev)); + IGRAPH_FINALLY(igraph_matrix_complex_destroy, vectors); + } + + if (do_vectors || tmp_vectors) { + igraph_matrix_complex_t tmp; + igraph_vector_t tmpev; + igraph_vector_complex_t tmpeval; + int i; + + which.pos = IGRAPH_EIGEN_SELECT; + which.il = (int) (no_of_nodes - evmax + 1); + which.iu = (int) (no_of_nodes - evmin + 1); + + if (values) { + IGRAPH_CHECK(igraph_vector_complex_init(&tmpeval, 0)); + IGRAPH_FINALLY(igraph_vector_complex_destroy, &tmpeval); + } + IGRAPH_CHECK(igraph_matrix_complex_init(&tmp, no_of_nodes, + which.iu - which.il + 1)); + IGRAPH_FINALLY(igraph_matrix_complex_destroy, &tmp); + IGRAPH_CHECK(igraph_eigen_matrix(mymatrix, mysparsemat, /*fun=*/ 0, + no_of_nodes, /*extra=*/ 0, use_arpack ? + IGRAPH_EIGEN_ARPACK : + IGRAPH_EIGEN_LAPACK, &which, &options, + /*storage=*/ 0, + values ? &tmpeval : 0, &tmp)); + + IGRAPH_VECTOR_INIT_FINALLY(&tmpev, no_of_ev); + for (i = 0; i < no_of_ev; i++) { + VECTOR(tmpev)[i] = evmax - VECTOR(*ev)[i]; + } + if (values) { + IGRAPH_CHECK(igraph_vector_complex_index(&tmpeval, values, &tmpev)); + } + IGRAPH_CHECK(igraph_matrix_complex_select_cols(&tmp, vectors, &tmpev)); + igraph_vector_destroy(&tmpev); + igraph_matrix_complex_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); + if (values) { + igraph_vector_complex_destroy(&tmpeval); + IGRAPH_FINALLY_CLEAN(1); + } + } + + /* Compute p if not supplied */ + if (do_p) { + igraph_eigen_which_t w; + igraph_matrix_complex_t tmp; + igraph_arpack_options_t o; + igraph_matrix_t trans, *mytrans = &trans; + igraph_sparsemat_t sparse_trans, *mysparse_trans = &sparse_trans; + int i; + igraph_arpack_options_init(&o); + if (!p) { + IGRAPH_VECTOR_INIT_FINALLY(&real_p, no_of_nodes); + myp = &real_p; + } else { + IGRAPH_CHECK(igraph_vector_resize(p, no_of_nodes)); + } + IGRAPH_CHECK(igraph_matrix_complex_init(&tmp, 0, 0)); + IGRAPH_FINALLY(igraph_matrix_complex_destroy, &tmp); + w.pos = IGRAPH_EIGEN_LR; + w.howmany = 1; + + if (mymatrix) { + IGRAPH_CHECK(igraph_matrix_copy(&trans, mymatrix)); + IGRAPH_FINALLY(igraph_matrix_destroy, &trans); + IGRAPH_CHECK(igraph_matrix_transpose(&trans)); + mysparse_trans = 0; + } else { + IGRAPH_CHECK(igraph_sparsemat_transpose(mysparsemat, &sparse_trans, + /*values=*/ 1)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparse_trans); + mytrans = 0; + } + + IGRAPH_CHECK(igraph_eigen_matrix(mytrans, mysparse_trans, /*fun=*/ 0, + no_of_nodes, /*extra=*/ 0, /*algorith=*/ + use_arpack ? + IGRAPH_EIGEN_ARPACK : + IGRAPH_EIGEN_LAPACK, &w, &o, + /*storage=*/ 0, /*values=*/ 0, &tmp)); + + if (mymatrix) { + igraph_matrix_destroy(&trans); + IGRAPH_FINALLY_CLEAN(1); + } else { + igraph_sparsemat_destroy(mysparse_trans); + IGRAPH_FINALLY_CLEAN(1); + } + + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*myp)[i] = fabs(IGRAPH_REAL(MATRIX(tmp, i, 0))); + } + igraph_matrix_complex_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + } + + /* -------------------------------------------------------------------- */ + /* Work out groups, if needed */ + /* TODO: use complex part as well */ + if (tmp_groups) { + groups = &mygroups; + IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)groups, no_of_nodes); + } + if (do_groups) { + igraph_matrix_t tmp; + IGRAPH_MATRIX_INIT_FINALLY(&tmp, 0, 0); + IGRAPH_CHECK(igraph_matrix_complex_real(vectors, &tmp)); + IGRAPH_CHECK(igraph_scg_grouping(&tmp, (igraph_vector_t*)groups, + nt, nt_vec, + IGRAPH_SCG_STOCHASTIC, algo, + myp, maxiter)); + igraph_matrix_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + } + + /* -------------------------------------------------------------------- */ + /* Perform coarse graining */ + if (tmp_lsparse) { + Lsparse = &myLsparse; + } + if (tmp_rsparse) { + Rsparse = &myRsparse; + } + IGRAPH_CHECK(igraph_scg_semiprojectors(groups, IGRAPH_SCG_STOCHASTIC, + L, R, Lsparse, Rsparse, myp, norm)); + if (tmp_groups) { + igraph_vector_destroy((igraph_vector_t*) groups); + IGRAPH_FINALLY_CLEAN(1); + } + if (!p && do_p) { + igraph_vector_destroy(myp); + IGRAPH_FINALLY_CLEAN(1); + } + if (tmp_vectors) { + igraph_matrix_complex_destroy(vectors); + IGRAPH_FINALLY_CLEAN(1); + } + if (Rsparse) { + IGRAPH_FINALLY(igraph_sparsemat_destroy, Rsparse); + } + if (Lsparse) { + IGRAPH_FINALLY(igraph_sparsemat_destroy, Lsparse); + } + + /* -------------------------------------------------------------------- */ + /* Compute coarse grained matrix/graph/sparse matrix */ + IGRAPH_CHECK(igraph_sparsemat_compress(Rsparse, &tmpsparse)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmpsparse); + IGRAPH_CHECK(igraph_sparsemat_transpose(&tmpsparse, &Rsparse_t, + /*values=*/ 1)); + igraph_sparsemat_destroy(&tmpsparse); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse_t); + + IGRAPH_CHECK(igraph_i_scg_get_result(IGRAPH_SCG_STOCHASTIC, + mymatrix, mysparsemat, + Lsparse, &Rsparse_t, + scg_graph, scg_matrix, + scg_sparsemat, /*directed=*/ 1)); + + /* -------------------------------------------------------------------- */ + /* Clean up */ + + igraph_sparsemat_destroy(&Rsparse_t); + IGRAPH_FINALLY_CLEAN(1); + if (Lsparse) { + IGRAPH_FINALLY_CLEAN(1); + } + if (Rsparse) { + IGRAPH_FINALLY_CLEAN(1); + } + + if (graph) { + igraph_sparsemat_destroy(mysparsemat); + IGRAPH_FINALLY_CLEAN(1); + } else if (matrix) { + igraph_matrix_destroy(mymatrix); + IGRAPH_FINALLY_CLEAN(1); + } else { + igraph_sparsemat_destroy(mysparsemat); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +/** + * \function igraph_scg_laplacian + * \brief Spectral coarse graining, Laplacian case. + * + * This function handles all the steps involved in the Spectral Coarse + * Graining (SCG) of some matrices and graphs as described in the + * reference below. + * + * \param graph The input graph. Exactly one of \p graph, \p matrix + * and \p sparsemat must be given, the other two must be \c NULL + * pointers. + * \param matrix The input matrix. Exactly one of \p graph, \p matrix + * and \p sparsemat must be given, the other two must be \c NULL + * pointers. + * \param sparsemat The input sparse matrix. Exactly one of \p graph, + * \p matrix and \p sparsemat must be given, the other two must be + * \c NULL pointers. + * \param ev A vector of positive integers giving the indexes of the + * eigenpairs to be preserved. 1 designates the eigenvalue with + * largest magnitude, 2 the one with second largest magnitude, etc. + * \param nt Positive integer. When \p algo is \c IGRAPH_SCG_OPTIMUM, + * it gives the number of groups to partition each eigenvector + * separately. When \p algo is \c IGRAPH_SCG_INTERV or \c + * IGRAPH_SCG_INTERV_KM, it gives the number of intervals to + * partition each eigenvector. This is ignored when \p algo is \c + * IGRAPH_SCG_EXACT. + * \param nt_vec A numeric vector of length one or the length must + * match the number of eigenvectors given in \p V, or a \c NULL + * pointer. If not \c NULL, then this argument gives the number of + * groups or intervals, and \p nt is ignored. Different number of + * groups or intervals can be specified for each eigenvector. + * \param algo The algorithm to solve the SCG problem. Possible + * values: \c IGRAPH_SCG_OPTIMUM, \c IGRAPH_SCG_INTERV_KM, \c + * IGRAPH_SCG_INTERV and \c IGRAPH_SCG_EXACT. Please see the + * details about them above. + * \param norm Either \c IGRAPH_SCG_NORM_ROW or \c IGRAPH_SCG_NORM_COL. + * Specifies whether the rows or the columns of the Laplacian + * matrix sum up to zero. + * \param direction Whether to work with left or right eigenvectors. + * Possible values: \c IGRAPH_SCG_DIRECTION_DEFAULT, \c + * IGRAPH_SCG_DIRECTION_LEFT, \c IGRAPH_SCG_DIRECTION_RIGHT. This + * argument is currently ignored and right eigenvectors are always + * used. + * \param values If this is not \c NULL and the eigenvectors are + * re-calculated, then the eigenvalues are stored here. + * \param vectors If this is not \c NULL, and not a zero-length + * matrix, then it is interpreted as the eigenvectors to use for + * the coarse-graining. Otherwise the eigenvectors are + * re-calculated, and they are stored here. (If this is not \c NULL.) + * \param groups If this is not \c NULL, and not a zero-length vector, + * then it is interpreted as the vector of group labels. (Group + * labels are integers from zero and are sequential.) Otherwise + * group labels are re-calculated and stored here, if this argument + * is not a null pointer. + * \param use_arpack Whether to use ARPACK for solving the + * eigenproblem. Currently ARPACK is not implemented. + * \param maxiter A positive integer giving the number of iterations + * of the k-means algorithm when \p algo is \c + * IGRAPH_SCG_INTERV_KM. It is ignored in other cases. A reasonable + * (initial) value for this argument is 100. + * \param scg_graph If not a \c NULL pointer, then the coarse-grained + * graph is returned here. + * \param scg_matrix If not a \c NULL pointer, then it must be an + * initialied matrix, and the coarse-grained matrix is returned + * here. + * \param scg_sparsemat If not a \c NULL pointer, then the coarse + * grained matrix is returned here, in sparse matrix form. + * \param L If not a \c NULL pointer, then it must be an initialized + * matrix and the left semi-projector is returned here. + * \param R If not a \c NULL pointer, then it must be an initialized + * matrix and the right semi-projector is returned here. + * \param Lsparse If not a \c NULL pointer, then the left + * semi-projector is returned here. + * \param Rsparse If not a \c NULL pointer, then the right + * semi-projector is returned here. + * \return Error code. + * + * Time complexity: TODO. + * + * \sa \ref igraph_scg_grouping(), \ref igraph_scg_semiprojectors(), + * \ref igraph_scg_stochastic() and \ref igraph_scg_adjacency(). + */ + +int igraph_scg_laplacian(const igraph_t *graph, + const igraph_matrix_t *matrix, + const igraph_sparsemat_t *sparsemat, + const igraph_vector_t *ev, + igraph_integer_t nt, + const igraph_vector_t *nt_vec, + igraph_scg_algorithm_t algo, + igraph_scg_norm_t norm, + igraph_scg_direction_t direction, + igraph_vector_complex_t *values, + igraph_matrix_complex_t *vectors, + igraph_vector_t *groups, + igraph_bool_t use_arpack, + igraph_integer_t maxiter, + igraph_t *scg_graph, + igraph_matrix_t *scg_matrix, + igraph_sparsemat_t *scg_sparsemat, + igraph_matrix_t *L, + igraph_matrix_t *R, + igraph_sparsemat_t *Lsparse, + igraph_sparsemat_t *Rsparse) { + + igraph_matrix_t *mymatrix = (igraph_matrix_t*) matrix, real_matrix; + igraph_sparsemat_t *mysparsemat = (igraph_sparsemat_t*) sparsemat, + real_sparsemat; + int no_of_nodes; + igraph_real_t evmin, evmax; + igraph_arpack_options_t options; + igraph_eigen_which_t which; + /* eigenvectors are calculated and returned */ + igraph_bool_t do_vectors = vectors && igraph_matrix_complex_size(vectors) == 0; + /* groups are calculated */ + igraph_bool_t do_groups = !groups || igraph_vector_size(groups) == 0; + igraph_bool_t tmp_groups = !groups; + /* eigenvectors are not returned but must be calculated for groups */ + igraph_bool_t tmp_vectors = !do_vectors && do_groups; + igraph_matrix_complex_t myvectors; + igraph_vector_t mygroups; + int no_of_ev = (int) igraph_vector_size(ev); + igraph_bool_t tmp_lsparse = !Lsparse, tmp_rsparse = !Rsparse; + igraph_sparsemat_t myLsparse, myRsparse, tmpsparse, Rsparse_t; + + IGRAPH_UNUSED(direction); + + /* --------------------------------------------------------------------*/ + /* Argument checks */ + + IGRAPH_CHECK(igraph_i_scg_common_checks(graph, matrix, sparsemat, + ev, nt, nt_vec, + 0, vectors, groups, scg_graph, + scg_matrix, scg_sparsemat, /*p=*/ 0, + &evmin, &evmax)); + + if (graph) { + no_of_nodes = igraph_vcount(graph); + } else if (matrix) { + no_of_nodes = (int) igraph_matrix_nrow(matrix); + } else { + no_of_nodes = (int) igraph_sparsemat_nrow(sparsemat); + } + + /* -------------------------------------------------------------------- */ + /* Convert graph, if needed, get Laplacian matrix */ + + if (graph) { + mysparsemat = &real_sparsemat; + IGRAPH_CHECK(igraph_sparsemat_init(mysparsemat, 0, 0, 0)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparsemat); + IGRAPH_CHECK(igraph_laplacian(graph, 0, mysparsemat, /*normalized=*/ 0, + /*weights=*/ 0)); + } else if (matrix) { + mymatrix = &real_matrix; + IGRAPH_MATRIX_INIT_FINALLY(mymatrix, no_of_nodes, no_of_nodes); + IGRAPH_CHECK(igraph_i_matrix_laplacian(matrix, mymatrix, norm)); + } else { /* sparsemat */ + mysparsemat = &real_sparsemat; + IGRAPH_CHECK(igraph_i_sparsemat_laplacian(sparsemat, mysparsemat, + norm == IGRAPH_SCG_NORM_COL)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparsemat); + } + + /* -------------------------------------------------------------------- */ + /* Compute eigenpairs, if needed */ + + if (tmp_vectors) { + vectors = &myvectors; + IGRAPH_CHECK(igraph_matrix_complex_init(vectors, no_of_nodes, no_of_ev)); + IGRAPH_FINALLY(igraph_matrix_complex_destroy, vectors); + } + + if (do_vectors || tmp_vectors) { + igraph_matrix_complex_t tmp; + igraph_vector_t tmpev; + igraph_vector_complex_t tmpeval; + int i; + + which.pos = IGRAPH_EIGEN_SELECT; + which.il = (int) (no_of_nodes - evmax + 1); + which.iu = (int) (no_of_nodes - evmin + 1); + + if (values) { + IGRAPH_CHECK(igraph_vector_complex_init(&tmpeval, 0)); + IGRAPH_FINALLY(igraph_vector_complex_destroy, &tmpeval); + } + IGRAPH_CHECK(igraph_matrix_complex_init(&tmp, no_of_nodes, + which.iu - which.il + 1)); + IGRAPH_FINALLY(igraph_matrix_complex_destroy, &tmp); + IGRAPH_CHECK(igraph_eigen_matrix(mymatrix, mysparsemat, /*fun=*/ 0, + no_of_nodes, /*extra=*/ 0, use_arpack ? + IGRAPH_EIGEN_ARPACK : + IGRAPH_EIGEN_LAPACK, &which, &options, + /*storage=*/ 0, + values ? &tmpeval : 0, &tmp)); + + IGRAPH_VECTOR_INIT_FINALLY(&tmpev, no_of_ev); + for (i = 0; i < no_of_ev; i++) { + VECTOR(tmpev)[i] = evmax - VECTOR(*ev)[i]; + } + if (values) { + IGRAPH_CHECK(igraph_vector_complex_index(&tmpeval, values, &tmpev)); + } + IGRAPH_CHECK(igraph_matrix_complex_select_cols(&tmp, vectors, &tmpev)); + igraph_vector_destroy(&tmpev); + igraph_matrix_complex_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); + if (values) { + igraph_vector_complex_destroy(&tmpeval); + IGRAPH_FINALLY_CLEAN(1); + } + } + + /* -------------------------------------------------------------------- */ + /* Work out groups, if needed */ + /* TODO: use complex part as well */ + if (tmp_groups) { + groups = &mygroups; + IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)groups, no_of_nodes); + } + if (do_groups) { + igraph_matrix_t tmp; + IGRAPH_MATRIX_INIT_FINALLY(&tmp, 0, 0); + IGRAPH_CHECK(igraph_matrix_complex_real(vectors, &tmp)); + IGRAPH_CHECK(igraph_scg_grouping(&tmp, (igraph_vector_t*)groups, + nt, nt_vec, + IGRAPH_SCG_LAPLACIAN, algo, + /*p=*/ 0, maxiter)); + igraph_matrix_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + } + + /* -------------------------------------------------------------------- */ + /* Perform coarse graining */ + if (tmp_lsparse) { + Lsparse = &myLsparse; + } + if (tmp_rsparse) { + Rsparse = &myRsparse; + } + IGRAPH_CHECK(igraph_scg_semiprojectors(groups, IGRAPH_SCG_LAPLACIAN, + L, R, Lsparse, Rsparse, /*p=*/ 0, + norm)); + if (tmp_groups) { + igraph_vector_destroy((igraph_vector_t*) groups); + IGRAPH_FINALLY_CLEAN(1); + } + if (tmp_vectors) { + igraph_matrix_complex_destroy(vectors); + IGRAPH_FINALLY_CLEAN(1); + } + if (Rsparse) { + IGRAPH_FINALLY(igraph_sparsemat_destroy, Rsparse); + } + if (Lsparse) { + IGRAPH_FINALLY(igraph_sparsemat_destroy, Lsparse); + } + + /* -------------------------------------------------------------------- */ + /* Compute coarse grained matrix/graph/sparse matrix */ + IGRAPH_CHECK(igraph_sparsemat_compress(Rsparse, &tmpsparse)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmpsparse); + IGRAPH_CHECK(igraph_sparsemat_transpose(&tmpsparse, &Rsparse_t, + /*values=*/ 1)); + igraph_sparsemat_destroy(&tmpsparse); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse_t); + + IGRAPH_CHECK(igraph_i_scg_get_result(IGRAPH_SCG_LAPLACIAN, + mymatrix, mysparsemat, + Lsparse, &Rsparse_t, + scg_graph, scg_matrix, + scg_sparsemat, /*directed=*/ 1)); + + /* -------------------------------------------------------------------- */ + /* Clean up */ + + igraph_sparsemat_destroy(&Rsparse_t); + IGRAPH_FINALLY_CLEAN(1); + if (Lsparse) { + IGRAPH_FINALLY_CLEAN(1); + } + if (Rsparse) { + IGRAPH_FINALLY_CLEAN(1); + } + + if (graph) { + igraph_sparsemat_destroy(mysparsemat); + IGRAPH_FINALLY_CLEAN(1); + } else if (matrix) { + igraph_matrix_destroy(mymatrix); + IGRAPH_FINALLY_CLEAN(1); + } else { + igraph_sparsemat_destroy(mysparsemat); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} diff --git a/src/rigraph/core/scg/scg_approximate_methods.c b/src/rigraph/core/scg/scg_approximate_methods.c new file mode 100644 index 0000000..fe8022d --- /dev/null +++ b/src/rigraph/core/scg/scg_approximate_methods.c @@ -0,0 +1,173 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-12 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* + * SCGlib : A C library for the spectral coarse graining of matrices + * as described in the paper: Shrinking Matrices while preserving their + * eigenpairs with Application to the Spectral Coarse Graining of Graphs. + * Preprint available at + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * 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 2 of the License, or + * (at your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * DESCRIPTION + * ----------- + * The intervals_method and intervals_plus_kmeans implements the + * methods of sec. 5.3.2 and sec. 5.3.3 of the above reference. + * They take an eigenvector 'v' as parameter and a vector 'breaks' + * of length 'nb', which provide the intervals used to cut 'v'. + * Then all components of 'v' that fall into the same interval are + * assigned the same group label in 'gr'. The group labels are + * positive consecutive integers starting from 0. + * The intervals_method function is adapted from bincode of the R + * base package. + * The intervals_plus_kmeans is initialized with regularly-spaced + * breaks, which rougly corresponds to the intervals_method. Then + * kmeans minimizes iteratively the objective function until it gets + * stuck in a (usually) local minimum, or until 'itermax' is reached. + * So far, the breaks_computation function allows computation of + * constant bins, as used in intervals_method, and of equidistant + * centers as used in intervals_plus_kmeans. + */ + +#include "scg_headers.h" + +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +int igraph_i_intervals_plus_kmeans(const igraph_vector_t *v, int *gr, + int n, int n_interv, + int maxiter) { + int i; + igraph_vector_t centers; + + IGRAPH_VECTOR_INIT_FINALLY(¢ers, n_interv); + + igraph_i_breaks_computation(v, ¢ers, n_interv, 2); + IGRAPH_CHECK(igraph_i_kmeans_Lloyd(v, n, 1, ¢ers, n_interv, gr, + maxiter)); + + /*renumber the groups*/ + for (i = 0; i < n; i++) { + gr[i] = gr[i] - 1; + } + + igraph_vector_destroy(¢ers); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +int igraph_i_intervals_method(const igraph_vector_t *v, int *gr, int n, + int n_interv) { + int i, lo, hi, new; + const int lft = 1; + const int include_border = 1; + igraph_vector_t breaks; + + IGRAPH_VECTOR_INIT_FINALLY(&breaks, n_interv + 1); + + IGRAPH_CHECK(igraph_i_breaks_computation(v, &breaks, n_interv + 1, 1)); + + for (i = 0; i < n; i++) { + lo = 0; + hi = n_interv; + if (VECTOR(*v)[i] < VECTOR(breaks)[lo] || + VECTOR(breaks)[hi] < VECTOR(*v)[i] || + (VECTOR(*v)[i] == VECTOR(breaks)[lft ? hi : lo] && !include_border)) { + /* Do nothing */ + } else { + while (hi - lo >= 2) { + new = (hi + lo) / 2; + if (VECTOR(*v)[i] > VECTOR(breaks)[new] || + (lft && VECTOR(*v)[i] == VECTOR(breaks)[new])) { + lo = new; + } else { + hi = new; + } + } + gr[i] = lo; + } + } + igraph_vector_destroy(&breaks); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +int igraph_i_breaks_computation(const igraph_vector_t *v, + igraph_vector_t *breaks, + int nb, int method) { + int i; + igraph_real_t eps, vmin, vmax; + igraph_vector_minmax(v, &vmin, &vmax); + + if (vmax == vmin) { + IGRAPH_ERROR("There is only one (repeated) value in argument 'v' " + "of bin_size_computation()", IGRAPH_EINVAL); + } + + if (nb < 2) { + IGRAPH_ERROR("'nb' in bin_size_computation() must be >= 2", + IGRAPH_EINVAL); + } + + switch (method) { + case 1: /* constant bins for fixed-size intervals method */ + eps = (vmax - vmin) / (igraph_real_t)(nb - 1); + VECTOR(*breaks)[0] = vmin; + for (i = 1; i < nb - 1; i++) { + VECTOR(*breaks)[i] = VECTOR(*breaks)[i - 1] + eps; + } + VECTOR(*breaks)[nb - 1] = vmax; + break; + case 2: /* equidistant centers for kmeans */ + eps = (vmax - vmin) / (igraph_real_t)nb; + VECTOR(*breaks)[0] = vmin + eps / 2.; + for (i = 1; i < nb; i++) { + VECTOR(*breaks)[i] = VECTOR(*breaks)[i - 1] + eps; + } + break; + /* TODO: implement logarithmic binning for power-law-like distributions */ + default: + IGRAPH_ERROR("Internal SCG error, this should ot happen", + IGRAPH_FAILURE); + } + + return 0; +} diff --git a/src/rigraph/core/scg/scg_exact_scg.c b/src/rigraph/core/scg/scg_exact_scg.c new file mode 100644 index 0000000..6495eb7 --- /dev/null +++ b/src/rigraph/core/scg/scg_exact_scg.c @@ -0,0 +1,69 @@ +/* + * SCGlib : A C library for the spectral coarse graining of matrices + * as described in the paper: Shrinking Matrices while preserving their + * eigenpairs with Application to the Spectral Coarse Graining of Graphs. + * Preprint available at + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * 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 2 of the License, or + * (at your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * DESCRIPTION + * ----------- + * The exact_coarse_graining function labels all the objects whose + * components in 'v' are equal. The result is stored in 'gr'. Labels + * are positive consecutive integers starting from 0. + * See also Section 5.4.1 (last paragraph) of the above reference. + */ + +#include "scg_headers.h" + +#include "igraph_memory.h" +#include "igraph_qsort.h" + +#include + +int igraph_i_exact_coarse_graining(const igraph_real_t *v, + int *gr, int n) { + int i, gr_nb; + igraph_i_scg_indval_t *w = IGRAPH_CALLOC(n, igraph_i_scg_indval_t); + + if (!w) { + IGRAPH_ERROR("SCG error", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, w); + + for (i = 0; i < n; i++) { + w[i].val = v[i]; + w[i].ind = i; + } + + igraph_qsort(w, (size_t) n, sizeof(igraph_i_scg_indval_t), igraph_i_compare_ind_val); + + gr_nb = 0; + gr[w[0].ind] = gr_nb; + for (i = 1; i < n; i++) { + if ( fabs(w[i].val - w[i - 1].val) > 1e-14 ) { + gr_nb++; + } + gr[w[i].ind] = gr_nb; + } + + IGRAPH_FREE(w); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} diff --git a/src/rigraph/core/scg/scg_headers.h b/src/rigraph/core/scg/scg_headers.h new file mode 100644 index 0000000..876ac31 --- /dev/null +++ b/src/rigraph/core/scg/scg_headers.h @@ -0,0 +1,128 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-2012 Gabor Csardi + 334 Harvard st, Cambridge, MA, 02138 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +/* + * SCGlib : A C library for the spectral coarse graining of matrices + * as described in the paper: Shrinking Matrices while preserving their + * eigenpairs with Application to the Spectral Coarse Graining of Graphs. + * Preprint available at + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * 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 2 of the License, or + * (at your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * DESCRIPTION + * ----------- + * This file contains the headers of the library SCGlib. + * For use with R software define + * the constant R_COMPIL and refer to the R documentation to compile + * a dynamic library. The scg_r_wrapper function should be useful. + */ + +#ifndef SCG_HEADERS_H +#define SCG_HEADERS_H + +#include "igraph_types.h" +#include "igraph_vector.h" + +#include +#include + +typedef struct ind_val { + int ind; + igraph_real_t val; +} igraph_i_scg_indval_t; + +int igraph_i_compare_ind_val(const void *a, const void *b); + +typedef struct groups { + int ind; + int n; + int* gr; +} igraph_i_scg_groups_t; + +/*------------------------------------------------- +------------DEFINED IN scg_approximate_methods.c--- +---------------------------------------------------*/ + +int igraph_i_breaks_computation(const igraph_vector_t *v, + igraph_vector_t *breaks, int nb, + int method); +int igraph_i_intervals_plus_kmeans(const igraph_vector_t *v, int *gr, + int n, int n_interv, + int maxiter); +int igraph_i_intervals_method(const igraph_vector_t *v, int *gr, + int n, int n_interv); + +/*------------------------------------------------- +------------DEFINED IN scg_optimal_method.c-------- +---------------------------------------------------*/ + +int igraph_i_cost_matrix(igraph_real_t *Cv, const igraph_i_scg_indval_t *vs, + int n, int matrix, const igraph_vector_t *ps); +int igraph_i_optimal_partition(const igraph_real_t *v, igraph_integer_t *gr, int n, int nt, + int matrix, const igraph_real_t *p, + igraph_real_t *value); + +/*------------------------------------------------- +------------DEFINED IN scg_kmeans.c---------------- +---------------------------------------------------*/ + +int igraph_i_kmeans_Lloyd(const igraph_vector_t *x, int n, + int p, igraph_vector_t *centers, + int k, int *cl, int maxiter); + +/*------------------------------------------------- +------------DEFINED IN scg_exact_scg.c------------- +---------------------------------------------------*/ + +int igraph_i_exact_coarse_graining(const igraph_real_t *v, int *gr, + int n); + +/*------------------------------------------------- +------------DEFINED IN scg_utils.c----------------- +---------------------------------------------------*/ + +int igraph_i_compare_groups(const void *a, const void *b); +int igraph_i_compare_real(const void *a, const void *b); +int igraph_i_compare_int(const void *a, const void *b); + +igraph_real_t *igraph_i_real_sym_matrix(int size); +#define igraph_i_real_sym_mat_get(S,i,j) S[i+j*(j+1)/2] +#define igraph_i_real_sym_mat_set(S,i,j,val) S[i+j*(j+1)/2] = val +#define igraph_i_free_real_sym_matrix(S) IGRAPH_FREE(S) + +#endif diff --git a/src/rigraph/core/scg/scg_kmeans.c b/src/rigraph/core/scg/scg_kmeans.c new file mode 100644 index 0000000..067af30 --- /dev/null +++ b/src/rigraph/core/scg/scg_kmeans.c @@ -0,0 +1,102 @@ +/* + * SCGlib : A C library for the spectral coarse graining of matrices + * as described in the paper: Shrinking Matrices while preserving their + * eigenpairs with Application to the Spectral Coarse Graining of Graphs. + * Preprint available at + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * 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 2 of the License, or + * (at your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * DESCRIPTION + * ----------- + * The kmeans_Lloyd function is adapted from the R-stats package. + * It perfoms Lloyd's k-means clustering on a p x n data matrix + * stored row-wise in a vector 'x'. 'cen' contains k initial centers. + * The group label to which each object belongs is stored in 'cl'. + * Labels are positive consecutive integers starting from 0. + * See also Section 5.3.3 of the above reference. + */ + +#include "scg_headers.h" + +int igraph_i_kmeans_Lloyd(const igraph_vector_t *x, int n, int p, + igraph_vector_t *cen, int k, int *cl, int maxiter) { + + int iter, i, j, c, it, inew = 0; + igraph_real_t best, dd, tmp; + int updated; + igraph_vector_int_t nc; + + IGRAPH_CHECK(igraph_vector_int_init(&nc, k)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &nc); + + for (i = 0; i < n; i++) { + cl[i] = -1; + } + for (iter = 0; iter < maxiter; iter++) { + updated = 0; + for (i = 0; i < n; i++) { + /* find nearest centre for each point */ + best = IGRAPH_INFINITY; + for (j = 0; j < k; j++) { + dd = 0.0; + for (c = 0; c < p; c++) { + tmp = VECTOR(*x)[i + n * c] - VECTOR(*cen)[j + k * c]; + dd += tmp * tmp; + } + if (dd < best) { + best = dd; + inew = j + 1; + } + } + if (cl[i] != inew) { + updated = 1; + cl[i] = inew; + } + } + if (!updated) { + break; + } + + /* update each centre */ + for (j = 0; j < k * p; j++) { + VECTOR(*cen)[j] = 0.0; + } + for (j = 0; j < k; j++) { + VECTOR(nc)[j] = 0; + } + for (i = 0; i < n; i++) { + it = cl[i] - 1; + VECTOR(nc)[it]++; + for (c = 0; c < p; c++) { + VECTOR(*cen)[it + c * k] += VECTOR(*x)[i + c * n]; + } + } + for (j = 0; j < k * p; j++) { + VECTOR(*cen)[j] /= VECTOR(nc)[j % k]; + } + } + igraph_vector_int_destroy(&nc); + IGRAPH_FINALLY_CLEAN(1); + + /* convervenge check */ + if (iter >= maxiter - 1) { + IGRAPH_ERROR("Lloyd k-means did not converge", IGRAPH_FAILURE); + } + + return 0; +} diff --git a/src/rigraph/core/scg/scg_optimal_method.c b/src/rigraph/core/scg/scg_optimal_method.c new file mode 100644 index 0000000..68eca3f --- /dev/null +++ b/src/rigraph/core/scg/scg_optimal_method.c @@ -0,0 +1,241 @@ +/* + * SCGlib : A C library for the spectral coarse graining of matrices + * as described in the paper: Shrinking Matrices while preserving their + * eigenpairs with Application to the Spectral Coarse Graining of Graphs. + * Preprint available at + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * 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 2 of the License, or + * (at your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * DESCRIPTION + * ----------- + * This file implements algorithm 5.8 of the above reference. + * The optimal_partition function returns the minimizing partition + * with size 'nt' of the objective function ||v-Pv||, where P is + * a problem-specific projector. So far, Symmetric (matrix=1), + * Laplacian (matrix=2) and Stochastic (matrix=3) projectors + * have been implemented (the cost_matrix function below). + * In the stochastic case, 'p' is expected to be a valid propability + * vector. In all other cases, 'p' is ignored and can be set to NULL. + * The group labels are given in 'gr' as positive consecutive integers + * starting from 0. + */ + +#include "scg_headers.h" + +#include "igraph_error.h" +#include "igraph_memory.h" +#include "igraph_matrix.h" +#include "igraph_vector.h" +#include "igraph_qsort.h" + +int igraph_i_optimal_partition(const igraph_real_t *v, igraph_integer_t *gr, int n, + int nt, int matrix, const igraph_real_t *p, + igraph_real_t *value) { + + int i, non_ties, q, j, l, part_ind, col; + igraph_i_scg_indval_t *vs = IGRAPH_CALLOC(n, igraph_i_scg_indval_t); + igraph_real_t *Cv, temp, sumOfSquares; + igraph_vector_t ps; + igraph_matrix_t F; + igraph_matrix_int_t Q; + + /*----------------------------------------------- + -----Sorts v and counts non-ties----------------- + -----------------------------------------------*/ + + if (!vs) { + IGRAPH_ERROR("SCG error", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, vs); + + for (i = 0; i < n; i++) { + vs[i].val = v[i]; + vs[i].ind = i; + } + + igraph_qsort(vs, (size_t) n, sizeof(igraph_i_scg_indval_t), + igraph_i_compare_ind_val); + + non_ties = 1; + for (i = 1; i < n; i++) { + if (vs[i].val < vs[i - 1].val - 1e-14 || + vs[i].val > vs[i - 1].val + 1e-14) { + non_ties++; + } + } + + if (nt >= non_ties) { + IGRAPH_ERROR("`Invalid number of intervals, should be smaller than " + "number of unique values in V", IGRAPH_EINVAL); + } + + /*------------------------------------------------ + ------Computes Cv, the matrix of costs------------ + ------------------------------------------------*/ + Cv = igraph_i_real_sym_matrix(n); + if (!Cv) { + IGRAPH_ERROR("SCG error", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, Cv); + + /* if stochastic SCG orders p */ + if (matrix == 3) { + IGRAPH_VECTOR_INIT_FINALLY(&ps, n); + for (i = 0; i < n; i++) { + VECTOR(ps)[i] = p[vs[i].ind]; + } + } + + IGRAPH_CHECK(igraph_i_cost_matrix(Cv, vs, n, matrix, &ps)); + if (matrix == 3) { + igraph_vector_destroy(&ps); + IGRAPH_FINALLY_CLEAN(1); + } + /*------------------------------------------------- + -------Fills up matrices F and Q------------------- + -------------------------------------------------*/ + /*here j also is a counter but the use of unsigned variables + is to be proscribed in "for (unsigned int j=...;j>=0;j--)", + for such loops never ends!*/ + + IGRAPH_MATRIX_INIT_FINALLY(&F, nt, n); + IGRAPH_CHECK(igraph_matrix_int_init(&Q, nt, n)); + IGRAPH_FINALLY(igraph_matrix_int_destroy, &Q); + + for (i = 0; i < n; i++) { + MATRIX(Q, 0, i)++; + } + for (i = 0; i < nt; i++) { + MATRIX(Q, i, i) = i + 1; + } + + for (i = 0; i < n; i++) { + MATRIX(F, 0, i) = igraph_i_real_sym_mat_get(Cv, 0, i); + } + + for (i = 1; i < nt; i++) + for (j = i + 1; j < n; j++) { + MATRIX(F, i, j) = MATRIX(F, i - 1, i - 1) + igraph_i_real_sym_mat_get(Cv, i, j); + MATRIX(Q, i, j) = 2; + + for (q = i - 1; q <= j - 1; q++) { + temp = MATRIX(F, i - 1, q) + igraph_i_real_sym_mat_get(Cv, q + 1, j); + if (temp < MATRIX(F, i, j)) { + MATRIX(F, i, j) = temp; + MATRIX(Q, i, j) = q + 2; + } + } + } + igraph_i_free_real_sym_matrix(Cv); + IGRAPH_FINALLY_CLEAN(1); + + /*-------------------------------------------------- + -------Back-tracks through Q to work out the groups- + --------------------------------------------------*/ + part_ind = nt; + col = n - 1; + + for (j = nt - 1; j >= 0; j--) { + for (i = MATRIX(Q, j, col) - 1; i <= col; i++) { + gr[vs[i].ind] = part_ind - 1; + } + if (MATRIX(Q, j, col) != 2) { + col = MATRIX(Q, j, col) - 2; + part_ind -= 1; + } else { + if (j > 1) { + for (l = 0; l <= (j - 1); l++) { + gr[vs[l].ind] = l; + } + break; + } else { + col = MATRIX(Q, j, col) - 2; + part_ind -= 1; + } + } + } + + sumOfSquares = MATRIX(F, nt - 1, n - 1); + + igraph_matrix_destroy(&F); + igraph_matrix_int_destroy(&Q); + IGRAPH_FREE(vs); + IGRAPH_FINALLY_CLEAN(3); + + if (value) { + *value = sumOfSquares; + } + return 0; +} + +int igraph_i_cost_matrix(igraph_real_t*Cv, const igraph_i_scg_indval_t *vs, + int n, int matrix, const igraph_vector_t *ps) { + + /* if symmetric of Laplacian SCG -> same Cv */ + if (matrix == 1 || matrix == 2) { + int i, j; + igraph_vector_t w, w2; + + IGRAPH_VECTOR_INIT_FINALLY(&w, n + 1); + IGRAPH_VECTOR_INIT_FINALLY(&w2, n + 1); + + VECTOR(w)[1] = vs[0].val; + VECTOR(w2)[1] = vs[0].val * vs[0].val; + + for (i = 2; i <= n; i++) { + VECTOR(w)[i] = VECTOR(w)[i - 1] + vs[i - 1].val; + VECTOR(w2)[i] = VECTOR(w2)[i - 1] + vs[i - 1].val * vs[i - 1].val; + } + + for (i = 0; i < n; i++) { + for (j = i + 1; j < n; j++) { + igraph_real_t v = (VECTOR(w2)[j + 1] - VECTOR(w2)[i]) - + (VECTOR(w)[j + 1] - VECTOR(w)[i]) * (VECTOR(w)[j + 1] - VECTOR(w)[i]) / + (j - i + 1); + igraph_i_real_sym_mat_set(Cv, i, j, v); + } + } + + igraph_vector_destroy(&w); + igraph_vector_destroy(&w2); + IGRAPH_FINALLY_CLEAN(2); + } + /* if stochastic */ + /* TODO: optimize it to O(n^2) instead of O(n^3) (as above) */ + if (matrix == 3) { + int i, j, k; + igraph_real_t t1, t2; + for (i = 0; i < n; i++) { + for (j = i + 1; j < n; j++) { + t1 = t2 = 0; + for (k = i; k < j; k++) { + t1 += VECTOR(*ps)[k]; + t2 += VECTOR(*ps)[k] * vs[k].val; + } + t1 = t2 / t1; + t2 = 0; + for (k = i; k < j; k++) { + t2 += (vs[k].val - t1) * (vs[k].val - t1); + } + igraph_i_real_sym_mat_set(Cv, i, j, t2); + } + } + } + + return 0; +} diff --git a/src/rigraph/core/scg/scg_utils.c b/src/rigraph/core/scg/scg_utils.c new file mode 100644 index 0000000..ed5500d --- /dev/null +++ b/src/rigraph/core/scg/scg_utils.c @@ -0,0 +1,94 @@ +/* + * SCGlib : A C library for the spectral coarse graining of matrices + * as described in the paper: Shrinking Matrices while preserving their + * eigenpairs with Application to the Spectral Coarse Graining of Graphs. + * Preprint available at + * + * Copyright (C) 2008 David Morton de Lachapelle + * + * 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 2 of the License, or + * (at your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * DESCRIPTION + * ----------- + * This files contains the data structures and error handing + * functions used throughout the SCGlib. + */ + +#include "scg_headers.h" + +#include "igraph_error.h" +#include "igraph_memory.h" + +/*to be used with qsort and struct ind_val arrays */ +int igraph_i_compare_ind_val(const void *a, const void *b) { + igraph_i_scg_indval_t *arg1 = (igraph_i_scg_indval_t *) a; + igraph_i_scg_indval_t *arg2 = (igraph_i_scg_indval_t *) b; + + if ( arg1->val < arg2->val ) { + return -1; + } else if ( arg1->val == arg2->val ) { + return 0; + } else { + return 1; + } +} + +/*to be used with qsort and struct groups*/ +int igraph_i_compare_groups(const void *a, const void *b) { + igraph_i_scg_groups_t *arg1 = (igraph_i_scg_groups_t *) a; + igraph_i_scg_groups_t *arg2 = (igraph_i_scg_groups_t *) b; + int i; + for (i = 0; i < arg1->n; i++) { + if (arg1->gr[i] > arg2->gr[i]) { + return 1; + } else if (arg1->gr[i] < arg2->gr[i]) { + return -1; + } + } + return 0; +} + +/*to be used with qsort and real_vectors */ +int igraph_i_compare_real(const void *a, const void *b) { + igraph_real_t arg1 = * (igraph_real_t *) a; + igraph_real_t arg2 = * (igraph_real_t *) b; + + if (arg1 < arg2) { + return -1; + } else if (arg1 == arg2) { + return 0; + } else { + return 1; + } +} + +/*to be used with qsort and integer vectors */ +int igraph_i_compare_int(const void *a, const void *b) { + int arg1 = * (int *) a; + int arg2 = * (int *) b; + return (arg1 - arg2); +} + +/* allocate a igraph_real_t symmetrix matrix with dimension size x size + in vector format*/ +igraph_real_t *igraph_i_real_sym_matrix(int size) { + igraph_real_t *S = IGRAPH_CALLOC(size * (size + 1) / 2, igraph_real_t); + if (!S) { + igraph_error("allocation failure in real_sym_matrix()", + IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + } + return S; +} diff --git a/src/rigraph/core/version.c b/src/rigraph/core/version.c new file mode 100644 index 0000000..72af7ca --- /dev/null +++ b/src/rigraph/core/version.c @@ -0,0 +1,67 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2008-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph_version.h" + +#include + +static const char *igraph_version_string = IGRAPH_VERSION; + +/** + * \function igraph_version + * Return the version of the igraph C library + * + * \param version_string Pointer to a string pointer. If not null, it + * is set to the igraph version string, e.g. "0.6" or "0.5.3". This + * string should not be modified or deallocated. + * \param major If not a null pointer, then it is set to the major + * igraph version. E.g. for version "0.5.3" this is 0. + * \param minor If not a null pointer, then it is set to the minor + * igraph version. E.g. for version "0.5.3" this is 5. + * \param subminor If not a null pointer, then it is set to the + * subminor igraph version. E.g. for version "0.5.3" this is 3. + * \return Error code. + * + * Time complexity: O(1). + * + * \example examples/simple/igraph_version.c + */ + +int igraph_version(const char **version_string, + int *major, + int *minor, + int *subminor) { + int i1, i2, i3; + int *p1 = major ? major : &i1, + *p2 = minor ? minor : &i2, + *p3 = subminor ? subminor : &i3; + + if (version_string) { + *version_string = igraph_version_string; + } + + *p1 = *p2 = *p3 = 0; + sscanf(IGRAPH_VERSION, "%i.%i.%i", p1, p2, p3); + + return 0; +} diff --git a/src/rigraph/include/config.h b/src/rigraph/include/config.h new file mode 100644 index 0000000..6f4c67c --- /dev/null +++ b/src/rigraph/include/config.h @@ -0,0 +1,139 @@ + +/* Define to 1 if you have the `expm1' function. */ +#undef HAVE_EXPM1 + +/* Define to 1 if you have the `finite' function. */ +#undef HAVE_FINITE + +/* Define to 1 if you have the `fmin' function. */ +#undef HAVE_FMIN + +/* Define to 1 if using the GNU Fortran compiler */ +#undef HAVE_GFORTRAN + +/* Define to 1 if you have the GLPK library */ +#undef HAVE_GLPK + +/* Define to 1 if you have the GMP library */ +#undef HAVE_GMP + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `isfinite' function. */ +#undef HAVE_ISFINITE + +/* Define to 1 if you have the libxml2 libraries installed */ +#undef HAVE_LIBXML + +/* Define to 1 if you have the `log1p' function. */ +#undef HAVE_LOG1P + +/* Define to 1 if you have the `log2' function. */ +#undef HAVE_LOG2 + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_DL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_H + +/* Define to 1 if you have the `rint' function. */ +#undef HAVE_RINT + +/* Define to 1 if you have the `rintf' function. */ +#undef HAVE_RINTF + +/* Define to 1 if you have the `round' function. */ +#undef HAVE_ROUND + +/* Define if struct sockaddr contains sa_len */ +#undef HAVE_SA_LEN + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `stpcpy' function. */ +#undef HAVE_STPCPY + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + +/* Define to 1 if you have the sys/times.h header */ +#undef HAVE_TIMES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `_stricmp' function. */ +#undef HAVE__STRICMP + +/* We don't care about thread-local storage in R */ +#undef IGRAPH_THREAD_LOCAL + +/* Define to 1 if you use the vendored mini-GMP library */ +#undef INTERNAL_GMP + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS +#define HAVE_ISFINITE HAVE_DECL_ISFINITE \ No newline at end of file diff --git a/src/leidenalg/igraph-R/igraph.h b/src/rigraph/include/igraph.h similarity index 100% rename from src/leidenalg/igraph-R/igraph.h rename to src/rigraph/include/igraph.h diff --git a/src/leidenalg/igraph-R/igraph_adjlist.h b/src/rigraph/include/igraph_adjlist.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_adjlist.h rename to src/rigraph/include/igraph_adjlist.h diff --git a/src/leidenalg/igraph-R/igraph_arpack.h b/src/rigraph/include/igraph_arpack.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_arpack.h rename to src/rigraph/include/igraph_arpack.h diff --git a/src/leidenalg/igraph-R/igraph_array.h b/src/rigraph/include/igraph_array.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_array.h rename to src/rigraph/include/igraph_array.h diff --git a/src/leidenalg/igraph-R/igraph_array_pmt.h b/src/rigraph/include/igraph_array_pmt.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_array_pmt.h rename to src/rigraph/include/igraph_array_pmt.h diff --git a/src/leidenalg/igraph-R/igraph_attributes.h b/src/rigraph/include/igraph_attributes.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_attributes.h rename to src/rigraph/include/igraph_attributes.h diff --git a/src/leidenalg/igraph-R/igraph_bipartite.h b/src/rigraph/include/igraph_bipartite.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_bipartite.h rename to src/rigraph/include/igraph_bipartite.h diff --git a/src/leidenalg/igraph-R/igraph_blas.h b/src/rigraph/include/igraph_blas.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_blas.h rename to src/rigraph/include/igraph_blas.h diff --git a/src/leidenalg/igraph-R/igraph_centrality.h b/src/rigraph/include/igraph_centrality.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_centrality.h rename to src/rigraph/include/igraph_centrality.h diff --git a/src/leidenalg/igraph-R/igraph_cliques.h b/src/rigraph/include/igraph_cliques.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_cliques.h rename to src/rigraph/include/igraph_cliques.h diff --git a/src/leidenalg/igraph-R/igraph_cocitation.h b/src/rigraph/include/igraph_cocitation.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_cocitation.h rename to src/rigraph/include/igraph_cocitation.h diff --git a/src/leidenalg/igraph-R/igraph_cohesive_blocks.h b/src/rigraph/include/igraph_cohesive_blocks.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_cohesive_blocks.h rename to src/rigraph/include/igraph_cohesive_blocks.h diff --git a/src/leidenalg/igraph-R/igraph_coloring.h b/src/rigraph/include/igraph_coloring.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_coloring.h rename to src/rigraph/include/igraph_coloring.h diff --git a/src/leidenalg/igraph-R/igraph_community.h b/src/rigraph/include/igraph_community.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_community.h rename to src/rigraph/include/igraph_community.h diff --git a/src/leidenalg/igraph-R/igraph_complex.h b/src/rigraph/include/igraph_complex.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_complex.h rename to src/rigraph/include/igraph_complex.h diff --git a/src/leidenalg/igraph-R/igraph_components.h b/src/rigraph/include/igraph_components.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_components.h rename to src/rigraph/include/igraph_components.h diff --git a/src/leidenalg/igraph-R/igraph_constants.h b/src/rigraph/include/igraph_constants.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_constants.h rename to src/rigraph/include/igraph_constants.h diff --git a/src/leidenalg/igraph-R/igraph_constructors.h b/src/rigraph/include/igraph_constructors.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_constructors.h rename to src/rigraph/include/igraph_constructors.h diff --git a/src/leidenalg/igraph-R/igraph_conversion.h b/src/rigraph/include/igraph_conversion.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_conversion.h rename to src/rigraph/include/igraph_conversion.h diff --git a/src/leidenalg/igraph-R/igraph_datatype.h b/src/rigraph/include/igraph_datatype.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_datatype.h rename to src/rigraph/include/igraph_datatype.h diff --git a/src/leidenalg/igraph-R/igraph_decls.h b/src/rigraph/include/igraph_decls.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_decls.h rename to src/rigraph/include/igraph_decls.h diff --git a/src/leidenalg/igraph-R/igraph_dqueue.h b/src/rigraph/include/igraph_dqueue.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_dqueue.h rename to src/rigraph/include/igraph_dqueue.h diff --git a/src/leidenalg/igraph-R/igraph_dqueue_pmt.h b/src/rigraph/include/igraph_dqueue_pmt.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_dqueue_pmt.h rename to src/rigraph/include/igraph_dqueue_pmt.h diff --git a/src/leidenalg/igraph-R/igraph_eigen.h b/src/rigraph/include/igraph_eigen.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_eigen.h rename to src/rigraph/include/igraph_eigen.h diff --git a/src/leidenalg/igraph-R/igraph_embedding.h b/src/rigraph/include/igraph_embedding.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_embedding.h rename to src/rigraph/include/igraph_embedding.h diff --git a/src/leidenalg/igraph-R/igraph_epidemics.h b/src/rigraph/include/igraph_epidemics.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_epidemics.h rename to src/rigraph/include/igraph_epidemics.h diff --git a/src/leidenalg/igraph-R/igraph_error.h b/src/rigraph/include/igraph_error.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_error.h rename to src/rigraph/include/igraph_error.h diff --git a/src/leidenalg/igraph-R/igraph_eulerian.h b/src/rigraph/include/igraph_eulerian.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_eulerian.h rename to src/rigraph/include/igraph_eulerian.h diff --git a/src/rigraph/include/igraph_export.h b/src/rigraph/include/igraph_export.h new file mode 100644 index 0000000..508cb6e --- /dev/null +++ b/src/rigraph/include/igraph_export.h @@ -0,0 +1,11 @@ + +#ifndef IGRAPH_EXPORT_H +#define IGRAPH_EXPORT_H + +#define IGRAPH_EXPORT +#define IGRAPH_NO_EXPORT +#define IGRAPH_DEPRECATED __attribute__ ((__deprecated__)) +#define IGRAPH_DEPRECATED_EXPORT IGRAPH_EXPORT IGRAPH_DEPRECATED +#define IGRAPH_DEPRECATED_NO_EXPORT IGRAPH_NO_EXPORT IGRAPH_DEPRECATED + +#endif /* IGRAPH_EXPORT_H */ diff --git a/src/leidenalg/igraph-R/igraph_flow.h b/src/rigraph/include/igraph_flow.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_flow.h rename to src/rigraph/include/igraph_flow.h diff --git a/src/leidenalg/igraph-R/igraph_foreign.h b/src/rigraph/include/igraph_foreign.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_foreign.h rename to src/rigraph/include/igraph_foreign.h diff --git a/src/leidenalg/igraph-R/igraph_games.h b/src/rigraph/include/igraph_games.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_games.h rename to src/rigraph/include/igraph_games.h diff --git a/src/leidenalg/igraph-R/igraph_graphicality.h b/src/rigraph/include/igraph_graphicality.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_graphicality.h rename to src/rigraph/include/igraph_graphicality.h diff --git a/src/leidenalg/igraph-R/igraph_graphlets.h b/src/rigraph/include/igraph_graphlets.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_graphlets.h rename to src/rigraph/include/igraph_graphlets.h diff --git a/src/leidenalg/igraph-R/igraph_heap.h b/src/rigraph/include/igraph_heap.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_heap.h rename to src/rigraph/include/igraph_heap.h diff --git a/src/leidenalg/igraph-R/igraph_heap_pmt.h b/src/rigraph/include/igraph_heap_pmt.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_heap_pmt.h rename to src/rigraph/include/igraph_heap_pmt.h diff --git a/src/leidenalg/igraph-R/igraph_hrg.h b/src/rigraph/include/igraph_hrg.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_hrg.h rename to src/rigraph/include/igraph_hrg.h diff --git a/src/leidenalg/igraph-R/igraph_interface.h b/src/rigraph/include/igraph_interface.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_interface.h rename to src/rigraph/include/igraph_interface.h diff --git a/src/leidenalg/igraph-R/igraph_interrupt.h b/src/rigraph/include/igraph_interrupt.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_interrupt.h rename to src/rigraph/include/igraph_interrupt.h diff --git a/src/leidenalg/igraph-R/igraph_iterators.h b/src/rigraph/include/igraph_iterators.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_iterators.h rename to src/rigraph/include/igraph_iterators.h diff --git a/src/leidenalg/igraph-R/igraph_lapack.h b/src/rigraph/include/igraph_lapack.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_lapack.h rename to src/rigraph/include/igraph_lapack.h diff --git a/src/leidenalg/igraph-R/igraph_layout.h b/src/rigraph/include/igraph_layout.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_layout.h rename to src/rigraph/include/igraph_layout.h diff --git a/src/leidenalg/igraph-R/igraph_lsap.h b/src/rigraph/include/igraph_lsap.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_lsap.h rename to src/rigraph/include/igraph_lsap.h diff --git a/src/leidenalg/igraph-R/igraph_matching.h b/src/rigraph/include/igraph_matching.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_matching.h rename to src/rigraph/include/igraph_matching.h diff --git a/src/leidenalg/igraph-R/igraph_matrix.h b/src/rigraph/include/igraph_matrix.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_matrix.h rename to src/rigraph/include/igraph_matrix.h diff --git a/src/leidenalg/igraph-R/igraph_matrix_pmt.h b/src/rigraph/include/igraph_matrix_pmt.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_matrix_pmt.h rename to src/rigraph/include/igraph_matrix_pmt.h diff --git a/src/leidenalg/igraph-R/igraph_memory.h b/src/rigraph/include/igraph_memory.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_memory.h rename to src/rigraph/include/igraph_memory.h diff --git a/src/leidenalg/igraph-R/igraph_microscopic_update.h b/src/rigraph/include/igraph_microscopic_update.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_microscopic_update.h rename to src/rigraph/include/igraph_microscopic_update.h diff --git a/src/leidenalg/igraph-R/igraph_mixing.h b/src/rigraph/include/igraph_mixing.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_mixing.h rename to src/rigraph/include/igraph_mixing.h diff --git a/src/leidenalg/igraph-R/igraph_motifs.h b/src/rigraph/include/igraph_motifs.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_motifs.h rename to src/rigraph/include/igraph_motifs.h diff --git a/src/leidenalg/igraph-R/igraph_neighborhood.h b/src/rigraph/include/igraph_neighborhood.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_neighborhood.h rename to src/rigraph/include/igraph_neighborhood.h diff --git a/src/leidenalg/igraph-R/igraph_nongraph.h b/src/rigraph/include/igraph_nongraph.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_nongraph.h rename to src/rigraph/include/igraph_nongraph.h diff --git a/src/leidenalg/igraph-R/igraph_operators.h b/src/rigraph/include/igraph_operators.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_operators.h rename to src/rigraph/include/igraph_operators.h diff --git a/src/leidenalg/igraph-R/igraph_paths.h b/src/rigraph/include/igraph_paths.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_paths.h rename to src/rigraph/include/igraph_paths.h diff --git a/src/leidenalg/igraph-R/igraph_pmt.h b/src/rigraph/include/igraph_pmt.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_pmt.h rename to src/rigraph/include/igraph_pmt.h diff --git a/src/leidenalg/igraph-R/igraph_pmt_off.h b/src/rigraph/include/igraph_pmt_off.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_pmt_off.h rename to src/rigraph/include/igraph_pmt_off.h diff --git a/src/leidenalg/igraph-R/igraph_progress.h b/src/rigraph/include/igraph_progress.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_progress.h rename to src/rigraph/include/igraph_progress.h diff --git a/src/leidenalg/igraph-R/igraph_psumtree.h b/src/rigraph/include/igraph_psumtree.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_psumtree.h rename to src/rigraph/include/igraph_psumtree.h diff --git a/src/leidenalg/igraph-R/igraph_qsort.h b/src/rigraph/include/igraph_qsort.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_qsort.h rename to src/rigraph/include/igraph_qsort.h diff --git a/src/leidenalg/igraph-R/igraph_random.h b/src/rigraph/include/igraph_random.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_random.h rename to src/rigraph/include/igraph_random.h diff --git a/src/leidenalg/igraph-R/igraph_scan.h b/src/rigraph/include/igraph_scan.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_scan.h rename to src/rigraph/include/igraph_scan.h diff --git a/src/leidenalg/igraph-R/igraph_scg.h b/src/rigraph/include/igraph_scg.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_scg.h rename to src/rigraph/include/igraph_scg.h diff --git a/src/leidenalg/igraph-R/igraph_separators.h b/src/rigraph/include/igraph_separators.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_separators.h rename to src/rigraph/include/igraph_separators.h diff --git a/src/leidenalg/igraph-R/igraph_sparsemat.h b/src/rigraph/include/igraph_sparsemat.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_sparsemat.h rename to src/rigraph/include/igraph_sparsemat.h diff --git a/src/leidenalg/igraph-R/igraph_spmatrix.h b/src/rigraph/include/igraph_spmatrix.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_spmatrix.h rename to src/rigraph/include/igraph_spmatrix.h diff --git a/src/leidenalg/igraph-R/igraph_stack.h b/src/rigraph/include/igraph_stack.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_stack.h rename to src/rigraph/include/igraph_stack.h diff --git a/src/leidenalg/igraph-R/igraph_stack_pmt.h b/src/rigraph/include/igraph_stack_pmt.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_stack_pmt.h rename to src/rigraph/include/igraph_stack_pmt.h diff --git a/src/leidenalg/igraph-R/igraph_statusbar.h b/src/rigraph/include/igraph_statusbar.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_statusbar.h rename to src/rigraph/include/igraph_statusbar.h diff --git a/src/leidenalg/igraph-R/igraph_structural.h b/src/rigraph/include/igraph_structural.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_structural.h rename to src/rigraph/include/igraph_structural.h diff --git a/src/leidenalg/igraph-R/igraph_strvector.h b/src/rigraph/include/igraph_strvector.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_strvector.h rename to src/rigraph/include/igraph_strvector.h diff --git a/src/leidenalg/igraph-R/igraph_threading.h b/src/rigraph/include/igraph_threading.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_threading.h rename to src/rigraph/include/igraph_threading.h diff --git a/src/leidenalg/igraph-R/igraph_threading.h.in b/src/rigraph/include/igraph_threading.h.in similarity index 100% rename from src/leidenalg/igraph-R/igraph_threading.h.in rename to src/rigraph/include/igraph_threading.h.in diff --git a/src/leidenalg/igraph-R/igraph_topology.h b/src/rigraph/include/igraph_topology.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_topology.h rename to src/rigraph/include/igraph_topology.h diff --git a/src/leidenalg/igraph-R/igraph_transitivity.h b/src/rigraph/include/igraph_transitivity.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_transitivity.h rename to src/rigraph/include/igraph_transitivity.h diff --git a/src/leidenalg/igraph-R/igraph_types.h b/src/rigraph/include/igraph_types.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_types.h rename to src/rigraph/include/igraph_types.h diff --git a/src/leidenalg/igraph-R/igraph_vector.h b/src/rigraph/include/igraph_vector.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_vector.h rename to src/rigraph/include/igraph_vector.h diff --git a/src/leidenalg/igraph-R/igraph_vector_pmt.h b/src/rigraph/include/igraph_vector_pmt.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_vector_pmt.h rename to src/rigraph/include/igraph_vector_pmt.h diff --git a/src/leidenalg/igraph-R/igraph_vector_ptr.h b/src/rigraph/include/igraph_vector_ptr.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_vector_ptr.h rename to src/rigraph/include/igraph_vector_ptr.h diff --git a/src/leidenalg/igraph-R/igraph_vector_type.h b/src/rigraph/include/igraph_vector_type.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_vector_type.h rename to src/rigraph/include/igraph_vector_type.h diff --git a/src/leidenalg/igraph-R/igraph_version.h b/src/rigraph/include/igraph_version.h similarity index 82% rename from src/leidenalg/igraph-R/igraph_version.h rename to src/rigraph/include/igraph_version.h index 221b338..05050e0 100644 --- a/src/leidenalg/igraph-R/igraph_version.h +++ b/src/rigraph/include/igraph_version.h @@ -28,11 +28,11 @@ __BEGIN_DECLS -#define IGRAPH_VERSION "0.9.7-33-g6514bc2a7" -#define IGRAPH_VERSION_MAJOR 0 -#define IGRAPH_VERSION_MINOR 9 -#define IGRAPH_VERSION_PATCH 7 -#define IGRAPH_VERSION_PRERELEASE "33-g6514bc2a7" +#define IGRAPH_VERSION "1.3.0.9023" +#define IGRAPH_VERSION_MAJOR @PACKAGE_VERSION_MAJOR@ +#define IGRAPH_VERSION_MINOR @PACKAGE_VERSION_MINOR@ +#define IGRAPH_VERSION_PATCH @PACKAGE_VERSION_PATCH@ +#define IGRAPH_VERSION_PRERELEASE "@PACKAGE_VERSION_PRERELEASE@" IGRAPH_EXPORT int igraph_version(const char **version_string, int *major, diff --git a/src/rigraph/include/igraph_version.h.in b/src/rigraph/include/igraph_version.h.in new file mode 100644 index 0000000..cae3cfa --- /dev/null +++ b/src/rigraph/include/igraph_version.h.in @@ -0,0 +1,44 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_VERSION_H +#define IGRAPH_VERSION_H + +#include "igraph_decls.h" + +__BEGIN_DECLS + +#define IGRAPH_VERSION "@PACKAGE_VERSION@" +#define IGRAPH_VERSION_MAJOR @PACKAGE_VERSION_MAJOR@ +#define IGRAPH_VERSION_MINOR @PACKAGE_VERSION_MINOR@ +#define IGRAPH_VERSION_PATCH @PACKAGE_VERSION_PATCH@ +#define IGRAPH_VERSION_PRERELEASE "@PACKAGE_VERSION_PRERELEASE@" + +IGRAPH_EXPORT int igraph_version(const char **version_string, + int *major, + int *minor, + int *subminor); + +__END_DECLS + +#endif diff --git a/src/leidenalg/igraph-R/igraph_visitor.h b/src/rigraph/include/igraph_visitor.h similarity index 100% rename from src/leidenalg/igraph-R/igraph_visitor.h rename to src/rigraph/include/igraph_visitor.h diff --git a/src/rigraph/init.c b/src/rigraph/init.c new file mode 100644 index 0000000..867478f --- /dev/null +++ b/src/rigraph/init.c @@ -0,0 +1,803 @@ +#include +#include +#include // for NULL +#include + +/* + The following symbols/expresssions for .NAME have been omitted + + call + + Most likely possible values need to be added below. +*/ + +/* FIXME: + Check these declarations against the C/Fortran source code. +*/ + +/* .C calls */ +extern void igraphhcass2(void *, void *, void *, void *, void *, void *); + +/* .Call calls */ +extern SEXP make_lazy(SEXP, SEXP, SEXP); +extern SEXP make_lazy_dots(SEXP, SEXP); +extern SEXP promise_env_(SEXP); +extern SEXP promise_expr_(SEXP); +extern SEXP UUID_gen(SEXP); + +/* non-autogenerated declarations */ +extern SEXP R_igraph_address(SEXP); +extern SEXP R_igraph_add_edges(SEXP, SEXP); +extern SEXP R_igraph_add_env(SEXP); +extern SEXP R_igraph_add_version_to_env(SEXP); +extern SEXP R_igraph_add_vertices(SEXP, SEXP); +extern SEXP R_igraph_adjacent_vertices(SEXP, SEXP, SEXP); +extern SEXP R_igraph_arpack(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_bfs(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_bipartite_projection(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_edge_betweenness(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_fastgreedy(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_leading_eigenvector(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_to_membership2(SEXP, SEXP, SEXP); +extern SEXP R_igraph_compose(SEXP, SEXP, SEXP); +extern SEXP R_igraph_dfs(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_diameter(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_disjoint_union(SEXP); +extern SEXP R_igraph_erdos_renyi_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_es_adj(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_es_pairs(SEXP, SEXP, SEXP); +extern SEXP R_igraph_es_path(SEXP, SEXP, SEXP); +extern SEXP R_igraph_farthest_points(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_finalizer(); +extern SEXP R_igraph_get_adjedgelist(SEXP, SEXP); +extern SEXP R_igraph_get_adjlist(SEXP, SEXP); +extern SEXP R_igraph_get_all_simple_paths_pp(SEXP); +extern SEXP R_igraph_get_attr_mode(SEXP, SEXP); +extern SEXP R_igraph_get_diameter(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_edge(SEXP, SEXP); +extern SEXP R_igraph_get_eids(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_graph_id(SEXP); +extern SEXP R_igraph_get_shortest_paths(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_getsphere(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_girth(SEXP, SEXP); +extern SEXP R_igraph_graph_adjacency(SEXP, SEXP); +extern SEXP R_igraph_graph_version(SEXP); +extern SEXP R_igraph_graphlets_project(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_grg_game(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_i_levc_arp(SEXP, SEXP, SEXP); +extern SEXP R_igraph_identical_graphs(SEXP, SEXP, SEXP); +extern SEXP R_igraph_incident_edges(SEXP, SEXP, SEXP); +extern SEXP R_igraph_is_chordal(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_laplacian(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_fruchterman_reingold(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_fruchterman_reingold_3d(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_graphopt(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_kamada_kawai(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_kamada_kawai_3d(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_reingold_tilford(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_make_weak_ref(SEXP, SEXP, SEXP); +extern SEXP R_igraph_maximal_cliques(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_maximal_cliques_count(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_maximal_cliques_file(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_mybracket(SEXP, SEXP); +extern SEXP R_igraph_mybracket2(SEXP, SEXP, SEXP); +extern SEXP R_igraph_mybracket2_copy(SEXP, SEXP, SEXP); +extern SEXP R_igraph_mybracket2_names(SEXP, SEXP, SEXP); +extern SEXP R_igraph_mybracket2_set(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_mybracket3_set(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_neighborhood(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_neighborhood_size(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_no_clusters(SEXP, SEXP); +extern SEXP R_igraph_psumtree_draw(SEXP, SEXP, SEXP); +extern SEXP R_igraph_scg_adjacency(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_scg_laplacian(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_scg_semiprojectors(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_scg_stochastic(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_set_verbose(SEXP); +extern SEXP R_igraph_shortest_paths(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_solve_lsap(SEXP, SEXP); +extern SEXP R_igraph_spinglass_community(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_spinglass_my_community(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_st_vertex_connectivity(SEXP, SEXP, SEXP); +extern SEXP R_igraph_subisomorphic_lad(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_transitivity_local_undirected_all(SEXP, SEXP); +extern SEXP R_igraph_version(); +extern SEXP R_igraph_vs_adj(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_vs_nei(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_walktrap_community(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_weak_ref_key(SEXP); +extern SEXP R_igraph_weak_ref_run_finalizer(SEXP); +extern SEXP R_igraph_weak_ref_value(SEXP); + +/* autogenerated declarations */ +extern SEXP R_igraph_adhesion(SEXP, SEXP); +extern SEXP R_igraph_adjacency_spectral_embedding(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_adjacent_triangles(SEXP, SEXP); +extern SEXP R_igraph_adjlist(SEXP, SEXP, SEXP); +extern SEXP R_igraph_all_minimal_st_separators(SEXP); +extern SEXP R_igraph_all_st_cuts(SEXP, SEXP, SEXP); +extern SEXP R_igraph_all_st_mincuts(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_are_connected(SEXP, SEXP, SEXP); +extern SEXP R_igraph_arpack_unpack_complex(SEXP, SEXP, SEXP); +extern SEXP R_igraph_articulation_points(SEXP); +extern SEXP R_igraph_assortativity(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_assortativity_degree(SEXP, SEXP); +extern SEXP R_igraph_assortativity_nominal(SEXP, SEXP, SEXP); +extern SEXP R_igraph_asymmetric_preference_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_atlas(SEXP); +extern SEXP R_igraph_authority_score(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_automorphism_group(SEXP, SEXP, SEXP); +extern SEXP R_igraph_automorphisms(SEXP, SEXP, SEXP); +extern SEXP R_igraph_average_local_efficiency(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_average_path_length_dijkstra(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_avg_nearest_neighbor_degree(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_barabasi_aging_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_barabasi_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_betweenness(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_betweenness_cutoff(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_bibcoupling(SEXP, SEXP); +extern SEXP R_igraph_biconnected_components(SEXP); +extern SEXP R_igraph_bipartite_game_gnm(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_bipartite_game_gnp(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_bipartite_projection_size(SEXP, SEXP); +extern SEXP R_igraph_bridges(SEXP); +extern SEXP R_igraph_callaway_traits_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_canonical_permutation(SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization(SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_betweenness(SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_betweenness_tmax(SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_closeness(SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_closeness_tmax(SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_degree(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_degree_tmax(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_eigenvector_centrality(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_centralization_eigenvector_centrality_tmax(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_cited_type_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_citing_cited_type_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_clique_number(SEXP); +extern SEXP R_igraph_clique_size_hist(SEXP, SEXP, SEXP); +extern SEXP R_igraph_cliques(SEXP, SEXP, SEXP); +extern SEXP R_igraph_closeness(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_closeness_cutoff(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_clusters(SEXP, SEXP); +extern SEXP R_igraph_cocitation(SEXP, SEXP); +extern SEXP R_igraph_cohesion(SEXP, SEXP); +extern SEXP R_igraph_cohesive_blocks(SEXP); +extern SEXP R_igraph_community_fluid_communities(SEXP, SEXP); +extern SEXP R_igraph_community_infomap(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_label_propagation(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_leiden(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_multilevel(SEXP, SEXP, SEXP); +extern SEXP R_igraph_community_optimal_modularity(SEXP, SEXP); +extern SEXP R_igraph_compare_communities(SEXP, SEXP, SEXP); +extern SEXP R_igraph_complementer(SEXP, SEXP); +extern SEXP R_igraph_connect_neighborhood(SEXP, SEXP, SEXP); +extern SEXP R_igraph_constraint(SEXP, SEXP, SEXP); +extern SEXP R_igraph_contract_vertices(SEXP, SEXP, SEXP); +extern SEXP R_igraph_convex_hull(SEXP); +extern SEXP R_igraph_coreness(SEXP, SEXP); +extern SEXP R_igraph_correlated_game(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_correlated_pair_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_count_isomorphisms_vf2(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_count_multiple(SEXP, SEXP); +extern SEXP R_igraph_count_subisomorphisms_vf2(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_create(SEXP, SEXP, SEXP); +extern SEXP R_igraph_create_bipartite(SEXP, SEXP, SEXP); +extern SEXP R_igraph_de_bruijn(SEXP, SEXP); +extern SEXP R_igraph_decompose(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_degree(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_degree_sequence_game(SEXP, SEXP, SEXP); +extern SEXP R_igraph_delete_edges(SEXP, SEXP); +extern SEXP R_igraph_delete_vertices(SEXP, SEXP); +extern SEXP R_igraph_density(SEXP, SEXP); +extern SEXP R_igraph_difference(SEXP, SEXP); +extern SEXP R_igraph_dim_select(SEXP); +extern SEXP R_igraph_diversity(SEXP, SEXP, SEXP); +extern SEXP R_igraph_dominator_tree(SEXP, SEXP, SEXP); +extern SEXP R_igraph_dot_product_game(SEXP, SEXP); +extern SEXP R_igraph_dyad_census(SEXP); +extern SEXP R_igraph_eccentricity(SEXP, SEXP, SEXP); +extern SEXP R_igraph_ecount(SEXP); +extern SEXP R_igraph_edge_betweenness(SEXP, SEXP, SEXP); +extern SEXP R_igraph_edge_betweenness_cutoff(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_edge_connectivity(SEXP, SEXP); +extern SEXP R_igraph_edge_disjoint_paths(SEXP, SEXP, SEXP); +extern SEXP R_igraph_edges(SEXP, SEXP); +extern SEXP R_igraph_eigen_adjacency(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_eigenvector_centrality(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_empty(SEXP, SEXP); +extern SEXP R_igraph_establishment_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_eulerian_cycle(SEXP); +extern SEXP R_igraph_eulerian_path(SEXP); +extern SEXP R_igraph_extended_chordal_ring(SEXP, SEXP, SEXP); +extern SEXP R_igraph_famous(SEXP); +extern SEXP R_igraph_feedback_arc_set(SEXP, SEXP, SEXP); +extern SEXP R_igraph_forest_fire_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_from_prufer(SEXP); +extern SEXP R_igraph_full(SEXP, SEXP, SEXP); +extern SEXP R_igraph_full_bipartite(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_full_citation(SEXP, SEXP); +extern SEXP R_igraph_get_adjacency(SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_all_shortest_paths(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_all_shortest_paths_dijkstra(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_all_simple_paths(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_edgelist(SEXP, SEXP); +extern SEXP R_igraph_get_incidence(SEXP, SEXP); +extern SEXP R_igraph_get_isomorphisms_vf2(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_get_stochastic(SEXP, SEXP); +extern SEXP R_igraph_get_stochastic_sparsemat(SEXP, SEXP); +extern SEXP R_igraph_get_subisomorphisms_vf2(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_global_efficiency(SEXP, SEXP, SEXP); +extern SEXP R_igraph_graphlets(SEXP, SEXP, SEXP); +extern SEXP R_igraph_graphlets_candidate_basis(SEXP, SEXP); +extern SEXP R_igraph_growing_random_game(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_harmonic_centrality_cutoff(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_has_loop(SEXP); +extern SEXP R_igraph_has_multiple(SEXP); +extern SEXP R_igraph_hrg_consensus(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_hrg_create(SEXP, SEXP); +extern SEXP R_igraph_hrg_dendrogram(SEXP); +extern SEXP R_igraph_hrg_fit(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_hrg_game(SEXP); +extern SEXP R_igraph_hrg_predict(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_hsbm_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_hsbm_list_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_hub_score(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_incidence(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_incident(SEXP, SEXP, SEXP); +extern SEXP R_igraph_independence_number(SEXP); +extern SEXP R_igraph_independent_vertex_sets(SEXP, SEXP, SEXP); +extern SEXP R_igraph_induced_subgraph(SEXP, SEXP, SEXP); +extern SEXP R_igraph_intersection(SEXP, SEXP); +extern SEXP R_igraph_is_bipartite(SEXP); +extern SEXP R_igraph_is_connected(SEXP, SEXP); +extern SEXP R_igraph_is_dag(SEXP); +extern SEXP R_igraph_is_directed(SEXP); +extern SEXP R_igraph_is_eulerian(SEXP); +extern SEXP R_igraph_is_graphical(SEXP, SEXP, SEXP); +extern SEXP R_igraph_is_loop(SEXP, SEXP); +extern SEXP R_igraph_is_matching(SEXP, SEXP, SEXP); +extern SEXP R_igraph_is_maximal_matching(SEXP, SEXP, SEXP); +extern SEXP R_igraph_is_minimal_separator(SEXP, SEXP); +extern SEXP R_igraph_is_multiple(SEXP, SEXP); +extern SEXP R_igraph_is_mutual(SEXP, SEXP); +extern SEXP R_igraph_is_separator(SEXP, SEXP); +extern SEXP R_igraph_is_simple(SEXP); +extern SEXP R_igraph_is_tree(SEXP, SEXP); +extern SEXP R_igraph_isoclass(SEXP); +extern SEXP R_igraph_isoclass_create(SEXP, SEXP, SEXP); +extern SEXP R_igraph_isoclass_subgraph(SEXP, SEXP); +extern SEXP R_igraph_isomorphic(SEXP, SEXP); +extern SEXP R_igraph_isomorphic_34(SEXP, SEXP); +extern SEXP R_igraph_isomorphic_bliss(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_isomorphic_vf2(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_k_regular_game(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_kautz(SEXP, SEXP); +extern SEXP R_igraph_laplacian_spectral_embedding(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_largest_cliques(SEXP); +extern SEXP R_igraph_largest_independent_vertex_sets(SEXP); +extern SEXP R_igraph_largest_weighted_cliques(SEXP, SEXP); +extern SEXP R_igraph_lastcit_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_lattice(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_bipartite(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_circle(SEXP, SEXP); +extern SEXP R_igraph_layout_davidson_harel(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_drl(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_drl_3d(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_gem(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_grid(SEXP, SEXP); +extern SEXP R_igraph_layout_grid_3d(SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_lgl(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_mds(SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_merge_dla(SEXP, SEXP); +extern SEXP R_igraph_layout_random(SEXP); +extern SEXP R_igraph_layout_random_3d(SEXP); +extern SEXP R_igraph_layout_sphere(SEXP); +extern SEXP R_igraph_layout_star(SEXP, SEXP, SEXP); +extern SEXP R_igraph_layout_sugiyama(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_lcf_vector(SEXP, SEXP, SEXP); +extern SEXP R_igraph_linegraph(SEXP); +extern SEXP R_igraph_list_triangles(SEXP); +extern SEXP R_igraph_local_efficiency(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_0(SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_0_them(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_1_ecount(SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_1_ecount_them(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_k_ecount(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_k_ecount_them(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_local_scan_neighborhood_ecount(SEXP, SEXP, SEXP); +extern SEXP R_igraph_maxflow(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_maximal_cliques_hist(SEXP, SEXP, SEXP); +extern SEXP R_igraph_maximal_independent_vertex_sets(SEXP); +extern SEXP R_igraph_maximum_bipartite_matching(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_maximum_cardinality_search(SEXP); +extern SEXP R_igraph_mincut(SEXP, SEXP); +extern SEXP R_igraph_mincut_value(SEXP, SEXP); +extern SEXP R_igraph_minimum_size_separators(SEXP); +extern SEXP R_igraph_minimum_spanning_tree_prim(SEXP, SEXP); +extern SEXP R_igraph_minimum_spanning_tree_unweighted(SEXP); +extern SEXP R_igraph_modularity(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_modularity_matrix(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_motifs_randesu(SEXP, SEXP, SEXP); +extern SEXP R_igraph_motifs_randesu_estimate(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_motifs_randesu_no(SEXP, SEXP, SEXP); +extern SEXP R_igraph_neighborhood_graphs(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_neighbors(SEXP, SEXP, SEXP); +extern SEXP R_igraph_path_length_hist(SEXP, SEXP); +extern SEXP R_igraph_permute_vertices(SEXP, SEXP); +extern SEXP R_igraph_personalized_pagerank(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_power_law_fit(SEXP, SEXP, SEXP); +extern SEXP R_igraph_preference_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_radius(SEXP, SEXP); +extern SEXP R_igraph_random_edge_walk(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_random_sample(SEXP, SEXP, SEXP); +extern SEXP R_igraph_random_spanning_tree(SEXP, SEXP); +extern SEXP R_igraph_random_walk(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_read_graph_dimacs(SEXP, SEXP); +extern SEXP R_igraph_read_graph_dl(SEXP, SEXP); +extern SEXP R_igraph_read_graph_edgelist(SEXP, SEXP, SEXP); +extern SEXP R_igraph_read_graph_gml(SEXP); +extern SEXP R_igraph_read_graph_graphdb(SEXP, SEXP); +extern SEXP R_igraph_read_graph_graphml(SEXP, SEXP); +extern SEXP R_igraph_read_graph_lgl(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_read_graph_ncol(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_read_graph_pajek(SEXP); +extern SEXP R_igraph_realize_degree_sequence(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_recent_degree_aging_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_recent_degree_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_reciprocity(SEXP, SEXP, SEXP); +extern SEXP R_igraph_rewire(SEXP, SEXP, SEXP); +extern SEXP R_igraph_rewire_directed_edges(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_rewire_edges(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_ring(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_running_mean(SEXP, SEXP); +extern SEXP R_igraph_sample_dirichlet(SEXP, SEXP); +extern SEXP R_igraph_sample_sphere_surface(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_sample_sphere_volume(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_sbm_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_scg_grouping(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_scg_norm_eps(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_similarity_dice(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_similarity_inverse_log_weighted(SEXP, SEXP, SEXP); +extern SEXP R_igraph_similarity_jaccard(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_simple_interconnected_islands_game(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_simplify(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_simplify_and_colorize(SEXP); +extern SEXP R_igraph_sir(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_split_join_distance(SEXP, SEXP); +extern SEXP R_igraph_st_edge_connectivity(SEXP, SEXP, SEXP); +extern SEXP R_igraph_st_mincut(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_st_mincut_value(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_star(SEXP, SEXP, SEXP); +extern SEXP R_igraph_static_fitness_game(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_static_power_law_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_strength(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_subcomponent(SEXP, SEXP, SEXP); +extern SEXP R_igraph_subgraph_edges(SEXP, SEXP, SEXP); +extern SEXP R_igraph_subisomorphic_vf2(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_to_directed(SEXP, SEXP); +extern SEXP R_igraph_to_prufer(SEXP); +extern SEXP R_igraph_to_undirected(SEXP, SEXP, SEXP); +extern SEXP R_igraph_topological_sorting(SEXP, SEXP); +extern SEXP R_igraph_transitivity_avglocal_undirected(SEXP, SEXP); +extern SEXP R_igraph_transitivity_barrat(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_transitivity_local_undirected(SEXP, SEXP, SEXP); +extern SEXP R_igraph_transitivity_undirected(SEXP, SEXP); +extern SEXP R_igraph_tree(SEXP, SEXP, SEXP); +extern SEXP R_igraph_tree_game(SEXP, SEXP, SEXP); +extern SEXP R_igraph_triad_census(SEXP); +extern SEXP R_igraph_unfold_tree(SEXP, SEXP, SEXP); +extern SEXP R_igraph_union(SEXP, SEXP); +extern SEXP R_igraph_vcount(SEXP); +extern SEXP R_igraph_vertex_coloring_greedy(SEXP, SEXP); +extern SEXP R_igraph_vertex_connectivity(SEXP, SEXP); +extern SEXP R_igraph_vertex_disjoint_paths(SEXP, SEXP, SEXP); +extern SEXP R_igraph_watts_strogatz_game(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_weighted_adjacency(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_weighted_clique_number(SEXP, SEXP); +extern SEXP R_igraph_weighted_cliques(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_dimacs(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_dot(SEXP, SEXP); +extern SEXP R_igraph_write_graph_edgelist(SEXP, SEXP); +extern SEXP R_igraph_write_graph_gml(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_graphml(SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_leda(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_lgl(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_ncol(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_igraph_write_graph_pajek(SEXP, SEXP); + +static const R_CMethodDef CEntries[] = { + {"igraphhcass2", (DL_FUNC) &igraphhcass2, 6}, + {NULL, NULL, 0} +}; + +static const R_CallMethodDef CallEntries[] = { + {"make_lazy", (DL_FUNC) &make_lazy, 3}, + {"make_lazy_dots", (DL_FUNC) &make_lazy_dots, 2}, + {"promise_env_", (DL_FUNC) &promise_env_, 1}, + {"promise_expr_", (DL_FUNC) &promise_expr_, 1}, + {"UUID_gen", (DL_FUNC) &UUID_gen, 1}, + + /* non-autogenerated entries */ + {"R_igraph_add_edges", (DL_FUNC) &R_igraph_add_edges, 2}, + {"R_igraph_add_env", (DL_FUNC) &R_igraph_add_env, 1}, + {"R_igraph_add_version_to_env", (DL_FUNC) &R_igraph_add_version_to_env, 1}, + {"R_igraph_add_vertices", (DL_FUNC) &R_igraph_add_vertices, 2}, + {"R_igraph_address", (DL_FUNC) &R_igraph_address, 1}, + {"R_igraph_adjacent_vertices", (DL_FUNC) &R_igraph_adjacent_vertices, 3}, + {"R_igraph_arpack", (DL_FUNC) &R_igraph_arpack, 5}, + {"R_igraph_bfs", (DL_FUNC) &R_igraph_bfs, 15}, + {"R_igraph_bipartite_projection", (DL_FUNC) &R_igraph_bipartite_projection, 4}, + {"R_igraph_community_edge_betweenness", (DL_FUNC) &R_igraph_community_edge_betweenness, 8}, + {"R_igraph_community_fastgreedy", (DL_FUNC) &R_igraph_community_fastgreedy, 5}, + {"R_igraph_community_to_membership2", (DL_FUNC) &R_igraph_community_to_membership2, 3}, + {"R_igraph_community_leading_eigenvector", (DL_FUNC) &R_igraph_community_leading_eigenvector, 9}, + {"R_igraph_compose", (DL_FUNC) &R_igraph_compose, 3}, + {"R_igraph_dfs", (DL_FUNC) &R_igraph_dfs, 12}, + {"R_igraph_diameter", (DL_FUNC) &R_igraph_diameter, 4}, + {"R_igraph_disjoint_union", (DL_FUNC) &R_igraph_disjoint_union, 1}, + {"R_igraph_erdos_renyi_game", (DL_FUNC) &R_igraph_erdos_renyi_game, 5}, + {"R_igraph_es_adj", (DL_FUNC) &R_igraph_es_adj, 4}, + {"R_igraph_es_pairs", (DL_FUNC) &R_igraph_es_pairs, 3}, + {"R_igraph_es_path", (DL_FUNC) &R_igraph_es_path, 3}, + {"R_igraph_farthest_points", (DL_FUNC) &R_igraph_farthest_points, 4}, + {"R_igraph_finalizer", (DL_FUNC) &R_igraph_finalizer, 0}, + {"R_igraph_get_adjedgelist", (DL_FUNC) &R_igraph_get_adjedgelist, 2}, + {"R_igraph_get_adjlist", (DL_FUNC) &R_igraph_get_adjlist, 2}, + {"R_igraph_get_all_simple_paths_pp", (DL_FUNC) &R_igraph_get_all_simple_paths_pp, 1}, + {"R_igraph_get_attr_mode", (DL_FUNC) &R_igraph_get_attr_mode, 2}, + {"R_igraph_get_diameter", (DL_FUNC) &R_igraph_get_diameter, 4}, + {"R_igraph_get_edge", (DL_FUNC) &R_igraph_get_edge, 2}, + {"R_igraph_get_eids", (DL_FUNC) &R_igraph_get_eids, 5}, + {"R_igraph_get_graph_id", (DL_FUNC) &R_igraph_get_graph_id, 1}, + {"R_igraph_get_shortest_paths", (DL_FUNC) &R_igraph_get_shortest_paths, 10}, + {"R_igraph_getsphere", (DL_FUNC) &R_igraph_getsphere, 8}, + {"R_igraph_girth", (DL_FUNC) &R_igraph_girth, 2}, + {"R_igraph_graph_adjacency", (DL_FUNC) &R_igraph_graph_adjacency, 2}, + {"R_igraph_graph_version", (DL_FUNC) &R_igraph_graph_version, 1}, + {"R_igraph_graphlets_project", (DL_FUNC) &R_igraph_graphlets_project, 5}, + {"R_igraph_grg_game", (DL_FUNC) &R_igraph_grg_game, 4}, + {"R_igraph_i_levc_arp", (DL_FUNC) &R_igraph_i_levc_arp, 3}, + {"R_igraph_identical_graphs", (DL_FUNC) &R_igraph_identical_graphs, 3}, + {"R_igraph_incident_edges", (DL_FUNC) &R_igraph_incident_edges, 3}, + {"R_igraph_is_chordal", (DL_FUNC) &R_igraph_is_chordal, 5}, + {"R_igraph_laplacian", (DL_FUNC) &R_igraph_laplacian, 4}, + {"R_igraph_layout_fruchterman_reingold", (DL_FUNC) &R_igraph_layout_fruchterman_reingold, 10}, + {"R_igraph_layout_fruchterman_reingold_3d", (DL_FUNC) &R_igraph_layout_fruchterman_reingold_3d, 11}, + {"R_igraph_layout_graphopt", (DL_FUNC) &R_igraph_layout_graphopt, 8}, + {"R_igraph_layout_kamada_kawai", (DL_FUNC) &R_igraph_layout_kamada_kawai, 10}, + {"R_igraph_layout_kamada_kawai_3d", (DL_FUNC) &R_igraph_layout_kamada_kawai_3d, 12}, + {"R_igraph_layout_reingold_tilford", (DL_FUNC) &R_igraph_layout_reingold_tilford, 5}, + {"R_igraph_make_weak_ref", (DL_FUNC) &R_igraph_make_weak_ref, 3}, + {"R_igraph_maximal_cliques", (DL_FUNC) &R_igraph_maximal_cliques, 4}, + {"R_igraph_maximal_cliques_count", (DL_FUNC) &R_igraph_maximal_cliques_count, 4}, + {"R_igraph_maximal_cliques_file", (DL_FUNC) &R_igraph_maximal_cliques_file, 5}, + {"R_igraph_mybracket", (DL_FUNC) &R_igraph_mybracket, 2}, + {"R_igraph_mybracket2", (DL_FUNC) &R_igraph_mybracket2, 3}, + {"R_igraph_mybracket2_copy", (DL_FUNC) &R_igraph_mybracket2_copy, 3}, + {"R_igraph_mybracket2_names", (DL_FUNC) &R_igraph_mybracket2_names, 3}, + {"R_igraph_mybracket2_set", (DL_FUNC) &R_igraph_mybracket2_set, 4}, + {"R_igraph_mybracket3_set", (DL_FUNC) &R_igraph_mybracket3_set, 5}, + {"R_igraph_neighborhood", (DL_FUNC) &R_igraph_neighborhood, 5}, + {"R_igraph_neighborhood_size", (DL_FUNC) &R_igraph_neighborhood_size, 5}, + {"R_igraph_no_clusters", (DL_FUNC) &R_igraph_no_clusters, 2}, + {"R_igraph_psumtree_draw", (DL_FUNC) &R_igraph_psumtree_draw, 3}, + {"R_igraph_scg_adjacency", (DL_FUNC) &R_igraph_scg_adjacency, 14}, + {"R_igraph_scg_laplacian", (DL_FUNC) &R_igraph_scg_laplacian, 16}, + {"R_igraph_scg_semiprojectors", (DL_FUNC) &R_igraph_scg_semiprojectors, 5}, + {"R_igraph_scg_stochastic", (DL_FUNC) &R_igraph_scg_stochastic, 17}, + {"R_igraph_set_verbose", (DL_FUNC) &R_igraph_set_verbose, 1}, + {"R_igraph_shortest_paths", (DL_FUNC) &R_igraph_shortest_paths, 6}, + {"R_igraph_solve_lsap", (DL_FUNC) &R_igraph_solve_lsap, 2}, + {"R_igraph_spinglass_community", (DL_FUNC) &R_igraph_spinglass_community, 11}, + {"R_igraph_spinglass_my_community", (DL_FUNC) &R_igraph_spinglass_my_community, 6}, + {"R_igraph_st_vertex_connectivity", (DL_FUNC) &R_igraph_st_vertex_connectivity, 3}, + {"R_igraph_subisomorphic_lad", (DL_FUNC) &R_igraph_subisomorphic_lad, 7}, + {"R_igraph_transitivity_local_undirected_all", (DL_FUNC) &R_igraph_transitivity_local_undirected_all, 2}, + {"R_igraph_version", (DL_FUNC) &R_igraph_version, 0}, + {"R_igraph_vs_adj", (DL_FUNC) &R_igraph_vs_adj, 4}, + {"R_igraph_vs_nei", (DL_FUNC) &R_igraph_vs_nei, 4}, + {"R_igraph_walktrap_community", (DL_FUNC) &R_igraph_walktrap_community, 6}, + {"R_igraph_weak_ref_key", (DL_FUNC) &R_igraph_weak_ref_key, 1}, + {"R_igraph_weak_ref_run_finalizer", (DL_FUNC) &R_igraph_weak_ref_run_finalizer, 1}, + {"R_igraph_weak_ref_value", (DL_FUNC) &R_igraph_weak_ref_value, 1}, + + /* autogenerated entries */ + {"R_igraph_adhesion", (DL_FUNC) &R_igraph_adhesion, 2}, + {"R_igraph_adjacency_spectral_embedding", (DL_FUNC) &R_igraph_adjacency_spectral_embedding, 7}, + {"R_igraph_adjacent_triangles", (DL_FUNC) &R_igraph_adjacent_triangles, 2}, + {"R_igraph_adjlist", (DL_FUNC) &R_igraph_adjlist, 3}, + {"R_igraph_all_minimal_st_separators", (DL_FUNC) &R_igraph_all_minimal_st_separators, 1}, + {"R_igraph_all_st_cuts", (DL_FUNC) &R_igraph_all_st_cuts, 3}, + {"R_igraph_all_st_mincuts", (DL_FUNC) &R_igraph_all_st_mincuts, 4}, + {"R_igraph_are_connected", (DL_FUNC) &R_igraph_are_connected, 3}, + {"R_igraph_arpack_unpack_complex", (DL_FUNC) &R_igraph_arpack_unpack_complex, 3}, + {"R_igraph_articulation_points", (DL_FUNC) &R_igraph_articulation_points, 1}, + {"R_igraph_assortativity", (DL_FUNC) &R_igraph_assortativity, 4}, + {"R_igraph_assortativity_degree", (DL_FUNC) &R_igraph_assortativity_degree, 2}, + {"R_igraph_assortativity_nominal", (DL_FUNC) &R_igraph_assortativity_nominal, 3}, + {"R_igraph_asymmetric_preference_game", (DL_FUNC) &R_igraph_asymmetric_preference_game, 6}, + {"R_igraph_atlas", (DL_FUNC) &R_igraph_atlas, 1}, + {"R_igraph_authority_score", (DL_FUNC) &R_igraph_authority_score, 4}, + {"R_igraph_automorphism_group", (DL_FUNC) &R_igraph_automorphism_group, 3}, + {"R_igraph_automorphisms", (DL_FUNC) &R_igraph_automorphisms, 3}, + {"R_igraph_average_local_efficiency", (DL_FUNC) &R_igraph_average_local_efficiency, 4}, + {"R_igraph_average_path_length_dijkstra", (DL_FUNC) &R_igraph_average_path_length_dijkstra, 4}, + {"R_igraph_avg_nearest_neighbor_degree", (DL_FUNC) &R_igraph_avg_nearest_neighbor_degree, 5}, + {"R_igraph_barabasi_aging_game", (DL_FUNC) &R_igraph_barabasi_aging_game, 12}, + {"R_igraph_barabasi_game", (DL_FUNC) &R_igraph_barabasi_game, 9}, + {"R_igraph_betweenness", (DL_FUNC) &R_igraph_betweenness, 4}, + {"R_igraph_betweenness_cutoff", (DL_FUNC) &R_igraph_betweenness_cutoff, 5}, + {"R_igraph_bibcoupling", (DL_FUNC) &R_igraph_bibcoupling, 2}, + {"R_igraph_biconnected_components", (DL_FUNC) &R_igraph_biconnected_components, 1}, + {"R_igraph_bipartite_game_gnm", (DL_FUNC) &R_igraph_bipartite_game_gnm, 5}, + {"R_igraph_bipartite_game_gnp", (DL_FUNC) &R_igraph_bipartite_game_gnp, 5}, + {"R_igraph_bipartite_projection_size", (DL_FUNC) &R_igraph_bipartite_projection_size, 2}, + {"R_igraph_bridges", (DL_FUNC) &R_igraph_bridges, 1}, + {"R_igraph_callaway_traits_game", (DL_FUNC) &R_igraph_callaway_traits_game, 6}, + {"R_igraph_canonical_permutation", (DL_FUNC) &R_igraph_canonical_permutation, 3}, + {"R_igraph_centralization", (DL_FUNC) &R_igraph_centralization, 3}, + {"R_igraph_centralization_betweenness", (DL_FUNC) &R_igraph_centralization_betweenness, 3}, + {"R_igraph_centralization_betweenness_tmax", (DL_FUNC) &R_igraph_centralization_betweenness_tmax, 3}, + {"R_igraph_centralization_closeness", (DL_FUNC) &R_igraph_centralization_closeness, 3}, + {"R_igraph_centralization_closeness_tmax", (DL_FUNC) &R_igraph_centralization_closeness_tmax, 3}, + {"R_igraph_centralization_degree", (DL_FUNC) &R_igraph_centralization_degree, 4}, + {"R_igraph_centralization_degree_tmax", (DL_FUNC) &R_igraph_centralization_degree_tmax, 4}, + {"R_igraph_centralization_eigenvector_centrality", (DL_FUNC) &R_igraph_centralization_eigenvector_centrality, 5}, + {"R_igraph_centralization_eigenvector_centrality_tmax", (DL_FUNC) &R_igraph_centralization_eigenvector_centrality_tmax, 4}, + {"R_igraph_cited_type_game", (DL_FUNC) &R_igraph_cited_type_game, 5}, + {"R_igraph_citing_cited_type_game", (DL_FUNC) &R_igraph_citing_cited_type_game, 5}, + {"R_igraph_clique_number", (DL_FUNC) &R_igraph_clique_number, 1}, + {"R_igraph_clique_size_hist", (DL_FUNC) &R_igraph_clique_size_hist, 3}, + {"R_igraph_cliques", (DL_FUNC) &R_igraph_cliques, 3}, + {"R_igraph_closeness", (DL_FUNC) &R_igraph_closeness, 5}, + {"R_igraph_closeness_cutoff", (DL_FUNC) &R_igraph_closeness_cutoff, 6}, + {"R_igraph_clusters", (DL_FUNC) &R_igraph_clusters, 2}, + {"R_igraph_cocitation", (DL_FUNC) &R_igraph_cocitation, 2}, + {"R_igraph_cohesion", (DL_FUNC) &R_igraph_cohesion, 2}, + {"R_igraph_cohesive_blocks", (DL_FUNC) &R_igraph_cohesive_blocks, 1}, + {"R_igraph_community_fluid_communities", (DL_FUNC) &R_igraph_community_fluid_communities, 2}, + {"R_igraph_community_infomap", (DL_FUNC) &R_igraph_community_infomap, 4}, + {"R_igraph_community_label_propagation", (DL_FUNC) &R_igraph_community_label_propagation, 4}, + {"R_igraph_community_leiden", (DL_FUNC) &R_igraph_community_leiden, 7}, + {"R_igraph_community_multilevel", (DL_FUNC) &R_igraph_community_multilevel, 3}, + {"R_igraph_community_optimal_modularity", (DL_FUNC) &R_igraph_community_optimal_modularity, 2}, + {"R_igraph_compare_communities", (DL_FUNC) &R_igraph_compare_communities, 3}, + {"R_igraph_complementer", (DL_FUNC) &R_igraph_complementer, 2}, + {"R_igraph_connect_neighborhood", (DL_FUNC) &R_igraph_connect_neighborhood, 3}, + {"R_igraph_constraint", (DL_FUNC) &R_igraph_constraint, 3}, + {"R_igraph_contract_vertices", (DL_FUNC) &R_igraph_contract_vertices, 3}, + {"R_igraph_convex_hull", (DL_FUNC) &R_igraph_convex_hull, 1}, + {"R_igraph_coreness", (DL_FUNC) &R_igraph_coreness, 2}, + {"R_igraph_correlated_game", (DL_FUNC) &R_igraph_correlated_game, 4}, + {"R_igraph_correlated_pair_game", (DL_FUNC) &R_igraph_correlated_pair_game, 5}, + {"R_igraph_count_isomorphisms_vf2", (DL_FUNC) &R_igraph_count_isomorphisms_vf2, 6}, + {"R_igraph_count_multiple", (DL_FUNC) &R_igraph_count_multiple, 2}, + {"R_igraph_count_subisomorphisms_vf2", (DL_FUNC) &R_igraph_count_subisomorphisms_vf2, 6}, + {"R_igraph_create", (DL_FUNC) &R_igraph_create, 3}, + {"R_igraph_create_bipartite", (DL_FUNC) &R_igraph_create_bipartite, 3}, + {"R_igraph_de_bruijn", (DL_FUNC) &R_igraph_de_bruijn, 2}, + {"R_igraph_decompose", (DL_FUNC) &R_igraph_decompose, 4}, + {"R_igraph_degree", (DL_FUNC) &R_igraph_degree, 4}, + {"R_igraph_degree_sequence_game", (DL_FUNC) &R_igraph_degree_sequence_game, 3}, + {"R_igraph_delete_edges", (DL_FUNC) &R_igraph_delete_edges, 2}, + {"R_igraph_delete_vertices", (DL_FUNC) &R_igraph_delete_vertices, 2}, + {"R_igraph_density", (DL_FUNC) &R_igraph_density, 2}, + {"R_igraph_difference", (DL_FUNC) &R_igraph_difference, 2}, + {"R_igraph_dim_select", (DL_FUNC) &R_igraph_dim_select, 1}, + {"R_igraph_diversity", (DL_FUNC) &R_igraph_diversity, 3}, + {"R_igraph_dominator_tree", (DL_FUNC) &R_igraph_dominator_tree, 3}, + {"R_igraph_dot_product_game", (DL_FUNC) &R_igraph_dot_product_game, 2}, + {"R_igraph_dyad_census", (DL_FUNC) &R_igraph_dyad_census, 1}, + {"R_igraph_eccentricity", (DL_FUNC) &R_igraph_eccentricity, 3}, + {"R_igraph_ecount", (DL_FUNC) &R_igraph_ecount, 1}, + {"R_igraph_edge_betweenness", (DL_FUNC) &R_igraph_edge_betweenness, 3}, + {"R_igraph_edge_betweenness_cutoff", (DL_FUNC) &R_igraph_edge_betweenness_cutoff, 4}, + {"R_igraph_edge_connectivity", (DL_FUNC) &R_igraph_edge_connectivity, 2}, + {"R_igraph_edge_disjoint_paths", (DL_FUNC) &R_igraph_edge_disjoint_paths, 3}, + {"R_igraph_edges", (DL_FUNC) &R_igraph_edges, 2}, + {"R_igraph_eigen_adjacency", (DL_FUNC) &R_igraph_eigen_adjacency, 4}, + {"R_igraph_eigenvector_centrality", (DL_FUNC) &R_igraph_eigenvector_centrality, 5}, + {"R_igraph_empty", (DL_FUNC) &R_igraph_empty, 2}, + {"R_igraph_establishment_game", (DL_FUNC) &R_igraph_establishment_game, 6}, + {"R_igraph_eulerian_cycle", (DL_FUNC) &R_igraph_eulerian_cycle, 1}, + {"R_igraph_eulerian_path", (DL_FUNC) &R_igraph_eulerian_path, 1}, + {"R_igraph_extended_chordal_ring", (DL_FUNC) &R_igraph_extended_chordal_ring, 3}, + {"R_igraph_famous", (DL_FUNC) &R_igraph_famous, 1}, + {"R_igraph_feedback_arc_set", (DL_FUNC) &R_igraph_feedback_arc_set, 3}, + {"R_igraph_forest_fire_game", (DL_FUNC) &R_igraph_forest_fire_game, 5}, + {"R_igraph_from_prufer", (DL_FUNC) &R_igraph_from_prufer, 1}, + {"R_igraph_full", (DL_FUNC) &R_igraph_full, 3}, + {"R_igraph_full_bipartite", (DL_FUNC) &R_igraph_full_bipartite, 4}, + {"R_igraph_full_citation", (DL_FUNC) &R_igraph_full_citation, 2}, + {"R_igraph_get_adjacency", (DL_FUNC) &R_igraph_get_adjacency, 3}, + {"R_igraph_get_all_shortest_paths", (DL_FUNC) &R_igraph_get_all_shortest_paths, 4}, + {"R_igraph_get_all_shortest_paths_dijkstra", (DL_FUNC) &R_igraph_get_all_shortest_paths_dijkstra, 5}, + {"R_igraph_get_all_simple_paths", (DL_FUNC) &R_igraph_get_all_simple_paths, 5}, + {"R_igraph_get_edgelist", (DL_FUNC) &R_igraph_get_edgelist, 2}, + {"R_igraph_get_incidence", (DL_FUNC) &R_igraph_get_incidence, 2}, + {"R_igraph_get_isomorphisms_vf2", (DL_FUNC) &R_igraph_get_isomorphisms_vf2, 6}, + {"R_igraph_get_stochastic", (DL_FUNC) &R_igraph_get_stochastic, 2}, + {"R_igraph_get_stochastic_sparsemat", (DL_FUNC) &R_igraph_get_stochastic_sparsemat, 2}, + {"R_igraph_get_subisomorphisms_vf2", (DL_FUNC) &R_igraph_get_subisomorphisms_vf2, 6}, + {"R_igraph_global_efficiency", (DL_FUNC) &R_igraph_global_efficiency, 3}, + {"R_igraph_graphlets", (DL_FUNC) &R_igraph_graphlets, 3}, + {"R_igraph_graphlets_candidate_basis", (DL_FUNC) &R_igraph_graphlets_candidate_basis, 2}, + {"R_igraph_growing_random_game", (DL_FUNC) &R_igraph_growing_random_game, 4}, + {"R_igraph_harmonic_centrality_cutoff", (DL_FUNC) &R_igraph_harmonic_centrality_cutoff, 6}, + {"R_igraph_has_loop", (DL_FUNC) &R_igraph_has_loop, 1}, + {"R_igraph_has_multiple", (DL_FUNC) &R_igraph_has_multiple, 1}, + {"R_igraph_hrg_consensus", (DL_FUNC) &R_igraph_hrg_consensus, 4}, + {"R_igraph_hrg_create", (DL_FUNC) &R_igraph_hrg_create, 2}, + {"R_igraph_hrg_dendrogram", (DL_FUNC) &R_igraph_hrg_dendrogram, 1}, + {"R_igraph_hrg_fit", (DL_FUNC) &R_igraph_hrg_fit, 4}, + {"R_igraph_hrg_game", (DL_FUNC) &R_igraph_hrg_game, 1}, + {"R_igraph_hrg_predict", (DL_FUNC) &R_igraph_hrg_predict, 5}, + {"R_igraph_hsbm_game", (DL_FUNC) &R_igraph_hsbm_game, 5}, + {"R_igraph_hsbm_list_game", (DL_FUNC) &R_igraph_hsbm_list_game, 5}, + {"R_igraph_hub_score", (DL_FUNC) &R_igraph_hub_score, 4}, + {"R_igraph_incidence", (DL_FUNC) &R_igraph_incidence, 4}, + {"R_igraph_incident", (DL_FUNC) &R_igraph_incident, 3}, + {"R_igraph_independence_number", (DL_FUNC) &R_igraph_independence_number, 1}, + {"R_igraph_independent_vertex_sets", (DL_FUNC) &R_igraph_independent_vertex_sets, 3}, + {"R_igraph_induced_subgraph", (DL_FUNC) &R_igraph_induced_subgraph, 3}, + {"R_igraph_intersection", (DL_FUNC) &R_igraph_intersection, 2}, + {"R_igraph_is_bipartite", (DL_FUNC) &R_igraph_is_bipartite, 1}, + {"R_igraph_is_connected", (DL_FUNC) &R_igraph_is_connected, 2}, + {"R_igraph_is_dag", (DL_FUNC) &R_igraph_is_dag, 1}, + {"R_igraph_is_directed", (DL_FUNC) &R_igraph_is_directed, 1}, + {"R_igraph_is_eulerian", (DL_FUNC) &R_igraph_is_eulerian, 1}, + {"R_igraph_is_graphical", (DL_FUNC) &R_igraph_is_graphical, 3}, + {"R_igraph_is_loop", (DL_FUNC) &R_igraph_is_loop, 2}, + {"R_igraph_is_matching", (DL_FUNC) &R_igraph_is_matching, 3}, + {"R_igraph_is_maximal_matching", (DL_FUNC) &R_igraph_is_maximal_matching, 3}, + {"R_igraph_is_minimal_separator", (DL_FUNC) &R_igraph_is_minimal_separator, 2}, + {"R_igraph_is_multiple", (DL_FUNC) &R_igraph_is_multiple, 2}, + {"R_igraph_is_mutual", (DL_FUNC) &R_igraph_is_mutual, 2}, + {"R_igraph_is_separator", (DL_FUNC) &R_igraph_is_separator, 2}, + {"R_igraph_is_simple", (DL_FUNC) &R_igraph_is_simple, 1}, + {"R_igraph_is_tree", (DL_FUNC) &R_igraph_is_tree, 2}, + {"R_igraph_isoclass", (DL_FUNC) &R_igraph_isoclass, 1}, + {"R_igraph_isoclass_create", (DL_FUNC) &R_igraph_isoclass_create, 3}, + {"R_igraph_isoclass_subgraph", (DL_FUNC) &R_igraph_isoclass_subgraph, 2}, + {"R_igraph_isomorphic", (DL_FUNC) &R_igraph_isomorphic, 2}, + {"R_igraph_isomorphic_34", (DL_FUNC) &R_igraph_isomorphic_34, 2}, + {"R_igraph_isomorphic_bliss", (DL_FUNC) &R_igraph_isomorphic_bliss, 5}, + {"R_igraph_isomorphic_vf2", (DL_FUNC) &R_igraph_isomorphic_vf2, 6}, + {"R_igraph_k_regular_game", (DL_FUNC) &R_igraph_k_regular_game, 4}, + {"R_igraph_kautz", (DL_FUNC) &R_igraph_kautz, 2}, + {"R_igraph_laplacian_spectral_embedding", (DL_FUNC) &R_igraph_laplacian_spectral_embedding, 7}, + {"R_igraph_largest_cliques", (DL_FUNC) &R_igraph_largest_cliques, 1}, + {"R_igraph_largest_independent_vertex_sets", (DL_FUNC) &R_igraph_largest_independent_vertex_sets, 1}, + {"R_igraph_largest_weighted_cliques", (DL_FUNC) &R_igraph_largest_weighted_cliques, 2}, + {"R_igraph_lastcit_game", (DL_FUNC) &R_igraph_lastcit_game, 5}, + {"R_igraph_lattice", (DL_FUNC) &R_igraph_lattice, 5}, + {"R_igraph_layout_bipartite", (DL_FUNC) &R_igraph_layout_bipartite, 5}, + {"R_igraph_layout_circle", (DL_FUNC) &R_igraph_layout_circle, 2}, + {"R_igraph_layout_davidson_harel", (DL_FUNC) &R_igraph_layout_davidson_harel, 11}, + {"R_igraph_layout_drl", (DL_FUNC) &R_igraph_layout_drl, 6}, + {"R_igraph_layout_drl_3d", (DL_FUNC) &R_igraph_layout_drl_3d, 6}, + {"R_igraph_layout_gem", (DL_FUNC) &R_igraph_layout_gem, 7}, + {"R_igraph_layout_grid", (DL_FUNC) &R_igraph_layout_grid, 2}, + {"R_igraph_layout_grid_3d", (DL_FUNC) &R_igraph_layout_grid_3d, 3}, + {"R_igraph_layout_lgl", (DL_FUNC) &R_igraph_layout_lgl, 8}, + {"R_igraph_layout_mds", (DL_FUNC) &R_igraph_layout_mds, 3}, + {"R_igraph_layout_merge_dla", (DL_FUNC) &R_igraph_layout_merge_dla, 2}, + {"R_igraph_layout_random", (DL_FUNC) &R_igraph_layout_random, 1}, + {"R_igraph_layout_random_3d", (DL_FUNC) &R_igraph_layout_random_3d, 1}, + {"R_igraph_layout_sphere", (DL_FUNC) &R_igraph_layout_sphere, 1}, + {"R_igraph_layout_star", (DL_FUNC) &R_igraph_layout_star, 3}, + {"R_igraph_layout_sugiyama", (DL_FUNC) &R_igraph_layout_sugiyama, 6}, + {"R_igraph_lcf_vector", (DL_FUNC) &R_igraph_lcf_vector, 3}, + {"R_igraph_linegraph", (DL_FUNC) &R_igraph_linegraph, 1}, + {"R_igraph_list_triangles", (DL_FUNC) &R_igraph_list_triangles, 1}, + {"R_igraph_local_efficiency", (DL_FUNC) &R_igraph_local_efficiency, 5}, + {"R_igraph_local_scan_0", (DL_FUNC) &R_igraph_local_scan_0, 3}, + {"R_igraph_local_scan_0_them", (DL_FUNC) &R_igraph_local_scan_0_them, 4}, + {"R_igraph_local_scan_1_ecount", (DL_FUNC) &R_igraph_local_scan_1_ecount, 3}, + {"R_igraph_local_scan_1_ecount_them", (DL_FUNC) &R_igraph_local_scan_1_ecount_them, 4}, + {"R_igraph_local_scan_k_ecount", (DL_FUNC) &R_igraph_local_scan_k_ecount, 4}, + {"R_igraph_local_scan_k_ecount_them", (DL_FUNC) &R_igraph_local_scan_k_ecount_them, 5}, + {"R_igraph_local_scan_neighborhood_ecount", (DL_FUNC) &R_igraph_local_scan_neighborhood_ecount, 3}, + {"R_igraph_maxflow", (DL_FUNC) &R_igraph_maxflow, 4}, + {"R_igraph_maximal_cliques_hist", (DL_FUNC) &R_igraph_maximal_cliques_hist, 3}, + {"R_igraph_maximal_independent_vertex_sets", (DL_FUNC) &R_igraph_maximal_independent_vertex_sets, 1}, + {"R_igraph_maximum_bipartite_matching", (DL_FUNC) &R_igraph_maximum_bipartite_matching, 4}, + {"R_igraph_maximum_cardinality_search", (DL_FUNC) &R_igraph_maximum_cardinality_search, 1}, + {"R_igraph_mincut", (DL_FUNC) &R_igraph_mincut, 2}, + {"R_igraph_mincut_value", (DL_FUNC) &R_igraph_mincut_value, 2}, + {"R_igraph_minimum_size_separators", (DL_FUNC) &R_igraph_minimum_size_separators, 1}, + {"R_igraph_minimum_spanning_tree_prim", (DL_FUNC) &R_igraph_minimum_spanning_tree_prim, 2}, + {"R_igraph_minimum_spanning_tree_unweighted", (DL_FUNC) &R_igraph_minimum_spanning_tree_unweighted, 1}, + {"R_igraph_modularity", (DL_FUNC) &R_igraph_modularity, 5}, + {"R_igraph_modularity_matrix", (DL_FUNC) &R_igraph_modularity_matrix, 4}, + {"R_igraph_motifs_randesu", (DL_FUNC) &R_igraph_motifs_randesu, 3}, + {"R_igraph_motifs_randesu_estimate", (DL_FUNC) &R_igraph_motifs_randesu_estimate, 5}, + {"R_igraph_motifs_randesu_no", (DL_FUNC) &R_igraph_motifs_randesu_no, 3}, + {"R_igraph_neighborhood_graphs", (DL_FUNC) &R_igraph_neighborhood_graphs, 5}, + {"R_igraph_neighbors", (DL_FUNC) &R_igraph_neighbors, 3}, + {"R_igraph_path_length_hist", (DL_FUNC) &R_igraph_path_length_hist, 2}, + {"R_igraph_permute_vertices", (DL_FUNC) &R_igraph_permute_vertices, 2}, + {"R_igraph_personalized_pagerank", (DL_FUNC) &R_igraph_personalized_pagerank, 8}, + {"R_igraph_power_law_fit", (DL_FUNC) &R_igraph_power_law_fit, 3}, + {"R_igraph_preference_game", (DL_FUNC) &R_igraph_preference_game, 7}, + {"R_igraph_radius", (DL_FUNC) &R_igraph_radius, 2}, + {"R_igraph_random_edge_walk", (DL_FUNC) &R_igraph_random_edge_walk, 6}, + {"R_igraph_random_sample", (DL_FUNC) &R_igraph_random_sample, 3}, + {"R_igraph_random_spanning_tree", (DL_FUNC) &R_igraph_random_spanning_tree, 2}, + {"R_igraph_random_walk", (DL_FUNC) &R_igraph_random_walk, 5}, + {"R_igraph_read_graph_dimacs", (DL_FUNC) &R_igraph_read_graph_dimacs, 2}, + {"R_igraph_read_graph_dl", (DL_FUNC) &R_igraph_read_graph_dl, 2}, + {"R_igraph_read_graph_edgelist", (DL_FUNC) &R_igraph_read_graph_edgelist, 3}, + {"R_igraph_read_graph_gml", (DL_FUNC) &R_igraph_read_graph_gml, 1}, + {"R_igraph_read_graph_graphdb", (DL_FUNC) &R_igraph_read_graph_graphdb, 2}, + {"R_igraph_read_graph_graphml", (DL_FUNC) &R_igraph_read_graph_graphml, 2}, + {"R_igraph_read_graph_lgl", (DL_FUNC) &R_igraph_read_graph_lgl, 4}, + {"R_igraph_read_graph_ncol", (DL_FUNC) &R_igraph_read_graph_ncol, 5}, + {"R_igraph_read_graph_pajek", (DL_FUNC) &R_igraph_read_graph_pajek, 1}, + {"R_igraph_realize_degree_sequence", (DL_FUNC) &R_igraph_realize_degree_sequence, 4}, + {"R_igraph_recent_degree_aging_game", (DL_FUNC) &R_igraph_recent_degree_aging_game, 10}, + {"R_igraph_recent_degree_game", (DL_FUNC) &R_igraph_recent_degree_game, 8}, + {"R_igraph_reciprocity", (DL_FUNC) &R_igraph_reciprocity, 3}, + {"R_igraph_rewire", (DL_FUNC) &R_igraph_rewire, 3}, + {"R_igraph_rewire_directed_edges", (DL_FUNC) &R_igraph_rewire_directed_edges, 4}, + {"R_igraph_rewire_edges", (DL_FUNC) &R_igraph_rewire_edges, 4}, + {"R_igraph_ring", (DL_FUNC) &R_igraph_ring, 4}, + {"R_igraph_running_mean", (DL_FUNC) &R_igraph_running_mean, 2}, + {"R_igraph_sample_dirichlet", (DL_FUNC) &R_igraph_sample_dirichlet, 2}, + {"R_igraph_sample_sphere_surface", (DL_FUNC) &R_igraph_sample_sphere_surface, 4}, + {"R_igraph_sample_sphere_volume", (DL_FUNC) &R_igraph_sample_sphere_volume, 4}, + {"R_igraph_sbm_game", (DL_FUNC) &R_igraph_sbm_game, 5}, + {"R_igraph_scg_grouping", (DL_FUNC) &R_igraph_scg_grouping, 7}, + {"R_igraph_scg_norm_eps", (DL_FUNC) &R_igraph_scg_norm_eps, 5}, + {"R_igraph_similarity_dice", (DL_FUNC) &R_igraph_similarity_dice, 4}, + {"R_igraph_similarity_inverse_log_weighted", (DL_FUNC) &R_igraph_similarity_inverse_log_weighted, 3}, + {"R_igraph_similarity_jaccard", (DL_FUNC) &R_igraph_similarity_jaccard, 4}, + {"R_igraph_simple_interconnected_islands_game", (DL_FUNC) &R_igraph_simple_interconnected_islands_game, 4}, + {"R_igraph_simplify", (DL_FUNC) &R_igraph_simplify, 4}, + {"R_igraph_simplify_and_colorize", (DL_FUNC) &R_igraph_simplify_and_colorize, 1}, + {"R_igraph_sir", (DL_FUNC) &R_igraph_sir, 4}, + {"R_igraph_split_join_distance", (DL_FUNC) &R_igraph_split_join_distance, 2}, + {"R_igraph_st_edge_connectivity", (DL_FUNC) &R_igraph_st_edge_connectivity, 3}, + {"R_igraph_st_mincut", (DL_FUNC) &R_igraph_st_mincut, 4}, + {"R_igraph_st_mincut_value", (DL_FUNC) &R_igraph_st_mincut_value, 4}, + {"R_igraph_star", (DL_FUNC) &R_igraph_star, 3}, + {"R_igraph_static_fitness_game", (DL_FUNC) &R_igraph_static_fitness_game, 5}, + {"R_igraph_static_power_law_game", (DL_FUNC) &R_igraph_static_power_law_game, 7}, + {"R_igraph_strength", (DL_FUNC) &R_igraph_strength, 5}, + {"R_igraph_subcomponent", (DL_FUNC) &R_igraph_subcomponent, 3}, + {"R_igraph_subgraph_edges", (DL_FUNC) &R_igraph_subgraph_edges, 3}, + {"R_igraph_subisomorphic_vf2", (DL_FUNC) &R_igraph_subisomorphic_vf2, 6}, + {"R_igraph_to_directed", (DL_FUNC) &R_igraph_to_directed, 2}, + {"R_igraph_to_prufer", (DL_FUNC) &R_igraph_to_prufer, 1}, + {"R_igraph_to_undirected", (DL_FUNC) &R_igraph_to_undirected, 3}, + {"R_igraph_topological_sorting", (DL_FUNC) &R_igraph_topological_sorting, 2}, + {"R_igraph_transitivity_avglocal_undirected", (DL_FUNC) &R_igraph_transitivity_avglocal_undirected, 2}, + {"R_igraph_transitivity_barrat", (DL_FUNC) &R_igraph_transitivity_barrat, 4}, + {"R_igraph_transitivity_local_undirected", (DL_FUNC) &R_igraph_transitivity_local_undirected, 3}, + {"R_igraph_transitivity_undirected", (DL_FUNC) &R_igraph_transitivity_undirected, 2}, + {"R_igraph_tree", (DL_FUNC) &R_igraph_tree, 3}, + {"R_igraph_tree_game", (DL_FUNC) &R_igraph_tree_game, 3}, + {"R_igraph_triad_census", (DL_FUNC) &R_igraph_triad_census, 1}, + {"R_igraph_unfold_tree", (DL_FUNC) &R_igraph_unfold_tree, 3}, + {"R_igraph_union", (DL_FUNC) &R_igraph_union, 2}, + {"R_igraph_vcount", (DL_FUNC) &R_igraph_vcount, 1}, + {"R_igraph_vertex_coloring_greedy", (DL_FUNC) &R_igraph_vertex_coloring_greedy, 2}, + {"R_igraph_vertex_connectivity", (DL_FUNC) &R_igraph_vertex_connectivity, 2}, + {"R_igraph_vertex_disjoint_paths", (DL_FUNC) &R_igraph_vertex_disjoint_paths, 3}, + {"R_igraph_watts_strogatz_game", (DL_FUNC) &R_igraph_watts_strogatz_game, 6}, + {"R_igraph_weighted_adjacency", (DL_FUNC) &R_igraph_weighted_adjacency, 4}, + {"R_igraph_weighted_clique_number", (DL_FUNC) &R_igraph_weighted_clique_number, 2}, + {"R_igraph_weighted_cliques", (DL_FUNC) &R_igraph_weighted_cliques, 5}, + {"R_igraph_write_graph_dimacs", (DL_FUNC) &R_igraph_write_graph_dimacs, 5}, + {"R_igraph_write_graph_dot", (DL_FUNC) &R_igraph_write_graph_dot, 2}, + {"R_igraph_write_graph_edgelist", (DL_FUNC) &R_igraph_write_graph_edgelist, 2}, + {"R_igraph_write_graph_gml", (DL_FUNC) &R_igraph_write_graph_gml, 4}, + {"R_igraph_write_graph_graphml", (DL_FUNC) &R_igraph_write_graph_graphml, 3}, + {"R_igraph_write_graph_leda", (DL_FUNC) &R_igraph_write_graph_leda, 4}, + {"R_igraph_write_graph_lgl", (DL_FUNC) &R_igraph_write_graph_lgl, 5}, + {"R_igraph_write_graph_ncol", (DL_FUNC) &R_igraph_write_graph_ncol, 4}, + {"R_igraph_write_graph_pajek", (DL_FUNC) &R_igraph_write_graph_pajek, 2}, + + {NULL, NULL, 0} +}; diff --git a/src/rigraph/lazyeval.c b/src/rigraph/lazyeval.c new file mode 100644 index 0000000..dbc1f4c --- /dev/null +++ b/src/rigraph/lazyeval.c @@ -0,0 +1,172 @@ +#include +#include + +SEXP promise_as_lazy(SEXP promise, SEXP env, int follow_symbols) { + // recurse until we find the real promise, not a promise of a promise + // never go past the global environment + while(TYPEOF(promise) == PROMSXP && env != R_GlobalEnv) { + + env = PRENV(promise); + promise = PREXPR(promise); + + // If the promise is threaded through multiple functions, we'll + // get some symbols along the way. If the symbol is bound to a promise + // keep going on up + if (follow_symbols && TYPEOF(promise) == SYMSXP) { + SEXP obj = findVar(promise, env); + if (TYPEOF(obj) == PROMSXP) { + promise = obj; + } + } + } + + // Make named list for output + SEXP lazy = PROTECT(allocVector(VECSXP, 2)); + SET_VECTOR_ELT(lazy, 0, promise); + SET_VECTOR_ELT(lazy, 1, env); + + SEXP names = PROTECT(allocVector(STRSXP, 2)); + SET_STRING_ELT(names, 0, mkChar("expr")); + SET_STRING_ELT(names, 1, mkChar("env")); + + setAttrib(lazy, install("names"), names); + setAttrib(lazy, install("class"), PROTECT(mkString("lazy"))); + + UNPROTECT(3); + + return lazy; +} + +SEXP make_lazy(SEXP name, SEXP env, SEXP follow_symbols_) { + SEXP promise = PROTECT(findVar(name, env)); + int follow_symbols = asLogical(follow_symbols_); + SEXP ret = promise_as_lazy(promise, env, follow_symbols); + UNPROTECT(1); + return ret; +} + +SEXP make_lazy_dots(SEXP env, SEXP follow_symbols_) { + SEXP dots = PROTECT(findVar(install("..."), env)); + int follow_symbols = asLogical(follow_symbols_); + + // Figure out how many elements in dots + int n = 0; + for(SEXP nxt = dots; nxt != R_NilValue; nxt = CDR(nxt)) { + n++; + } + + // Allocate list to store results + SEXP lazy_dots = PROTECT(allocVector(VECSXP, n)); + SEXP names = PROTECT(allocVector(STRSXP, n)); + + // Iterate through all elements of dots, converting promises into lazy exprs + int i = 0; + SEXP nxt = dots; + while(nxt != R_NilValue) { + SEXP promise = CAR(nxt); + + SEXP lazy = promise_as_lazy(promise, env, follow_symbols); + SET_VECTOR_ELT(lazy_dots, i, lazy); + if (TAG(nxt) != R_NilValue) + SET_STRING_ELT(names, i, PRINTNAME(TAG(nxt))); + + nxt = CDR(nxt); + i++; + } + setAttrib(lazy_dots, install("names"), names); + setAttrib(lazy_dots, install("class"), PROTECT(mkString("lazy_dots"))); + + UNPROTECT(4); + + return lazy_dots; +} +#include +#include + +/* For now, replace with pure R alternative ------------------------------------ + +// This is a bit naughty, but there's no other way to create a promise +SEXP Rf_mkPROMISE(SEXP, SEXP); +SEXP Rf_installTrChar(SEXP); + +SEXP lazy_to_promise(SEXP x) { + // arg is a list of length 2 - LANGSXP/SYMSXP, followed by ENVSXP + return Rf_mkPROMISE(VECTOR_ELT(x, 0), VECTOR_ELT(x, 1)); +} + +SEXP eval_call_(SEXP fun, SEXP dots, SEXP env) { + if (TYPEOF(fun) != SYMSXP && TYPEOF(fun) != LANGSXP) { + error("fun must be a call or a symbol"); + } + if (TYPEOF(dots) != VECSXP) { + error("dots must be a list"); + } + if (!inherits(dots, "lazy_dots")) { + error("dots must be of class lazy_dots"); + } + if (TYPEOF(env) != ENVSXP) { + error("env must be an environment"); + } + + int n = length(dots); + if (n == 0) { + return LCONS(fun, R_NilValue); + } + + SEXP names = GET_NAMES(dots); + + SEXP args = R_NilValue; + for (int i = n - 1; i >= 0; --i) { + SEXP dot = VECTOR_ELT(dots, i); + SEXP prom = lazy_to_promise(dot); + args = PROTECT(CONS(prom, args)); + if (names != R_NilValue) { + SEXP name = STRING_ELT(names, i); + if (strlen(CHAR(name)) > 0) + SET_TAG(args, Rf_installTrChar(name)); + } + } + UNPROTECT(n); + + SEXP call = LCONS(fun, args); + + return eval(call, env); +} + +*/ +#include +#include + +/* Fails on Linux -------------------------------------------------------------- + +SEXP Rf_mkPROMISE(SEXP, SEXP); + +SEXP promise_(SEXP expr, SEXP env) { + if (TYPEOF(expr) != SYMSXP && TYPEOF(expr) != LANGSXP) { + error("expr must be a call or a symbol"); + } + if (TYPEOF(env) != ENVSXP) { + error("env must be an environment"); + } + + return Rf_mkPROMISE(expr, env); +} + +*/ + +SEXP promise_expr_(SEXP prom) { + if (TYPEOF(prom) != PROMSXP) { + error("prom must be a promise"); + } + + return PREXPR(prom); +} + +SEXP promise_env_(SEXP prom) { + if (TYPEOF(prom) != PROMSXP) { + error("prom must be a promise"); + } + + return PRENV(prom); +} + diff --git a/src/rigraph/rinterface.c b/src/rigraph/rinterface.c new file mode 100644 index 0000000..12c2ae1 --- /dev/null +++ b/src/rigraph/rinterface.c @@ -0,0 +1,16754 @@ +/* -*- mode: C -*- */ +/* + IGraph library R interface. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph.h" +#include "igraph_error.h" + +#include "config.h" + +#include +#include +#include +#include + +#include "rinterface.h" +#include "rrandom.h" + +#include "init.c" /* registration table */ + +#include + +void igraph_free(void *p); + +SEXP R_igraph_vector_to_SEXP(const igraph_vector_t *v); +SEXP R_igraph_vector_int_to_SEXP(const igraph_vector_int_t *v); +SEXP R_igraph_vector_int_to_SEXPp1(const igraph_vector_int_t *v); +SEXP R_igraph_vector_bool_to_SEXP(const igraph_vector_bool_t *v); +SEXP R_igraph_vector_long_to_SEXP(const igraph_vector_long_t *v); +SEXP R_igraph_vector_complex_to_SEXP(const igraph_vector_complex_t* v); +SEXP R_igraph_0orvector_to_SEXP(const igraph_vector_t *v); +SEXP R_igraph_0orvector_bool_to_SEXP(const igraph_vector_bool_t *v); +SEXP R_igraph_0orvector_long_to_SEXP(const igraph_vector_long_t *v); +SEXP R_igraph_0orvector_complex_to_SEXP(const igraph_vector_complex_t *v); +SEXP R_igraph_matrix_to_SEXP(const igraph_matrix_t *m); +SEXP R_igraph_matrix_complex_to_SEXP(const igraph_matrix_complex_t *m); +SEXP R_igraph_0ormatrix_complex_to_SEXP(const igraph_matrix_complex_t *m); +SEXP R_igraph_strvector_to_SEXP(const igraph_strvector_t *m); +SEXP R_igraph_to_SEXP(const igraph_t *graph); +SEXP R_igraph_vectorlist_to_SEXP(const igraph_vector_ptr_t *ptr); +SEXP R_igraph_vectorlist_int_to_SEXP(const igraph_vector_ptr_t *ptr); +void R_igraph_vectorlist_int_destroy(igraph_vector_ptr_t *ptr); +SEXP R_igraph_0orvectorlist_to_SEXP(const igraph_vector_ptr_t *ptr); +void R_igraph_vectorlist_destroy(igraph_vector_ptr_t *ptr); +SEXP R_igraph_matrixlist_to_SEXP(const igraph_vector_ptr_t *ptr); +void R_igraph_matrixlist_destroy(igraph_vector_ptr_t *ptr); +SEXP R_igraph_graphlist_to_SEXP(const igraph_vector_ptr_t *ptr); +void R_igraph_graphlist_destroy(igraph_vector_ptr_t *ptr); +SEXP R_igraph_hrg_to_SEXP(const igraph_hrg_t *hrg); +SEXP R_igraph_plfit_result_to_SEXP(const igraph_plfit_result_t *plfit); +SEXP R_igraph_sparsemat_to_SEXP(const igraph_sparsemat_t *sp); +SEXP R_igraph_0orsparsemat_to_SEXP(const igraph_sparsemat_t *sp); +SEXP R_igraph_maxflow_stats_to_SEXP(const igraph_maxflow_stats_t *st); +SEXP R_igraph_sirlist_to_SEXP(const igraph_vector_ptr_t *sl); +void R_igraph_sirlist_destroy(igraph_vector_ptr_t *sl); + +int R_igraph_SEXP_to_strvector(SEXP rval, igraph_strvector_t *sv); +int R_igraph_SEXP_to_strvector_copy(SEXP rval, igraph_strvector_t *sv); +int R_SEXP_to_vector(SEXP sv, igraph_vector_t *v); +int R_SEXP_to_vector_copy(SEXP sv, igraph_vector_t *v); +int R_SEXP_to_matrix(SEXP pakl, igraph_matrix_t *akl); +int R_SEXP_to_matrix_complex(SEXP pakl, igraph_matrix_complex_t *akl); +int R_SEXP_to_igraph_matrix_copy(SEXP pakl, igraph_matrix_t *akl); +int R_SEXP_to_igraph(SEXP graph, igraph_t *res); +int R_SEXP_to_igraph_copy(SEXP graph, igraph_t *res); +int R_SEXP_to_igraph_vs(SEXP rit, igraph_t *graph, igraph_vs_t *it); +int R_SEXP_to_igraph_es(SEXP rit, igraph_t *graph, igraph_es_t *it); +int R_SEXP_to_igraph_adjlist(SEXP vectorlist, igraph_adjlist_t *ptr); +int R_igraph_SEXP_to_0orvectorlist(SEXP vectorlist, + igraph_vector_ptr_t *ptr); +int R_igraph_SEXP_to_vectorlist(SEXP vectorlist, igraph_vector_ptr_t *ptr); +int R_igraph_SEXP_to_vectorlist_int(SEXP vectorlist, + igraph_vector_ptr_t *ptr); +int R_igraph_SEXP_to_matrixlist(SEXP matrixlist, igraph_vector_ptr_t *ptr); +int R_SEXP_to_vector_bool(SEXP sv, igraph_vector_bool_t *v); +int R_SEXP_to_vector_bool_copy(SEXP sv, igraph_vector_bool_t *v); +int R_SEXP_to_vector_int(SEXP sv, igraph_vector_int_t *v); +int R_SEXP_to_vector_long_copy(SEXP sv, igraph_vector_long_t *v); +int R_SEXP_to_hrg(SEXP shrg, igraph_hrg_t *hrg); +int R_SEXP_to_hrg_copy(SEXP shrg, igraph_hrg_t *hrg); +int R_SEXP_to_sparsemat(SEXP pakl, igraph_sparsemat_t *akl); + +SEXP R_igraph_i_lang7(SEXP s, SEXP t, SEXP u, SEXP v, SEXP w, SEXP x, SEXP y) +{ + PROTECT(s); + PROTECT(t); + PROTECT(u); + s = LCONS(s, LCONS(t, LCONS(u, list4(v, w, x, y)))); + UNPROTECT(3); + return s; +} + +/* get the list element named str, or return NULL */ +/* from the R Manual */ + +SEXP R_igraph_getListElement(SEXP list, const char *str) +{ + SEXP elmt = R_NilValue, names = getAttrib(list, R_NamesSymbol); + int i; + + for (i = 0; i < length(list); i++) + if(strcmp(CHAR(STRING_ELT(names, i)), str) == 0) { + elmt = VECTOR_ELT(list, i); + break; + } + return elmt; +} + +SEXP R_igraph_c2(SEXP x1, SEXP x2) { + SEXP cc = PROTECT(install("c")); + SEXP lc = PROTECT(lang3(cc, x1, x2)); + SEXP ret = EVAL(lc); + UNPROTECT(2); + return ret; +} + +/* evaluate an expression in a tryCatch() block to ensure that errors do not + * longjmp() back to the top level. Adapted from include/Rcpp/api/meat/Rcpp_eval.h + * in the Rcpp project */ + +typedef enum { + SAFEEVAL_OK = 0, + SAFEEVAL_ERROR = 1, + SAFEEVAL_INTERRUPTION = 2 +} R_igraph_safe_eval_result_t; + +R_igraph_safe_eval_result_t R_igraph_safe_eval_classify_result(SEXP result) { + if (Rf_inherits(result, "condition")) { + if (Rf_inherits(result, "error")) { + return SAFEEVAL_ERROR; + } else if (Rf_inherits(result, "interrupt")) { + return SAFEEVAL_INTERRUPTION; + } + } + + return SAFEEVAL_OK; +} + +SEXP R_igraph_safe_eval(SEXP expr_call, R_igraph_safe_eval_result_t* result) { + /* find `identity` function used to capture errors */ + SEXP identity = PROTECT(Rf_install("identity")); + SEXP identity_func = PROTECT(Rf_findFun(identity, R_BaseNamespace)); + if (identity_func == R_UnboundValue) { + Rf_error("Failed to find 'base::identity()'"); + } + + /* define the call -- enclose with `tryCatch` so we can record errors */ + SEXP try_catch = PROTECT(Rf_install("tryCatch")); + SEXP try_catch_call = PROTECT(Rf_lang4(try_catch, expr_call, identity_func, identity_func)); + + SET_TAG(CDDR(try_catch_call), Rf_install("error")); + SET_TAG(CDDR(CDR(try_catch_call)), Rf_install("interrupt")); + + /* execute the call */ + SEXP retval = PROTECT(Rf_eval(try_catch_call, R_GlobalEnv)); + + /* did we get an error or an interrupt? */ + if (result) { + *result = R_igraph_safe_eval_classify_result(retval); + } + + UNPROTECT(5); + + return retval; +} + +SEXP R_igraph_handle_safe_eval_result(SEXP result) { + switch (R_igraph_safe_eval_classify_result(result)) { + case SAFEEVAL_OK: + return result; + + case SAFEEVAL_ERROR: + /* extract the error message, call IGRAPH_FINALLY_FREE() and then throw + * the error. We cannot raise the error directly because that would + * longjmp() and could potentially overwrite stack-allocated data structures + * that are also in the "finally" stack */ + IGRAPH_FINALLY_FREE(); + + SEXP condition_message = PROTECT(Rf_install("conditionMessage")); + SEXP condition_message_call = PROTECT(Rf_lang2(condition_message, result)); + SEXP evaluated_condition_message = PROTECT(Rf_eval(condition_message_call, R_GlobalEnv)); + error(CHAR(STRING_ELT(evaluated_condition_message, 0))); + UNPROTECT(3); + return R_NilValue; + + case SAFEEVAL_INTERRUPTION: + IGRAPH_FINALLY_FREE(); + error("Interrupted by user"); + return R_NilValue; + + default: + error( + "Invalid object type returned from R_igraph_safe_eval(). This is a " + "bug; please report it to the developers." + ); + return R_NilValue; + } +} + +/****************************************************** + * Attributes * + *****************************************************/ + +SEXP R_igraph_get_attr_mode(SEXP graph, SEXP pwhich) { + int which=INTEGER(pwhich)[0]-1; + SEXP obj=VECTOR_ELT(VECTOR_ELT(graph, 8), which); + int i, len=GET_LENGTH(obj); + SEXP result; + + PROTECT(result=NEW_CHARACTER(len)); + for (i=0; iattr=result; + + /* Add graph attributes */ + attrno= attr==NULL ? 0 : igraph_vector_ptr_size(attr); + SET_VECTOR_ELT(result, 1, NEW_LIST(attrno)); + gal=VECTOR_ELT(result, 1); + PROTECT(names=NEW_CHARACTER(attrno)); px++; + for (i=0; iname)); + SET_VECTOR_ELT(gal, i, R_NilValue); + switch (rec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + vec=(igraph_vector_t*) rec->value; + if (igraph_vector_size(vec) > 0) { + SET_VECTOR_ELT(gal, i, NEW_NUMERIC(1)); + REAL(VECTOR_ELT(gal, i))[0]=VECTOR(*vec)[0]; + } + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + log=(igraph_vector_bool_t*) rec->value; + if (igraph_vector_bool_size(log) > 0) { + SET_VECTOR_ELT(gal, i, NEW_LOGICAL(1)); + LOGICAL(VECTOR_ELT(gal, i))[0]=VECTOR(*log)[0]; + } + break; + case IGRAPH_ATTRIBUTE_STRING: + strvec=(igraph_strvector_t*) rec->value; + if (igraph_strvector_size(strvec) > 0) { + SET_VECTOR_ELT(gal, i, NEW_CHARACTER(1)); + SET_STRING_ELT(VECTOR_ELT(gal,i), 0, mkChar(STR(*strvec, 0))); + } + break; + case IGRAPH_ATTRIBUTE_R_OBJECT: + UNPROTECT(px); + IGRAPH_ERROR("R_objects not implemented yet", IGRAPH_UNIMPLEMENTED); + break; + case IGRAPH_ATTRIBUTE_DEFAULT: + case IGRAPH_ATTRIBUTE_PY_OBJECT: + default: + UNPROTECT(px); + IGRAPH_ERROR("Unknown attribute type, this should not happen", + IGRAPH_EINTERNAL); + break; + } + } + SET_NAMES(gal, names); + + UNPROTECT(px); + return 0; +} + +void R_igraph_attribute_destroy(igraph_t *graph) { + SEXP attr=graph->attr; + REAL(VECTOR_ELT(attr, 0))[1] -= 1; /* refcount for igraph_t */ + if (!R_igraph_attribute_protected && + REAL(VECTOR_ELT(attr, 0))[1]==0 && + REAL(VECTOR_ELT(attr, 0))[2]==1) { + R_ReleaseObject(attr); + } + graph->attr=0; +} + +/* If not copying all three attribute kinds are requested, then + we don't refcount, but really copy the requested ones, because + 1) we can only refcount all three at the same time, and + 2) the not-copied attributes will be set up by subsequent calls + to permute_vertices and/or permute/edges anyway. */ + +int R_igraph_attribute_copy(igraph_t *to, const igraph_t *from, + igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea) { + SEXP fromattr=from->attr; + if (ga && va && ea) { + to->attr=from->attr; + REAL(VECTOR_ELT(fromattr, 0))[1] += 1; /* refcount only */ + if (!R_igraph_attribute_protected && + REAL(VECTOR_ELT(fromattr, 0))[1] == 1) { + R_PreserveObject(to->attr); + } + } else { + R_igraph_attribute_init(to,0); /* Sets up many things */ + SEXP toattr=to->attr; + if (ga) { + SET_VECTOR_ELT(toattr, 1, duplicate(VECTOR_ELT(fromattr, 1))); + } + if (va) { + SET_VECTOR_ELT(toattr, 2, duplicate(VECTOR_ELT(fromattr, 2))); + } + if (ea) { + SET_VECTOR_ELT(toattr, 3, duplicate(VECTOR_ELT(fromattr, 3))); + } + } + return 0; +} + +SEXP R_igraph_attribute_add_vertices_append1(igraph_vector_ptr_t *nattr, + int j, int nv) { + + SEXP app = R_NilValue; + igraph_attribute_record_t *tmprec=VECTOR(*nattr)[j-1]; + long int len = 0; + + switch (tmprec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + len = igraph_vector_size(tmprec->value); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + len = igraph_vector_bool_size(tmprec->value); + break; + case IGRAPH_ATTRIBUTE_STRING: + len = igraph_strvector_size(tmprec->value); + break; + case IGRAPH_ATTRIBUTE_R_OBJECT: + igraph_error("R objects not implemented yet", __FILE__, __LINE__, + IGRAPH_UNIMPLEMENTED); + return R_NilValue; + break; + default: + igraph_error("Unknown attribute type, internal error", __FILE__, __LINE__, + IGRAPH_EINVAL); + return R_NilValue; + break; + } + + if (len != nv) { + igraph_error("Invalid attribute length", __FILE__, __LINE__, + IGRAPH_EINVAL); + return R_NilValue; + } + + switch (tmprec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + PROTECT(app=NEW_NUMERIC(nv)); + igraph_vector_copy_to(tmprec->value, REAL(app)); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + PROTECT(app=R_igraph_vector_bool_to_SEXP(tmprec->value)); + break; + default: /* IGRAPH_ATTRIBUTE_STRING */ + PROTECT(app=R_igraph_strvector_to_SEXP(tmprec->value)); + break; + } + + UNPROTECT(1); + return app; +} + +void R_igraph_attribute_add_vertices_append(SEXP val, long int nv, + igraph_vector_ptr_t *nattr) { + SEXP names; + long int valno, i, nattrno; + SEXP rep = R_NilValue; + int px = 0; + + valno=GET_LENGTH(val); + names=PROTECT(GET_NAMES(val)); px++; + if (nattr==NULL) { + nattrno=0; + } else { + nattrno=igraph_vector_ptr_size(nattr); + } + + for (i=0; iname); + } + if (l) { + /* This attribute is present in nattr */ + SEXP app = PROTECT(R_igraph_attribute_add_vertices_append1(nattr, j, nv)); + SEXP newva = PROTECT(R_igraph_c2(oldva, app)); + SET_VECTOR_ELT(val, i, newva); + UNPROTECT(2); + } else { + /* No such attribute, append NA's */ + if (isNull(rep)) { + SEXP l1 = PROTECT(install("rep")); px++; + SEXP l2 = PROTECT(ScalarLogical(NA_LOGICAL)); px++; + SEXP l3 = PROTECT(ScalarInteger((int) nv)); px++; + SEXP l4 = PROTECT(lang3(l1, l2, l3)); px++; + PROTECT(rep=EVAL(l4)); px++; + } + PROTECT(newva=R_igraph_c2(oldva, rep)); + SET_VECTOR_ELT(val, i, newva); + UNPROTECT(1); + } + } + + UNPROTECT(px); +} + +SEXP R_igraph_attribute_add_vertices_dup(SEXP attr) { + SEXP newattr=duplicate(attr); + int px = 0; + + if (R_igraph_attribute_protected) { + PROTECT(newattr); px++; + } else { + R_PreserveObject(newattr); + } + + REAL(VECTOR_ELT(attr, 0))[1] -= 1; + if (!R_igraph_attribute_protected && + REAL(VECTOR_ELT(attr, 0))[1] == 0) { + R_ReleaseObject(attr); + } + REAL(VECTOR_ELT(newattr, 0))[0] = 0; + REAL(VECTOR_ELT(newattr, 0))[1] = 1; + if (R_igraph_attribute_protected) { + long int pos, alen=LENGTH(VECTOR_ELT(attr, 0)); + if (alen == 4) { + pos=REAL(VECTOR_ELT(attr, 0))[3]; + SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); + } else { + SEXP tmp=PROTECT(NEW_NUMERIC(4)); px++; + REAL(tmp)[0] = REAL(VECTOR_ELT(attr, 0))[0]; + REAL(tmp)[1] = REAL(VECTOR_ELT(attr, 0))[1]; + REAL(tmp)[2] = REAL(VECTOR_ELT(attr, 0))[2]; + pos = REAL(tmp)[3] = R_igraph_attribute_protected_size; + R_igraph_attribute_protected_size += 1; + SET_VECTOR_ELT(newattr, 0, tmp); + } + SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); + } + + UNPROTECT(px); + return newattr; +} + +int R_igraph_attribute_add_vertices(igraph_t *graph, long int nv, + igraph_vector_ptr_t *nattr) { + SEXP attr=graph->attr; + SEXP val, rep=0, names, newnames; + igraph_vector_t news; + long int valno, i, origlen, nattrno, newattrs; + int px = 0; + + if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { + SEXP newattr = PROTECT(R_igraph_attribute_add_vertices_dup(attr)); px++; + attr=graph->attr=newattr; + } + + val=VECTOR_ELT(attr, 2); + valno=GET_LENGTH(val); + names=PROTECT(GET_NAMES(val)); px++; + if (nattr==NULL) { + nattrno=0; + } else { + nattrno=igraph_vector_ptr_size(nattr); + } + origlen=igraph_vcount(graph)-nv; + + /* First add the new attributes, if any */ + newattrs=0; + if (igraph_vector_init(&news, 0)) error("Out of memory"); + IGRAPH_FINALLY(igraph_vector_destroy, &news); + for (i=0; iname; + long int j; + igraph_bool_t l=0; + for (j=0; !l && jname)); + } + PROTECT(newval=R_igraph_c2(val, app)); + PROTECT(newnames=R_igraph_c2(names, newnames)); + SET_NAMES(newval, newnames); + SET_VECTOR_ELT(attr, 2, newval); + val=VECTOR_ELT(attr, 2); + UNPROTECT(9); + } + igraph_vector_destroy(&news); + IGRAPH_FINALLY_CLEAN(1); /* news */ + + /* Now append the new values */ + R_igraph_attribute_add_vertices_append(val, nv, nattr); + + UNPROTECT(px); + + return 0; +} + +/* void R_igraph_attribute_delete_vertices(igraph_t *graph, */ +/* const igraph_vector_t *eidx, */ +/* const igraph_vector_t *vidx) { */ +/* SEXP attr=graph->attr; */ +/* SEXP eal, val; */ +/* long int valno, ealno, i; */ +/* if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { */ +/* SEXP newattr; */ +/* PROTECT(newattr=duplicate(attr)); */ +/* REAL(VECTOR_ELT(attr, 0))[1] -= 1; */ +/* if (REAL(VECTOR_ELT(attr, 0))[1] == 0) { */ +/* R_ReleaseObject(attr); */ +/* } */ +/* REAL(VECTOR_ELT(newattr, 0))[0] = 0; */ +/* REAL(VECTOR_ELT(newattr, 0))[1] = 1; */ +/* attr=graph->attr=newattr; */ +/* } */ + +/* /\* Vertices *\/ */ +/* val=VECTOR_ELT(attr, 2); */ +/* valno=GET_LENGTH(val); */ +/* for (i=0; i 0) { */ +/* newlen++; */ +/* } */ +/* } */ +/* PROTECT(ss=NEW_NUMERIC(newlen)); */ +/* for (j=0; j0) { */ +/* REAL(ss)[(long int)VECTOR(*vidx)[j]-1]=j+1; */ +/* } */ +/* } */ +/* PROTECT(newva=EVAL(lang3(install("["), oldva, ss))); */ +/* SET_VECTOR_ELT(val, i, newva); */ +/* UNPROTECT(2); */ +/* } */ + +/* /\* Edges *\/ */ +/* eal=VECTOR_ELT(attr, 3); */ +/* ealno=GET_LENGTH(eal); */ +/* for (i=0; i 0) { */ +/* newlen++; */ +/* } */ +/* } */ +/* PROTECT(ss=NEW_NUMERIC(newlen)); */ +/* for (j=0; j0) { */ +/* REAL(ss)[(long int)VECTOR(*eidx)[j]-1]=j+1; */ +/* } */ +/* } */ +/* PROTECT(newea=EVAL(lang3(install("["), oldea, ss))); */ +/* SET_VECTOR_ELT(eal, i, newea); */ +/* UNPROTECT(2); */ +/* } */ +/* } */ + +int R_igraph_attribute_permute_vertices_same(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx) { + SEXP attr=newgraph->attr; + SEXP val; + long int i, valno; + long int idxlen=igraph_vector_size(idx); + SEXP ss; + int px = 0; + + /* We copy if we need to */ + if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { + SEXP newattr = duplicate(attr); + if (R_igraph_attribute_protected) { + PROTECT(newattr); px++; + } else { + R_PreserveObject(newattr); + } + REAL(VECTOR_ELT(attr, 0))[1] -= 1; + if (!R_igraph_attribute_protected && + REAL(VECTOR_ELT(attr, 0))[1] == 0) { + R_ReleaseObject(attr); + } + REAL(VECTOR_ELT(newattr, 0))[0] = 0; + REAL(VECTOR_ELT(newattr, 0))[1] = 1; + if (R_igraph_attribute_protected) { + long int pos, alen=LENGTH(VECTOR_ELT(attr, 0)); + if (alen == 4) { + pos=REAL(VECTOR_ELT(attr, 0))[3]; + SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); + } else { + SEXP tmp=PROTECT(NEW_NUMERIC(4)); px++; + REAL(tmp)[0] = REAL(VECTOR_ELT(attr, 0))[0]; + REAL(tmp)[1] = REAL(VECTOR_ELT(attr, 0))[1]; + REAL(tmp)[2] = REAL(VECTOR_ELT(attr, 0))[2]; + pos = REAL(tmp)[3] = R_igraph_attribute_protected_size; + R_igraph_attribute_protected_size += 1; + SET_VECTOR_ELT(newattr, 0, tmp); + } + SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); + } + attr=newgraph->attr=newattr; + } + + val=VECTOR_ELT(attr,2); + valno=GET_LENGTH(val); + + /* If we have no vertex attributes, then we don't need to do anything */ + if (valno==0) { UNPROTECT(px); return 0; } + + /* Convert idx to an R object, we will use this for indexing */ + PROTECT(ss=NEW_INTEGER(idxlen)); px++; + for (i=0; iattr; + SEXP toattr=newgraph->attr; + SEXP val, toval; + SEXP names; + long int i, valno; + long int idxlen=igraph_vector_size(idx); + SEXP ss; + int px = 0; + + val=VECTOR_ELT(attr,2); + valno=GET_LENGTH(val); + + /* If we have no vertex attributes, then we don't need to do anything */ + if (valno==0) { return 0; } + + /* Convert idx to an R object, we will use this for indexing */ + PROTECT(ss=NEW_INTEGER(idxlen)); px++; + for (i=0; itype) { + case IGRAPH_ATTRIBUTE_NUMERIC: + len = igraph_vector_size(tmprec->value); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + len = igraph_vector_bool_size(tmprec->value); + break; + case IGRAPH_ATTRIBUTE_STRING: + len = igraph_strvector_size(tmprec->value); + break; + case IGRAPH_ATTRIBUTE_R_OBJECT: + igraph_error("R objects not implemented yet", __FILE__, __LINE__, + IGRAPH_UNIMPLEMENTED); + return R_NilValue; + break; + default: + igraph_error("Unknown attribute type, internal error", __FILE__, __LINE__, + IGRAPH_EINVAL); + return R_NilValue; + break; + } + + if (len != ne) { + igraph_error("Invalid attribute length", __FILE__, __LINE__, + IGRAPH_EINVAL); + return R_NilValue; + } + + switch (tmprec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + PROTECT(app=NEW_NUMERIC(ne)); + igraph_vector_copy_to(tmprec->value, REAL(app)); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + PROTECT(app=R_igraph_vector_bool_to_SEXP(tmprec->value)); + break; + default: /* IGRAPH_ATTRIBUTE_STRING */ + PROTECT(app=R_igraph_strvector_to_SEXP(tmprec->value)); + break; + } + + UNPROTECT(1); + return app; +} + +void R_igraph_attribute_add_edges_append(SEXP eal, + const igraph_vector_t *edges, + igraph_vector_ptr_t *nattr) { + SEXP names; + long int ealno, i; + long int ne=igraph_vector_size(edges)/2, nattrno; + SEXP rep = R_NilValue; + int px = 0; + + ealno=GET_LENGTH(eal); + names=PROTECT(GET_NAMES(eal)); px++; + if (nattr==NULL) { + nattrno=0; + } else { + nattrno=igraph_vector_ptr_size(nattr); + } + + for (i=0; iname); + } + if (l) { + /* This attribute is present in nattr */ + SEXP app = PROTECT(R_igraph_attribute_add_edges_append1(nattr, j, ne)); + SEXP newea = PROTECT(R_igraph_c2(oldea, app)); + SET_VECTOR_ELT(eal, i, newea); + UNPROTECT(2); + } else { + /* No such attribute, append NA's */ + if (isNull(rep)) { + SEXP l1 = PROTECT(install("rep")); px++; + SEXP l2 = PROTECT(ScalarLogical(NA_LOGICAL)); px++; + SEXP l3 = PROTECT(ScalarInteger((int) ne)); px++; + SEXP l4 = PROTECT(lang3(l1, l2, l3)); px++; + PROTECT(rep = EVAL(l4)); px++; + } + SEXP newea = PROTECT(R_igraph_c2(oldea, rep)); + SET_VECTOR_ELT(eal, i, newea); + UNPROTECT(1); + } + } + + UNPROTECT(px); +} + +int R_igraph_attribute_add_edges(igraph_t *graph, + const igraph_vector_t *edges, + igraph_vector_ptr_t *nattr) { + SEXP attr=graph->attr; + SEXP eal, names, newnames; + igraph_vector_t news; + long int ealno, i, origlen, nattrno, newattrs; + long int ne=igraph_vector_size(edges)/2; + int px = 0; + + if (igraph_vector_init(&news, 0)) error("Out of memory"); + IGRAPH_FINALLY(igraph_vector_destroy, &news); + + if (REAL(VECTOR_ELT(attr, 0))[0] + REAL(VECTOR_ELT(attr, 0))[1] > 1) { + SEXP newattr = PROTECT(R_igraph_attribute_add_edges_dup(attr)); px++; + attr=graph->attr=newattr; + } + + eal=VECTOR_ELT(attr, 3); + ealno=GET_LENGTH(eal); + names=PROTECT(GET_NAMES(eal)); px++; + if (nattr==NULL) { + nattrno=0; + } else { + nattrno=igraph_vector_ptr_size(nattr); + } + origlen=igraph_ecount(graph)-ne; + + /* First add the new attributes, if any */ + newattrs=0; + for (i=0; iname; + long int j; + igraph_bool_t l=0; + for (j=0; !l && jname)); + } + PROTECT(neweal=R_igraph_c2(eal, app)); + PROTECT(newnames=R_igraph_c2(names, newnames)); + SET_NAMES(neweal, newnames); + SET_VECTOR_ELT(attr, 3, neweal); + eal=VECTOR_ELT(attr, 3); + UNPROTECT(9); + } + igraph_vector_destroy(&news); + IGRAPH_FINALLY_CLEAN(1); + + /* Now append the new values */ + R_igraph_attribute_add_edges_append(eal, edges, nattr); + + UNPROTECT(px); + + return 0; +} + +/* void R_igraph_attribute_delete_edges(igraph_t *graph, */ +/* const igraph_vector_t *idx) { */ +/* SEXP attr=graph->attr; */ +/* SEXP eal; */ +/* long int ealno, i; */ +/* if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { */ +/* SEXP newattr; */ +/* PROTECT(newattr=duplicate(attr)); */ +/* REAL(VECTOR_ELT(attr, 0))[1] -= 1; */ +/* if (REAL(VECTOR_ELT(attr, 0))[1] == 0) { */ +/* R_ReleaseObject(attr); */ +/* } */ +/* REAL(VECTOR_ELT(newattr, 0))[0] = 0; */ +/* REAL(VECTOR_ELT(newattr, 0))[1] = 1; */ +/* attr=graph->attr=newattr; */ +/* } */ + +/* eal=VECTOR_ELT(attr, 3); */ +/* ealno=GET_LENGTH(eal); */ +/* for (i=0; i 0) { */ +/* newlen++; */ +/* } */ +/* } */ +/* PROTECT(ss=NEW_NUMERIC(newlen)); */ +/* for (j=0; j 0) { */ +/* REAL(ss)[(long int)VECTOR(*idx)[j]-1] = j+1; */ +/* } */ +/* } */ +/* PROTECT(newea=EVAL(lang3(install("["), oldea, ss))); */ +/* SET_VECTOR_ELT(eal, i, newea); */ +/* UNPROTECT(2); */ +/* } */ +/* } */ + +int R_igraph_attribute_permute_edges_same(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx) { + + SEXP attr=newgraph->attr; + SEXP eal; + long int i, ealno; + long int idxlen=igraph_vector_size(idx); + SEXP ss; + int px = 0; + + /* We copy if we need to */ + if (REAL(VECTOR_ELT(attr, 0))[0]+REAL(VECTOR_ELT(attr, 0))[1] > 1) { + SEXP newattr=duplicate(attr); + if (R_igraph_attribute_protected) { + PROTECT(newattr); px++; + } else { + R_PreserveObject(newattr); + } + REAL(VECTOR_ELT(attr, 0))[1] -= 1; + if (!R_igraph_attribute_protected && + REAL(VECTOR_ELT(attr, 0))[1] == 0) { + R_ReleaseObject(attr); + } + REAL(VECTOR_ELT(newattr, 0))[0] = 0; + REAL(VECTOR_ELT(newattr, 0))[1] = 1; + if (R_igraph_attribute_protected) { + long int pos, alen=LENGTH(VECTOR_ELT(attr, 0)); + if (alen == 4) { + pos=REAL(VECTOR_ELT(attr, 0))[3]; + SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); + } else { + SEXP tmp=PROTECT(NEW_NUMERIC(4)); px++; + REAL(tmp)[0] = REAL(VECTOR_ELT(attr, 0))[0]; + REAL(tmp)[1] = REAL(VECTOR_ELT(attr, 0))[1]; + REAL(tmp)[2] = REAL(VECTOR_ELT(attr, 0))[2]; + pos = REAL(tmp)[3] = R_igraph_attribute_protected_size; + R_igraph_attribute_protected_size += 1; + SET_VECTOR_ELT(newattr, 0, tmp); + } + SET_VECTOR_ELT(R_igraph_attribute_protected, pos, newattr); + } + attr=newgraph->attr=newattr; + } + + eal=VECTOR_ELT(attr,3); + ealno=GET_LENGTH(eal); + + /* If we have no edge attributes, then we don't need to do anything */ + if (ealno==0) { UNPROTECT(px); return 0; } + + /* Convert idx to an R object, we will use this for indexing */ + PROTECT(ss=NEW_INTEGER(idxlen)); px++; + for (i=0; iattr; + SEXP toattr=newgraph->attr; + SEXP eal, toeal; + SEXP names; + long int i, ealno; + long int idxlen=igraph_vector_size(idx); + SEXP ss; + + eal=VECTOR_ELT(attr,3); + ealno=GET_LENGTH(eal); + + /* If we have no vertex attributes, then we don't need to do anything */ + if (ealno==0) { return 0; } + + /* Convert idx to an R object, we will use this for indexing */ + PROTECT(ss=NEW_INTEGER(idxlen)); + for (i=0; iattr; + + for (i=0; i<3; i++) { + igraph_strvector_t *n=names[i]; + igraph_vector_t *t=types[i]; + SEXP al=VECTOR_ELT(attr, i+1); + + if (n) { /* return names */ + SEXP names = PROTECT(GET_NAMES(al)); + R_igraph_SEXP_to_strvector_copy(names, n); + UNPROTECT(1); + } + + if (t) { /* return types */ + igraph_vector_resize(t, GET_LENGTH(al)); + for (j=0; jattr, attrnum), name); + return res != R_NilValue; +} + +int R_igraph_attribute_gettype(const igraph_t *graph, + igraph_attribute_type_t *type, + igraph_attribute_elemtype_t elemtype, + const char *name) { + long int attrnum; + SEXP res; + + switch (elemtype) { + case IGRAPH_ATTRIBUTE_GRAPH: + attrnum=1; + break; + case IGRAPH_ATTRIBUTE_VERTEX: + attrnum=2; + break; + case IGRAPH_ATTRIBUTE_EDGE: + attrnum=3; + break; + default: + IGRAPH_ERROR("Unkwown attribute element type", IGRAPH_EINVAL); + break; + } + + res=R_igraph_getListElement(VECTOR_ELT(graph->attr, attrnum), name); + if (IS_NUMERIC(res) || IS_INTEGER(res)) { + *type=IGRAPH_ATTRIBUTE_NUMERIC; + } else if (IS_LOGICAL(res)) { + *type=IGRAPH_ATTRIBUTE_BOOLEAN; + } else if (IS_CHARACTER(res)) { + *type=IGRAPH_ATTRIBUTE_STRING; + } else { + *type=IGRAPH_ATTRIBUTE_R_OBJECT; + } + return 0; +} + +int R_igraph_attribute_get_numeric_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_t *value) { + SEXP gal=VECTOR_ELT(graph->attr, 1); + SEXP ga=R_igraph_getListElement(gal, name); + + if (ga == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_NUMERIC(ga) && !IS_INTEGER(ga)) { + IGRAPH_ERROR("Attribute not numeric", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_resize(value, 1)); + if (IS_NUMERIC(ga)) { + VECTOR(*value)[0]=REAL(ga)[0]; + } else { /* INTEGER */ + VECTOR(*value)[0]=INTEGER(ga)[0]; + } + + return 0; +} + +int R_igraph_attribute_get_bool_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_bool_t *value) { + SEXP gal=VECTOR_ELT(graph->attr, 1); + SEXP ga=R_igraph_getListElement(gal, name); + + if (ga == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_LOGICAL(ga)) { + IGRAPH_ERROR("Attribute not logical", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_vector_bool_resize(value, 1)); + VECTOR(*value)[0]=LOGICAL(ga)[0]; + + return 0; +} + +int R_igraph_attribute_get_string_graph_attr(const igraph_t *graph, + const char *name, + igraph_strvector_t *value) { + /* TODO: serialization */ + SEXP gal=VECTOR_ELT(graph->attr, 1); + SEXP ga=R_igraph_getListElement(gal, name); + + if (ga == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_CHARACTER(ga)) { + IGRAPH_ERROR("Attribute is not character", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_strvector_resize(value, 1)); + IGRAPH_CHECK(igraph_strvector_set(value, 0, CHAR(STRING_ELT(ga, 0)))); + + return 0; +} + +int R_igraph_attribute_get_numeric_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_t *value) { + /* TODO: serialization */ + SEXP val=VECTOR_ELT(graph->attr, 2); + SEXP va=R_igraph_getListElement(val, name); + igraph_vector_t newvalue; + + if (va == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_NUMERIC(va) && !IS_INTEGER(va)) { + IGRAPH_ERROR("Attribute not numeric", IGRAPH_EINVAL); + } + + if (igraph_vs_is_all(&vs)) { + R_SEXP_to_vector_copy(AS_NUMERIC(va), &newvalue); + igraph_vector_destroy(value); + *value=newvalue; + } else { + igraph_vit_t it; + long int i=0; + IGRAPH_CHECK(igraph_vit_create(graph, vs, &it)); + IGRAPH_FINALLY(igraph_vit_destroy, &it); + IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_VIT_SIZE(it))); + if (IS_NUMERIC(va)) { + while (!IGRAPH_VIT_END(it)) { + long int v=IGRAPH_VIT_GET(it); + VECTOR(*value)[i]=REAL(va)[v]; + IGRAPH_VIT_NEXT(it); + i++; + } + } else if (IS_INTEGER(va)) { + while (!IGRAPH_VIT_END(it)) { + long int v=IGRAPH_VIT_GET(it); + VECTOR(*value)[i]=INTEGER(va)[v]; + IGRAPH_VIT_NEXT(it); + i++; + } + } + igraph_vit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +int R_igraph_attribute_get_bool_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_bool_t *value) { + /* TODO: serialization */ + SEXP val=VECTOR_ELT(graph->attr, 2); + SEXP va=R_igraph_getListElement(val, name); + igraph_vector_bool_t newvalue; + + if (va == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_LOGICAL(va)) { + IGRAPH_ERROR("Attribute not logical", IGRAPH_EINVAL); + } + + if (igraph_vs_is_all(&vs)) { + R_SEXP_to_vector_bool_copy(va, &newvalue); + igraph_vector_bool_destroy(value); + *value=newvalue; + } else { + igraph_vit_t it; + long int i=0; + IGRAPH_CHECK(igraph_vit_create(graph, vs, &it)); + IGRAPH_FINALLY(igraph_vit_destroy, &it); + IGRAPH_CHECK(igraph_vector_bool_resize(value, IGRAPH_VIT_SIZE(it))); + while (!IGRAPH_VIT_END(it)) { + long int v=IGRAPH_VIT_GET(it); + VECTOR(*value)[i]=LOGICAL(va)[v]; + IGRAPH_VIT_NEXT(it); + i++; + } + igraph_vit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +int R_igraph_attribute_get_string_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_strvector_t *value) { + /* TODO: serialization */ + SEXP val, va; + + val=VECTOR_ELT(graph->attr, 2); + va=R_igraph_getListElement(val, name); + if (va == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_CHARACTER(va)) { + IGRAPH_ERROR("Attribute is not character", IGRAPH_EINVAL); + } + + if (igraph_vs_is_all(&vs)) { + R_igraph_SEXP_to_strvector_copy(va, value); + } else { + igraph_vit_t it; + long int i=0; + IGRAPH_CHECK(igraph_vit_create(graph, vs, &it)); + IGRAPH_FINALLY(igraph_vit_destroy, &it); + IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_VIT_SIZE(it))); + while (!IGRAPH_VIT_END(it)) { + long int v=IGRAPH_VIT_GET(it); + const char *str=CHAR(STRING_ELT(va, v)); + IGRAPH_CHECK(igraph_strvector_set(value, i, str)); + IGRAPH_VIT_NEXT(it); + i++; + } + igraph_vit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +int R_igraph_attribute_get_numeric_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_t *value) { + /* TODO: serialization */ + SEXP eal=VECTOR_ELT(graph->attr, 3); + SEXP ea=R_igraph_getListElement(eal, name); + igraph_vector_t newvalue; + + if (ea == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_NUMERIC(ea) && !IS_INTEGER(ea)) { + IGRAPH_ERROR("Attribute is not numeric", IGRAPH_EINVAL); + } + + if (igraph_es_is_all(&es)) { + R_SEXP_to_vector_copy(AS_NUMERIC(ea), &newvalue); + igraph_vector_destroy(value); + *value=newvalue; + } else { + igraph_eit_t it; + long int i=0; + IGRAPH_CHECK(igraph_eit_create(graph, es, &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_EIT_SIZE(it))); + if (IS_NUMERIC(ea)) { + while (!IGRAPH_EIT_END(it)) { + long int e=IGRAPH_EIT_GET(it); + VECTOR(*value)[i]=REAL(ea)[e]; + IGRAPH_EIT_NEXT(it); + i++; + } + } else { /* INTEGER */ + while (!IGRAPH_EIT_END(it)) { + long int e=IGRAPH_EIT_GET(it); + VECTOR(*value)[i]=INTEGER(ea)[e]; + IGRAPH_EIT_NEXT(it); + i++; + } + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +int R_igraph_attribute_get_bool_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_bool_t *value) { + /* TODO: serialization */ + SEXP eal=VECTOR_ELT(graph->attr, 3); + SEXP ea=R_igraph_getListElement(eal, name); + igraph_vector_bool_t newvalue; + + if (ea == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_LOGICAL(ea)) { + IGRAPH_ERROR("Attribute not logical", IGRAPH_EINVAL); + } + + if (igraph_es_is_all(&es)) { + R_SEXP_to_vector_bool_copy(ea, &newvalue); + igraph_vector_bool_destroy(value); + *value=newvalue; + } else { + igraph_eit_t it; + long int i=0; + IGRAPH_CHECK(igraph_eit_create(graph, es, &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + IGRAPH_CHECK(igraph_vector_bool_resize(value, IGRAPH_EIT_SIZE(it))); + while (!IGRAPH_EIT_END(it)) { + long int e=IGRAPH_EIT_GET(it); + VECTOR(*value)[i]=LOGICAL(ea)[e]; + IGRAPH_EIT_NEXT(it); + i++; + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +int R_igraph_attribute_get_string_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_strvector_t *value) { + /* TODO: serialization */ + SEXP eal=VECTOR_ELT(graph->attr, 3); + SEXP ea=R_igraph_getListElement(eal, name); + + if (ea == R_NilValue) { + IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL); + } + if (!IS_CHARACTER(ea)) { + IGRAPH_ERROR("Attribute is not character", IGRAPH_EINVAL); + } + + if (igraph_es_is_all(&es)) { + R_igraph_SEXP_to_strvector_copy(ea, value); + } else { + igraph_eit_t it; + long int i=0; + IGRAPH_CHECK(igraph_eit_create(graph, es, &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_EIT_SIZE(it))); + while (!IGRAPH_EIT_END(it)) { + long int e=IGRAPH_EIT_GET(it); + const char *str=CHAR(STRING_ELT(ea, e)); + IGRAPH_CHECK(igraph_strvector_set(value, i, str)); + IGRAPH_EIT_NEXT(it); + i++; + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +SEXP R_igraph_ac_sum_numeric(SEXP attr, + const igraph_vector_ptr_t *merges) { + SEXP res; + SEXP attr2; + long int i, len=igraph_vector_ptr_size(merges); + + PROTECT(attr2=AS_NUMERIC(attr)); + PROTECT(res=NEW_NUMERIC(len)); + + for (i=0; i 0 ? REAL(attr2)[(long) VECTOR(*v)[0] ] : NA_REAL; + for (j=1; j 0 ? REAL(attr2)[(long) VECTOR(*v)[0] ] : NA_REAL; + for (j=1; j m) { + m=val; + } + } + REAL(res)[i] = m; + } + + UNPROTECT(2); + return res; +} + +SEXP R_igraph_ac_random_numeric(SEXP attr, + const igraph_vector_ptr_t *merges) { + SEXP res; + SEXP attr2; + long int i, len=igraph_vector_ptr_size(merges); + + PROTECT(attr2=AS_NUMERIC(attr)); + PROTECT(res=NEW_NUMERIC(len)); + + RNG_BEGIN(); + + for (i=0; i0 ? 0.0 : NA_REAL; + for (j=0; j0) { s=s/n; } + REAL(res)[i] = s; + } + + UNPROTECT(2); + return res; +} + +SEXP R_igraph_ac_median_numeric(SEXP attr, + const igraph_vector_ptr_t *merges) { + SEXP res; + SEXP attr2; + long int i, len=igraph_vector_ptr_size(merges); + + PROTECT(attr2=AS_NUMERIC(attr)); + PROTECT(res=NEW_NUMERIC(len)); + + for (i=0; iattr; + SEXP toattr=newgraph->attr; + SEXP val=VECTOR_ELT(attr, 2); + long int i, j, valno=GET_LENGTH(val); + SEXP names, newnames; + SEXP res; + int keepno=0; + int *TODO; + igraph_function_pointer_t *funcs; + int px = 0; + + /* Create the TODO list first */ + PROTECT(names=GET_NAMES(val)); px++; + TODO=igraph_Calloc(valno, int); + if (!TODO) { + UNPROTECT(px); + IGRAPH_ERROR("Cannot combine edge attributes", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, TODO); + funcs=igraph_Calloc(valno, igraph_function_pointer_t); + if (!funcs) { + UNPROTECT(px); + IGRAPH_ERROR("Cannot combine edge attributes", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, funcs); + for (i=0; iattr; + SEXP toattr=newgraph->attr; + SEXP eal=VECTOR_ELT(attr, 3); + long int i, j, ealno=GET_LENGTH(eal); + SEXP names, newnames; + SEXP res; + int keepno=0; + int *TODO; + igraph_function_pointer_t *funcs; + int px = 0; + + /* Create the TODO list first */ + PROTECT(names=GET_NAMES(eal)); px++; + TODO=igraph_Calloc(ealno, int); + if (!TODO) { + UNPROTECT(px); + IGRAPH_ERROR("Cannot combine edge attributes", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, TODO); + funcs=igraph_Calloc(ealno, igraph_function_pointer_t); + if (!funcs) { + UNPROTECT(px); + IGRAPH_ERROR("Cannot combine edge attributes", + IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, funcs); + for (i=0; idata, REAL(result)); + PROTECT(dim=NEW_INTEGER(3)); + INTEGER(dim)[0]=(int) igraph_array3_n(a, 1); + INTEGER(dim)[1]=(int) igraph_array3_n(a, 2); + INTEGER(dim)[2]=(int) igraph_array3_n(a, 3); + SET_DIM(result, dim); + + UNPROTECT(2); + return result; +} + +SEXP R_igraph_0orarray3_to_SEXP(const igraph_array3_t *a) { + SEXP result; + if (a) { + PROTECT(result=R_igraph_array3_to_SEXP(a)); + } else { + PROTECT(result=R_NilValue); + } + UNPROTECT(1); + return result; +} + +SEXP R_igraph_strvector_to_SEXP(const igraph_strvector_t *m) { + SEXP result; + long int i; + char *str; + long int len; + + len=igraph_strvector_size(m); + PROTECT(result=NEW_CHARACTER(len)); + for (i=0; idirected; + memcpy(REAL(VECTOR_ELT(result, 2)), graph->from.stor_begin, + sizeof(igraph_real_t)*(size_t) no_of_edges); + memcpy(REAL(VECTOR_ELT(result, 3)), graph->to.stor_begin, + sizeof(igraph_real_t)*(size_t) no_of_edges); + memcpy(REAL(VECTOR_ELT(result, 4)), graph->oi.stor_begin, + sizeof(igraph_real_t)*(size_t) no_of_edges); + memcpy(REAL(VECTOR_ELT(result, 5)), graph->ii.stor_begin, + sizeof(igraph_real_t)*(size_t) no_of_edges); + memcpy(REAL(VECTOR_ELT(result, 6)), graph->os.stor_begin, + sizeof(igraph_real_t)*(size_t) (no_of_nodes+1)); + memcpy(REAL(VECTOR_ELT(result, 7)), graph->is.stor_begin, + sizeof(igraph_real_t)*(size_t) (no_of_nodes+1)); + + SET_CLASS(result, ScalarString(CREATE_STRING_VECTOR("igraph"))); + + /* Attributes */ + SET_VECTOR_ELT(result, 8, graph->attr); + REAL(VECTOR_ELT(graph->attr, 0))[0] += 1; + + /* Environment for vertex/edge seqs */ + SET_VECTOR_ELT(result, 9, R_NilValue); + R_igraph_add_env(result); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_vectorlist_to_SEXP(const igraph_vector_ptr_t *ptr) { + SEXP result; + long int i, n=igraph_vector_ptr_size(ptr); + + PROTECT(result=NEW_LIST(n)); + for (i=0; ileft)); + SET_VECTOR_ELT(result, 1, R_igraph_vector_to_SEXP(&hrg->right)); + SET_VECTOR_ELT(result, 2, R_igraph_vector_to_SEXP(&hrg->prob)); + SET_VECTOR_ELT(result, 3, R_igraph_vector_to_SEXP(&hrg->edges)); + SET_VECTOR_ELT(result, 4, R_igraph_vector_to_SEXP(&hrg->vertices)); + + PROTECT(names=NEW_CHARACTER(5)); + SET_STRING_ELT(names, 0, mkChar("left")); + SET_STRING_ELT(names, 1, mkChar("right")); + SET_STRING_ELT(names, 2, mkChar("prob")); + SET_STRING_ELT(names, 3, mkChar("edges")); + SET_STRING_ELT(names, 4, mkChar("vertices")); + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +int R_SEXP_to_hrg(SEXP shrg, igraph_hrg_t *hrg) { + R_SEXP_to_vector(VECTOR_ELT(shrg, 0), &hrg->left); + R_SEXP_to_vector(VECTOR_ELT(shrg, 1), &hrg->right); + R_SEXP_to_vector(VECTOR_ELT(shrg, 2), &hrg->prob); + R_SEXP_to_vector(VECTOR_ELT(shrg, 3), &hrg->edges); + R_SEXP_to_vector(VECTOR_ELT(shrg, 4), &hrg->vertices); + return 0; +} + +int R_SEXP_to_hrg_copy(SEXP shrg, igraph_hrg_t *hrg) { + R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 0), &hrg->left); + R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 1), &hrg->right); + R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 2), &hrg->prob); + R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 3), &hrg->edges); + R_SEXP_to_vector_copy(VECTOR_ELT(shrg, 4), &hrg->vertices); + return 0; +} + +SEXP R_igraph_plfit_result_to_SEXP(const igraph_plfit_result_t *plfit) { + SEXP result, names; + + PROTECT(result=NEW_LIST(6)); + SET_VECTOR_ELT(result, 0, ScalarLogical(plfit->continuous)); + SET_VECTOR_ELT(result, 1, ScalarReal(plfit->alpha)); + SET_VECTOR_ELT(result, 2, ScalarReal(plfit->xmin)); + SET_VECTOR_ELT(result, 3, ScalarReal(plfit->L)); + SET_VECTOR_ELT(result, 4, ScalarReal(plfit->D)); + SET_VECTOR_ELT(result, 5, ScalarReal(plfit->p)); + + PROTECT(names=NEW_CHARACTER(6)); + SET_STRING_ELT(names, 0, mkChar("continuous")); + SET_STRING_ELT(names, 1, mkChar("alpha")); + SET_STRING_ELT(names, 2, mkChar("xmin")); + SET_STRING_ELT(names, 3, mkChar("logLik")); + SET_STRING_ELT(names, 4, mkChar("KS.stat")); + SET_STRING_ELT(names, 5, mkChar("KS.p")); + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +SEXP R_igraph_maxflow_stats_to_SEXP(const igraph_maxflow_stats_t *st) { + SEXP result, names; + + PROTECT(result=NEW_LIST(5)); + SET_VECTOR_ELT(result, 0, ScalarInteger(st->nopush)); + SET_VECTOR_ELT(result, 1, ScalarInteger(st->norelabel)); + SET_VECTOR_ELT(result, 2, ScalarInteger(st->nogap)); + SET_VECTOR_ELT(result, 3, ScalarInteger(st->nogapnodes)); + SET_VECTOR_ELT(result, 4, ScalarInteger(st->nobfs)); + + PROTECT(names=NEW_CHARACTER(5)); + SET_STRING_ELT(names, 0, mkChar("nopush")); + SET_STRING_ELT(names, 1, mkChar("norelabel")); + SET_STRING_ELT(names, 2, mkChar("nogap")); + SET_STRING_ELT(names, 3, mkChar("nogapnodes")); + SET_STRING_ELT(names, 4, mkChar("nobfs")); + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +SEXP R_igraph_sirlist_to_SEXP(const igraph_vector_ptr_t *sl) { + SEXP result, names; + int i, n=igraph_vector_ptr_size(sl); + PROTECT(result=NEW_LIST(n)); + + PROTECT(names=NEW_CHARACTER(4)); + SET_STRING_ELT(names, 0, mkChar("times")); + SET_STRING_ELT(names, 1, mkChar("NS")); + SET_STRING_ELT(names, 2, mkChar("NI")); + SET_STRING_ELT(names, 3, mkChar("NR")); + + for (i=0; itimes)); + SET_VECTOR_ELT(tmp, 1, R_igraph_vector_int_to_SEXP(&sir->no_s)); + SET_VECTOR_ELT(tmp, 2, R_igraph_vector_int_to_SEXP(&sir->no_i)); + SET_VECTOR_ELT(tmp, 3, R_igraph_vector_int_to_SEXP(&sir->no_r)); + SET_VECTOR_ELT(result, i, tmp); + SET_NAMES(tmp, names); + UNPROTECT(1); + } + + UNPROTECT(2); + return result; +} + +void R_igraph_sirlist_destroy(igraph_vector_ptr_t *sl) { + int i, n=igraph_vector_ptr_size(sl); + for (i=0; itimes); + igraph_vector_int_destroy(&sir->no_s); + igraph_vector_int_destroy(&sir->no_i); + igraph_vector_int_destroy(&sir->no_r); + igraph_free(sir); + } + igraph_vector_ptr_destroy(sl); +} + +int R_SEXP_to_sparsemat(SEXP pakl, igraph_sparsemat_t *akl) { + SEXP Dim=GET_SLOT(pakl, install("Dim")); + SEXP i=GET_SLOT(pakl, install("i")); + SEXP p=GET_SLOT(pakl, install("p")); + SEXP x=GET_SLOT(pakl, install("x")); + + igraph_sparsemat_view(akl, /*nzmax=*/ GET_LENGTH(x), + /*m=*/ INTEGER(Dim)[0], /*n=*/ INTEGER(Dim)[1], + /*p=*/ INTEGER(p), /*i=*/ INTEGER(i), + /*x=*/ REAL(x), /*nz=*/ -1); + + return 0; +} + +SEXP R_igraph_sparsemat_to_SEXP_triplet(const igraph_sparsemat_t *sp) { + SEXP res, names; + int nz=igraph_sparsemat_nonzero_storage(sp); + + PROTECT(res=NEW_LIST(5)); + SET_VECTOR_ELT(res, 0, ScalarString(CREATE_STRING_VECTOR("triplet"))); + SET_VECTOR_ELT(res, 1, NEW_INTEGER(2)); + INTEGER(VECTOR_ELT(res, 1))[0] = (int) igraph_sparsemat_nrow(sp); + INTEGER(VECTOR_ELT(res, 1))[1] = (int) igraph_sparsemat_ncol(sp); + SET_VECTOR_ELT(res, 2, NEW_INTEGER(nz)); + SET_VECTOR_ELT(res, 3, NEW_INTEGER(nz)); + SET_VECTOR_ELT(res, 4, NEW_NUMERIC(nz)); + if (nz > 0) { + igraph_vector_int_t i, j; + igraph_vector_t x; + igraph_vector_int_view(&i, INTEGER(VECTOR_ELT(res, 2)), nz); + igraph_vector_int_view(&j, INTEGER(VECTOR_ELT(res, 3)), nz); + igraph_vector_view(&x, REAL(VECTOR_ELT(res, 4)), nz); + igraph_sparsemat_getelements(sp, &j, &i, &x); + } + + PROTECT(names=NEW_CHARACTER(5)); + SET_STRING_ELT(names, 0, mkChar("type")); + SET_STRING_ELT(names, 1, mkChar("dim")); + SET_STRING_ELT(names, 2, mkChar("p")); + SET_STRING_ELT(names, 3, mkChar("i")); + SET_STRING_ELT(names, 4, mkChar("x")); + SET_NAMES(res, names); + SET_CLASS(res, ScalarString(CREATE_STRING_VECTOR("igraph.tmp.sparse"))); + + UNPROTECT(2); + return res; +} + +SEXP R_igraph_sparsemat_to_SEXP_cc(const igraph_sparsemat_t *sp) { + SEXP res, names; + int nz=igraph_sparsemat_nonzero_storage(sp); + int m=(int) igraph_sparsemat_nrow(sp); + int n=(int) igraph_sparsemat_ncol(sp); + + PROTECT(res=NEW_LIST(5)); + SET_VECTOR_ELT(res, 0, ScalarString(CREATE_STRING_VECTOR("cc"))); + SET_VECTOR_ELT(res, 1, NEW_INTEGER(2)); + INTEGER(VECTOR_ELT(res, 1))[0] = m; + INTEGER(VECTOR_ELT(res, 1))[1] = n; + SET_VECTOR_ELT(res, 2, NEW_INTEGER(n+1)); + SET_VECTOR_ELT(res, 3, NEW_INTEGER(nz)); + SET_VECTOR_ELT(res, 4, NEW_NUMERIC(nz)); + if (nz > 0) { + igraph_vector_int_t i, p; + igraph_vector_t x; + igraph_vector_int_view(&p, INTEGER(VECTOR_ELT(res, 2)), n+1); + igraph_vector_int_view(&i, INTEGER(VECTOR_ELT(res, 3)), nz); + igraph_vector_view(&x, REAL(VECTOR_ELT(res, 4)), nz); + igraph_sparsemat_getelements_sorted(sp, &i, &p, &x); + } + + PROTECT(names=NEW_CHARACTER(5)); + SET_STRING_ELT(names, 0, mkChar("type")); + SET_STRING_ELT(names, 1, mkChar("dim")); + SET_STRING_ELT(names, 2, mkChar("p")); + SET_STRING_ELT(names, 3, mkChar("i")); + SET_STRING_ELT(names, 4, mkChar("x")); + SET_NAMES(res, names); + SET_CLASS(res, ScalarString(CREATE_STRING_VECTOR("igraph.tmp.sparse"))); + + UNPROTECT(2); + return res; +} + +SEXP R_igraph_sparsemat_to_SEXP(const igraph_sparsemat_t *sp) { + if (igraph_sparsemat_is_triplet(sp)) { + return R_igraph_sparsemat_to_SEXP_triplet(sp); + } else { + return R_igraph_sparsemat_to_SEXP_cc(sp); + } +} + +SEXP R_igraph_0orsparsemat_to_SEXP(const igraph_sparsemat_t *sp) { + if (!sp) { + return R_NilValue; + } else { + return R_igraph_sparsemat_to_SEXP(sp); + } +} + +int R_SEXP_to_igraph_adjlist(SEXP vectorlist, igraph_adjlist_t *ptr) { + int length=GET_LENGTH(vectorlist); + int i; + + ptr->length=length; + ptr->adjs = (igraph_vector_int_t*) R_alloc((size_t) length, + sizeof(igraph_vector_int_t)); + for (i=0; iadjs[i], INTEGER(vec), GET_LENGTH(vec)); + } + return 0; +} + +int R_igraph_SEXP_to_0orvectorlist(SEXP vectorlist, + igraph_vector_ptr_t *ptr) { + if (!isNull(vectorlist)) { + return R_igraph_SEXP_to_vectorlist(vectorlist, ptr); + } + return 0; +} + +int R_igraph_SEXP_to_vectorlist(SEXP vectorlist, igraph_vector_ptr_t *ptr) { + int length=GET_LENGTH(vectorlist); + int i; + igraph_vector_t *vecs; + igraph_vector_t **vecsptr; + + vecs = (igraph_vector_t *) R_alloc((size_t) length, sizeof(igraph_vector_t)); + vecsptr = (igraph_vector_t **) R_alloc((size_t) length, + sizeof(igraph_vector_t*)); + igraph_vector_ptr_view(ptr, (void**) vecsptr, length); + for (i=0; ilen=GET_LENGTH(rval); + sv->data=(char**) R_alloc((size_t) (sv->len), sizeof(char*)); + for (i=0; ilen; i++) { + sv->data[i]=(char*) CHAR(STRING_ELT(rval, i)); + } + + return 0; +} + +int R_igraph_SEXP_to_strvector_copy(SEXP rval, igraph_strvector_t *sv) { + long int i; + igraph_strvector_init(sv, GET_LENGTH(rval)); + for (i=0; ilen; i++) { + igraph_strvector_set(sv, i, CHAR(STRING_ELT(rval, i))); + } + + return 0; +} + +int R_SEXP_to_vector(SEXP sv, igraph_vector_t *v) { + v->stor_begin=REAL(sv); + v->stor_end=v->stor_begin+GET_LENGTH(sv); + v->end=v->stor_end; + return 0; +} + +int R_SEXP_to_vector_copy(SEXP sv, igraph_vector_t *v) { + return igraph_vector_init_copy(v, REAL(sv), GET_LENGTH(sv)); +} + +int R_SEXP_to_vector_bool(SEXP sv, igraph_vector_bool_t *v) { + v->stor_begin=LOGICAL(sv); + v->stor_end=v->stor_begin+GET_LENGTH(sv); + v->end=v->stor_end; + return 0; +} + +int R_SEXP_to_vector_bool_copy(SEXP sv, igraph_vector_bool_t *v) { + long int i, n=GET_LENGTH(sv); + int *svv=LOGICAL(sv); + igraph_vector_bool_init(v, n); + for (i=0; istor_begin=(int*) INTEGER(sv); + v->stor_end=v->stor_begin+GET_LENGTH(sv); + v->end=v->stor_end; + return 0; +} + +int R_SEXP_to_vector_long_copy(SEXP sv, igraph_vector_long_t *v) { + long int i, n=GET_LENGTH(sv); + double *svv=REAL(sv); + igraph_vector_long_init(v, n); + for (i=0; idata); + akl->nrow=INTEGER(GET_DIM(pakl))[0]; + akl->ncol=INTEGER(GET_DIM(pakl))[1]; + + return 0; +} + +int R_SEXP_to_igraph_matrix_copy(SEXP pakl, igraph_matrix_t *akl) { + igraph_vector_init_copy(&akl->data, REAL(pakl), GET_LENGTH(pakl)); + akl->nrow=INTEGER(GET_DIM(pakl))[0]; + akl->ncol=INTEGER(GET_DIM(pakl))[1]; + + return 0; +} + +int R_SEXP_to_vector_complex(SEXP pv, igraph_vector_complex_t *v) { + v->stor_begin=(igraph_complex_t*) COMPLEX(pv); + v->stor_end=v->stor_begin+GET_LENGTH(pv); + v->end=v->stor_end; + return 0; +} + +int R_SEXP_to_vector_complex_copy(SEXP pv, igraph_vector_complex_t *v) { + igraph_vector_complex_init_copy(v, (igraph_complex_t*) COMPLEX(pv), + GET_LENGTH(pv)); + return 0; +} + +int R_SEXP_to_matrix_complex(SEXP pakl, igraph_matrix_complex_t *akl) { + R_SEXP_to_vector_complex(pakl, &akl->data); + akl->nrow=INTEGER(GET_DIM(pakl))[0]; + akl->ncol=INTEGER(GET_DIM(pakl))[1]; + return 0; +} + +int R_SEXP_to_matrix_complex_copy(SEXP pakl, igraph_matrix_complex_t *akl) { + igraph_vector_complex_init_copy(&akl->data, + (igraph_complex_t*) COMPLEX(pakl), + GET_LENGTH(pakl)); + akl->nrow=INTEGER(GET_DIM(pakl))[0]; + akl->ncol=INTEGER(GET_DIM(pakl))[1]; + return 0; +} + +int R_igraph_SEXP_to_array3(SEXP rval, igraph_array3_t *a) { + R_SEXP_to_vector(rval, &a->data); + a->n1=INTEGER(GET_DIM(rval))[0]; + a->n2=INTEGER(GET_DIM(rval))[1]; + a->n3=INTEGER(GET_DIM(rval))[2]; + a->n1n2=(a->n1) * (a->n2); + + return 0; +} + +int R_igraph_SEXP_to_array3_copy(SEXP rval, igraph_array3_t *a) { + igraph_vector_init_copy(&a->data, REAL(rval), GET_LENGTH(rval)); + a->n1=INTEGER(GET_DIM(rval))[0]; + a->n2=INTEGER(GET_DIM(rval))[1]; + a->n3=INTEGER(GET_DIM(rval))[2]; + a->n1n2=(a->n1) * (a->n2); + + return 0; +} + +int R_SEXP_to_igraph(SEXP graph, igraph_t *res) { + + res->n=(igraph_integer_t) REAL(VECTOR_ELT(graph, 0))[0]; + res->directed=LOGICAL(VECTOR_ELT(graph, 1))[0]; + R_SEXP_to_vector(VECTOR_ELT(graph, 2), &res->from); + R_SEXP_to_vector(VECTOR_ELT(graph, 3), &res->to); + R_SEXP_to_vector(VECTOR_ELT(graph, 4), &res->oi); + R_SEXP_to_vector(VECTOR_ELT(graph, 5), &res->ii); + R_SEXP_to_vector(VECTOR_ELT(graph, 6), &res->os); + R_SEXP_to_vector(VECTOR_ELT(graph, 7), &res->is); + + /* attributes */ + REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[0] = 1; /* R objects refcount */ + REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[1] = 0; /* igraph_t objects */ + res->attr=VECTOR_ELT(graph, 8); + + return 0; +} + +int R_SEXP_to_igraph_copy(SEXP graph, igraph_t *res) { + + res->n=(igraph_integer_t) REAL(VECTOR_ELT(graph, 0))[0]; + res->directed=LOGICAL(VECTOR_ELT(graph, 1))[0]; + igraph_vector_init_copy(&res->from, REAL(VECTOR_ELT(graph, 2)), + GET_LENGTH(VECTOR_ELT(graph, 2))); + igraph_vector_init_copy(&res->to, REAL(VECTOR_ELT(graph, 3)), + GET_LENGTH(VECTOR_ELT(graph, 3))); + igraph_vector_init_copy(&res->oi, REAL(VECTOR_ELT(graph, 4)), + GET_LENGTH(VECTOR_ELT(graph, 4))); + igraph_vector_init_copy(&res->ii, REAL(VECTOR_ELT(graph, 5)), + GET_LENGTH(VECTOR_ELT(graph, 5))); + igraph_vector_init_copy(&res->os, REAL(VECTOR_ELT(graph, 6)), + GET_LENGTH(VECTOR_ELT(graph, 6))); + igraph_vector_init_copy(&res->is, REAL(VECTOR_ELT(graph, 7)), + GET_LENGTH(VECTOR_ELT(graph, 7))); + + /* attributes */ + REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[0] = 1; /* R objects */ + REAL(VECTOR_ELT(VECTOR_ELT(graph, 8), 0))[1] = 1; /* igraph_t objects */ + R_PreserveObject(res->attr=VECTOR_ELT(graph, 8)); + + return 0; +} + +/* + * We have only vector type + */ + +int R_SEXP_to_igraph_vs(SEXP rit, igraph_t *graph, igraph_vs_t *it) { + + igraph_vector_t *tmpv=(igraph_vector_t*)R_alloc(1,sizeof(igraph_vector_t)); + igraph_vs_vector(it, igraph_vector_view(tmpv, REAL(rit), + GET_LENGTH(rit))); + return 0; +} + +/* + * We have only vector type + */ + +int R_SEXP_to_igraph_es(SEXP rit, igraph_t *graph, igraph_es_t *it) { + + igraph_vector_t *tmpv=(igraph_vector_t*)R_alloc(1,sizeof(igraph_vector_t)); + igraph_es_vector(it, igraph_vector_view(tmpv, REAL(rit), + GET_LENGTH(rit))); + return 0; +} + +int R_SEXP_to_igraph_layout_drl_options(SEXP in, igraph_layout_drl_options_t *opt) { + opt->edge_cut = REAL(AS_NUMERIC(R_igraph_getListElement(in, "edge.cut")))[0]; + opt->init_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "init.iterations")))[0]; + opt->init_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "init.temperature")))[0]; + opt->init_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "init.attraction")))[0]; + opt->init_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "init.damping.mult")))[0]; + opt->liquid_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "liquid.iterations")))[0]; + opt->liquid_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "liquid.temperature")))[0]; + opt->liquid_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "liquid.attraction")))[0]; + opt->liquid_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "liquid.damping.mult")))[0]; + opt->expansion_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "expansion.iterations")))[0]; + opt->expansion_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "expansion.temperature")))[0]; + opt->expansion_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "expansion.attraction")))[0]; + opt->expansion_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "expansion.damping.mult")))[0]; + opt->cooldown_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "cooldown.iterations")))[0]; + opt->cooldown_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "cooldown.temperature")))[0]; + opt->cooldown_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "cooldown.attraction")))[0]; + opt->cooldown_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "cooldown.damping.mult")))[0]; + opt->crunch_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "crunch.iterations")))[0]; + opt->crunch_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "crunch.temperature")))[0]; + opt->crunch_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "crunch.attraction")))[0]; + opt->crunch_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "crunch.damping.mult")))[0]; + opt->simmer_iterations = (igraph_integer_t) REAL(AS_NUMERIC(R_igraph_getListElement(in, "simmer.iterations")))[0]; + opt->simmer_temperature = REAL(AS_NUMERIC(R_igraph_getListElement(in, "simmer.temperature")))[0]; + opt->simmer_attraction = REAL(AS_NUMERIC(R_igraph_getListElement(in, "simmer.attraction")))[0]; + opt->simmer_damping_mult = REAL(AS_NUMERIC(R_igraph_getListElement(in, "simmer.damping.mult")))[0]; + + return 0; +} + +int R_SEXP_to_igraph_arpack_options(SEXP in, igraph_arpack_options_t *opt) { + const char *tmpstr; + igraph_arpack_options_init(opt); + opt -> bmat[0] = CHAR(STRING_ELT(AS_CHARACTER + (R_igraph_getListElement(in, "bmat")), 0))[0]; + opt -> n = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "n")))[0]; + tmpstr=CHAR(STRING_ELT(AS_CHARACTER(R_igraph_getListElement(in, "which")), 0)); + opt -> which[0]=tmpstr[0]; opt -> which[1]=tmpstr[1]; + opt -> nev = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "nev")))[0]; + opt -> tol = REAL(AS_NUMERIC(R_igraph_getListElement(in, "tol")))[0]; + opt -> ncv = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "ncv")))[0]; + opt -> ldv = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "ldv")))[0]; + opt -> ishift = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "ishift")))[0]; + opt -> mxiter = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "maxiter")))[0]; + opt -> nb = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "nb")))[0]; + opt -> mode = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "mode")))[0]; + opt -> start = INTEGER(AS_INTEGER(R_igraph_getListElement(in, "start")))[0]; + opt -> lworkl = 0; + opt -> sigma = REAL(AS_NUMERIC(R_igraph_getListElement(in, "sigma")))[0]; + opt -> sigmai = REAL(AS_NUMERIC(R_igraph_getListElement(in, "sigmai")))[0]; + opt -> info = opt -> start; + + opt->iparam[0]=opt->ishift; + opt->iparam[2]=opt->mxiter; + opt->iparam[3]=opt->nb; + opt->iparam[6]=opt->mode; + + return 0; +} + +SEXP R_igraph_arpack_options_to_SEXP(const igraph_arpack_options_t *opt) { + SEXP result, names; + char bmat[2], which[3]; + + bmat[0]=opt->bmat[0]; bmat[1]='\0'; + which[0]=opt->which[0]; which[1]=opt->which[1]; which[2]='\0'; + + PROTECT(result = NEW_LIST(20)); + SET_VECTOR_ELT(result, 0, ScalarString(CREATE_STRING_VECTOR(bmat))); + SET_VECTOR_ELT(result, 1, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 1))[0]=opt->n; + SET_VECTOR_ELT(result, 2, ScalarString(CREATE_STRING_VECTOR(which))); + SET_VECTOR_ELT(result, 3, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 3))[0]=opt->nev; + SET_VECTOR_ELT(result, 4, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 4))[0]=opt->tol; + SET_VECTOR_ELT(result, 5, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 5))[0]=opt->ncv; + SET_VECTOR_ELT(result, 6, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 6))[0]=opt->ldv; + SET_VECTOR_ELT(result, 7, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 7))[0]=opt->ishift; + SET_VECTOR_ELT(result, 8, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 8))[0]=opt->mxiter; + SET_VECTOR_ELT(result, 9, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 9))[0]=opt->nb; + SET_VECTOR_ELT(result, 10, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 10))[0]=opt->mode; + SET_VECTOR_ELT(result, 11, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 11))[0]=opt->start; + SET_VECTOR_ELT(result, 12, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 12))[0]=opt->sigma; + SET_VECTOR_ELT(result, 13, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 13))[0]=opt->sigmai; + + SET_VECTOR_ELT(result, 14, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 14))[0]=opt->info; + SET_VECTOR_ELT(result, 15, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 15))[0]=opt->iparam[2];/* mxiter */ + SET_VECTOR_ELT(result, 16, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 16))[0]=opt->iparam[4];/* nconv */ + SET_VECTOR_ELT(result, 17, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 17))[0]=opt->iparam[8];/* numop */ + SET_VECTOR_ELT(result, 18, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 18))[0]=opt->iparam[9];/* numopb */ + SET_VECTOR_ELT(result, 19, NEW_INTEGER(1)); INTEGER(VECTOR_ELT(result, 19))[0]=opt->iparam[10];/* numreo */ + + PROTECT(names=NEW_CHARACTER(20)); + SET_STRING_ELT(names, 0, mkChar("bmat")); + SET_STRING_ELT(names, 1, mkChar("n")); + SET_STRING_ELT(names, 2, mkChar("which")); + SET_STRING_ELT(names, 3, mkChar("nev")); + SET_STRING_ELT(names, 4, mkChar("tol")); + SET_STRING_ELT(names, 5, mkChar("ncv")); + SET_STRING_ELT(names, 6, mkChar("ldv")); + SET_STRING_ELT(names, 7, mkChar("ishift")); + SET_STRING_ELT(names, 8, mkChar("maxiter")); + SET_STRING_ELT(names, 9, mkChar("nb")); + SET_STRING_ELT(names, 10, mkChar("mode")); + SET_STRING_ELT(names, 11, mkChar("start")); + SET_STRING_ELT(names, 12, mkChar("sigma")); + SET_STRING_ELT(names, 13, mkChar("sigmai")); + SET_STRING_ELT(names, 14, mkChar("info")); + SET_STRING_ELT(names, 15, mkChar("iter")); + SET_STRING_ELT(names, 16, mkChar("nconv")); + SET_STRING_ELT(names, 17, mkChar("numop")); + SET_STRING_ELT(names, 18, mkChar("numopb")); + SET_STRING_ELT(names, 19, mkChar("numreo")); + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +int R_SEXP_to_igraph_eigen_which(SEXP in, igraph_eigen_which_t *out) { + SEXP pos=PROTECT(AS_CHARACTER(R_igraph_getListElement(in, "pos"))); + SEXP balance=PROTECT(AS_CHARACTER(R_igraph_getListElement + (in, "balance"))); + + if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "lm")) { + out->pos=IGRAPH_EIGEN_LM; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "sm")) { + out->pos=IGRAPH_EIGEN_SM; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "la")) { + out->pos=IGRAPH_EIGEN_LA; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "sa")) { + out->pos=IGRAPH_EIGEN_SA; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "be")) { + out->pos=IGRAPH_EIGEN_BE; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "lr")) { + out->pos=IGRAPH_EIGEN_LR; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "sr")) { + out->pos=IGRAPH_EIGEN_SR; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "li")) { + out->pos=IGRAPH_EIGEN_LI; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "si")) { + out->pos=IGRAPH_EIGEN_SI; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "all")) { + out->pos=IGRAPH_EIGEN_ALL; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "interval")) { + out->pos=IGRAPH_EIGEN_INTERVAL; + } else if (!strcasecmp(CHAR(STRING_ELT(pos, 0)), "select")) { + out->pos=IGRAPH_EIGEN_SELECT; + } else { + UNPROTECT(2); + IGRAPH_ERROR("Unknown eigenvalue position specification", + IGRAPH_EINVAL); + } + + out->howmany=INTEGER(AS_INTEGER(R_igraph_getListElement + (in, "howmany")))[0]; + out->il=INTEGER(AS_INTEGER(R_igraph_getListElement(in, "il")))[0]; + out->iu=INTEGER(AS_INTEGER(R_igraph_getListElement(in, "iu")))[0]; + out->vl=REAL(AS_NUMERIC(R_igraph_getListElement(in, "vl")))[0]; + out->vu=REAL(AS_NUMERIC(R_igraph_getListElement(in, "vu")))[0]; + out->vestimate=INTEGER(AS_INTEGER(R_igraph_getListElement + (in, "vestimate")))[0]; + + if (!strcasecmp(CHAR(STRING_ELT(balance, 0)), "none")) { + out->balance=IGRAPH_LAPACK_DGEEVX_BALANCE_NONE; + } else if (!strcasecmp(CHAR(STRING_ELT(balance, 0)), "perm")) { + out->balance=IGRAPH_LAPACK_DGEEVX_BALANCE_PERM; + } else if (!strcasecmp(CHAR(STRING_ELT(balance, 0)), "scale")) { + out->balance=IGRAPH_LAPACK_DGEEVX_BALANCE_SCALE; + } else if (!strcasecmp(CHAR(STRING_ELT(balance, 0)), "both")) { + out->balance=IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH; + } else { + UNPROTECT(2); + IGRAPH_ERROR("Unknown balance specification", IGRAPH_EINVAL); + } + + UNPROTECT(2); + return 0; +} + +SEXP R_igraph_bliss_info_to_SEXP(const igraph_bliss_info_t *info) { + SEXP result, names; + + PROTECT(result=NEW_LIST(6)); + SET_VECTOR_ELT(result, 0, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 0))[0]=info->nof_nodes; + SET_VECTOR_ELT(result, 1, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 1))[0]=info->nof_leaf_nodes; + SET_VECTOR_ELT(result, 2, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 2))[0]=info->nof_bad_nodes; + SET_VECTOR_ELT(result, 3, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 3))[0]=info->nof_canupdates; + SET_VECTOR_ELT(result, 4, NEW_NUMERIC(1)); REAL(VECTOR_ELT(result, 4))[0]=info->max_level; + if (info->group_size) { + SET_VECTOR_ELT(result, 5, NEW_CHARACTER(1)); + SET_STRING_ELT(VECTOR_ELT(result, 5), 0, mkChar(info->group_size)); + } else { + SET_VECTOR_ELT(result, 5, R_NilValue); + } + + PROTECT(names=NEW_CHARACTER(6)); + SET_STRING_ELT(names, 0, mkChar("nof_nodes")); + SET_STRING_ELT(names, 1, mkChar("nof_leaf_nodes")); + SET_STRING_ELT(names, 2, mkChar("nof_bad_nodes")); + SET_STRING_ELT(names, 3, mkChar("nof_canupdates")); + SET_STRING_ELT(names, 4, mkChar("max_level")); + SET_STRING_ELT(names, 5, mkChar("group_size")); + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +/*******************************************************************/ + +SEXP R_igraph_mybracket(SEXP graph, SEXP pidx) { + int idx=INTEGER(pidx)[0]-1; + return duplicate(VECTOR_ELT(graph, idx)); +} + +SEXP R_igraph_mybracket2(SEXP graph, SEXP pidx1, SEXP pidx2) { + int idx1=INTEGER(pidx1)[0]-1; + int idx2=INTEGER(pidx2)[0]-1; + return duplicate(VECTOR_ELT(VECTOR_ELT(graph, idx1), idx2)); +} + +SEXP R_igraph_mybracket2_names(SEXP graph, SEXP pidx1, SEXP pidx2) { + SEXP result; + int idx1=INTEGER(pidx1)[0]-1; + int idx2=INTEGER(pidx2)[0]-1; + result=duplicate(GET_NAMES(VECTOR_ELT(VECTOR_ELT(graph, idx1), idx2))); + return result; +} + +SEXP R_igraph_mybracket2_copy(SEXP graph, SEXP pidx1, SEXP pidx2) { + int idx1=INTEGER(pidx1)[0]-1; + int idx2=INTEGER(pidx2)[0]-1; + return duplicate(VECTOR_ELT(VECTOR_ELT(graph, idx1), idx2)); +} + +SEXP R_igraph_mybracket2_set(SEXP graph, SEXP pidx1, SEXP pidx2, + SEXP value) { + SEXP newgraph; + int idx1=INTEGER(pidx1)[0]-1; + int idx2=INTEGER(pidx2)[0]-1; + PROTECT(newgraph=duplicate(graph)); + SET_VECTOR_ELT(VECTOR_ELT(newgraph, idx1), idx2, value); + UNPROTECT(1); + return newgraph; +} + +SEXP R_igraph_mybracket3_set(SEXP graph, SEXP pidx1, SEXP pidx2, + SEXP pname, SEXP value) { + SEXP newgraph; + int idx1=INTEGER(pidx1)[0]-1; + int idx2=INTEGER(pidx2)[0]-1; + const char *name=CHAR(STRING_ELT(pname, 0)); + SEXP attrs, names; + int i, n; + + PROTECT(newgraph=duplicate(graph)); + attrs=VECTOR_ELT(VECTOR_ELT(newgraph, idx1), idx2); + names=PROTECT(getAttrib(attrs, R_NamesSymbol)); + n=length(attrs); + + for (i=0; i 0 && igraph_vector_min(&w) < 0; + } + igraph_matrix_init(&res, 0, 0); + switch (algo) { + case 0: /* automatic */ + if (negw && mode==IGRAPH_OUT && GET_LENGTH(pvids)>100) { + igraph_shortest_paths_johnson(&g, &res, vs, to, pw); + } else if (negw) { + igraph_shortest_paths_bellman_ford(&g, &res, vs, to, pw, + (igraph_neimode_t) mode); + } else { + /* This one chooses 'unweighted' if there are no weights */ + igraph_shortest_paths_dijkstra(&g, &res, vs, to, pw, + (igraph_neimode_t) mode); + } + break; + case 1: /* unweighted */ + igraph_shortest_paths(&g, &res, vs, to, (igraph_neimode_t) mode); + break; + case 2: /* dijkstra */ + igraph_shortest_paths_dijkstra(&g, &res, vs, to, pw, + (igraph_neimode_t) mode); + break; + case 3: /* bellman-ford */ + igraph_shortest_paths_bellman_ford(&g, &res, vs, to, pw, + (igraph_neimode_t) mode); + break; + case 4: /* johnson */ + igraph_shortest_paths_johnson(&g, &res, vs, to, pw); + break; + } + PROTECT(result=R_igraph_matrix_to_SEXP(&res)); + igraph_matrix_destroy(&res); + igraph_vs_destroy(&vs); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_lattice(SEXP pdimvector, SEXP pnei, SEXP pdirected, + SEXP pmutual, SEXP pcircular) { + + igraph_t g; + igraph_vector_t dimvector; + igraph_integer_t nei=(igraph_integer_t) REAL(pnei)[0]; + igraph_bool_t directed=LOGICAL(pdirected)[0]; + igraph_bool_t mutual=LOGICAL(pmutual)[0]; + igraph_bool_t circular=LOGICAL(pcircular)[0]; + SEXP result; + + R_SEXP_to_vector(pdimvector, &dimvector); + + igraph_lattice(&g, &dimvector, nei, directed, mutual, circular); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_barabasi_game(SEXP pn, SEXP ppower, SEXP pm, SEXP poutseq, + SEXP poutpref, SEXP pA, SEXP pdirected, + SEXP palgo, SEXP pstart) { + + igraph_t g; + igraph_integer_t n=(igraph_integer_t) REAL(pn)[0]; + igraph_real_t power=REAL(ppower)[0]; + igraph_integer_t m=isNull(pm) ? 0 : (igraph_integer_t) REAL(pm)[0]; + igraph_vector_t outseq, *myoutseq=0; + igraph_bool_t outpref=LOGICAL(poutpref)[0]; + igraph_real_t A=REAL(pA)[0]; + igraph_bool_t directed=LOGICAL(pdirected)[0]; + igraph_barabasi_algorithm_t algo = (igraph_barabasi_algorithm_t) Rf_asInteger(palgo); + igraph_t start, *ppstart=0; + SEXP result; + + if (!isNull(poutseq)) { + R_SEXP_to_vector(poutseq, &outseq); + myoutseq=&outseq; + } + + if (!isNull(pstart)) { + R_SEXP_to_igraph(pstart, &start); + ppstart=&start; + } + + igraph_barabasi_game(&g, n, power, m, myoutseq, outpref, + A, directed, algo, ppstart); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_recent_degree_game(SEXP pn, SEXP ppower, SEXP pwindow, + SEXP pm, SEXP poutseq, SEXP poutpref, + SEXP pzero_appeal, + SEXP pdirected) { + igraph_t g; + igraph_integer_t n=(igraph_integer_t) REAL(pn)[0]; + igraph_real_t power=REAL(ppower)[0]; + igraph_integer_t window=(igraph_integer_t) REAL(pwindow)[0]; + igraph_integer_t m=(igraph_integer_t) REAL(pm)[0]; + igraph_vector_t outseq; + igraph_bool_t outpref=LOGICAL(poutpref)[0]; + igraph_bool_t directed=LOGICAL(pdirected)[0]; + igraph_real_t zero_appeal=REAL(pzero_appeal)[0]; + SEXP result; + + R_SEXP_to_vector(poutseq, &outseq); + + igraph_recent_degree_game(&g, n, power, window, m, &outseq, outpref, + zero_appeal, directed); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_layout_fruchterman_reingold(SEXP graph, SEXP coords, + SEXP niter, SEXP start_temp, + SEXP weights, + SEXP minx, SEXP maxx, + SEXP miny, SEXP maxy, SEXP grid) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_coords; + igraph_integer_t c_niter; + igraph_real_t c_start_temp; + igraph_vector_t c_weights; + igraph_vector_t c_minx; + igraph_vector_t c_maxx; + igraph_vector_t c_miny; + igraph_vector_t c_maxy; + igraph_layout_grid_t c_grid=INTEGER(grid)[0]; + + SEXP result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(coords)) { + if (0 != R_SEXP_to_igraph_matrix_copy(coords, &c_coords)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } else { + igraph_matrix_init(&c_coords, 0, 0); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_coords); + c_niter=INTEGER(niter)[0]; + c_start_temp=REAL(start_temp)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(minx)) { R_SEXP_to_vector(minx, &c_minx); } + if (!isNull(maxx)) { R_SEXP_to_vector(maxx, &c_maxx); } + if (!isNull(miny)) { R_SEXP_to_vector(miny, &c_miny); } + if (!isNull(maxy)) { R_SEXP_to_vector(maxy, &c_maxy); } + /* Call igraph */ + igraph_layout_fruchterman_reingold(&c_graph, &c_coords, + !isNull(coords), + c_niter, c_start_temp, c_grid, + (isNull(weights) ? 0 : &c_weights), + (isNull(minx) ? 0 : &c_minx), + (isNull(maxx) ? 0 : &c_maxx), + (isNull(miny) ? 0 : &c_miny), + (isNull(maxy) ? 0 : &c_maxy)); + + /* Convert output */ + PROTECT(coords=R_igraph_matrix_to_SEXP(&c_coords)); + igraph_matrix_destroy(&c_coords); + IGRAPH_FINALLY_CLEAN(1); + result=coords; + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_layout_fruchterman_reingold_3d(SEXP graph, SEXP coords, + SEXP niter, SEXP start_temp, + SEXP weights, + SEXP minx, SEXP maxx, + SEXP miny, SEXP maxy, + SEXP minz, SEXP maxz) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_coords; + igraph_integer_t c_niter; + igraph_real_t c_start_temp; + igraph_vector_t c_weights; + igraph_vector_t c_minx; + igraph_vector_t c_maxx; + igraph_vector_t c_miny; + igraph_vector_t c_maxy; + igraph_vector_t c_minz; + igraph_vector_t c_maxz; + + SEXP result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(coords)) { + if (0 != R_SEXP_to_igraph_matrix_copy(coords, &c_coords)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } else { + igraph_matrix_init(&c_coords, 0, 0); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_coords); + c_niter=INTEGER(niter)[0]; + c_start_temp=REAL(start_temp)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(minx)) { R_SEXP_to_vector(minx, &c_minx); } + if (!isNull(maxx)) { R_SEXP_to_vector(maxx, &c_maxx); } + if (!isNull(miny)) { R_SEXP_to_vector(miny, &c_miny); } + if (!isNull(maxy)) { R_SEXP_to_vector(maxy, &c_maxy); } + if (!isNull(minz)) { R_SEXP_to_vector(minz, &c_minz); } + if (!isNull(maxz)) { R_SEXP_to_vector(maxz, &c_maxz); } + /* Call igraph */ + igraph_layout_fruchterman_reingold_3d(&c_graph, &c_coords, !isNull(coords), + c_niter, c_start_temp, + (isNull(weights) ? 0 : &c_weights), + (isNull(minx) ? 0 : &c_minx), + (isNull(maxx) ? 0 : &c_maxx), + (isNull(miny) ? 0 : &c_miny), + (isNull(maxy) ? 0 : &c_maxy), + (isNull(minz) ? 0 : &c_minz), + (isNull(maxz) ? 0 : &c_maxz)); + + /* Convert output */ + PROTECT(coords=R_igraph_matrix_to_SEXP(&c_coords)); + igraph_matrix_destroy(&c_coords); + IGRAPH_FINALLY_CLEAN(1); + result=coords; + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_layout_kamada_kawai(SEXP graph, SEXP coords, SEXP maxiter, + SEXP epsilon, SEXP kkconst, SEXP weights, + SEXP minx, SEXP maxx, + SEXP miny, SEXP maxy) { + + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_coords; + igraph_integer_t c_maxiter; + igraph_real_t c_epsilon; + igraph_real_t c_kkconst; + igraph_vector_t c_weights; + igraph_vector_t c_minx; + igraph_vector_t c_maxx; + igraph_vector_t c_miny; + igraph_vector_t c_maxy; + + SEXP result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(coords)) { + if (0 != R_SEXP_to_igraph_matrix_copy(coords, &c_coords)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } else { + igraph_matrix_init(&c_coords, 0, 0); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_coords); + c_maxiter=INTEGER(maxiter)[0]; + c_epsilon=REAL(epsilon)[0]; + c_kkconst=REAL(kkconst)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(minx)) { R_SEXP_to_vector(minx, &c_minx); } + if (!isNull(maxx)) { R_SEXP_to_vector(maxx, &c_maxx); } + if (!isNull(miny)) { R_SEXP_to_vector(miny, &c_miny); } + if (!isNull(maxy)) { R_SEXP_to_vector(maxy, &c_maxy); } + /* Call igraph */ + igraph_layout_kamada_kawai(&c_graph, &c_coords, !isNull(coords), + c_maxiter, c_epsilon, c_kkconst, + (isNull(weights) ? 0 : &c_weights), + (isNull(minx) ? 0 : &c_minx), + (isNull(maxx) ? 0 : &c_maxx), + (isNull(miny) ? 0 : &c_miny), + (isNull(maxy) ? 0 : &c_maxy)); + + /* Convert output */ + PROTECT(coords=R_igraph_matrix_to_SEXP(&c_coords)); + igraph_matrix_destroy(&c_coords); + IGRAPH_FINALLY_CLEAN(1); + result=coords; + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_layout_kamada_kawai_3d(SEXP graph, SEXP coords, SEXP maxiter, + SEXP epsilon, SEXP kkconst, + SEXP weights, + SEXP minx, SEXP maxx, + SEXP miny, SEXP maxy, + SEXP minz, SEXP maxz) { + + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_coords; + igraph_integer_t c_maxiter; + igraph_real_t c_epsilon; + igraph_real_t c_kkconst; + igraph_vector_t c_weights; + igraph_vector_t c_minx; + igraph_vector_t c_maxx; + igraph_vector_t c_miny; + igraph_vector_t c_maxy; + igraph_vector_t c_minz; + igraph_vector_t c_maxz; + + SEXP result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(coords)) { + if (0 != R_SEXP_to_igraph_matrix_copy(coords, &c_coords)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } else { + igraph_matrix_init(&c_coords, 0, 0); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_coords); + c_maxiter=INTEGER(maxiter)[0]; + c_epsilon=REAL(epsilon)[0]; + c_kkconst=REAL(kkconst)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(minx)) { R_SEXP_to_vector(minx, &c_minx); } + if (!isNull(maxx)) { R_SEXP_to_vector(maxx, &c_maxx); } + if (!isNull(miny)) { R_SEXP_to_vector(miny, &c_miny); } + if (!isNull(maxy)) { R_SEXP_to_vector(maxy, &c_maxy); } + if (!isNull(minz)) { R_SEXP_to_vector(minz, &c_minz); } + if (!isNull(maxz)) { R_SEXP_to_vector(maxz, &c_maxz); } + /* Call igraph */ + igraph_layout_kamada_kawai_3d(&c_graph, &c_coords, !isNull(coords), + c_maxiter, c_epsilon, c_kkconst, + (isNull(weights) ? 0 : &c_weights), + (isNull(minx) ? 0 : &c_minx), + (isNull(maxx) ? 0 : &c_maxx), + (isNull(miny) ? 0 : &c_miny), + (isNull(maxy) ? 0 : &c_maxy), + (isNull(minz) ? 0 : &c_minz), + (isNull(maxz) ? 0 : &c_maxz)); + + /* Convert output */ + PROTECT(coords=R_igraph_matrix_to_SEXP(&c_coords)); + igraph_matrix_destroy(&c_coords); + IGRAPH_FINALLY_CLEAN(1); + result=coords; + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_layout_graphopt(SEXP graph, SEXP pniter, SEXP pcharge, + SEXP pmass, SEXP pspring_length, + SEXP pspring_constant, SEXP pmax_sa_movement, + SEXP start) { + igraph_t g; + igraph_integer_t niter=(igraph_integer_t) REAL(pniter)[0]; + igraph_real_t charge=REAL(pcharge)[0]; + igraph_real_t mass=REAL(pmass)[0]; + igraph_real_t spring_length=REAL(pspring_length)[0]; + igraph_real_t spring_constant=REAL(pspring_constant)[0]; + igraph_real_t max_sa_movement=REAL(pmax_sa_movement)[0]; + igraph_matrix_t res; + SEXP result; + + R_SEXP_to_igraph(graph, &g); + if (isNull(start)) { + igraph_matrix_init(&res, 0, 0); + } else { + R_SEXP_to_igraph_matrix_copy(start, &res); + } + igraph_layout_graphopt(&g, &res, niter, charge, mass, spring_length, + spring_constant, max_sa_movement, !isNull(start)); + PROTECT(result=R_igraph_matrix_to_SEXP(&res)); + igraph_matrix_destroy(&res); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_layout_lgl(SEXP graph, SEXP pmaxiter, SEXP pmaxdelta, + SEXP parea, SEXP pcoolexp, SEXP prepulserad, + SEXP pcellsize, SEXP proot) { + + igraph_t g; + igraph_matrix_t res; + igraph_integer_t maxiter=(igraph_integer_t) REAL(pmaxiter)[0]; + igraph_real_t maxdelta=REAL(pmaxdelta)[0]; + igraph_real_t area=REAL(parea)[0]; + igraph_real_t coolexp=REAL(pcoolexp)[0]; + igraph_real_t repulserad=REAL(prepulserad)[0]; + igraph_real_t cellsize=REAL(pcellsize)[0]; + igraph_integer_t root=(igraph_integer_t) REAL(proot)[0]; + SEXP result; + + R_SEXP_to_igraph(graph, &g); + igraph_matrix_init(&res, 0, 0); + igraph_layout_lgl(&g, &res, maxiter, maxdelta, area, coolexp, repulserad, + cellsize, root); + PROTECT(result=R_igraph_matrix_to_SEXP(&res)); + igraph_matrix_destroy(&res); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_minimum_spanning_tree_unweighted(SEXP graph) { + + igraph_t g; + igraph_t mst; + SEXP result; + + R_SEXP_to_igraph(graph, &g); + igraph_minimum_spanning_tree_unweighted(&g, &mst); + PROTECT(result=R_igraph_to_SEXP(&mst)); + igraph_destroy(&mst); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_minimum_spanning_tree_prim(SEXP graph, SEXP pweights) { + + igraph_t g; + igraph_t mst; + igraph_vector_t weights; + SEXP result; + + R_SEXP_to_vector(pweights, &weights); + + R_SEXP_to_igraph(graph, &g); + igraph_minimum_spanning_tree_prim(&g, &mst, &weights); + PROTECT(result=R_igraph_to_SEXP(&mst)); + igraph_destroy(&mst); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_get_shortest_paths(SEXP graph, SEXP pfrom, SEXP pto, + SEXP pmode, SEXP pno, SEXP weights, + SEXP output, SEXP ppred, SEXP pinbound, + SEXP palgo) { + + igraph_t g; + igraph_integer_t from=(igraph_integer_t) REAL(pfrom)[0]; + igraph_vs_t to; + igraph_integer_t mode=(igraph_integer_t) REAL(pmode)[0]; + igraph_vector_t *vects, *evects; + long int i; + igraph_vector_ptr_t ptrvec, ptrevec; + igraph_vector_t w, *pw=&w; + igraph_bool_t negw=0; + SEXP result, result1, result2, names; + igraph_bool_t verts=REAL(output)[0]==0 || REAL(output)[0]==2; + igraph_bool_t edges=REAL(output)[0]==1 || REAL(output)[0]==2; + igraph_bool_t pred=LOGICAL(ppred)[0]; + igraph_bool_t inbound=LOGICAL(pinbound)[0]; + long int algo=(long int) REAL(palgo)[0]; + igraph_vector_long_t predvec, inboundvec; + + long int no=(long int) REAL(pno)[0]; + + R_SEXP_to_igraph(graph, &g); + R_SEXP_to_igraph_vs(pto, &g, &to); + + if (verts) { + igraph_vector_ptr_init(&ptrvec, no); + vects=(igraph_vector_t*) R_alloc((size_t) GET_LENGTH(pto), + sizeof(igraph_vector_t)); + for (i=0; i 0 && igraph_vector_min(&w) < 0; + } + + if (pred) { igraph_vector_long_init(&predvec, no); } + if (inbound) { igraph_vector_long_init(&inboundvec, no); } + + switch (algo) { + case 0: /* automatic */ + if (negw) { + igraph_get_shortest_paths_bellman_ford(&g, + verts ? &ptrvec : 0, + edges ? &ptrevec : 0, + from, to, pw, (igraph_neimode_t) mode, + pred ? &predvec : 0, + inbound ? &inboundvec : 0); + } else { + /* This one chooses 'unweighted' if there are no weights */ + igraph_get_shortest_paths_dijkstra(&g, + verts ? &ptrvec : 0, + edges ? &ptrevec : 0, + from, to, pw, (igraph_neimode_t) mode, + pred ? &predvec : 0, + inbound ? &inboundvec : 0); + } + break; + case 1: /* unweighted */ + igraph_get_shortest_paths(&g, + verts ? &ptrvec : 0, + edges ? &ptrevec : 0, + from, to, (igraph_neimode_t) mode, + pred ? &predvec : 0, + inbound ? &inboundvec : 0); + break; + case 2: /* dijkstra */ + igraph_get_shortest_paths_dijkstra(&g, + verts ? &ptrvec : 0, + edges ? &ptrevec : 0, + from, to, pw, (igraph_neimode_t) mode, + pred ? &predvec : 0, + inbound ? &inboundvec : 0); + break; + case 3: /* bellman-ford */ + igraph_get_shortest_paths_bellman_ford(&g, + verts ? &ptrvec : 0, + edges ? &ptrevec : 0, + from, to, pw, (igraph_neimode_t) mode, + pred ? &predvec : 0, + inbound ? &inboundvec : 0); + break; + } + + PROTECT(result=NEW_LIST(4)); + if (verts) { + SET_VECTOR_ELT(result, 0, NEW_LIST(no)); + result1=VECTOR_ELT(result, 0); + for (i=0; i0) { + R_igraph_SEXP_to_strvector(ppredef, &predef); + predefptr=&predef; + } + igraph_read_graph_ncol(&g, file, predefptr, names, weights, directed); + fclose(file); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_write_graph_ncol(SEXP graph, SEXP file, SEXP pnames, + SEXP pweights) { + igraph_t g; + FILE *stream; +#if HAVE_OPEN_MEMSTREAM == 1 + char *bp; + size_t size; +#endif + const char *names, *weights; + SEXP result; + + if (isNull(pnames)) { + names=0; + } else { + names=CHAR(STRING_ELT(pnames, 0)); + } + if (isNull(pweights)) { + weights=0; + } else { + weights=CHAR(STRING_ELT(pweights, 0)); + } + + R_SEXP_to_igraph(graph, &g); +#if HAVE_OPEN_MEMSTREAM == 1 + stream=open_memstream(&bp, &size); +#else + stream=fopen(CHAR(STRING_ELT(file,0)), "w"); +#endif + if (stream==0) { igraph_error("Cannot write .ncol file", __FILE__, __LINE__, + IGRAPH_EFILE); } + igraph_write_graph_ncol(&g, stream, names, weights); + fclose(stream); +#if HAVE_OPEN_MEMSTREAM == 1 + PROTECT(result=allocVector(RAWSXP, size)); + memcpy(RAW(result), bp, sizeof(char)*size); + free(bp); +#else + PROTECT(result=NEW_NUMERIC(0)); +#endif + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_read_graph_lgl(SEXP pvfile, SEXP pnames, SEXP pweights, SEXP pdirected) { + igraph_t g; + igraph_bool_t names=LOGICAL(pnames)[0]; + igraph_add_weights_t weights=REAL(pweights)[0]; + igraph_bool_t directed=LOGICAL(pdirected)[0]; + FILE *file; + SEXP result; + +#if HAVE_FMEMOPEN == 1 + file=fmemopen(RAW(pvfile), GET_LENGTH(pvfile), "r"); +#else + file=fopen(CHAR(STRING_ELT(pvfile, 0)), "r"); +#endif + if (file==0) { igraph_error("Cannot read edgelist", __FILE__, __LINE__, + IGRAPH_EFILE); } + igraph_read_graph_lgl(&g, file, names, weights, directed); + fclose(file); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_write_graph_lgl(SEXP graph, SEXP file, SEXP pnames, + SEXP pweights, SEXP pisolates) { + igraph_t g; + FILE *stream; +#if HAVE_OPEN_MEMSTREAM == 1 + char *bp; + size_t size; +#endif + const char *names, *weights; + igraph_bool_t isolates=LOGICAL(pisolates)[0]; + SEXP result; + + if (isNull(pnames)) { + names=0; + } else { + names=CHAR(STRING_ELT(pnames, 0)); + } + if (isNull(pweights)) { + weights=0; + } else { + weights=CHAR(STRING_ELT(pweights, 0)); + } + + R_SEXP_to_igraph(graph, &g); +#if HAVE_OPEN_MEMSTREAM == 1 + stream=open_memstream(&bp, &size); +#else + stream=fopen(CHAR(STRING_ELT(file, 0)), "w"); +#endif + igraph_write_graph_lgl(&g, stream, names, weights, isolates); + fclose(stream); +#if HAVE_OPEN_MEMSTREAM == 1 + PROTECT(result=allocVector(RAWSXP, size)); + memcpy(RAW(result), bp, sizeof(char)*size); + free(bp); +#else + PROTECT(result=NEW_NUMERIC(0)); +#endif + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_read_graph_pajek(SEXP pvfile) { + igraph_t g; + FILE *file; + SEXP result; + +#if HAVE_FMEMOPEN == 1 + file=fmemopen(RAW(pvfile), GET_LENGTH(pvfile), "r"); +#else + file=fopen(CHAR(STRING_ELT(pvfile, 0)), "r"); +#endif + if (file==0) { igraph_error("Cannot read Pajek file", __FILE__, __LINE__, + IGRAPH_EFILE); } + igraph_read_graph_pajek(&g, file); + fclose(file); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_decompose(SEXP graph, SEXP pmode, SEXP pmaxcompno, + SEXP pminelements) { + + igraph_t g; + igraph_connectedness_t mode = (igraph_connectedness_t) Rf_asInteger(pmode); + igraph_integer_t maxcompno=(igraph_integer_t) REAL(pmaxcompno)[0]; + igraph_integer_t minelements=(igraph_integer_t) REAL(pminelements)[0]; + igraph_vector_ptr_t comps; + SEXP result; + long int i; + + R_PreserveObject(R_igraph_attribute_protected=NEW_LIST(100)); + R_igraph_attribute_protected_size=0; + IGRAPH_FINALLY(R_igraph_attribute_protected_destroy, 0); + + R_SEXP_to_igraph(graph, &g); + igraph_vector_ptr_init(&comps, 0); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &comps); + igraph_decompose(&g, &comps, mode, maxcompno, minelements); + PROTECT(result=NEW_LIST(igraph_vector_ptr_size(&comps))); + for (i=0; ifun, s_from, data->extra)); + PROTECT(s_to = eval(R_fcall, data->rho)); + memcpy(to, REAL(s_to), sizeof(igraph_real_t) * (size_t) n); + + UNPROTECT(3); + return 0; +} + +SEXP R_igraph_arpack(SEXP function, SEXP extra, SEXP options, SEXP rho, + SEXP sym) { + + igraph_vector_t values; + igraph_matrix_t vectors, values2; + R_igraph_i_arpack_data_t data; + igraph_arpack_options_t c_options; + SEXP result, names; + + if (0 != igraph_matrix_init(&vectors, 0, 0)) { + igraph_error("Cannot run ARPACK", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &vectors); + if (LOGICAL(sym)[0]) { + if (0 != igraph_vector_init(&values, 0)) { + igraph_error("Cannot run ARPACK", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &values); + } else { + if (0 != igraph_matrix_init(&values2, 0, 0)) { + igraph_error("Cannot run ARPACK", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &values2); + } + + data.fun=function; + data.extra=extra; + data.rho=rho; + + R_SEXP_to_igraph_arpack_options(options, &c_options); + if (LOGICAL(sym)[0]) { + if (0 != igraph_arpack_rssolve(R_igraph_i_arpack_callback, &data, + &c_options, 0, &values, &vectors)) { + igraph_error("ARPACK failed", __FILE__, __LINE__, IGRAPH_FAILURE); + } + } else { + if (0 != igraph_arpack_rnsolve(R_igraph_i_arpack_callback, &data, + &c_options, 0, &values2, &vectors)) { + igraph_error("ARPACK failed", __FILE__, __LINE__, IGRAPH_FAILURE); + } + } + + PROTECT(result=NEW_LIST(3)); + if (LOGICAL(sym)[0]) { + SET_VECTOR_ELT(result, 0, R_igraph_vector_to_SEXP(&values)); + igraph_vector_destroy(&values); IGRAPH_FINALLY_CLEAN(1); + } else { + SET_VECTOR_ELT(result, 0, R_igraph_matrix_to_SEXP(&values2)); + igraph_matrix_destroy(&values2); IGRAPH_FINALLY_CLEAN(1); + } + SET_VECTOR_ELT(result, 1, R_igraph_matrix_to_SEXP(&vectors)); + igraph_matrix_destroy(&vectors); IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(result, 2, R_igraph_arpack_options_to_SEXP(&c_options)); + PROTECT(names=NEW_CHARACTER(3)); + SET_STRING_ELT(names, 0, mkChar("values")); + SET_STRING_ELT(names, 1, mkChar("vectors")); + SET_STRING_ELT(names, 2, mkChar("options")); + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +SEXP R_igraph_is_chordal(SEXP graph, SEXP alpha, SEXP alpham1, + SEXP pfillin, SEXP pnewgraph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_alpha; + igraph_vector_t c_alpham1; + igraph_bool_t c_chordal; + igraph_vector_t c_fillin; + igraph_t c_newgraph; + SEXP chordal; + SEXP fillin; + SEXP newgraph; + SEXP result, names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(alpha)) { R_SEXP_to_vector(alpha, &c_alpha); } + if (!isNull(alpham1)) { R_SEXP_to_vector(alpham1, &c_alpham1); } + if (LOGICAL(pfillin)[0]) { + if (0 != igraph_vector_init(&c_fillin, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_fillin); + } + igraph_is_chordal(&c_graph, (isNull(alpha) ? 0 : &c_alpha), + (isNull(alpham1) ? 0 : &c_alpham1), &c_chordal, + (LOGICAL(pfillin)[0] ? &c_fillin : 0), + (LOGICAL(pnewgraph)[0] ? &c_newgraph : 0)); + + /* Convert output */ + PROTECT(result=NEW_LIST(3)); + PROTECT(names=NEW_CHARACTER(3)); + PROTECT(chordal=NEW_LOGICAL(1)); + LOGICAL(chordal)[0]=c_chordal; + if (LOGICAL(pfillin)[0]) { + PROTECT(fillin=R_igraph_vector_to_SEXP(&c_fillin)); + igraph_vector_destroy(&c_fillin); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(fillin=R_NilValue); + } + if (LOGICAL(pnewgraph)[0]) { + IGRAPH_FINALLY(igraph_destroy, &c_newgraph); + PROTECT(newgraph=R_igraph_to_SEXP(&c_newgraph)); + igraph_destroy(&c_newgraph); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(newgraph=R_NilValue); + } + SET_VECTOR_ELT(result, 0, chordal); + SET_VECTOR_ELT(result, 1, fillin); + SET_VECTOR_ELT(result, 2, newgraph); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("chordal")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("fillin")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("newgraph")); + SET_NAMES(result, names); + UNPROTECT(4); + + UNPROTECT(1); + return(result); +} + +typedef struct { + SEXP graph, fun, extra, rho; +} R_igraph_i_bfs_data_t; + +igraph_bool_t R_igraph_bfshandler(const igraph_t *graph, + igraph_integer_t vid, + igraph_integer_t pred, + igraph_integer_t succ, + igraph_integer_t rank, + igraph_integer_t dist, + void *extra) { + + R_igraph_i_bfs_data_t *data=extra; + SEXP args, R_fcall, result, names; + igraph_bool_t cres; + + PROTECT(args=NEW_NUMERIC(5)); + PROTECT(names=NEW_CHARACTER(5)); + + SET_STRING_ELT(names, 0, mkChar("vid")); + SET_STRING_ELT(names, 1, mkChar("pred")); + SET_STRING_ELT(names, 2, mkChar("succ")); + SET_STRING_ELT(names, 3, mkChar("rank")); + SET_STRING_ELT(names, 4, mkChar("dist")); + REAL(args)[0]=vid + 1; + REAL(args)[1]=pred + 1; + REAL(args)[2]=succ + 1; + REAL(args)[3]=rank + 1; + REAL(args)[4]=dist; + SET_NAMES(args, names); + + PROTECT(R_fcall = lang4(data->fun, data->graph, args, data->extra)); + PROTECT(result = eval(R_fcall, data->rho)); + + cres=Rf_asLogical(result); + + UNPROTECT(4); + return cres; +} + +SEXP R_igraph_bfs(SEXP graph, SEXP proot, SEXP proots, SEXP pneimode, + SEXP punreachable, SEXP prestricted, + SEXP porder, SEXP prank, SEXP pfather, + SEXP ppred, SEXP psucc, SEXP pdist, + SEXP pcallback, SEXP pextra, SEXP prho) { + + igraph_t g; + SEXP result, names; + + igraph_integer_t root=(igraph_integer_t) REAL(proot)[0]; + igraph_vector_t roots; + igraph_bool_t unreachable=LOGICAL(punreachable)[0]; + igraph_vector_t restricted; + igraph_integer_t neimode=(igraph_integer_t) REAL(pneimode)[0]; + igraph_vector_t order, rank, father, pred, succ, dist; + igraph_vector_t *p_order=0, *p_rank=0, *p_father=0, *p_pred=0, + *p_succ=0, *p_dist=0; + igraph_bfshandler_t *callback=0; + R_igraph_i_bfs_data_t cb_data, *p_cb_data=0; + + R_SEXP_to_igraph(graph, &g); + if (!isNull(proots)) { + R_SEXP_to_vector(proots, &roots); + } + if (!isNull(prestricted)) { + R_SEXP_to_vector(prestricted, &restricted); + } + + if (LOGICAL(porder)[0]) { + igraph_vector_init(&order, 0); p_order=ℴ + } + if (LOGICAL(prank)[0]) { + igraph_vector_init(&rank, 0); p_rank=&rank; + } + if (LOGICAL(pfather)[0]) { + igraph_vector_init(&father, 0); p_father=&father; + } + if (LOGICAL(ppred)[0]) { + igraph_vector_init(&pred, 0); p_pred=&pred; + } + if (LOGICAL(psucc)[0]) { + igraph_vector_init(&succ, 0); p_succ=≻ + } + if (LOGICAL(pdist)[0]) { + igraph_vector_init(&dist, 0); p_dist=&dist; + } + + if (!isNull(pcallback)) { + cb_data.graph=graph; + cb_data.fun=pcallback; + cb_data.extra=pextra; + cb_data.rho=prho; + callback=R_igraph_bfshandler; + p_cb_data = &cb_data; + } + + igraph_bfs(&g, root, isNull(proots) ? 0 : &roots, (igraph_neimode_t) neimode, + unreachable, isNull(prestricted) ? 0 : &restricted, + p_order, p_rank, p_father, p_pred, p_succ, p_dist, + (igraph_bfshandler_t*) callback, p_cb_data); + + PROTECT(result=NEW_LIST(8)); + PROTECT(names=NEW_CHARACTER(8)); + + SET_STRING_ELT(names, 0, mkChar("root")); + SET_VECTOR_ELT(result, 0, NEW_NUMERIC(1)); + REAL(VECTOR_ELT(result, 0))[0] = root+1; + + SET_STRING_ELT(names, 1, mkChar("mode")); + SET_VECTOR_ELT(result, 1, NEW_CHARACTER(1)); + if (neimode==1) { + SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("out")); + } else if (neimode==2) { + SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("in")); + } else { + SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("all")); + } + + SET_STRING_ELT(names, 2, mkChar("order")); + SET_VECTOR_ELT(result, 2, R_igraph_0orvector_to_SEXP_d(p_order)); + SET_STRING_ELT(names, 3, mkChar("rank")); + SET_VECTOR_ELT(result, 3, R_igraph_0orvector_to_SEXP_d(p_rank)); + SET_STRING_ELT(names, 4, mkChar("father")); + SET_VECTOR_ELT(result, 4, R_igraph_0orvector_to_SEXP_d(p_father)); + SET_STRING_ELT(names, 5, mkChar("pred")); + SET_VECTOR_ELT(result, 5, R_igraph_0orvector_to_SEXP_d(p_pred)); + SET_STRING_ELT(names, 6, mkChar("succ")); + SET_VECTOR_ELT(result, 6, R_igraph_0orvector_to_SEXP_d(p_succ)); + SET_STRING_ELT(names, 7, mkChar("dist")); + SET_VECTOR_ELT(result, 7, R_igraph_0orvector_to_SEXP_d(p_dist)); + + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + +typedef struct { + SEXP graph, fun_in, fun_out, extra, rho; +} R_igraph_i_dfs_data_t; + +igraph_bool_t R_igraph_dfshandler(const igraph_t *graph, + igraph_integer_t vid, + igraph_integer_t dist, + void *extra, + int which) { + + R_igraph_i_dfs_data_t *data=extra; + SEXP args, R_fcall, result, names; + igraph_bool_t cres; + + PROTECT(args=NEW_NUMERIC(2)); + PROTECT(names=NEW_CHARACTER(2)); + + SET_STRING_ELT(names, 0, mkChar("vid")); + SET_STRING_ELT(names, 1, mkChar("dist")); + REAL(args)[0]=vid + 1; + REAL(args)[1]=dist; + SET_NAMES(args, names); + + PROTECT(R_fcall = lang4(which==0 ? data->fun_in : data->fun_out, + data->graph, args, data->extra)); + PROTECT(result = eval(R_fcall, data->rho)); + + cres=Rf_asLogical(result); + + UNPROTECT(4); + return cres; +} + +igraph_bool_t R_igraph_dfshandler_in(const igraph_t *graph, + igraph_integer_t vid, + igraph_integer_t dist, + void *extra) { + + return R_igraph_dfshandler(graph, vid, dist, extra, 0); +} + + +igraph_bool_t R_igraph_dfshandler_out(const igraph_t *graph, + igraph_integer_t vid, + igraph_integer_t dist, + void *extra) { + + return R_igraph_dfshandler(graph, vid, dist, extra, 1); +} + +SEXP R_igraph_dfs(SEXP graph, SEXP proot, SEXP pneimode, SEXP punreachable, + SEXP porder, SEXP porder_out, SEXP pfather, SEXP pdist, + SEXP pin_callback, SEXP pout_callback, + SEXP pextra, SEXP prho) { + + igraph_t g; + SEXP result, names; + + igraph_integer_t root=(igraph_integer_t) REAL(proot)[0]; + igraph_integer_t neimode=(igraph_integer_t) REAL(pneimode)[0]; + igraph_bool_t unreachable=LOGICAL(punreachable)[0]; + igraph_vector_t order, order_out, father, dist; + igraph_vector_t *p_order=0, *p_order_out=0, *p_father=0, *p_dist=0; + igraph_dfshandler_t *in_callback=0, *out_callback=0; + R_igraph_i_dfs_data_t cb_data, *p_cb_data=0; + + R_SEXP_to_igraph(graph, &g); + + if (LOGICAL(porder)[0]) { + igraph_vector_init(&order, 0); p_order=ℴ + } + if (LOGICAL(porder_out)[0]) { + igraph_vector_init(&order_out, 0); p_order_out=&order_out; + } + if (LOGICAL(pfather)[0]) { + igraph_vector_init(&father, 0); p_father=&father; + } + if (LOGICAL(pdist)[0]) { + igraph_vector_init(&dist, 0); p_dist=&dist; + } + + if (!isNull(pin_callback) || !isNull(pout_callback)) { + cb_data.graph=graph; + cb_data.fun_in=pin_callback; + cb_data.fun_out=pout_callback; + cb_data.extra=pextra; + cb_data.rho=prho; + p_cb_data = &cb_data; + } + if (!isNull(pin_callback)) { + in_callback=R_igraph_dfshandler_in; + } + if (!isNull(pout_callback)) { + out_callback=R_igraph_dfshandler_out; + } + + igraph_dfs(&g, root, (igraph_neimode_t) neimode, unreachable, + p_order, p_order_out, p_father, p_dist, + (igraph_dfshandler_t*) in_callback, + (igraph_dfshandler_t*) out_callback, p_cb_data); + + PROTECT(result=NEW_LIST(6)); + PROTECT(names=NEW_CHARACTER(6)); + + SET_STRING_ELT(names, 0, mkChar("root")); + SET_VECTOR_ELT(result, 0, NEW_NUMERIC(1)); + REAL(VECTOR_ELT(result, 0))[0] = root; + + SET_STRING_ELT(names, 1, mkChar("mode")); + SET_VECTOR_ELT(result, 1, NEW_CHARACTER(1)); + if (neimode==1) { + SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("out")); + } else if (neimode==2) { + SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("in")); + } else { + SET_STRING_ELT(VECTOR_ELT(result, 1), 0, mkChar("all")); + } + + SET_STRING_ELT(names, 2, mkChar("order")); + SET_VECTOR_ELT(result, 2, R_igraph_0orvector_to_SEXP_d(p_order)); + SET_STRING_ELT(names, 3, mkChar("order.out")); + SET_VECTOR_ELT(result, 3, R_igraph_0orvector_to_SEXP_d(p_order_out)); + SET_STRING_ELT(names, 4, mkChar("father")); + SET_VECTOR_ELT(result, 4, R_igraph_0orvector_to_SEXP_d(p_father)); + SET_STRING_ELT(names, 5, mkChar("dist")); + SET_VECTOR_ELT(result, 5, R_igraph_0orvector_to_SEXP_d(p_dist)); + + SET_NAMES(result, names); + + UNPROTECT(2); + return result; +} + + +SEXP R_igraph_cohesive_blocks(SEXP graph) { + igraph_vector_ptr_t c_blocks; + igraph_vector_t c_cohesion; + igraph_vector_t c_parent; + igraph_t c_blockTree; + igraph_t c_graph; + SEXP blocks; + SEXP cohesion; + SEXP parent; + SEXP blockTree; + SEXP result; + SEXP names; + + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_blocks, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_blocks); + if (0 != igraph_vector_init(&c_cohesion, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_cohesion); + if (0 != igraph_vector_init(&c_parent, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_parent); + + igraph_cohesive_blocks(&c_graph, &c_blocks, &c_cohesion, &c_parent, &c_blockTree); + + PROTECT(result=NEW_LIST(4)); + PROTECT(names=NEW_CHARACTER(4)); + PROTECT(blocks=R_igraph_vectorlist_to_SEXP_p1(&c_blocks)); + R_igraph_vectorlist_destroy(&c_blocks); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(cohesion=R_igraph_vector_to_SEXP(&c_cohesion)); + igraph_vector_destroy(&c_cohesion); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(parent=R_igraph_vector_to_SEXPp1(&c_parent)); + igraph_vector_destroy(&c_parent); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, &c_blockTree); + PROTECT(blockTree=R_igraph_to_SEXP(&c_blockTree)); + igraph_destroy(&c_blockTree); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(result, 0, blocks); + SET_VECTOR_ELT(result, 1, cohesion); + SET_VECTOR_ELT(result, 2, parent); + SET_VECTOR_ELT(result, 3, blockTree); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("blocks")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("cohesion")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("parent")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("blockTree")); + SET_NAMES(result, names); + + UNPROTECT(6); + return result; +} + +typedef struct { + igraph_arpack_function_t *fun; +} R_igraph_i_function_container_t; + +SEXP R_igraph_i_levc_arp(SEXP extP, SEXP extE, SEXP pv) { + R_igraph_i_function_container_t *cont = R_ExternalPtrAddr(extP); + igraph_arpack_function_t *fun= cont->fun; + void *extra=R_ExternalPtrAddr(extE); + SEXP res; + + PROTECT(res=NEW_NUMERIC(GET_LENGTH(pv))); + fun(REAL(res), REAL(pv), GET_LENGTH(pv), extra); + + UNPROTECT(1); + return res; +} + +typedef struct R_igraph_i_levc_data_t { + SEXP fun; + SEXP extra; + SEXP rho; + SEXP rho2; +} R_igraph_i_levc_data_t; + +int R_igraph_i_levc_callback(const igraph_vector_t *membership, + long int comm, + igraph_real_t eigenvalue, + const igraph_vector_t *eigenvector, + igraph_arpack_function_t *arpack_multiplier, + void *arpack_extra, + void *extra) { + + SEXP s_memb, s_comm, s_evalue, s_evector, s_multip; + SEXP R_fcall, R_multip_call; + SEXP res, l1, l2, l3; + int result; + R_igraph_i_levc_data_t *data=extra; + R_igraph_i_function_container_t cont = { arpack_multiplier }; + + PROTECT(s_memb=R_igraph_vector_to_SEXP(membership)); + PROTECT(s_comm=NEW_NUMERIC(1)); REAL(s_comm)[0]=comm; + PROTECT(s_evalue=NEW_NUMERIC(1)); REAL(s_evalue)[0]=eigenvalue; + PROTECT(s_evector=R_igraph_vector_to_SEXP(eigenvector)); + PROTECT(l1 = install("igraph.i.levc.arp")); + PROTECT(l2 = R_MakeExternalPtr((void*) &cont, R_NilValue, R_NilValue)); + PROTECT(l3 = R_MakeExternalPtr(arpack_extra, R_NilValue, R_NilValue)); + PROTECT(R_multip_call = lang3(l1, l2, l3)); + PROTECT(s_multip = eval(R_multip_call, data->rho2)); + + PROTECT(R_fcall = R_igraph_i_lang7(data->fun, s_memb, s_comm, + s_evalue, s_evector, s_multip, + data->extra)); + PROTECT(res = eval(R_fcall, data->rho)); + + result=(int) REAL(AS_NUMERIC(res))[0]; + + UNPROTECT(11); + return result; +} + +SEXP R_igraph_community_leading_eigenvector(SEXP graph, SEXP steps, + SEXP weights, + SEXP options, SEXP pstart, + SEXP callback, + SEXP callback_extra, + SEXP callback_env, + SEXP callback_env2) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_merges; + igraph_vector_t c_membership; + igraph_integer_t c_steps; + igraph_vector_t v_weights, *pweights=0; + igraph_bool_t c_start=!isNull(pstart); + igraph_arpack_options_t c_options; + igraph_real_t c_modularity; + igraph_vector_t c_eigenvalues; + igraph_vector_ptr_t c_eigenvectors; + igraph_vector_t c_history; + SEXP merges; + SEXP membership; + SEXP modularity; + SEXP eigenvalues; + SEXP eigenvectors; + SEXP history; + SEXP result, names; + + R_igraph_i_levc_data_t callback_data = { callback, callback_extra, + callback_env, callback_env2 }; + + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { + pweights=&v_weights; R_SEXP_to_vector(weights, &v_weights); + } + if (0 != igraph_matrix_init(&c_merges, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_merges); + if (c_start) { + R_SEXP_to_vector_copy(pstart, &c_membership); + } else { + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + c_steps=INTEGER(steps)[0]; + R_SEXP_to_igraph_arpack_options(options, &c_options); + if (0 != igraph_vector_init(&c_eigenvalues, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + if (0 != igraph_vector_ptr_init(&c_eigenvectors, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + if (0 != igraph_vector_init(&c_history, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + /* Call igraph */ + igraph_community_leading_eigenvector(&c_graph, pweights, + &c_merges, + &c_membership, c_steps, + &c_options, &c_modularity, + c_start, &c_eigenvalues, + &c_eigenvectors, + &c_history, + isNull(callback) ? 0 : + R_igraph_i_levc_callback, + &callback_data); + + /* Convert output */ + PROTECT(result=NEW_LIST(7)); + PROTECT(names=NEW_CHARACTER(7)); + PROTECT(merges=R_igraph_matrix_to_SEXP(&c_merges)); + igraph_matrix_destroy(&c_merges); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + PROTECT(modularity=NEW_NUMERIC(1)); + REAL(modularity)[0]=c_modularity; + PROTECT(eigenvalues=R_igraph_vector_to_SEXP(&c_eigenvalues)); + igraph_vector_destroy(&c_eigenvalues); + PROTECT(eigenvectors=R_igraph_vectorlist_to_SEXP(&c_eigenvectors)); + R_igraph_vectorlist_destroy(&c_eigenvectors); + PROTECT(history=R_igraph_vector_to_SEXP(&c_history)); + igraph_vector_destroy(&c_history); + SET_VECTOR_ELT(result, 0, merges); + SET_VECTOR_ELT(result, 1, membership); + SET_VECTOR_ELT(result, 2, options); + SET_VECTOR_ELT(result, 3, modularity); + SET_VECTOR_ELT(result, 4, eigenvalues); + SET_VECTOR_ELT(result, 5, eigenvectors); + SET_VECTOR_ELT(result, 6, history); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("merges")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("options")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("modularity")); + SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("eigenvalues")); + SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("eigenvectors")); + SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("history")); + SET_NAMES(result, names); + + UNPROTECT(8); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_get_eids(SEXP graph, SEXP pvp, SEXP pdirected, + SEXP perror, SEXP pmulti) { + igraph_t g; + igraph_vector_t vp; + igraph_vector_t res; + igraph_bool_t directed=LOGICAL(pdirected)[0]; + igraph_bool_t error=LOGICAL(perror)[0]; + igraph_bool_t multi=LOGICAL(pmulti)[0]; + SEXP result; + + R_SEXP_to_igraph(graph, &g); + R_SEXP_to_vector(pvp, &vp); + igraph_vector_init(&res, 0); + + if (multi) { + igraph_get_eids_multi(&g, &res, /*pairs=*/ &vp, /*path=*/ 0, directed, + error); + } else { + igraph_get_eids(&g, &res, /*pairs=*/ &vp, /*path=*/ 0, directed, error); + } + + PROTECT(result=R_igraph_vector_to_SEXP(&res)); + igraph_vector_destroy(&res); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_scg_semiprojectors(SEXP groups, SEXP matrix_type, SEXP p, + SEXP norm, SEXP psparse) { + /* Declarations */ + igraph_vector_t c_groups; + igraph_integer_t c_matrix_type; + igraph_matrix_t c_L; + igraph_matrix_t c_R; + igraph_sparsemat_t c_Lsparse; + igraph_sparsemat_t c_Rsparse; + igraph_vector_t c_p; + igraph_integer_t c_norm; + SEXP L; + SEXP R; + SEXP Lsparse; + SEXP Rsparse; + igraph_bool_t sparse=LOGICAL(psparse)[0]; + SEXP result, names; + /* Convert input */ + R_SEXP_to_vector(groups, &c_groups); + c_matrix_type=(igraph_integer_t) REAL(matrix_type)[0]; + if (!sparse) { + if (0 != igraph_matrix_init(&c_L, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_L); + if (0 != igraph_matrix_init(&c_R, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_R); + } else { + /* Nothing to do, because igraph_scg_semiprojectors + expect uninitialized sparse matrices */ + } + if (!isNull(p)) { R_SEXP_to_vector(p, &c_p); } + c_norm=(igraph_integer_t) REAL(norm)[0]; + /* Call igraph */ + igraph_scg_semiprojectors(&c_groups, (igraph_scg_matrix_t) + c_matrix_type, + (sparse ? 0 : &c_L), (sparse ? 0 : &c_R), + (sparse ? &c_Lsparse : 0), + (sparse ? &c_Rsparse : 0), + (isNull(p) ? 0 : &c_p), + (igraph_scg_norm_t) c_norm); + + /* Convert output */ + PROTECT(result=NEW_LIST(2)); + PROTECT(names=NEW_CHARACTER(2)); + if (!sparse) { + PROTECT(L=R_igraph_0ormatrix_to_SEXP(&c_L)); + igraph_matrix_destroy(&c_L); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(R=R_igraph_0ormatrix_to_SEXP(&c_R)); + igraph_matrix_destroy(&c_R); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(result, 0, L); + SET_VECTOR_ELT(result, 1, R); + } else { + PROTECT(Lsparse=R_igraph_0orsparsemat_to_SEXP(&c_Lsparse)); + igraph_sparsemat_destroy(&c_Lsparse); + PROTECT(Rsparse=R_igraph_0orsparsemat_to_SEXP(&c_Rsparse)); + igraph_sparsemat_destroy(&c_Rsparse); + SET_VECTOR_ELT(result, 0, Lsparse); + SET_VECTOR_ELT(result, 1, Rsparse); + } + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("L")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("R")); + SET_NAMES(result, names); + UNPROTECT(3); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_laplacian(SEXP graph, SEXP normalized, SEXP weights, + SEXP psparse) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_sparsemat_t c_sparseres; + igraph_bool_t c_normalized; + igraph_vector_t c_weights; + igraph_bool_t c_sparse=LOGICAL(psparse)[0]; + SEXP result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!c_sparse) { + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + } + if (c_sparse) { + if (0 != igraph_sparsemat_init(&c_sparseres, 0, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_sparsemat_destroy, &c_sparseres); + } + c_normalized=LOGICAL(normalized)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + /* Call igraph */ + igraph_laplacian(&c_graph, (c_sparse ? 0 : &c_res), + (c_sparse ? &c_sparseres : 0), + c_normalized, (isNull(weights) ? 0 : &c_weights)); + + /* Convert output */ + if (!c_sparse) { + PROTECT(result=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(result=R_igraph_sparsemat_to_SEXP(&c_sparseres)); + igraph_sparsemat_destroy(&c_sparseres); + IGRAPH_FINALLY_CLEAN(1); + } + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_scg_adjacency(SEXP graph, SEXP matrix, SEXP sparsmat, SEXP ev, + SEXP intervals_vector, + SEXP algorithm, SEXP evec, + SEXP groups, SEXP use_arpack, SEXP maxiter, + SEXP sparse, SEXP output, SEXP semproj, + SEXP epairs) { + + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_matrix; + igraph_sparsemat_t c_sparsmat; + igraph_vector_t c_ev; + igraph_vector_t c_intervals_vector; + igraph_integer_t c_algorithm=(igraph_integer_t) REAL(algorithm)[0]; + igraph_vector_t c_eval; + igraph_matrix_t c_evec; + igraph_vector_t c_groups; + igraph_bool_t c_use_arpack=LOGICAL(use_arpack)[0]; + igraph_integer_t c_maxiter=INTEGER(maxiter)[0]; + igraph_bool_t c_sparse=LOGICAL(sparse)[0]; + igraph_real_t c_output=REAL(output)[0]; + igraph_bool_t c_semproj=LOGICAL(semproj)[0]; + igraph_bool_t c_epairs=LOGICAL(epairs)[0]; + igraph_t c_scg_graph; + igraph_matrix_t c_scg_matrix; + igraph_sparsemat_t c_scg_sparsemat; + igraph_matrix_t c_L; + igraph_matrix_t c_R; + igraph_sparsemat_t c_Lsparse; + igraph_sparsemat_t c_Rsparse; + SEXP scg_graph; + SEXP scg_matrix; + SEXP scg_sparsemat; + SEXP L; + SEXP R; + SEXP Lsparse; + SEXP Rsparse; + SEXP result, names; + SEXP eval; + /* What to return */ + igraph_bool_t do_scg_graph= + (!isNull(graph) && c_output==1 /*default*/) || c_output==3 /*graph*/; + igraph_bool_t do_scg_matrix=!c_sparse && + ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); + igraph_bool_t do_scg_sparsemat=c_sparse && + ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); + igraph_bool_t do_L=c_semproj && !c_sparse; + igraph_bool_t do_R=c_semproj && !c_sparse; + igraph_bool_t do_Lsparse=c_semproj && c_sparse; + igraph_bool_t do_Rsparse=c_semproj && c_sparse; + igraph_bool_t do_eval=c_epairs; + igraph_bool_t do_evec=c_epairs; + + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + if (!isNull(matrix)) { R_SEXP_to_matrix(matrix, &c_matrix); } + if (!isNull(sparsmat)) { R_SEXP_to_sparsemat(sparsmat, &c_sparsmat); } + + R_SEXP_to_vector(ev, &c_ev); + R_SEXP_to_vector(intervals_vector, &c_intervals_vector); + + if (do_eval) { igraph_vector_init(&c_eval, 0); } + if (!isNull(evec)) { + R_SEXP_to_igraph_matrix_copy(evec, &c_evec); + } else if (do_evec) { + igraph_matrix_init(&c_evec, 0, 0); + } + if (!isNull(groups)) { + R_SEXP_to_vector_copy(groups, &c_groups); + } else { + igraph_vector_init(&c_groups, 0); + } + + if (do_scg_matrix) { igraph_matrix_init(&c_scg_matrix, 0, 0); } + if (do_L) { igraph_matrix_init(&c_L, 0, 0); } + if (do_R) { igraph_matrix_init(&c_R, 0, 0); } + + if (do_scg_sparsemat) { igraph_sparsemat_init(&c_scg_sparsemat, 0, 0, 0); } + + /* Call igraph */ + igraph_scg_adjacency((isNull(graph) ? 0 : &c_graph), + (isNull(matrix) ? 0 : &c_matrix), + (isNull(sparsmat) ? 0 : &c_sparsmat), &c_ev, + /*intervals=*/ 0, &c_intervals_vector, + (igraph_scg_algorithm_t) c_algorithm, + (do_eval ? &c_eval : 0), + (!isNull(evec) || do_evec ? &c_evec : 0), + &c_groups, c_use_arpack, c_maxiter, + (do_scg_graph ? &c_scg_graph : 0), + (do_scg_matrix ? &c_scg_matrix : 0), + (do_scg_sparsemat ? &c_scg_sparsemat : 0), + (do_L ? &c_L : 0), (do_R ? &c_R : 0), + (do_Lsparse ? &c_Lsparse : 0), + (do_Rsparse ? &c_Rsparse : 0)); + + if (!isNull(sparsmat)) { igraph_free(c_sparsmat.cs); } + + /* Convert output */ + PROTECT(result=NEW_LIST(6)); + PROTECT(names=NEW_CHARACTER(6)); + + if (do_eval) { + eval=R_igraph_vector_to_SEXP(&c_eval); + igraph_vector_destroy(&c_eval); + } else { + eval=R_NilValue; + } + PROTECT(eval); + + if (do_evec) { + evec=R_igraph_matrix_to_SEXP(&c_evec); + igraph_matrix_destroy(&c_evec); + } else { + evec=R_NilValue; + } + PROTECT(evec); + + PROTECT(groups=R_igraph_vector_to_SEXPp1(&c_groups)); + igraph_vector_destroy(&c_groups); + + if (do_scg_graph) { + PROTECT(scg_graph=R_igraph_to_SEXP(&c_scg_graph)); + igraph_destroy(&c_scg_graph); + UNPROTECT(1); + } else { + scg_graph=R_NilValue; + } + PROTECT(scg_graph); + + if (do_scg_matrix) { + scg_matrix=R_igraph_matrix_to_SEXP(&c_scg_matrix); + igraph_matrix_destroy(&c_scg_matrix); + } else { + scg_matrix=R_NilValue; + } + PROTECT(scg_matrix); + + if (do_scg_sparsemat) { + scg_sparsemat=R_igraph_sparsemat_to_SEXP(&c_scg_sparsemat); + igraph_sparsemat_destroy(&c_scg_sparsemat); + } else { + scg_sparsemat=R_NilValue; + } + PROTECT(scg_sparsemat); + + if (do_L) { + L=R_igraph_matrix_to_SEXP(&c_L); + igraph_matrix_destroy(&c_L); + } else { + L=R_NilValue; + } + PROTECT(L); + + if (do_R) { + R=R_igraph_matrix_to_SEXP(&c_R); + igraph_matrix_destroy(&c_R); + } else { + R=R_NilValue; + } + PROTECT(R); + + if (do_Lsparse) { + Lsparse=R_igraph_sparsemat_to_SEXP(&c_Lsparse); + igraph_sparsemat_destroy(&c_Lsparse); + } else { + Lsparse=R_NilValue; + } + PROTECT(Lsparse); + + if (do_Rsparse) { + Rsparse=R_igraph_sparsemat_to_SEXP(&c_Rsparse); + igraph_sparsemat_destroy(&c_Rsparse); + } else { + Rsparse=R_NilValue; + } + PROTECT(Rsparse); + + if (do_scg_graph) { SET_VECTOR_ELT(result, 0, scg_graph); } + if (do_scg_matrix) { SET_VECTOR_ELT(result, 0, scg_matrix); } + if (do_scg_sparsemat) { SET_VECTOR_ELT(result, 0, scg_sparsemat); } + SET_VECTOR_ELT(result, 1, groups); + if (do_L) { SET_VECTOR_ELT(result, 2, L); } + if (do_Lsparse) { SET_VECTOR_ELT(result, 2, Lsparse); } + if (do_R) { SET_VECTOR_ELT(result, 3, R); } + if (do_Rsparse) { SET_VECTOR_ELT(result, 3, Rsparse); } + SET_VECTOR_ELT(result, 4, eval); + SET_VECTOR_ELT(result, 5, evec); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("Xt")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("groups")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("L")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("R")); + SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("values")); + SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("vectors")); + SET_NAMES(result, names); + + UNPROTECT(12); + return(result); +} + +SEXP R_igraph_scg_stochastic(SEXP graph, SEXP matrix, SEXP sparsmat, SEXP ev, + SEXP intervals_vector, SEXP algorithm, SEXP norm, + SEXP evec, SEXP groups, SEXP p, SEXP use_arpack, + SEXP maxiter, SEXP sparse, SEXP output, + SEXP semproj, SEXP epairs, SEXP stat_prob) { + + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_matrix; + igraph_sparsemat_t c_sparsmat; + igraph_vector_t c_ev; + igraph_vector_t c_intervals_vector; + igraph_integer_t c_algorithm=(igraph_integer_t) REAL(algorithm)[0]; + igraph_integer_t c_norm=(igraph_integer_t) REAL(norm)[0]; + igraph_vector_complex_t c_eval; + igraph_matrix_complex_t c_evec; + igraph_vector_t c_groups; + igraph_vector_t c_p; + igraph_bool_t c_use_arpack=LOGICAL(use_arpack)[0]; + igraph_integer_t c_maxiter=INTEGER(maxiter)[0]; + igraph_bool_t c_sparse=LOGICAL(sparse)[0]; + igraph_real_t c_output=REAL(output)[0]; + igraph_bool_t c_semproj=LOGICAL(semproj)[0]; + igraph_bool_t c_epairs=LOGICAL(epairs)[0]; + igraph_bool_t c_stat_prob=LOGICAL(stat_prob)[0]; + igraph_t c_scg_graph; + igraph_matrix_t c_scg_matrix; + igraph_sparsemat_t c_scg_sparsemat; + igraph_matrix_t c_L; + igraph_matrix_t c_R; + igraph_sparsemat_t c_Lsparse; + igraph_sparsemat_t c_Rsparse; + SEXP scg_graph; + SEXP scg_matrix; + SEXP scg_sparsemat; + SEXP L; + SEXP R; + SEXP Lsparse; + SEXP Rsparse; + SEXP result, names; + SEXP eval; + /* What to return */ + igraph_bool_t do_scg_graph= + (!isNull(graph) && c_output==1 /*default*/) || c_output==3 /*graph*/; + igraph_bool_t do_scg_matrix=!c_sparse && + ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); + igraph_bool_t do_scg_sparsemat=c_sparse && + ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); + igraph_bool_t do_L=c_semproj && !c_sparse; + igraph_bool_t do_R=c_semproj && !c_sparse; + igraph_bool_t do_Lsparse=c_semproj && c_sparse; + igraph_bool_t do_Rsparse=c_semproj && c_sparse; + igraph_bool_t do_eval=c_epairs; + igraph_bool_t do_evec=c_epairs; + igraph_bool_t do_p=c_stat_prob; + + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + if (!isNull(matrix)) { R_SEXP_to_matrix(matrix, &c_matrix); } + if (!isNull(sparsmat)) { R_SEXP_to_sparsemat(sparsmat, &c_sparsmat); } + + R_SEXP_to_vector(ev, &c_ev); + R_SEXP_to_vector(intervals_vector, &c_intervals_vector); + if (do_eval) { igraph_vector_complex_init(&c_eval, 0); } + if (!isNull(evec)) { + R_SEXP_to_matrix_complex_copy(evec, &c_evec); + } else if (do_evec) { + igraph_matrix_complex_init(&c_evec, 0, 0); + } + if (!isNull(groups)) { + R_SEXP_to_vector_copy(groups, &c_groups); + } else { + igraph_vector_init(&c_groups, 0); + } + + if (!isNull(p)) { + R_SEXP_to_vector_copy(p, &c_p); + } else if (do_p) { + igraph_vector_init(&c_p, 0); + } + + if (do_scg_matrix) { igraph_matrix_init(&c_scg_matrix, 0, 0); } + if (do_L) { igraph_matrix_init(&c_L, 0, 0); } + if (do_R) { igraph_matrix_init(&c_R, 0, 0); } + + /* Call igraph */ + igraph_scg_stochastic((isNull(graph) ? 0 : &c_graph), + (isNull(matrix) ? 0 : &c_matrix), + (isNull(sparsmat) ? 0 : &c_sparsmat), &c_ev, + /*intervals=*/ 0, &c_intervals_vector, + (igraph_scg_algorithm_t) c_algorithm, + (igraph_scg_norm_t) c_norm, + (do_eval ? &c_eval : 0), + (!isNull(evec) || do_evec ? &c_evec : 0), + &c_groups, (!isNull(p) || do_p ? &c_p : 0), + c_use_arpack, c_maxiter, + (do_scg_graph ? &c_scg_graph : 0), + (do_scg_matrix ? &c_scg_matrix : 0), + (do_scg_sparsemat ? &c_scg_sparsemat : 0), + (do_L ? &c_L : 0), (do_R ? &c_R : 0), + (do_Lsparse ? &c_Lsparse : 0), + (do_Rsparse ? &c_Rsparse : 0)); + + if (!isNull(sparsmat)) { igraph_free(c_sparsmat.cs); } + + /* Convert output */ + PROTECT(result=NEW_LIST(7)); + PROTECT(names=NEW_CHARACTER(7)); + + if (do_eval) { + PROTECT(eval=R_igraph_vector_complex_to_SEXP(&c_eval)); + igraph_vector_complex_destroy(&c_eval); + } else { + PROTECT(eval=R_NilValue); + } + + if (do_evec) { + PROTECT(evec=R_igraph_matrix_complex_to_SEXP(&c_evec)); + igraph_matrix_complex_destroy(&c_evec); + } else { + PROTECT(evec=R_NilValue); + } + + if (do_p) { + PROTECT(p=R_igraph_vector_to_SEXP(&c_p)); + igraph_vector_destroy(&c_p); + } else { + PROTECT(p=R_NilValue); + } + + PROTECT(groups=R_igraph_vector_to_SEXPp1(&c_groups)); + igraph_vector_destroy(&c_groups); + + if (do_scg_graph) { + PROTECT(scg_graph=R_igraph_to_SEXP(&c_scg_graph)); + igraph_destroy(&c_scg_graph); + } else { + PROTECT(scg_graph=R_NilValue); + } + if (do_scg_matrix) { + PROTECT(scg_matrix=R_igraph_matrix_to_SEXP(&c_scg_matrix)); + igraph_matrix_destroy(&c_scg_matrix); + } else { + PROTECT(scg_matrix=R_NilValue); + } + if (do_scg_sparsemat) { + PROTECT(scg_sparsemat=R_igraph_sparsemat_to_SEXP(&c_scg_sparsemat)); + igraph_sparsemat_destroy(&c_scg_sparsemat); + } else { + PROTECT(scg_sparsemat=R_NilValue); + } + if (do_L) { + PROTECT(L=R_igraph_matrix_to_SEXP(&c_L)); + igraph_matrix_destroy(&c_L); + } else { + PROTECT(L=R_NilValue); + } + if (do_R) { + PROTECT(R=R_igraph_matrix_to_SEXP(&c_R)); + igraph_matrix_destroy(&c_R); + } else { + PROTECT(R=R_NilValue); + } + if (do_Lsparse) { + PROTECT(Lsparse=R_igraph_sparsemat_to_SEXP(&c_Lsparse)); + igraph_sparsemat_destroy(&c_Lsparse); + } else { + PROTECT(Lsparse=R_NilValue); + } + if (do_Rsparse) { + PROTECT(Rsparse=R_igraph_sparsemat_to_SEXP(&c_Rsparse)); + igraph_sparsemat_destroy(&c_Rsparse); + } else { + PROTECT(Rsparse=R_NilValue); + } + + if (do_scg_graph) { SET_VECTOR_ELT(result, 0, scg_graph); } + if (do_scg_matrix) { SET_VECTOR_ELT(result, 0, scg_matrix); } + if (do_scg_sparsemat) { SET_VECTOR_ELT(result, 0, scg_sparsemat); } + SET_VECTOR_ELT(result, 1, groups); + if (do_L) { SET_VECTOR_ELT(result, 2, L); } + if (do_Lsparse) { SET_VECTOR_ELT(result, 2, Lsparse); } + if (do_R) { SET_VECTOR_ELT(result, 3, R); } + if (do_Rsparse) { SET_VECTOR_ELT(result, 3, Rsparse); } + SET_VECTOR_ELT(result, 4, eval); + SET_VECTOR_ELT(result, 5, evec); + if (do_p) { SET_VECTOR_ELT(result, 6, p); } + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("Xt")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("groups")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("L")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("R")); + SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("values")); + SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("vectors")); + SET_STRING_ELT(names, 6, CREATE_STRING_VECTOR("p")); + SET_NAMES(result, names); + UNPROTECT(12); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_scg_laplacian(SEXP graph, SEXP matrix, SEXP sparsmat, SEXP ev, + SEXP intervals_vector, SEXP algorithm, SEXP norm, + SEXP direction, SEXP evec, SEXP groups, + SEXP use_arpack, SEXP maxiter, SEXP sparse, + SEXP output, SEXP semproj, SEXP epairs) { + + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_matrix; + igraph_sparsemat_t c_sparsmat; + igraph_vector_t c_ev; + igraph_vector_t c_intervals_vector; + igraph_integer_t c_algorithm=(igraph_integer_t) REAL(algorithm)[0]; + igraph_integer_t c_norm=(igraph_integer_t) REAL(norm)[0]; + igraph_integer_t c_direction=(igraph_integer_t) REAL(direction)[0]; + igraph_vector_complex_t c_eval; + igraph_matrix_complex_t c_evec; + igraph_vector_t c_groups; + igraph_bool_t c_use_arpack=LOGICAL(use_arpack)[0]; + igraph_integer_t c_maxiter=INTEGER(maxiter)[0]; + igraph_bool_t c_sparse=LOGICAL(sparse)[0]; + igraph_real_t c_output=REAL(output)[0]; + igraph_bool_t c_semproj=LOGICAL(semproj)[0]; + igraph_bool_t c_epairs=LOGICAL(epairs)[0]; + igraph_t c_scg_graph; + igraph_matrix_t c_scg_matrix; + igraph_sparsemat_t c_scg_sparsemat; + igraph_matrix_t c_L; + igraph_matrix_t c_R; + igraph_sparsemat_t c_Lsparse; + igraph_sparsemat_t c_Rsparse; + SEXP eval; + SEXP scg_graph; + SEXP scg_matrix; + SEXP scg_sparsemat; + SEXP L; + SEXP R; + SEXP Lsparse; + SEXP Rsparse; + SEXP result, names; + /* What to return */ + igraph_bool_t do_scg_graph= + (!isNull(graph) && c_output==1 /*default*/) || c_output==3 /*graph*/; + igraph_bool_t do_scg_matrix=!c_sparse && + ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); + igraph_bool_t do_scg_sparsemat=c_sparse && + ((isNull(graph) && c_output==1 /*default*/) || c_output==2 /*matrix*/); + igraph_bool_t do_L=c_semproj && !c_sparse; + igraph_bool_t do_R=c_semproj && !c_sparse; + igraph_bool_t do_Lsparse=c_semproj && c_sparse; + igraph_bool_t do_Rsparse=c_semproj && c_sparse; + igraph_bool_t do_eval=c_epairs; + igraph_bool_t do_evec=c_epairs; + + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + if (!isNull(matrix)) { R_SEXP_to_matrix(matrix, &c_matrix); } + if (!isNull(sparsmat)) { R_SEXP_to_sparsemat(sparsmat, &c_sparsmat); } + + R_SEXP_to_vector(ev, &c_ev); + R_SEXP_to_vector(intervals_vector, &c_intervals_vector); + + if (do_eval) { igraph_vector_complex_init(&c_eval, 0); } + if (!isNull(evec)) { + R_SEXP_to_matrix_complex_copy(evec, &c_evec); + } else if (do_evec) { + igraph_matrix_complex_init(&c_evec, 0, 0); + } + if (!isNull(groups)) { + R_SEXP_to_vector_copy(groups, &c_groups); + } else { + igraph_vector_init(&c_groups, 0); + } + + if (do_scg_matrix) { igraph_matrix_init(&c_scg_matrix, 0, 0); } + if (do_L) { igraph_matrix_init(&c_L, 0, 0); } + if (do_R) { igraph_matrix_init(&c_R, 0, 0); } + + /* Call igraph */ + igraph_scg_laplacian((isNull(graph) ? 0 : &c_graph), + (isNull(matrix) ? 0 : &c_matrix), + (isNull(sparsmat) ? 0 : &c_sparsmat), &c_ev, + /*intervals=*/ 0, &c_intervals_vector, + (igraph_scg_algorithm_t) c_algorithm, + (igraph_scg_norm_t) c_norm, + (igraph_scg_direction_t) c_direction, + (do_eval ? &c_eval : 0), + (!isNull(evec) || do_evec ? &c_evec : 0), + &c_groups, c_use_arpack, c_maxiter, + (do_scg_graph ? &c_scg_graph : 0), + (do_scg_matrix ? &c_scg_matrix : 0), + (do_scg_sparsemat ? &c_scg_sparsemat : 0), + (do_L ? &c_L : 0), (do_R ? &c_R : 0), + (do_Lsparse ? &c_Lsparse : 0), + (do_Rsparse ? &c_Rsparse : 0)); + + if (!isNull(sparsmat)) { igraph_free(c_sparsmat.cs); } + + /* Convert output */ + PROTECT(result=NEW_LIST(6)); + PROTECT(names=NEW_CHARACTER(6)); + + if (do_eval) { + PROTECT(eval=R_igraph_vector_complex_to_SEXP(&c_eval)); + igraph_vector_complex_destroy(&c_eval); + } else { + PROTECT(eval=R_NilValue); + } + + if (do_evec) { + PROTECT(evec=R_igraph_matrix_complex_to_SEXP(&c_evec)); + igraph_matrix_complex_destroy(&c_evec); + } else { + PROTECT(evec=R_NilValue); + } + + PROTECT(groups=R_igraph_vector_to_SEXPp1(&c_groups)); + igraph_vector_destroy(&c_groups); + + if (do_scg_graph) { + PROTECT(scg_graph=R_igraph_to_SEXP(&c_scg_graph)); + igraph_destroy(&c_scg_graph); + } else { + PROTECT(scg_graph=R_NilValue); + } + if (do_scg_matrix) { + PROTECT(scg_matrix=R_igraph_matrix_to_SEXP(&c_scg_matrix)); + igraph_matrix_destroy(&c_scg_matrix); + } else { + PROTECT(scg_matrix=R_NilValue); + } + if (do_scg_sparsemat) { + PROTECT(scg_sparsemat=R_igraph_sparsemat_to_SEXP(&c_scg_sparsemat)); + igraph_sparsemat_destroy(&c_scg_sparsemat); + } else { + PROTECT(scg_sparsemat=R_NilValue); + } + if (do_L) { + PROTECT(L=R_igraph_matrix_to_SEXP(&c_L)); + igraph_matrix_destroy(&c_L); + } else { + PROTECT(L=R_NilValue); + } + if (do_R) { + PROTECT(R=R_igraph_matrix_to_SEXP(&c_R)); + igraph_matrix_destroy(&c_R); + } else { + PROTECT(R=R_NilValue); + } + if (do_Lsparse) { + PROTECT(Lsparse=R_igraph_sparsemat_to_SEXP(&c_Lsparse)); + igraph_sparsemat_destroy(&c_Lsparse); + } else { + PROTECT(Lsparse=R_NilValue); + } + if (do_Rsparse) { + PROTECT(Rsparse=R_igraph_sparsemat_to_SEXP(&c_Rsparse)); + igraph_sparsemat_destroy(&c_Rsparse); + } else { + PROTECT(Rsparse=R_NilValue); + } + + if (do_scg_graph) { SET_VECTOR_ELT(result, 0, scg_graph); } + if (do_scg_matrix) { SET_VECTOR_ELT(result, 0, scg_matrix); } + if (do_scg_sparsemat) { SET_VECTOR_ELT(result, 0, scg_sparsemat); } + SET_VECTOR_ELT(result, 1, groups); + if (do_L) { SET_VECTOR_ELT(result, 2, L); } + if (do_Lsparse) { SET_VECTOR_ELT(result, 2, Lsparse); } + if (do_R) { SET_VECTOR_ELT(result, 3, R); } + if (do_Rsparse) { SET_VECTOR_ELT(result, 3, Rsparse); } + SET_VECTOR_ELT(result, 4, eval); + SET_VECTOR_ELT(result, 5, evec); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("Xt")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("groups")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("L")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("R")); + SET_STRING_ELT(names, 4, CREATE_STRING_VECTOR("values")); + SET_STRING_ELT(names, 5, CREATE_STRING_VECTOR("vectors")); + SET_NAMES(result, names); + UNPROTECT(11); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_subisomorphic_lad(SEXP pattern, SEXP target, SEXP domains, + SEXP induced, SEXP time_limit, + SEXP pqmap, SEXP pqall_maps) { + /* Declarations */ + igraph_t c_pattern; + igraph_t c_target; + igraph_vector_ptr_t c_domains; + igraph_bool_t c_iso; + igraph_vector_t c_map; + igraph_vector_ptr_t c_maps; + igraph_bool_t c_induced; + int c_time_limit; + igraph_bool_t c_qmap; + igraph_bool_t c_qall_maps; + SEXP iso; + SEXP map; + SEXP maps; + + SEXP result, names; + /* Convert input */ + R_SEXP_to_igraph(pattern, &c_pattern); + R_SEXP_to_igraph(target, &c_target); + + R_igraph_SEXP_to_0orvectorlist(domains, &c_domains); + + c_qmap=LOGICAL(pqmap)[0]; + c_qall_maps=LOGICAL(pqall_maps)[0]; + if (c_qmap) { + if (0 != igraph_vector_init(&c_map, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map); + map=R_GlobalEnv; /* hack to have a non-NULL value */ + } else { + map=R_NilValue; + } + + if (c_qall_maps) { + if (0 != igraph_vector_ptr_init(&c_maps, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_maps); + maps=R_GlobalEnv; /* hack to have a non-NULL value */ + } else { + maps=R_NilValue; + } + + c_induced=LOGICAL(induced)[0]; + c_time_limit=INTEGER(time_limit)[0]; + /* Call igraph */ + igraph_subisomorphic_lad(&c_pattern, &c_target, + (isNull(domains) ? 0 : &c_domains), &c_iso, + (isNull(map) ? 0 : &c_map), + (isNull(maps) ? 0 : &c_maps), + c_induced, c_time_limit); + + /* Convert output */ + PROTECT(result=NEW_LIST(3)); + PROTECT(names=NEW_CHARACTER(3)); + PROTECT(iso=NEW_LOGICAL(1)); + LOGICAL(iso)[0]=c_iso; + if (!isNull(map)) { + PROTECT(map=R_igraph_0orvector_to_SEXP(&c_map)); + igraph_vector_destroy(&c_map); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(map=R_NilValue); + } + if (!isNull(maps)) { + PROTECT(maps=R_igraph_0orvectorlist_to_SEXP(&c_maps)); + R_igraph_vectorlist_destroy(&c_maps); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(maps=R_NilValue); + } + SET_VECTOR_ELT(result, 0, iso); + SET_VECTOR_ELT(result, 1, map); + SET_VECTOR_ELT(result, 2, maps); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("iso")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("map")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("maps")); + SET_NAMES(result, names); + UNPROTECT(4); + + UNPROTECT(1); + return(result); +} + +/*-------------------------------------------/ + / igraph_graphlets / + /-------------------------------------------*/ +SEXP R_igraph_graphlets(SEXP graph, SEXP weights, SEXP niter) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_vector_ptr_t c_cliques; + igraph_vector_t c_Mu; + int c_niter; + SEXP cliques; + SEXP Mu; + SEXP result, names; + + R_PreserveObject(R_igraph_attribute_protected=NEW_LIST(100)); + R_igraph_attribute_protected_size=0; + IGRAPH_FINALLY(R_igraph_attribute_protected_destroy, 0); + + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (0 != igraph_vector_ptr_init(&c_cliques, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_cliques); + if (0 != igraph_vector_init(&c_Mu, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_Mu); + c_niter=INTEGER(niter)[0]; + /* Call igraph */ + igraph_graphlets(&c_graph, (isNull(weights) ? 0 : &c_weights), &c_cliques, + &c_Mu, c_niter); + + /* Convert output */ + PROTECT(result=NEW_LIST(2)); + PROTECT(names=NEW_CHARACTER(2)); + PROTECT(cliques=R_igraph_vectorlist_to_SEXP_p1(&c_cliques)); + R_igraph_vectorlist_destroy(&c_cliques); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(Mu=R_igraph_vector_to_SEXP(&c_Mu)); + igraph_vector_destroy(&c_Mu); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(result, 0, cliques); + SET_VECTOR_ELT(result, 1, Mu); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("cliques")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("Mu")); + SET_NAMES(result, names); + + UNPROTECT(4); + IGRAPH_FINALLY_CLEAN(1); + R_igraph_attribute_protected_destroy(0); + + return(result); +} + +/*-------------------------------------------/ +/ igraph_graphlets_candidate_basis / +/-------------------------------------------*/ +SEXP R_igraph_graphlets_candidate_basis(SEXP graph, SEXP weights) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_vector_ptr_t c_cliques; + igraph_vector_t c_thresholds; + SEXP cliques; + SEXP thresholds; + + SEXP result, names; + + R_PreserveObject(R_igraph_attribute_protected=NEW_LIST(100)); + R_igraph_attribute_protected_size=0; + IGRAPH_FINALLY(R_igraph_attribute_protected_destroy, 0); + + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (0 != igraph_vector_ptr_init(&c_cliques, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_cliques); + if (0 != igraph_vector_init(&c_thresholds, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_thresholds); + /* Call igraph */ + igraph_graphlets_candidate_basis(&c_graph, (isNull(weights) ? 0 : &c_weights), + &c_cliques, &c_thresholds); + + /* Convert output */ + PROTECT(result=NEW_LIST(2)); + PROTECT(names=NEW_CHARACTER(2)); + PROTECT(cliques=R_igraph_vectorlist_to_SEXP_p1(&c_cliques)); + R_igraph_vectorlist_destroy(&c_cliques); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(thresholds=R_igraph_vector_to_SEXP(&c_thresholds)); + igraph_vector_destroy(&c_thresholds); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(result, 0, cliques); + SET_VECTOR_ELT(result, 1, thresholds); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("cliques")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("thresholds")); + SET_NAMES(result, names); + + UNPROTECT(4); + IGRAPH_FINALLY_CLEAN(1); + R_igraph_attribute_protected_destroy(0); + + return(result); +} + +int igraph_i_graphlets_project(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_vector_ptr_t *cliques, + igraph_vector_t *Mu, igraph_bool_t startMu, + int niter, int vid1); + +/*-------------------------------------------/ +/ igraph_graphlets_project / +/-------------------------------------------*/ + +SEXP R_igraph_graphlets_project(SEXP graph, SEXP weights, SEXP cliques, + SEXP Mu, SEXP niter) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_vector_ptr_t c_cliques; + igraph_vector_t c_Mu; + int c_niter; + + SEXP result; + + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(cliques)) { R_igraph_SEXP_to_vectorlist(cliques, &c_cliques); } + if (0 != R_SEXP_to_vector_copy(Mu, &c_Mu)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_Mu); + c_niter=INTEGER(niter)[0]; + + /* TODO: Change igraph_i_graphlets_project to igraph_graphlets_project, because + * we should not depend on non-public functions from igraph. + */ + /* Call igraph */ + igraph_i_graphlets_project(&c_graph, (isNull(weights) ? 0 : &c_weights), + &c_cliques, &c_Mu, /*startMu=*/ 1, c_niter, + /*vid1=*/ 1); + + /* Convert output */ + PROTECT(Mu=R_igraph_vector_to_SEXP(&c_Mu)); + igraph_vector_destroy(&c_Mu); + IGRAPH_FINALLY_CLEAN(1); + result=Mu; + + UNPROTECT(1); + return(result); +} + + +SEXP R_igraph_adjacency_spectral_embedding(SEXP graph, SEXP no, + SEXP pweights, SEXP pwhich, + SEXP scaled, SEXP cvec, + SEXP options) { + + /* Declarations */ + igraph_t c_graph; + igraph_vector_t weights; + igraph_eigen_which_position_t c_which; + igraph_integer_t c_no; + igraph_bool_t c_scaled; + igraph_matrix_t c_X; + igraph_matrix_t c_Y; + igraph_vector_t c_D; + igraph_vector_t c_cvec; + igraph_arpack_options_t c_options; + SEXP X; + SEXP Y; + SEXP D; + igraph_bool_t directed; + + SEXP result, names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + directed=igraph_is_directed(&c_graph); + if (!isNull(pweights)) { + R_SEXP_to_vector(pweights, &weights); + } + c_which=INTEGER(pwhich)[0]; + c_no=INTEGER(no)[0]; + c_scaled=LOGICAL(scaled)[0]; + if (0 != igraph_matrix_init(&c_X, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_X); + if (directed) { + if (0 != igraph_matrix_init(&c_Y, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_Y); + } + if (0 != igraph_vector_init(&c_D, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_D); + R_SEXP_to_vector(cvec, &c_cvec); + R_SEXP_to_igraph_arpack_options(options, &c_options); + +/* Call igraph */ + igraph_adjacency_spectral_embedding(&c_graph, c_no, + isNull(pweights) ? 0 : &weights, + c_which, c_scaled, &c_X, + directed ? &c_Y : 0, &c_D, + &c_cvec, &c_options); + + /* Convert output */ + PROTECT(result=NEW_LIST(4)); + PROTECT(names=NEW_CHARACTER(4)); + PROTECT(X=R_igraph_matrix_to_SEXP(&c_X)); + igraph_matrix_destroy(&c_X); + IGRAPH_FINALLY_CLEAN(1); + if (directed) { + PROTECT(Y=R_igraph_matrix_to_SEXP(&c_Y)); + igraph_matrix_destroy(&c_Y); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(Y=R_NilValue); + } + PROTECT(D=R_igraph_vector_to_SEXP(&c_D)); + igraph_vector_destroy(&c_D); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + SET_VECTOR_ELT(result, 0, X); + SET_VECTOR_ELT(result, 1, Y); + SET_VECTOR_ELT(result, 2, D); + SET_VECTOR_ELT(result, 3, options); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("X")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("Y")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("D")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("options")); + SET_NAMES(result, names); + UNPROTECT(5); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_laplacian_spectral_embedding(SEXP graph, SEXP no, + SEXP weights, SEXP which, + SEXP type, + SEXP scaled, SEXP options) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no; + igraph_vector_t c_weights; + igraph_eigen_which_position_t c_which; + igraph_laplacian_spectral_embedding_type_t c_type; + igraph_bool_t c_scaled; + igraph_matrix_t c_X; + igraph_matrix_t c_Y; + igraph_vector_t c_D; + igraph_arpack_options_t c_options; + SEXP X; + SEXP Y; + SEXP D; + igraph_bool_t directed; + + SEXP result, names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + directed=igraph_is_directed(&c_graph); + c_no=INTEGER(no)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_which=INTEGER(which)[0]; + c_type=(igraph_laplacian_spectral_embedding_type_t) INTEGER(type)[0]; + c_scaled=LOGICAL(scaled)[0]; + if (0 != igraph_matrix_init(&c_X, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_X); + if (directed) { + if (0 != igraph_matrix_init(&c_Y, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_Y); + } + if (0 != igraph_vector_init(&c_D, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_D); + R_SEXP_to_igraph_arpack_options(options, &c_options); + /* Call igraph */ + igraph_laplacian_spectral_embedding(&c_graph, c_no, + (isNull(weights) ? 0 : &c_weights), + c_which, c_type, c_scaled, + &c_X, directed ? &c_Y : 0, + &c_D, &c_options); + + /* Convert output */ + PROTECT(result=NEW_LIST(4)); + PROTECT(names=NEW_CHARACTER(4)); + PROTECT(X=R_igraph_matrix_to_SEXP(&c_X)); + igraph_matrix_destroy(&c_X); + IGRAPH_FINALLY_CLEAN(1); + if (directed) { + PROTECT(Y=R_igraph_matrix_to_SEXP(&c_Y)); + igraph_matrix_destroy(&c_Y); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(Y=R_NilValue); + } + PROTECT(D=R_igraph_0orvector_to_SEXP(&c_D)); + igraph_vector_destroy(&c_D); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + SET_VECTOR_ELT(result, 0, X); + SET_VECTOR_ELT(result, 1, Y); + SET_VECTOR_ELT(result, 2, D); + SET_VECTOR_ELT(result, 3, options); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("X")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("Y")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("D")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("options")); + SET_NAMES(result, names); + UNPROTECT(5); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_simple_interconnected_islands_game(SEXP islands_n, SEXP islands_size, SEXP islands_pin, SEXP n_inter) { + + igraph_t g; + igraph_integer_t a=INTEGER(islands_n)[0]; + igraph_integer_t b=INTEGER(islands_size)[0]; + igraph_real_t c=REAL(islands_pin)[0]; + igraph_integer_t d=INTEGER(n_inter)[0]; + SEXP result; + + igraph_simple_interconnected_islands_game(&g, a, b, c, d); + PROTECT(result=R_igraph_to_SEXP(&g)); + igraph_destroy(&g); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_version() { + const char *version; + SEXP result; + igraph_version(&version, /*major=*/ 0, /*minor=*/ 0, /*patch=*/ 0); + PROTECT(result=NEW_CHARACTER(1)); + SET_STRING_ELT(result, 0, mkChar(version)); + UNPROTECT(1); + return result; +} + +SEXP R_igraph_bipartite_projection(SEXP graph, SEXP types, SEXP probe1, + SEXP pwhich) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_t c_proj1; + igraph_t c_proj2; + igraph_vector_t c_multiplicity1; + igraph_vector_t c_multiplicity2; + igraph_integer_t c_probe1; + igraph_integer_t which=INTEGER(pwhich)[0]; + igraph_bool_t do_1=(which == 0 || which == 1); + igraph_bool_t do_2=(which == 0 || which == 2); + SEXP proj1; + SEXP proj2; + SEXP multiplicity1; + SEXP multiplicity2; + + SEXP result, names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + if (0 != igraph_vector_init(&c_multiplicity1, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_multiplicity1); + multiplicity1 = R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_multiplicity2, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_multiplicity2); + multiplicity2=R_GlobalEnv; /* hack to have a non-NULL value */ + c_probe1=INTEGER(probe1)[0]; + /* Call igraph */ + igraph_bipartite_projection(&c_graph, (isNull(types) ? 0 : &c_types), + do_1 ? &c_proj1 : 0, do_2 ? &c_proj2 : 0, + (isNull(multiplicity1) ? 0 : &c_multiplicity1), + (isNull(multiplicity2) ? 0 : &c_multiplicity2), + c_probe1); + + /* Convert output */ + PROTECT(result=NEW_LIST(4)); + PROTECT(names=NEW_CHARACTER(4)); + if (do_1) { + IGRAPH_FINALLY(igraph_destroy, &c_proj1); + PROTECT(proj1=R_igraph_to_SEXP(&c_proj1)); + igraph_destroy(&c_proj1); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(proj1=R_NilValue); + } + if (do_2) { + IGRAPH_FINALLY(igraph_destroy, &c_proj2); + PROTECT(proj2=R_igraph_to_SEXP(&c_proj2)); + igraph_destroy(&c_proj2); + IGRAPH_FINALLY_CLEAN(1); + } else { + PROTECT(proj2=R_NilValue); + } + PROTECT(multiplicity1=R_igraph_0orvector_to_SEXP(&c_multiplicity1)); + igraph_vector_destroy(&c_multiplicity1); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(multiplicity2=R_igraph_0orvector_to_SEXP(&c_multiplicity2)); + igraph_vector_destroy(&c_multiplicity2); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(result, 0, proj1); + SET_VECTOR_ELT(result, 1, proj2); + SET_VECTOR_ELT(result, 2, multiplicity1); + SET_VECTOR_ELT(result, 3, multiplicity2); + SET_STRING_ELT(names, 0, CREATE_STRING_VECTOR("proj1")); + SET_STRING_ELT(names, 1, CREATE_STRING_VECTOR("proj2")); + SET_STRING_ELT(names, 2, CREATE_STRING_VECTOR("multiplicity1")); + SET_STRING_ELT(names, 3, CREATE_STRING_VECTOR("multiplicity2")); + SET_NAMES(result, names); + UNPROTECT(5); + + UNPROTECT(1); + return(result); +} + +SEXP R_igraph_solve_lsap(SEXP px, SEXP pnc) { + + igraph_matrix_t x; + igraph_integer_t nc = INTEGER(pnc)[0]; + igraph_vector_int_t p; + SEXP result; + + R_SEXP_to_matrix(px, &x); + + igraph_vector_int_init(&p, nc); + IGRAPH_FINALLY(igraph_vector_int_destroy, &p); + + igraph_solve_lsap(&x, nc, &p); + + PROTECT(result = R_igraph_vector_int_to_SEXP(&p)); + igraph_vector_int_destroy(&p); + IGRAPH_FINALLY_CLEAN(1); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_adjacent_vertices(SEXP pgraph, SEXP pv, SEXP pmode) { + + igraph_t graph; + igraph_vs_t vs; + igraph_vit_t vit; + igraph_integer_t mode = (igraph_integer_t) REAL(pmode)[0]; + SEXP result; + size_t i, n; + igraph_lazy_adjlist_t adjlist; + + R_SEXP_to_igraph(pgraph, &graph); + R_SEXP_to_igraph_vs(pv, &graph, &vs); + IGRAPH_FINALLY(igraph_vs_destroy, &vs); + + igraph_vit_create(&graph, vs, &vit); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + n = IGRAPH_VIT_SIZE(vit); + + igraph_lazy_adjlist_init( + &graph, &adjlist, mode, + /* loops = */ IGRAPH_LOOPS_TWICE, + /* multiple = */ IGRAPH_MULTIPLE + ); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + + PROTECT(result = NEW_LIST(n)); + for (IGRAPH_VIT_RESET(vit), i=0; + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) { + long int vid = IGRAPH_VIT_GET(vit); + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, vid); + SET_VECTOR_ELT(result, i, R_igraph_vector_int_to_SEXP(neis)); + } + + igraph_lazy_adjlist_destroy(&adjlist); + igraph_vit_destroy(&vit); + igraph_vs_destroy(&vs); + IGRAPH_FINALLY_CLEAN(3); + + UNPROTECT(1); + return result; +} + +SEXP R_igraph_incident_edges(SEXP pgraph, SEXP pe, SEXP pmode) { + + igraph_t graph; + igraph_vs_t vs; + igraph_vit_t vit; + igraph_integer_t mode = (igraph_integer_t) REAL(pmode)[0]; + SEXP result; + size_t i, n; + igraph_lazy_inclist_t adjlist; + + R_SEXP_to_igraph(pgraph, &graph); + R_SEXP_to_igraph_vs(pe, &graph, &vs); + IGRAPH_FINALLY(igraph_vs_destroy, &vs); + + igraph_vit_create(&graph, vs, &vit); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + n = IGRAPH_VIT_SIZE(vit); + + igraph_lazy_inclist_init(&graph, &adjlist, mode, IGRAPH_LOOPS_TWICE); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &adjlist); + + PROTECT(result = NEW_LIST(n)); + for (IGRAPH_VIT_RESET(vit), i=0; + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) { + long int eid = IGRAPH_VIT_GET(vit); + igraph_vector_int_t *neis = igraph_lazy_inclist_get(&adjlist, eid); + SET_VECTOR_ELT(result, i, R_igraph_vector_int_to_SEXP(neis)); + } + + igraph_lazy_inclist_destroy(&adjlist); + igraph_vit_destroy(&vit); + igraph_vs_destroy(&vs); + IGRAPH_FINALLY_CLEAN(3); + + UNPROTECT(1); + return result; +} + +/***********************************************/ +/* THE REST IS GENERATED BY stimulus */ +/***********************************************/ + +/*-------------------------------------------/ +/ igraph_empty / +/-------------------------------------------*/ +SEXP R_igraph_empty(SEXP n, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_empty(&c_graph, c_n, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_vcount / +/-------------------------------------------*/ +SEXP R_igraph_vcount(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_result; + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Call igraph */ + c_result= igraph_vcount(&c_graph); + + /* Convert output */ + + PROTECT(r_result=NEW_INTEGER(1)); + INTEGER(r_result)[0]=c_result; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_full_citation / +/-------------------------------------------*/ +SEXP R_igraph_full_citation(SEXP n, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_full_citation(&c_graph, c_n, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_extended_chordal_ring / +/-------------------------------------------*/ +SEXP R_igraph_extended_chordal_ring(SEXP nodes, SEXP W, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_nodes; + igraph_matrix_t c_W; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_nodes=INTEGER(nodes)[0]; + R_SEXP_to_matrix(W, &c_W); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_extended_chordal_ring(&c_graph, c_nodes, &c_W, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_lcf_vector / +/-------------------------------------------*/ +SEXP R_igraph_lcf_vector(SEXP n, SEXP shifts, SEXP repeats) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_vector_t c_shifts; + igraph_integer_t c_repeats; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + R_SEXP_to_vector(shifts, &c_shifts); + c_repeats=INTEGER(repeats)[0]; + /* Call igraph */ + igraph_lcf_vector(&c_graph, c_n, &c_shifts, c_repeats); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_adjlist / +/-------------------------------------------*/ +SEXP R_igraph_adjlist(SEXP adjlist, SEXP mode, SEXP duplicate) { + /* Declarations */ + igraph_t c_graph; + igraph_adjlist_t c_adjlist; + igraph_neimode_t c_mode; + igraph_bool_t c_duplicate; + SEXP graph; + + SEXP r_result; + /* Convert input */ + if (0 != R_SEXP_to_igraph_adjlist(adjlist, &c_adjlist)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_duplicate=LOGICAL(duplicate)[0]; + /* Call igraph */ + igraph_adjlist(&c_graph, &c_adjlist, c_mode, c_duplicate); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_full_bipartite / +/-------------------------------------------*/ +SEXP R_igraph_full_bipartite(SEXP n1, SEXP n2, SEXP directed, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_integer_t c_n1; + igraph_integer_t c_n2; + igraph_bool_t c_directed; + igraph_neimode_t c_mode; + SEXP graph; + SEXP types; + + SEXP r_result, r_names; + /* Convert input */ + if (0 != igraph_vector_bool_init(&c_types, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_types); + types=R_GlobalEnv; /* hack to have a non-NULL value */ + c_n1=INTEGER(n1)[0]; + c_n2=INTEGER(n2)[0]; + c_directed=LOGICAL(directed)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_full_bipartite(&c_graph, (isNull(types) ? 0 : &c_types), c_n1, c_n2, c_directed, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(types=R_igraph_0orvector_bool_to_SEXP(&c_types)); + igraph_vector_bool_destroy(&c_types); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, graph); + SET_VECTOR_ELT(r_result, 1, types); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("graph")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("types")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_realize_degree_sequence / +/-------------------------------------------*/ +SEXP R_igraph_realize_degree_sequence(SEXP out_deg, SEXP in_deg, SEXP allowed_edge_types, SEXP method) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_out_deg; + igraph_vector_t c_in_deg; + igraph_edge_type_sw_t c_allowed_edge_types; + igraph_realize_degseq_t c_method; + SEXP graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector(out_deg, &c_out_deg); + if (!isNull(in_deg)) { R_SEXP_to_vector(in_deg, &c_in_deg); } + c_allowed_edge_types = (igraph_edge_type_sw_t) Rf_asInteger(allowed_edge_types); + c_method = (igraph_realize_degseq_t) Rf_asInteger(method); + /* Call igraph */ + igraph_realize_degree_sequence(&c_graph, &c_out_deg, (isNull(in_deg) ? 0 : &c_in_deg), c_allowed_edge_types, c_method); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_rewire_edges / +/-------------------------------------------*/ +SEXP R_igraph_rewire_edges(SEXP graph, SEXP prob, SEXP loops, SEXP multiple) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_prob; + igraph_bool_t c_loops; + igraph_bool_t c_multiple; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph_copy(graph, &c_graph); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + c_prob=REAL(prob)[0]; + c_loops=LOGICAL(loops)[0]; + c_multiple=LOGICAL(multiple)[0]; + /* Call igraph */ + igraph_rewire_edges(&c_graph, c_prob, c_loops, c_multiple); + + /* Convert output */ + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_rewire_directed_edges / +/-------------------------------------------*/ +SEXP R_igraph_rewire_directed_edges(SEXP graph, SEXP prob, SEXP loops, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_prob; + igraph_bool_t c_loops; + igraph_neimode_t c_mode; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph_copy(graph, &c_graph); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + c_prob=REAL(prob)[0]; + c_loops=LOGICAL(loops)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_rewire_directed_edges(&c_graph, c_prob, c_loops, c_mode); + + /* Convert output */ + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_forest_fire_game / +/-------------------------------------------*/ +SEXP R_igraph_forest_fire_game(SEXP nodes, SEXP fw_prob, SEXP bw_factor, SEXP ambs, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_nodes; + igraph_real_t c_fw_prob; + igraph_real_t c_bw_factor; + igraph_integer_t c_ambs; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_nodes=INTEGER(nodes)[0]; + c_fw_prob=REAL(fw_prob)[0]; + c_bw_factor=REAL(bw_factor)[0]; + c_ambs=INTEGER(ambs)[0]; + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_forest_fire_game(&c_graph, c_nodes, c_fw_prob, c_bw_factor, c_ambs, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_static_fitness_game / +/-------------------------------------------*/ +SEXP R_igraph_static_fitness_game(SEXP no_of_edges, SEXP fitness_out, SEXP fitness_in, SEXP loops, SEXP multiple) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no_of_edges; + igraph_vector_t c_fitness_out; + igraph_vector_t c_fitness_in; + igraph_bool_t c_loops; + igraph_bool_t c_multiple; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_no_of_edges=INTEGER(no_of_edges)[0]; + R_SEXP_to_vector(fitness_out, &c_fitness_out); + if (!isNull(fitness_in)) { R_SEXP_to_vector(fitness_in, &c_fitness_in); } + c_loops=LOGICAL(loops)[0]; + c_multiple=LOGICAL(multiple)[0]; + /* Call igraph */ + igraph_static_fitness_game(&c_graph, c_no_of_edges, &c_fitness_out, (isNull(fitness_in) ? 0 : &c_fitness_in), c_loops, c_multiple); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_static_power_law_game / +/-------------------------------------------*/ +SEXP R_igraph_static_power_law_game(SEXP no_of_nodes, SEXP no_of_edges, SEXP exponent_out, SEXP exponent_in, SEXP loops, SEXP multiple, SEXP finite_size_correction) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no_of_nodes; + igraph_integer_t c_no_of_edges; + igraph_real_t c_exponent_out; + igraph_real_t c_exponent_in; + igraph_bool_t c_loops; + igraph_bool_t c_multiple; + igraph_bool_t c_finite_size_correction; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_no_of_nodes=INTEGER(no_of_nodes)[0]; + c_no_of_edges=INTEGER(no_of_edges)[0]; + c_exponent_out=REAL(exponent_out)[0]; + c_exponent_in=REAL(exponent_in)[0]; + c_loops=LOGICAL(loops)[0]; + c_multiple=LOGICAL(multiple)[0]; + c_finite_size_correction=LOGICAL(finite_size_correction)[0]; + /* Call igraph */ + igraph_static_power_law_game(&c_graph, c_no_of_nodes, c_no_of_edges, c_exponent_out, c_exponent_in, c_loops, c_multiple, c_finite_size_correction); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_k_regular_game / +/-------------------------------------------*/ +SEXP R_igraph_k_regular_game(SEXP no_of_nodes, SEXP k, SEXP directed, SEXP multiple) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no_of_nodes; + igraph_integer_t c_k; + igraph_bool_t c_directed; + igraph_bool_t c_multiple; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_no_of_nodes=INTEGER(no_of_nodes)[0]; + c_k=INTEGER(k)[0]; + c_directed=LOGICAL(directed)[0]; + c_multiple=LOGICAL(multiple)[0]; + /* Call igraph */ + igraph_k_regular_game(&c_graph, c_no_of_nodes, c_k, c_directed, c_multiple); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_sbm_game / +/-------------------------------------------*/ +SEXP R_igraph_sbm_game(SEXP n, SEXP pref_matrix, SEXP block_sizes, SEXP directed, SEXP loops) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_matrix_t c_pref_matrix; + igraph_vector_int_t c_block_sizes; + igraph_bool_t c_directed; + igraph_bool_t c_loops; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + R_SEXP_to_matrix(pref_matrix, &c_pref_matrix); + R_SEXP_to_vector_int(block_sizes, &c_block_sizes); + c_directed=LOGICAL(directed)[0]; + c_loops=LOGICAL(loops)[0]; + /* Call igraph */ + igraph_sbm_game(&c_graph, c_n, &c_pref_matrix, &c_block_sizes, c_directed, c_loops); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hsbm_game / +/-------------------------------------------*/ +SEXP R_igraph_hsbm_game(SEXP n, SEXP m, SEXP rho, SEXP C, SEXP p) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_integer_t c_m; + igraph_vector_t c_rho; + igraph_matrix_t c_C; + igraph_real_t c_p; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + c_m=INTEGER(m)[0]; + R_SEXP_to_vector(rho, &c_rho); + R_SEXP_to_matrix(C, &c_C); + c_p=REAL(p)[0]; + /* Call igraph */ + igraph_hsbm_game(&c_graph, c_n, c_m, &c_rho, &c_C, c_p); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hsbm_list_game / +/-------------------------------------------*/ +SEXP R_igraph_hsbm_list_game(SEXP n, SEXP mlist, SEXP rholist, SEXP Clist, SEXP p) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_vector_int_t c_mlist; + igraph_vector_ptr_t c_rholist; + igraph_vector_ptr_t c_Clist; + igraph_real_t c_p; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + R_SEXP_to_vector_int(mlist, &c_mlist); + R_igraph_SEXP_to_vectorlist(rholist, &c_rholist); + R_igraph_SEXP_to_matrixlist(Clist, &c_Clist); + c_p=REAL(p)[0]; + /* Call igraph */ + igraph_hsbm_list_game(&c_graph, c_n, &c_mlist, &c_rholist, &c_Clist, c_p); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_correlated_game / +/-------------------------------------------*/ +SEXP R_igraph_correlated_game(SEXP old_graph, SEXP corr, SEXP p, SEXP permutation) { + /* Declarations */ + igraph_t c_old_graph; + igraph_t c_new_graph; + igraph_real_t c_corr; + igraph_real_t c_p; + igraph_vector_t c_permutation; + SEXP new_graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(old_graph, &c_old_graph); + c_corr=REAL(corr)[0]; + c_p=REAL(p)[0]; + if (!isNull(permutation)) { R_SEXP_to_vector(permutation, &c_permutation); } + /* Call igraph */ + igraph_correlated_game(&c_old_graph, &c_new_graph, c_corr, c_p, (isNull(permutation) ? 0 : &c_permutation)); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_new_graph); + PROTECT(new_graph=R_igraph_to_SEXP(&c_new_graph)); + igraph_destroy(&c_new_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = new_graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_correlated_pair_game / +/-------------------------------------------*/ +SEXP R_igraph_correlated_pair_game(SEXP n, SEXP corr, SEXP p, SEXP directed, SEXP permutation) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_integer_t c_n; + igraph_real_t c_corr; + igraph_real_t c_p; + igraph_bool_t c_directed; + igraph_vector_t c_permutation; + SEXP graph1; + SEXP graph2; + + SEXP r_result, r_names; + /* Convert input */ + c_n=INTEGER(n)[0]; + c_corr=REAL(corr)[0]; + c_p=REAL(p)[0]; + c_directed=LOGICAL(directed)[0]; + if (!isNull(permutation)) { R_SEXP_to_vector(permutation, &c_permutation); } + /* Call igraph */ + igraph_correlated_pair_game(&c_graph1, &c_graph2, c_n, c_corr, c_p, c_directed, (isNull(permutation) ? 0 : &c_permutation)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + IGRAPH_FINALLY(igraph_destroy, &c_graph1); + PROTECT(graph1=R_igraph_to_SEXP(&c_graph1)); + igraph_destroy(&c_graph1); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, &c_graph2); + PROTECT(graph2=R_igraph_to_SEXP(&c_graph2)); + igraph_destroy(&c_graph2); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, graph1); + SET_VECTOR_ELT(r_result, 1, graph2); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("graph1")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("graph2")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_dot_product_game / +/-------------------------------------------*/ +SEXP R_igraph_dot_product_game(SEXP vecs, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_vecs; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_matrix(vecs, &c_vecs); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_dot_product_game(&c_graph, &c_vecs, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_sample_sphere_surface / +/-------------------------------------------*/ +SEXP R_igraph_sample_sphere_surface(SEXP dim, SEXP n, SEXP radius, SEXP positive) { + /* Declarations */ + igraph_integer_t c_dim; + igraph_integer_t c_n; + igraph_real_t c_radius; + igraph_bool_t c_positive; + igraph_matrix_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + c_dim=INTEGER(dim)[0]; + c_n=INTEGER(n)[0]; + c_radius=REAL(radius)[0]; + c_positive=LOGICAL(positive)[0]; + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + /* Call igraph */ + igraph_sample_sphere_surface(c_dim, c_n, c_radius, c_positive, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_sample_sphere_volume / +/-------------------------------------------*/ +SEXP R_igraph_sample_sphere_volume(SEXP dim, SEXP n, SEXP radius, SEXP positive) { + /* Declarations */ + igraph_integer_t c_dim; + igraph_integer_t c_n; + igraph_real_t c_radius; + igraph_bool_t c_positive; + igraph_matrix_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + c_dim=INTEGER(dim)[0]; + c_n=INTEGER(n)[0]; + c_radius=REAL(radius)[0]; + c_positive=LOGICAL(positive)[0]; + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + /* Call igraph */ + igraph_sample_sphere_volume(c_dim, c_n, c_radius, c_positive, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_sample_dirichlet / +/-------------------------------------------*/ +SEXP R_igraph_sample_dirichlet(SEXP n, SEXP alpha) { + /* Declarations */ + igraph_integer_t c_n; + igraph_vector_t c_alpha; + igraph_matrix_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + R_SEXP_to_vector(alpha, &c_alpha); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + /* Call igraph */ + igraph_sample_dirichlet(c_n, &c_alpha, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_closeness / +/-------------------------------------------*/ +SEXP R_igraph_closeness(SEXP graph, SEXP vids, SEXP mode, SEXP weights, SEXP normalized) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vector_t c_reachable_count; + igraph_bool_t c_all_reachable; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_vector_t c_weights; + igraph_bool_t c_normalized; + SEXP res; + SEXP reachable_count; + SEXP all_reachable; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (0 != igraph_vector_init(&c_reachable_count, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_reachable_count); + reachable_count=R_GlobalEnv; /* hack to have a non-NULL value */ + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_normalized=LOGICAL(normalized)[0]; + /* Call igraph */ + igraph_closeness(&c_graph, &c_res, (isNull(reachable_count) ? 0 : &c_reachable_count), &c_all_reachable, c_vids, c_mode, (isNull(weights) ? 0 : &c_weights), c_normalized); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(reachable_count=R_igraph_0orvector_to_SEXP(&c_reachable_count)); + igraph_vector_destroy(&c_reachable_count); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(all_reachable=NEW_LOGICAL(1)); + LOGICAL(all_reachable)[0]=c_all_reachable; + igraph_vs_destroy(&c_vids); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, reachable_count); + SET_VECTOR_ELT(r_result, 2, all_reachable); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("reachable_count")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("all_reachable")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_closeness_cutoff / +/-------------------------------------------*/ +SEXP R_igraph_closeness_cutoff(SEXP graph, SEXP vids, SEXP mode, SEXP weights, SEXP normalized, SEXP cutoff) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vector_t c_reachable_count; + igraph_bool_t c_all_reachable; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_vector_t c_weights; + igraph_bool_t c_normalized; + igraph_real_t c_cutoff; + SEXP res; + SEXP reachable_count; + SEXP all_reachable; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (0 != igraph_vector_init(&c_reachable_count, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_reachable_count); + reachable_count=R_GlobalEnv; /* hack to have a non-NULL value */ + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_normalized=LOGICAL(normalized)[0]; + c_cutoff=REAL(cutoff)[0]; + /* Call igraph */ + igraph_closeness_cutoff(&c_graph, &c_res, (isNull(reachable_count) ? 0 : &c_reachable_count), &c_all_reachable, c_vids, c_mode, (isNull(weights) ? 0 : &c_weights), c_normalized, c_cutoff); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(reachable_count=R_igraph_0orvector_to_SEXP(&c_reachable_count)); + igraph_vector_destroy(&c_reachable_count); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(all_reachable=NEW_LOGICAL(1)); + LOGICAL(all_reachable)[0]=c_all_reachable; + igraph_vs_destroy(&c_vids); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, reachable_count); + SET_VECTOR_ELT(r_result, 2, all_reachable); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("reachable_count")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("all_reachable")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_all_shortest_paths / +/-------------------------------------------*/ +SEXP R_igraph_get_all_shortest_paths(SEXP graph, SEXP from, SEXP to, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_res; + igraph_vector_t c_nrgeo; + igraph_integer_t c_from; + igraph_vs_t c_to; + igraph_neimode_t c_mode; + SEXP res; + SEXP nrgeo; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); + if (0 != igraph_vector_init(&c_nrgeo, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_nrgeo); + c_from = (igraph_integer_t) REAL(from)[0]; + R_SEXP_to_igraph_vs(to, &c_graph, &c_to); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_get_all_shortest_paths(&c_graph, &c_res, &c_nrgeo, c_from, c_to, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); + R_igraph_vectorlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(nrgeo=R_igraph_vector_to_SEXP(&c_nrgeo)); + igraph_vector_destroy(&c_nrgeo); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_to); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, nrgeo); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("nrgeo")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_all_shortest_paths_dijkstra / +/-------------------------------------------*/ +SEXP R_igraph_get_all_shortest_paths_dijkstra(SEXP graph, SEXP from, SEXP to, SEXP weights, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_res; + igraph_vector_t c_nrgeo; + igraph_integer_t c_from; + igraph_vs_t c_to; + igraph_vector_t c_weights; + igraph_neimode_t c_mode; + SEXP res; + SEXP nrgeo; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); + if (0 != igraph_vector_init(&c_nrgeo, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_nrgeo); + c_from = (igraph_integer_t) REAL(from)[0]; + R_SEXP_to_igraph_vs(to, &c_graph, &c_to); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_get_all_shortest_paths_dijkstra(&c_graph, &c_res, &c_nrgeo, c_from, c_to, (isNull(weights) ? 0 : &c_weights), c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); + R_igraph_vectorlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(nrgeo=R_igraph_vector_to_SEXP(&c_nrgeo)); + igraph_vector_destroy(&c_nrgeo); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_to); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, nrgeo); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("nrgeo")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_all_simple_paths / +/-------------------------------------------*/ +SEXP R_igraph_get_all_simple_paths(SEXP graph, SEXP from, SEXP to, SEXP cutoff, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_res; + igraph_integer_t c_from; + igraph_vs_t c_to; + igraph_integer_t c_cutoff; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_int_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_int_destroy, &c_res); + c_from = (igraph_integer_t) REAL(from)[0]; + R_SEXP_to_igraph_vs(to, &c_graph, &c_to); + c_cutoff=INTEGER(cutoff)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_get_all_simple_paths(&c_graph, &c_res, c_from, c_to, c_cutoff, c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_int_to_SEXPp1(&c_res)); + igraph_vector_int_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_to); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_betweenness_cutoff / +/-------------------------------------------*/ +SEXP R_igraph_betweenness_cutoff(SEXP graph, SEXP vids, SEXP directed, SEXP weights, SEXP cutoff) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vs_t c_vids; + igraph_bool_t c_directed; + igraph_vector_t c_weights; + igraph_real_t c_cutoff; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_directed=LOGICAL(directed)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_cutoff=REAL(cutoff)[0]; + /* Call igraph */ + igraph_betweenness_cutoff(&c_graph, &c_res, c_vids, c_directed, (isNull(weights) ? 0 : &c_weights), c_cutoff); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_edge_betweenness / +/-------------------------------------------*/ +SEXP R_igraph_edge_betweenness(SEXP graph, SEXP directed, SEXP weights) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_bool_t c_directed; + igraph_vector_t c_weights; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_directed=LOGICAL(directed)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + /* Call igraph */ + igraph_edge_betweenness(&c_graph, &c_res, c_directed, (isNull(weights) ? 0 : &c_weights)); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_edge_betweenness_cutoff / +/-------------------------------------------*/ +SEXP R_igraph_edge_betweenness_cutoff(SEXP graph, SEXP directed, SEXP weights, SEXP cutoff) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_bool_t c_directed; + igraph_vector_t c_weights; + igraph_real_t c_cutoff; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_directed=LOGICAL(directed)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_cutoff=REAL(cutoff)[0]; + /* Call igraph */ + igraph_edge_betweenness_cutoff(&c_graph, &c_res, c_directed, (isNull(weights) ? 0 : &c_weights), c_cutoff); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_harmonic_centrality_cutoff / +/-------------------------------------------*/ +SEXP R_igraph_harmonic_centrality_cutoff(SEXP graph, SEXP vids, SEXP mode, SEXP weights, SEXP normalized, SEXP cutoff) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_vector_t c_weights; + igraph_bool_t c_normalized; + igraph_real_t c_cutoff; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_normalized=LOGICAL(normalized)[0]; + c_cutoff=REAL(cutoff)[0]; + /* Call igraph */ + igraph_harmonic_centrality_cutoff(&c_graph, &c_res, c_vids, c_mode, (isNull(weights) ? 0 : &c_weights), c_normalized, c_cutoff); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_personalized_pagerank / +/-------------------------------------------*/ +SEXP R_igraph_personalized_pagerank(SEXP graph, SEXP algo, SEXP vids, SEXP directed, SEXP damping, SEXP personalized, SEXP weights, SEXP options) { + /* Declarations */ + igraph_t c_graph; + igraph_pagerank_algo_t c_algo; + igraph_vector_t c_vector; + igraph_real_t c_value; + igraph_vs_t c_vids; + igraph_bool_t c_directed; + igraph_real_t c_damping; + igraph_vector_t c_personalized; + igraph_vector_t c_weights; + igraph_arpack_options_t c_options1; + void* c_options; + SEXP vector; + SEXP value; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_algo = (igraph_pagerank_algo_t) Rf_asInteger(algo); + if (0 != igraph_vector_init(&c_vector, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_directed=LOGICAL(directed)[0]; + c_damping=REAL(damping)[0]; + if (!isNull(personalized)) { R_SEXP_to_vector(personalized, &c_personalized); } + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (c_algo == IGRAPH_PAGERANK_ALGO_ARPACK) { + R_SEXP_to_igraph_arpack_options(options, &c_options1); + c_options = &c_options1; + } else { + c_options = 0; + } + /* Call igraph */ + igraph_personalized_pagerank(&c_graph, c_algo, &c_vector, &c_value, c_vids, c_directed, c_damping, (isNull(personalized) ? 0 : &c_personalized), (isNull(weights) ? 0 : &c_weights), c_options); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); + igraph_vector_destroy(&c_vector); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + igraph_vs_destroy(&c_vids); + if (c_algo == IGRAPH_PAGERANK_ALGO_ARPACK) { + PROTECT(options = R_igraph_arpack_options_to_SEXP(&c_options1)); + } else { + PROTECT(options); + } + SET_VECTOR_ELT(r_result, 0, vector); + SET_VECTOR_ELT(r_result, 1, value); + SET_VECTOR_ELT(r_result, 2, options); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vector")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("options")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_induced_subgraph / +/-------------------------------------------*/ +SEXP R_igraph_induced_subgraph(SEXP graph, SEXP vids, SEXP impl) { + /* Declarations */ + igraph_t c_graph; + igraph_t c_res; + igraph_vs_t c_vids; + igraph_subgraph_implementation_t c_impl; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_impl = (igraph_subgraph_implementation_t) Rf_asInteger(impl); + /* Call igraph */ + igraph_induced_subgraph(&c_graph, &c_res, c_vids, c_impl); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_res); + PROTECT(res=R_igraph_to_SEXP(&c_res)); + igraph_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_subgraph_edges / +/-------------------------------------------*/ +SEXP R_igraph_subgraph_edges(SEXP graph, SEXP eids, SEXP delete_vertices) { + /* Declarations */ + igraph_t c_graph; + igraph_t c_res; + igraph_es_t c_eids; + igraph_bool_t c_delete_vertices; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_igraph_es(eids, &c_graph, &c_eids); + c_delete_vertices=LOGICAL(delete_vertices)[0]; + /* Call igraph */ + igraph_subgraph_edges(&c_graph, &c_res, c_eids, c_delete_vertices); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_res); + PROTECT(res=R_igraph_to_SEXP(&c_res)); + igraph_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_es_destroy(&c_eids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_average_path_length_dijkstra / +/-------------------------------------------*/ +SEXP R_igraph_average_path_length_dijkstra(SEXP graph, SEXP weights, SEXP directed, SEXP unconn) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_res; + igraph_real_t c_unconn_pairs; + igraph_vector_t c_weights; + igraph_bool_t c_directed; + igraph_bool_t c_unconn; + SEXP res; + SEXP unconn_pairs; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_directed=LOGICAL(directed)[0]; + c_unconn=LOGICAL(unconn)[0]; + /* Call igraph */ + igraph_average_path_length_dijkstra(&c_graph, &c_res, &c_unconn_pairs, (isNull(weights) ? 0 : &c_weights), c_directed, c_unconn); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + PROTECT(unconn_pairs=NEW_NUMERIC(1)); + REAL(unconn_pairs)[0]=c_unconn_pairs; + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, unconn_pairs); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("unconnected")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_path_length_hist / +/-------------------------------------------*/ +SEXP R_igraph_path_length_hist(SEXP graph, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_real_t c_unconnected; + igraph_bool_t c_directed; + SEXP res; + SEXP unconnected; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_path_length_hist(&c_graph, &c_res, &c_unconnected, c_directed); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(unconnected=NEW_NUMERIC(1)); + REAL(unconnected)[0]=c_unconnected; + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, unconnected); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("unconnected")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_simplify / +/-------------------------------------------*/ +SEXP R_igraph_simplify(SEXP graph, SEXP remove_multiple, SEXP remove_loops, SEXP edge_attr_comb) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_remove_multiple; + igraph_bool_t c_remove_loops; + igraph_attribute_combination_t c_edge_attr_comb; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph_copy(graph, &c_graph); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + c_remove_multiple=LOGICAL(remove_multiple)[0]; + c_remove_loops=LOGICAL(remove_loops)[0]; + R_SEXP_to_attr_comb(edge_attr_comb, &c_edge_attr_comb); + /* Call igraph */ + igraph_simplify(&c_graph, c_remove_multiple, c_remove_loops, &c_edge_attr_comb); + + /* Convert output */ + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + igraph_attribute_combination_destroy(&c_edge_attr_comb); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_feedback_arc_set / +/-------------------------------------------*/ +SEXP R_igraph_feedback_arc_set(SEXP graph, SEXP weights, SEXP algo) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_result; + igraph_vector_t c_weights; + igraph_fas_algorithm_t c_algo; + SEXP result; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_result, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_result); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_algo = (igraph_fas_algorithm_t) Rf_asInteger(algo); + /* Call igraph */ + igraph_feedback_arc_set(&c_graph, &c_result, (isNull(weights) ? 0 : &c_weights), c_algo); + + /* Convert output */ + PROTECT(result=R_igraph_vector_to_SEXPp1(&c_result)); + igraph_vector_destroy(&c_result); + IGRAPH_FINALLY_CLEAN(1); + r_result = result; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_loop / +/-------------------------------------------*/ +SEXP R_igraph_is_loop(SEXP graph, SEXP es) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_res; + igraph_es_t c_es; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_bool_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_res); + R_SEXP_to_igraph_es(es, &c_graph, &c_es); + /* Call igraph */ + igraph_is_loop(&c_graph, &c_res, c_es); + + /* Convert output */ + PROTECT(res=R_igraph_vector_bool_to_SEXP(&c_res)); + igraph_vector_bool_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_es_destroy(&c_es); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_dag / +/-------------------------------------------*/ +SEXP R_igraph_is_dag(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Call igraph */ + igraph_is_dag(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_simple / +/-------------------------------------------*/ +SEXP R_igraph_is_simple(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Call igraph */ + igraph_is_simple(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_multiple / +/-------------------------------------------*/ +SEXP R_igraph_is_multiple(SEXP graph, SEXP es) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_res; + igraph_es_t c_es; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_bool_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_res); + R_SEXP_to_igraph_es(es, &c_graph, &c_es); + /* Call igraph */ + igraph_is_multiple(&c_graph, &c_res, c_es); + + /* Convert output */ + PROTECT(res=R_igraph_vector_bool_to_SEXP(&c_res)); + igraph_vector_bool_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_es_destroy(&c_es); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_has_loop / +/-------------------------------------------*/ +SEXP R_igraph_has_loop(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Call igraph */ + igraph_has_loop(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_has_multiple / +/-------------------------------------------*/ +SEXP R_igraph_has_multiple(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Call igraph */ + igraph_has_multiple(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_count_multiple / +/-------------------------------------------*/ +SEXP R_igraph_count_multiple(SEXP graph, SEXP es) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_es_t c_es; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_es(es, &c_graph, &c_es); + /* Call igraph */ + igraph_count_multiple(&c_graph, &c_res, c_es); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_es_destroy(&c_es); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_eigenvector_centrality / +/-------------------------------------------*/ +SEXP R_igraph_eigenvector_centrality(SEXP graph, SEXP directed, SEXP scale, SEXP weights, SEXP options) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vector; + igraph_real_t c_value; + igraph_bool_t c_directed; + igraph_bool_t c_scale; + igraph_vector_t c_weights; + igraph_arpack_options_t c_options; + SEXP vector; + SEXP value; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_vector, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); + c_directed=LOGICAL(directed)[0]; + c_scale=LOGICAL(scale)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + R_SEXP_to_igraph_arpack_options(options, &c_options); + /* Call igraph */ + igraph_eigenvector_centrality(&c_graph, &c_vector, &c_value, c_directed, c_scale, (isNull(weights) ? 0 : &c_weights), &c_options); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); + igraph_vector_destroy(&c_vector); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + SET_VECTOR_ELT(r_result, 0, vector); + SET_VECTOR_ELT(r_result, 1, value); + SET_VECTOR_ELT(r_result, 2, options); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vector")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("options")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hub_score / +/-------------------------------------------*/ +SEXP R_igraph_hub_score(SEXP graph, SEXP scale, SEXP weights, SEXP options) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vector; + igraph_real_t c_value; + igraph_bool_t c_scale; + igraph_vector_t c_weights; + igraph_arpack_options_t c_options; + SEXP vector; + SEXP value; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_vector, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); + c_scale=LOGICAL(scale)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + R_SEXP_to_igraph_arpack_options(options, &c_options); + /* Call igraph */ + igraph_hub_score(&c_graph, &c_vector, &c_value, c_scale, (isNull(weights) ? 0 : &c_weights), &c_options); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); + igraph_vector_destroy(&c_vector); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + SET_VECTOR_ELT(r_result, 0, vector); + SET_VECTOR_ELT(r_result, 1, value); + SET_VECTOR_ELT(r_result, 2, options); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vector")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("options")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_authority_score / +/-------------------------------------------*/ +SEXP R_igraph_authority_score(SEXP graph, SEXP scale, SEXP weights, SEXP options) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vector; + igraph_real_t c_value; + igraph_bool_t c_scale; + igraph_vector_t c_weights; + igraph_arpack_options_t c_options; + SEXP vector; + SEXP value; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_vector, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); + c_scale=LOGICAL(scale)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + R_SEXP_to_igraph_arpack_options(options, &c_options); + /* Call igraph */ + igraph_authority_score(&c_graph, &c_vector, &c_value, c_scale, (isNull(weights) ? 0 : &c_weights), &c_options); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); + igraph_vector_destroy(&c_vector); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + SET_VECTOR_ELT(r_result, 0, vector); + SET_VECTOR_ELT(r_result, 1, value); + SET_VECTOR_ELT(r_result, 2, options); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vector")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("options")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_arpack_unpack_complex / +/-------------------------------------------*/ +SEXP R_igraph_arpack_unpack_complex(SEXP vectors, SEXP values, SEXP nev) { + /* Declarations */ + igraph_matrix_t c_vectors; + igraph_matrix_t c_values; + long int c_nev; + + SEXP r_result, r_names; + /* Convert input */ + if (0 != R_SEXP_to_igraph_matrix_copy(vectors, &c_vectors)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_vectors); + if (0 != R_SEXP_to_igraph_matrix_copy(values, &c_values)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_values); + c_nev=INTEGER(nev)[0]; + /* Call igraph */ + igraph_arpack_unpack_complex(&c_vectors, &c_values, c_nev); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(vectors=R_igraph_matrix_to_SEXP(&c_vectors)); + igraph_matrix_destroy(&c_vectors); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(values=R_igraph_matrix_to_SEXP(&c_values)); + igraph_matrix_destroy(&c_values); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, vectors); + SET_VECTOR_ELT(r_result, 1, values); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vectors")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("values")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_unfold_tree / +/-------------------------------------------*/ +SEXP R_igraph_unfold_tree(SEXP graph, SEXP mode, SEXP roots) { + /* Declarations */ + igraph_t c_graph; + igraph_t c_tree; + igraph_neimode_t c_mode; + igraph_vector_t c_roots; + igraph_vector_t c_vertex_index; + SEXP tree; + SEXP vertex_index; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + R_SEXP_to_vector(roots, &c_roots); + if (0 != igraph_vector_init(&c_vertex_index, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vertex_index); + vertex_index=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_unfold_tree(&c_graph, &c_tree, c_mode, &c_roots, (isNull(vertex_index) ? 0 : &c_vertex_index)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + IGRAPH_FINALLY(igraph_destroy, &c_tree); + PROTECT(tree=R_igraph_to_SEXP(&c_tree)); + igraph_destroy(&c_tree); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(vertex_index=R_igraph_0orvector_to_SEXPp1(&c_vertex_index)); + igraph_vector_destroy(&c_vertex_index); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, tree); + SET_VECTOR_ELT(r_result, 1, vertex_index); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("tree")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("vertex_index")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_mutual / +/-------------------------------------------*/ +SEXP R_igraph_is_mutual(SEXP graph, SEXP es) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_res; + igraph_es_t c_es; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_bool_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_res); + R_SEXP_to_igraph_es(es, &c_graph, &c_es); + /* Call igraph */ + igraph_is_mutual(&c_graph, &c_res, c_es); + + /* Convert output */ + PROTECT(res=R_igraph_vector_bool_to_SEXP(&c_res)); + igraph_vector_bool_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_es_destroy(&c_es); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_maximum_cardinality_search / +/-------------------------------------------*/ +SEXP R_igraph_maximum_cardinality_search(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_alpha; + igraph_vector_t c_alpham1; + SEXP alpha; + SEXP alpham1; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_alpha, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_alpha); + if (0 != igraph_vector_init(&c_alpham1, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_alpham1); + /* Call igraph */ + igraph_maximum_cardinality_search(&c_graph, &c_alpha, &c_alpham1); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(alpha=R_igraph_vector_to_SEXPp1(&c_alpha)); + igraph_vector_destroy(&c_alpha); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(alpham1=R_igraph_vector_to_SEXPp1(&c_alpham1)); + igraph_vector_destroy(&c_alpham1); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, alpha); + SET_VECTOR_ELT(r_result, 1, alpham1); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("alpha")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("alpham1")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_avg_nearest_neighbor_degree / +/-------------------------------------------*/ +SEXP R_igraph_avg_nearest_neighbor_degree(SEXP graph, SEXP vids, SEXP mode, SEXP neighbor_degree_mode, SEXP weights) { + /* Declarations */ + igraph_t c_graph; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_neimode_t c_neighbor_degree_mode; + igraph_vector_t c_knn; + igraph_vector_t c_knnk; + igraph_vector_t c_weights; + SEXP knn; + SEXP knnk; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_neighbor_degree_mode = (igraph_neimode_t) Rf_asInteger(neighbor_degree_mode); + if (0 != igraph_vector_init(&c_knn, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_knn); + if (0 != igraph_vector_init(&c_knnk, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_knnk); + knnk=R_GlobalEnv; /* hack to have a non-NULL value */ + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + /* Call igraph */ + igraph_avg_nearest_neighbor_degree(&c_graph, c_vids, c_mode, c_neighbor_degree_mode, &c_knn, (isNull(knnk) ? 0 : &c_knnk), (isNull(weights) ? 0 : &c_weights)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + igraph_vs_destroy(&c_vids); + PROTECT(knn=R_igraph_vector_to_SEXP(&c_knn)); + igraph_vector_destroy(&c_knn); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(knnk=R_igraph_0orvector_to_SEXP(&c_knnk)); + igraph_vector_destroy(&c_knnk); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, knn); + SET_VECTOR_ELT(r_result, 1, knnk); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("knn")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("knnk")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_strength / +/-------------------------------------------*/ +SEXP R_igraph_strength(SEXP graph, SEXP vids, SEXP mode, SEXP loops, SEXP weights) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_bool_t c_loops; + igraph_vector_t c_weights; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_loops=LOGICAL(loops)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + /* Call igraph */ + igraph_strength(&c_graph, &c_res, c_vids, c_mode, c_loops, (isNull(weights) ? 0 : &c_weights)); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization / +/-------------------------------------------*/ +SEXP R_igraph_centralization(SEXP scores, SEXP theoretical_max, SEXP normalized) { + /* Declarations */ + igraph_vector_t c_scores; + igraph_real_t c_theoretical_max; + igraph_bool_t c_normalized; + igraph_real_t c_result; + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector(scores, &c_scores); + c_theoretical_max=REAL(theoretical_max)[0]; + c_normalized=LOGICAL(normalized)[0]; + /* Call igraph */ + c_result= igraph_centralization(&c_scores, c_theoretical_max, c_normalized); + + /* Convert output */ + + PROTECT(r_result=NEW_NUMERIC(1)); + REAL(r_result)[0]=c_result; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_degree / +/-------------------------------------------*/ +SEXP R_igraph_centralization_degree(SEXP graph, SEXP mode, SEXP loops, SEXP normalized) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_neimode_t c_mode; + igraph_bool_t c_loops; + igraph_real_t c_centralization; + igraph_real_t c_theoretical_max; + igraph_bool_t c_normalized; + SEXP res; + SEXP centralization; + SEXP theoretical_max; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_loops=LOGICAL(loops)[0]; + c_normalized=LOGICAL(normalized)[0]; + /* Call igraph */ + igraph_centralization_degree(&c_graph, &c_res, c_mode, c_loops, &c_centralization, &c_theoretical_max, c_normalized); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(centralization=NEW_NUMERIC(1)); + REAL(centralization)[0]=c_centralization; + PROTECT(theoretical_max=NEW_NUMERIC(1)); + REAL(theoretical_max)[0]=c_theoretical_max; + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, centralization); + SET_VECTOR_ELT(r_result, 2, theoretical_max); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("centralization")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("theoretical_max")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_degree_tmax / +/-------------------------------------------*/ +SEXP R_igraph_centralization_degree_tmax(SEXP graph, SEXP nodes, SEXP mode, SEXP loops) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_nodes; + igraph_neimode_t c_mode; + igraph_bool_t c_loops; + igraph_real_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + c_nodes=INTEGER(nodes)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_loops=LOGICAL(loops)[0]; + /* Call igraph */ + igraph_centralization_degree_tmax((isNull(graph) ? 0 : &c_graph), c_nodes, c_mode, c_loops, &c_res); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_betweenness / +/-------------------------------------------*/ +SEXP R_igraph_centralization_betweenness(SEXP graph, SEXP directed, SEXP normalized) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_bool_t c_directed; + igraph_real_t c_centralization; + igraph_real_t c_theoretical_max; + igraph_bool_t c_normalized; + SEXP res; + SEXP centralization; + SEXP theoretical_max; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_directed=LOGICAL(directed)[0]; + c_normalized=LOGICAL(normalized)[0]; + /* Call igraph */ + igraph_centralization_betweenness(&c_graph, &c_res, c_directed, &c_centralization, &c_theoretical_max, c_normalized); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(centralization=NEW_NUMERIC(1)); + REAL(centralization)[0]=c_centralization; + PROTECT(theoretical_max=NEW_NUMERIC(1)); + REAL(theoretical_max)[0]=c_theoretical_max; + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, centralization); + SET_VECTOR_ELT(r_result, 2, theoretical_max); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("centralization")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("theoretical_max")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_betweenness_tmax / +/-------------------------------------------*/ +SEXP R_igraph_centralization_betweenness_tmax(SEXP graph, SEXP nodes, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_nodes; + igraph_bool_t c_directed; + igraph_real_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + c_nodes=INTEGER(nodes)[0]; + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_centralization_betweenness_tmax((isNull(graph) ? 0 : &c_graph), c_nodes, c_directed, &c_res); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_closeness / +/-------------------------------------------*/ +SEXP R_igraph_centralization_closeness(SEXP graph, SEXP mode, SEXP normalized) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_neimode_t c_mode; + igraph_real_t c_centralization; + igraph_real_t c_theoretical_max; + igraph_bool_t c_normalized; + SEXP res; + SEXP centralization; + SEXP theoretical_max; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_normalized=LOGICAL(normalized)[0]; + /* Call igraph */ + igraph_centralization_closeness(&c_graph, &c_res, c_mode, &c_centralization, &c_theoretical_max, c_normalized); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(centralization=NEW_NUMERIC(1)); + REAL(centralization)[0]=c_centralization; + PROTECT(theoretical_max=NEW_NUMERIC(1)); + REAL(theoretical_max)[0]=c_theoretical_max; + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, centralization); + SET_VECTOR_ELT(r_result, 2, theoretical_max); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("centralization")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("theoretical_max")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_closeness_tmax / +/-------------------------------------------*/ +SEXP R_igraph_centralization_closeness_tmax(SEXP graph, SEXP nodes, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_nodes; + igraph_neimode_t c_mode; + igraph_real_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + c_nodes=INTEGER(nodes)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_centralization_closeness_tmax((isNull(graph) ? 0 : &c_graph), c_nodes, c_mode, &c_res); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_eigenvector_centrality / +/-------------------------------------------*/ +SEXP R_igraph_centralization_eigenvector_centrality(SEXP graph, SEXP directed, SEXP scale, SEXP options, SEXP normalized) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vector; + igraph_real_t c_value; + igraph_bool_t c_directed; + igraph_bool_t c_scale; + igraph_arpack_options_t c_options; + igraph_real_t c_centralization; + igraph_real_t c_theoretical_max; + igraph_bool_t c_normalized; + SEXP vector; + SEXP value; + SEXP centralization; + SEXP theoretical_max; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_vector, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vector); + c_directed=LOGICAL(directed)[0]; + c_scale=LOGICAL(scale)[0]; + R_SEXP_to_igraph_arpack_options(options, &c_options); + c_normalized=LOGICAL(normalized)[0]; + /* Call igraph */ + igraph_centralization_eigenvector_centrality(&c_graph, &c_vector, &c_value, c_directed, c_scale, &c_options, &c_centralization, &c_theoretical_max, c_normalized); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(5)); + PROTECT(r_names=NEW_CHARACTER(5)); + PROTECT(vector=R_igraph_vector_to_SEXP(&c_vector)); + igraph_vector_destroy(&c_vector); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + PROTECT(centralization=NEW_NUMERIC(1)); + REAL(centralization)[0]=c_centralization; + PROTECT(theoretical_max=NEW_NUMERIC(1)); + REAL(theoretical_max)[0]=c_theoretical_max; + SET_VECTOR_ELT(r_result, 0, vector); + SET_VECTOR_ELT(r_result, 1, value); + SET_VECTOR_ELT(r_result, 2, options); + SET_VECTOR_ELT(r_result, 3, centralization); + SET_VECTOR_ELT(r_result, 4, theoretical_max); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vector")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("options")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("centralization")); + SET_STRING_ELT(r_names, 4, CREATE_STRING_VECTOR("theoretical_max")); + SET_NAMES(r_result, r_names); + UNPROTECT(6); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_centralization_eigenvector_centrality_tmax / +/-------------------------------------------*/ +SEXP R_igraph_centralization_eigenvector_centrality_tmax(SEXP graph, SEXP nodes, SEXP directed, SEXP scale) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_nodes; + igraph_bool_t c_directed; + igraph_bool_t c_scale; + igraph_real_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + if (!isNull(graph)) { R_SEXP_to_igraph(graph, &c_graph); } + c_nodes=INTEGER(nodes)[0]; + c_directed=LOGICAL(directed)[0]; + c_scale=LOGICAL(scale)[0]; + /* Call igraph */ + igraph_centralization_eigenvector_centrality_tmax((isNull(graph) ? 0 : &c_graph), c_nodes, c_directed, c_scale, &c_res); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_assortativity_nominal / +/-------------------------------------------*/ +SEXP R_igraph_assortativity_nominal(SEXP graph, SEXP types, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_types; + igraph_real_t c_res; + igraph_bool_t c_directed; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_vector(types, &c_types); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_assortativity_nominal(&c_graph, &c_types, &c_res, c_directed); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_assortativity / +/-------------------------------------------*/ +SEXP R_igraph_assortativity(SEXP graph, SEXP types1, SEXP types2, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_types1; + igraph_vector_t c_types2; + igraph_real_t c_res; + igraph_bool_t c_directed; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_vector(types1, &c_types1); + if (!isNull(types2)) { R_SEXP_to_vector(types2, &c_types2); } + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_assortativity(&c_graph, &c_types1, (isNull(types2) ? 0 : &c_types2), &c_res, c_directed); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_assortativity_degree / +/-------------------------------------------*/ +SEXP R_igraph_assortativity_degree(SEXP graph, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_res; + igraph_bool_t c_directed; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_assortativity_degree(&c_graph, &c_res, c_directed); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_contract_vertices / +/-------------------------------------------*/ +SEXP R_igraph_contract_vertices(SEXP graph, SEXP mapping, SEXP vertex_attr_comb) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_mapping; + igraph_attribute_combination_t c_vertex_attr_comb; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph_copy(graph, &c_graph); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + R_SEXP_to_vector(mapping, &c_mapping); + R_SEXP_to_attr_comb(vertex_attr_comb, &c_vertex_attr_comb); + /* Call igraph */ + igraph_contract_vertices(&c_graph, &c_mapping, &c_vertex_attr_comb); + + /* Convert output */ + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + igraph_attribute_combination_destroy(&c_vertex_attr_comb); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_eccentricity / +/-------------------------------------------*/ +SEXP R_igraph_eccentricity(SEXP graph, SEXP vids, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_eccentricity(&c_graph, &c_res, c_vids, c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_radius / +/-------------------------------------------*/ +SEXP R_igraph_radius(SEXP graph, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_radius; + igraph_neimode_t c_mode; + SEXP radius; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_radius(&c_graph, &c_radius, c_mode); + + /* Convert output */ + PROTECT(radius=NEW_NUMERIC(1)); + REAL(radius)[0]=c_radius; + r_result = radius; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_diversity / +/-------------------------------------------*/ +SEXP R_igraph_diversity(SEXP graph, SEXP weights, SEXP vids) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_vector_t c_res; + igraph_vs_t c_vids; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + /* Call igraph */ + igraph_diversity(&c_graph, (isNull(weights) ? 0 : &c_weights), &c_res, c_vids); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_random_walk / +/-------------------------------------------*/ +SEXP R_igraph_random_walk(SEXP graph, SEXP start, SEXP mode, SEXP steps, SEXP stuck) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_walk; + igraph_integer_t c_start; + igraph_neimode_t c_mode; + igraph_integer_t c_steps; + igraph_random_walk_stuck_t c_stuck; + SEXP walk; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_walk, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_walk); + c_start = (igraph_integer_t) REAL(start)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_steps=INTEGER(steps)[0]; + c_stuck = (igraph_random_walk_stuck_t) Rf_asInteger(stuck); + /* Call igraph */ + igraph_random_walk(&c_graph, &c_walk, c_start, c_mode, c_steps, c_stuck); + + /* Convert output */ + PROTECT(walk=R_igraph_vector_to_SEXPp1(&c_walk)); + igraph_vector_destroy(&c_walk); + IGRAPH_FINALLY_CLEAN(1); + r_result = walk; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_random_edge_walk / +/-------------------------------------------*/ +SEXP R_igraph_random_edge_walk(SEXP graph, SEXP weights, SEXP start, SEXP mode, SEXP steps, SEXP stuck) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_vector_t c_edgewalk; + igraph_integer_t c_start; + igraph_neimode_t c_mode; + igraph_integer_t c_steps; + igraph_random_walk_stuck_t c_stuck; + SEXP edgewalk; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (0 != igraph_vector_init(&c_edgewalk, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_edgewalk); + c_start = (igraph_integer_t) REAL(start)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_steps=INTEGER(steps)[0]; + c_stuck = (igraph_random_walk_stuck_t) Rf_asInteger(stuck); + /* Call igraph */ + igraph_random_edge_walk(&c_graph, (isNull(weights) ? 0 : &c_weights), &c_edgewalk, c_start, c_mode, c_steps, c_stuck); + + /* Convert output */ + PROTECT(edgewalk=R_igraph_vector_to_SEXPp1(&c_edgewalk)); + igraph_vector_destroy(&c_edgewalk); + IGRAPH_FINALLY_CLEAN(1); + r_result = edgewalk; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_global_efficiency / +/-------------------------------------------*/ +SEXP R_igraph_global_efficiency(SEXP graph, SEXP weights, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_res; + igraph_vector_t c_weights; + igraph_bool_t c_directed; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_global_efficiency(&c_graph, &c_res, (isNull(weights) ? 0 : &c_weights), c_directed); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_efficiency / +/-------------------------------------------*/ +SEXP R_igraph_local_efficiency(SEXP graph, SEXP vids, SEXP weights, SEXP directed, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vs_t c_vids; + igraph_vector_t c_weights; + igraph_bool_t c_directed; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_directed=LOGICAL(directed)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_efficiency(&c_graph, &c_res, c_vids, (isNull(weights) ? 0 : &c_weights), c_directed, c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_average_local_efficiency / +/-------------------------------------------*/ +SEXP R_igraph_average_local_efficiency(SEXP graph, SEXP weights, SEXP directed, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_res; + igraph_vector_t c_weights; + igraph_bool_t c_directed; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_directed=LOGICAL(directed)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_average_local_efficiency(&c_graph, &c_res, (isNull(weights) ? 0 : &c_weights), c_directed, c_mode); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_graphical / +/-------------------------------------------*/ +SEXP R_igraph_is_graphical(SEXP out_deg, SEXP in_deg, SEXP allowed_edge_types) { + /* Declarations */ + igraph_vector_t c_out_deg; + igraph_vector_t c_in_deg; + igraph_edge_type_sw_t c_allowed_edge_types; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector(out_deg, &c_out_deg); + if (!isNull(in_deg)) { R_SEXP_to_vector(in_deg, &c_in_deg); } + c_allowed_edge_types = (igraph_edge_type_sw_t) Rf_asInteger(allowed_edge_types); + /* Call igraph */ + igraph_is_graphical(&c_out_deg, (isNull(in_deg) ? 0 : &c_in_deg), c_allowed_edge_types, &c_res); + + /* Convert output */ + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_bipartite_projection_size / +/-------------------------------------------*/ +SEXP R_igraph_bipartite_projection_size(SEXP graph, SEXP types) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_integer_t c_vcount1; + igraph_integer_t c_ecount1; + igraph_integer_t c_vcount2; + igraph_integer_t c_ecount2; + SEXP vcount1; + SEXP ecount1; + SEXP vcount2; + SEXP ecount2; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + c_vcount1=0; + c_ecount1=0; + c_vcount2=0; + c_ecount2=0; + /* Call igraph */ + igraph_bipartite_projection_size(&c_graph, (isNull(types) ? 0 : &c_types), &c_vcount1, &c_ecount1, &c_vcount2, &c_ecount2); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(4)); + PROTECT(r_names=NEW_CHARACTER(4)); + PROTECT(vcount1=NEW_INTEGER(1)); + INTEGER(vcount1)[0]=c_vcount1; + PROTECT(ecount1=NEW_INTEGER(1)); + INTEGER(ecount1)[0]=c_ecount1; + PROTECT(vcount2=NEW_INTEGER(1)); + INTEGER(vcount2)[0]=c_vcount2; + PROTECT(ecount2=NEW_INTEGER(1)); + INTEGER(ecount2)[0]=c_ecount2; + SET_VECTOR_ELT(r_result, 0, vcount1); + SET_VECTOR_ELT(r_result, 1, ecount1); + SET_VECTOR_ELT(r_result, 2, vcount2); + SET_VECTOR_ELT(r_result, 3, ecount2); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("vcount1")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("ecount1")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("vcount2")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("ecount2")); + SET_NAMES(r_result, r_names); + UNPROTECT(5); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_create_bipartite / +/-------------------------------------------*/ +SEXP R_igraph_create_bipartite(SEXP types, SEXP edges, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_vector_t c_edges; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector_bool(types, &c_types); + R_SEXP_to_vector(edges, &c_edges); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_create_bipartite(&c_graph, &c_types, &c_edges, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_incidence / +/-------------------------------------------*/ +SEXP R_igraph_incidence(SEXP incidence, SEXP directed, SEXP mode, SEXP multiple) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_matrix_t c_incidence; + igraph_bool_t c_directed; + igraph_neimode_t c_mode; + igraph_bool_t c_multiple; + SEXP graph; + SEXP types; + + SEXP r_result, r_names; + /* Convert input */ + if (0 != igraph_vector_bool_init(&c_types, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_types); + R_SEXP_to_matrix(incidence, &c_incidence); + c_directed=LOGICAL(directed)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_multiple=LOGICAL(multiple)[0]; + /* Call igraph */ + igraph_incidence(&c_graph, &c_types, &c_incidence, c_directed, c_mode, c_multiple); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(types=R_igraph_vector_bool_to_SEXP(&c_types)); + igraph_vector_bool_destroy(&c_types); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, graph); + SET_VECTOR_ELT(r_result, 1, types); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("graph")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("types")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_incidence / +/-------------------------------------------*/ +SEXP R_igraph_get_incidence(SEXP graph, SEXP types) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_matrix_t c_res; + igraph_vector_t c_row_ids; + igraph_vector_t c_col_ids; + SEXP res; + SEXP row_ids; + SEXP col_ids; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + if (0 != igraph_vector_init(&c_row_ids, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_row_ids); + row_ids=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_col_ids, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_col_ids); + col_ids=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_get_incidence(&c_graph, (isNull(types) ? 0 : &c_types), &c_res, (isNull(row_ids) ? 0 : &c_row_ids), (isNull(col_ids) ? 0 : &c_col_ids)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(row_ids=R_igraph_0orvector_to_SEXP(&c_row_ids)); + igraph_vector_destroy(&c_row_ids); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(col_ids=R_igraph_0orvector_to_SEXP(&c_col_ids)); + igraph_vector_destroy(&c_col_ids); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, row_ids); + SET_VECTOR_ELT(r_result, 2, col_ids); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("row_ids")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("col_ids")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_bipartite / +/-------------------------------------------*/ +SEXP R_igraph_is_bipartite(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + igraph_vector_bool_t c_type; + SEXP res; + SEXP type; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_bool_init(&c_type, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_type); + type=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_is_bipartite(&c_graph, &c_res, (isNull(type) ? 0 : &c_type)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + PROTECT(type=R_igraph_0orvector_bool_to_SEXP(&c_type)); + igraph_vector_bool_destroy(&c_type); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, type); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("type")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_bipartite_game_gnp / +/-------------------------------------------*/ +SEXP R_igraph_bipartite_game_gnp(SEXP n1, SEXP n2, SEXP p, SEXP directed, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_integer_t c_n1; + igraph_integer_t c_n2; + igraph_real_t c_p; + igraph_bool_t c_directed; + igraph_neimode_t c_mode; + SEXP graph; + SEXP types; + + SEXP r_result, r_names; + /* Convert input */ + if (0 != igraph_vector_bool_init(&c_types, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_types); + types=R_GlobalEnv; /* hack to have a non-NULL value */ + c_n1=INTEGER(n1)[0]; + c_n2=INTEGER(n2)[0]; + c_p=REAL(p)[0]; + c_directed=LOGICAL(directed)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_bipartite_game_gnp(&c_graph, (isNull(types) ? 0 : &c_types), c_n1, c_n2, c_p, c_directed, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(types=R_igraph_0orvector_bool_to_SEXP(&c_types)); + igraph_vector_bool_destroy(&c_types); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, graph); + SET_VECTOR_ELT(r_result, 1, types); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("graph")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("types")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_bipartite_game_gnm / +/-------------------------------------------*/ +SEXP R_igraph_bipartite_game_gnm(SEXP n1, SEXP n2, SEXP m, SEXP directed, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_integer_t c_n1; + igraph_integer_t c_n2; + igraph_integer_t c_m; + igraph_bool_t c_directed; + igraph_neimode_t c_mode; + SEXP graph; + SEXP types; + + SEXP r_result, r_names; + /* Convert input */ + if (0 != igraph_vector_bool_init(&c_types, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_bool_destroy, &c_types); + types=R_GlobalEnv; /* hack to have a non-NULL value */ + c_n1=INTEGER(n1)[0]; + c_n2=INTEGER(n2)[0]; + c_m=INTEGER(m)[0]; + c_directed=LOGICAL(directed)[0]; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_bipartite_game_gnm(&c_graph, (isNull(types) ? 0 : &c_types), c_n1, c_n2, c_m, c_directed, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(types=R_igraph_0orvector_bool_to_SEXP(&c_types)); + igraph_vector_bool_destroy(&c_types); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, graph); + SET_VECTOR_ELT(r_result, 1, types); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("graph")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("types")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_clusters / +/-------------------------------------------*/ +SEXP R_igraph_clusters(SEXP graph, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_membership; + igraph_vector_t c_csize; + igraph_integer_t c_no; + igraph_connectedness_t c_mode; + SEXP membership; + SEXP csize; + SEXP no; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + if (0 != igraph_vector_init(&c_csize, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_csize); + c_no=0; + c_mode=REAL(mode)[0]; + /* Call igraph */ + igraph_clusters(&c_graph, &c_membership, &c_csize, &c_no, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(csize=R_igraph_vector_to_SEXP(&c_csize)); + igraph_vector_destroy(&c_csize); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(no=NEW_INTEGER(1)); + INTEGER(no)[0]=c_no; + SET_VECTOR_ELT(r_result, 0, membership); + SET_VECTOR_ELT(r_result, 1, csize); + SET_VECTOR_ELT(r_result, 2, no); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("csize")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("no")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_connected / +/-------------------------------------------*/ +SEXP R_igraph_is_connected(SEXP graph, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + igraph_connectedness_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_mode=REAL(mode)[0]; + /* Call igraph */ + igraph_is_connected(&c_graph, &c_res, c_mode); + + /* Convert output */ + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_articulation_points / +/-------------------------------------------*/ +SEXP R_igraph_articulation_points(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + /* Call igraph */ + igraph_articulation_points(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXPp1(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_biconnected_components / +/-------------------------------------------*/ +SEXP R_igraph_biconnected_components(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no; + igraph_vector_ptr_t c_tree_edges; + igraph_vector_ptr_t c_component_edges; + igraph_vector_ptr_t c_components; + igraph_vector_t c_articulation_points; + SEXP no; + SEXP tree_edges; + SEXP component_edges; + SEXP components; + SEXP articulation_points; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_no=0; + if (0 != igraph_vector_ptr_init(&c_tree_edges, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_tree_edges); + if (0 != igraph_vector_ptr_init(&c_component_edges, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_component_edges); + if (0 != igraph_vector_ptr_init(&c_components, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_components); + if (0 != igraph_vector_init(&c_articulation_points, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_articulation_points); + /* Call igraph */ + igraph_biconnected_components(&c_graph, &c_no, &c_tree_edges, &c_component_edges, &c_components, &c_articulation_points); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(5)); + PROTECT(r_names=NEW_CHARACTER(5)); + PROTECT(no=NEW_INTEGER(1)); + INTEGER(no)[0]=c_no; + PROTECT(tree_edges=R_igraph_vectorlist_to_SEXP_p1(&c_tree_edges)); + R_igraph_vectorlist_destroy(&c_tree_edges); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(component_edges=R_igraph_vectorlist_to_SEXP_p1(&c_component_edges)); + R_igraph_vectorlist_destroy(&c_component_edges); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(components=R_igraph_vectorlist_to_SEXP_p1(&c_components)); + R_igraph_vectorlist_destroy(&c_components); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(articulation_points=R_igraph_vector_to_SEXPp1(&c_articulation_points)); + igraph_vector_destroy(&c_articulation_points); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, no); + SET_VECTOR_ELT(r_result, 1, tree_edges); + SET_VECTOR_ELT(r_result, 2, component_edges); + SET_VECTOR_ELT(r_result, 3, components); + SET_VECTOR_ELT(r_result, 4, articulation_points); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("no")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("tree_edges")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("component_edges")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("components")); + SET_STRING_ELT(r_names, 4, CREATE_STRING_VECTOR("articulation_points")); + SET_NAMES(r_result, r_names); + UNPROTECT(6); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_bridges / +/-------------------------------------------*/ +SEXP R_igraph_bridges(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + /* Call igraph */ + igraph_bridges(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXPp1(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_cliques / +/-------------------------------------------*/ +SEXP R_igraph_cliques(SEXP graph, SEXP min_size, SEXP max_size) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_res; + igraph_integer_t c_min_size; + igraph_integer_t c_max_size; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); + c_min_size=INTEGER(min_size)[0]; + c_max_size=INTEGER(max_size)[0]; + /* Call igraph */ + igraph_cliques(&c_graph, &c_res, c_min_size, c_max_size); + + /* Convert output */ + PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); + R_igraph_vectorlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_clique_size_hist / +/-------------------------------------------*/ +SEXP R_igraph_clique_size_hist(SEXP graph, SEXP min_size, SEXP max_size) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_hist; + igraph_integer_t c_min_size; + igraph_integer_t c_max_size; + SEXP hist; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_hist, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_hist); + c_min_size=INTEGER(min_size)[0]; + c_max_size=INTEGER(max_size)[0]; + /* Call igraph */ + igraph_clique_size_hist(&c_graph, &c_hist, c_min_size, c_max_size); + + /* Convert output */ + PROTECT(hist=R_igraph_vector_to_SEXP(&c_hist)); + igraph_vector_destroy(&c_hist); + IGRAPH_FINALLY_CLEAN(1); + r_result = hist; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_largest_cliques / +/-------------------------------------------*/ +SEXP R_igraph_largest_cliques(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); + /* Call igraph */ + igraph_largest_cliques(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); + R_igraph_vectorlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_maximal_cliques_hist / +/-------------------------------------------*/ +SEXP R_igraph_maximal_cliques_hist(SEXP graph, SEXP min_size, SEXP max_size) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_hist; + igraph_integer_t c_min_size; + igraph_integer_t c_max_size; + SEXP hist; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_hist, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_hist); + c_min_size=INTEGER(min_size)[0]; + c_max_size=INTEGER(max_size)[0]; + /* Call igraph */ + igraph_maximal_cliques_hist(&c_graph, &c_hist, c_min_size, c_max_size); + + /* Convert output */ + PROTECT(hist=R_igraph_vector_to_SEXP(&c_hist)); + igraph_vector_destroy(&c_hist); + IGRAPH_FINALLY_CLEAN(1); + r_result = hist; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_clique_number / +/-------------------------------------------*/ +SEXP R_igraph_clique_number(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no; + SEXP no; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_no=0; + /* Call igraph */ + igraph_clique_number(&c_graph, &c_no); + + /* Convert output */ + PROTECT(no=NEW_INTEGER(1)); + INTEGER(no)[0]=c_no; + r_result = no; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_weighted_cliques / +/-------------------------------------------*/ +SEXP R_igraph_weighted_cliques(SEXP graph, SEXP vertex_weights, SEXP min_weight, SEXP max_weight, SEXP maximal) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vertex_weights; + igraph_vector_ptr_t c_res; + igraph_real_t c_min_weight; + igraph_real_t c_max_weight; + igraph_bool_t c_maximal; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(vertex_weights)) { R_SEXP_to_vector(vertex_weights, &c_vertex_weights); } + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); + c_min_weight=REAL(min_weight)[0]; + c_max_weight=REAL(max_weight)[0]; + c_maximal=LOGICAL(maximal)[0]; + /* Call igraph */ + igraph_weighted_cliques(&c_graph, (isNull(vertex_weights) ? 0 : &c_vertex_weights), &c_res, c_min_weight, c_max_weight, c_maximal); + + /* Convert output */ + PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); + R_igraph_vectorlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_largest_weighted_cliques / +/-------------------------------------------*/ +SEXP R_igraph_largest_weighted_cliques(SEXP graph, SEXP vertex_weights) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vertex_weights; + igraph_vector_ptr_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(vertex_weights)) { R_SEXP_to_vector(vertex_weights, &c_vertex_weights); } + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_res); + /* Call igraph */ + igraph_largest_weighted_cliques(&c_graph, (isNull(vertex_weights) ? 0 : &c_vertex_weights), &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_vectorlist_to_SEXP_p1(&c_res)); + R_igraph_vectorlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_weighted_clique_number / +/-------------------------------------------*/ +SEXP R_igraph_weighted_clique_number(SEXP graph, SEXP vertex_weights) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vertex_weights; + igraph_real_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(vertex_weights)) { R_SEXP_to_vector(vertex_weights, &c_vertex_weights); } + /* Call igraph */ + igraph_weighted_clique_number(&c_graph, (isNull(vertex_weights) ? 0 : &c_vertex_weights), &c_res); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_star / +/-------------------------------------------*/ +SEXP R_igraph_layout_star(SEXP graph, SEXP center, SEXP order) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_integer_t c_center; + igraph_vector_t c_order; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_center = (igraph_integer_t) REAL(center)[0]; + if (!isNull(order)) { R_SEXP_to_vector(order, &c_order); } + /* Call igraph */ + igraph_layout_star(&c_graph, &c_res, c_center, (isNull(order) ? 0 : &c_order)); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_grid / +/-------------------------------------------*/ +SEXP R_igraph_layout_grid(SEXP graph, SEXP width) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + long int c_width; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_width=INTEGER(width)[0]; + /* Call igraph */ + igraph_layout_grid(&c_graph, &c_res, c_width); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_grid_3d / +/-------------------------------------------*/ +SEXP R_igraph_layout_grid_3d(SEXP graph, SEXP width, SEXP height) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + long int c_width; + long int c_height; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_width=INTEGER(width)[0]; + c_height=INTEGER(height)[0]; + /* Call igraph */ + igraph_layout_grid_3d(&c_graph, &c_res, c_width, c_height); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_drl / +/-------------------------------------------*/ +SEXP R_igraph_layout_drl(SEXP graph, SEXP res, SEXP use_seed, SEXP options, SEXP weights, SEXP fixed) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_bool_t c_use_seed; + igraph_layout_drl_options_t c_options; + igraph_vector_t c_weights; + igraph_vector_bool_t c_fixed; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != R_SEXP_to_igraph_matrix_copy(res, &c_res)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_use_seed=LOGICAL(use_seed)[0]; + R_SEXP_to_igraph_layout_drl_options(options, &c_options); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(fixed)) { R_SEXP_to_vector_bool(fixed, &c_fixed); } + /* Call igraph */ + igraph_layout_drl(&c_graph, &c_res, c_use_seed, &c_options, (isNull(weights) ? 0 : &c_weights), (isNull(fixed) ? 0 : &c_fixed)); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_drl_3d / +/-------------------------------------------*/ +SEXP R_igraph_layout_drl_3d(SEXP graph, SEXP res, SEXP use_seed, SEXP options, SEXP weights, SEXP fixed) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_bool_t c_use_seed; + igraph_layout_drl_options_t c_options; + igraph_vector_t c_weights; + igraph_vector_bool_t c_fixed; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != R_SEXP_to_igraph_matrix_copy(res, &c_res)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_use_seed=LOGICAL(use_seed)[0]; + R_SEXP_to_igraph_layout_drl_options(options, &c_options); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(fixed)) { R_SEXP_to_vector_bool(fixed, &c_fixed); } + /* Call igraph */ + igraph_layout_drl_3d(&c_graph, &c_res, c_use_seed, &c_options, (isNull(weights) ? 0 : &c_weights), (isNull(fixed) ? 0 : &c_fixed)); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_sugiyama / +/-------------------------------------------*/ +SEXP R_igraph_layout_sugiyama(SEXP graph, SEXP layers, SEXP hgap, SEXP vgap, SEXP maxiter, SEXP weights) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_t c_extd_graph; + igraph_vector_t c_extd_to_orig_eids; + igraph_vector_t c_layers; + igraph_real_t c_hgap; + igraph_real_t c_vgap; + long int c_maxiter; + igraph_vector_t c_weights; + SEXP res; + SEXP extd_graph; + SEXP extd_to_orig_eids; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + extd_graph=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_extd_to_orig_eids, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_extd_to_orig_eids); + extd_to_orig_eids=R_GlobalEnv; /* hack to have a non-NULL value */ + if (!isNull(layers)) { R_SEXP_to_vector(layers, &c_layers); } + c_hgap=REAL(hgap)[0]; + c_vgap=REAL(vgap)[0]; + c_maxiter=INTEGER(maxiter)[0]; + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + /* Call igraph */ + igraph_layout_sugiyama(&c_graph, &c_res, (isNull(extd_graph) ? 0 : &c_extd_graph), (isNull(extd_to_orig_eids) ? 0 : &c_extd_to_orig_eids), (isNull(layers) ? 0 : &c_layers), c_hgap, c_vgap, c_maxiter, (isNull(weights) ? 0 : &c_weights)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, &c_extd_graph); + PROTECT(extd_graph=R_igraph_to_SEXP(&c_extd_graph)); + igraph_destroy(&c_extd_graph); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(extd_to_orig_eids=R_igraph_0orvector_to_SEXPp1(&c_extd_to_orig_eids)); + igraph_vector_destroy(&c_extd_to_orig_eids); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, extd_graph); + SET_VECTOR_ELT(r_result, 2, extd_to_orig_eids); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("extd_graph")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("extd_to_orig_eids")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_mds / +/-------------------------------------------*/ +SEXP R_igraph_layout_mds(SEXP graph, SEXP dist, SEXP dim) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_matrix_t c_dist; + long int c_dim; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + if (!isNull(dist)) { R_SEXP_to_matrix(dist, &c_dist); } + c_dim=INTEGER(dim)[0]; + /* Call igraph */ + igraph_layout_mds(&c_graph, &c_res, (isNull(dist) ? 0 : &c_dist), c_dim); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_bipartite / +/-------------------------------------------*/ +SEXP R_igraph_layout_bipartite(SEXP graph, SEXP types, SEXP hgap, SEXP vgap, SEXP maxiter) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_matrix_t c_res; + igraph_real_t c_hgap; + igraph_real_t c_vgap; + long int c_maxiter; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_hgap=REAL(hgap)[0]; + c_vgap=REAL(vgap)[0]; + c_maxiter=INTEGER(maxiter)[0]; + /* Call igraph */ + igraph_layout_bipartite(&c_graph, (isNull(types) ? 0 : &c_types), &c_res, c_hgap, c_vgap, c_maxiter); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_gem / +/-------------------------------------------*/ +SEXP R_igraph_layout_gem(SEXP graph, SEXP res, SEXP use_seed, SEXP maxiter, SEXP temp_max, SEXP temp_min, SEXP temp_init) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_bool_t c_use_seed; + igraph_integer_t c_maxiter; + igraph_real_t c_temp_max; + igraph_real_t c_temp_min; + igraph_real_t c_temp_init; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != R_SEXP_to_igraph_matrix_copy(res, &c_res)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_use_seed=LOGICAL(use_seed)[0]; + c_maxiter=INTEGER(maxiter)[0]; + c_temp_max=REAL(temp_max)[0]; + c_temp_min=REAL(temp_min)[0]; + c_temp_init=REAL(temp_init)[0]; + /* Call igraph */ + igraph_layout_gem(&c_graph, &c_res, c_use_seed, c_maxiter, c_temp_max, c_temp_min, c_temp_init); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_layout_davidson_harel / +/-------------------------------------------*/ +SEXP R_igraph_layout_davidson_harel(SEXP graph, SEXP res, SEXP use_seed, SEXP maxiter, SEXP fineiter, SEXP cool_fact, SEXP weight_node_dist, SEXP weight_border, SEXP weight_edge_lengths, SEXP weight_edge_crossings, SEXP weight_node_edge_dist) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_bool_t c_use_seed; + igraph_integer_t c_maxiter; + igraph_integer_t c_fineiter; + igraph_real_t c_cool_fact; + igraph_real_t c_weight_node_dist; + igraph_real_t c_weight_border; + igraph_real_t c_weight_edge_lengths; + igraph_real_t c_weight_edge_crossings; + igraph_real_t c_weight_node_edge_dist; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != R_SEXP_to_igraph_matrix_copy(res, &c_res)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_use_seed=LOGICAL(use_seed)[0]; + c_maxiter=INTEGER(maxiter)[0]; + c_fineiter=INTEGER(fineiter)[0]; + c_cool_fact=REAL(cool_fact)[0]; + c_weight_node_dist=REAL(weight_node_dist)[0]; + c_weight_border=REAL(weight_border)[0]; + c_weight_edge_lengths=REAL(weight_edge_lengths)[0]; + c_weight_edge_crossings=REAL(weight_edge_crossings)[0]; + c_weight_node_edge_dist=REAL(weight_node_edge_dist)[0]; + /* Call igraph */ + igraph_layout_davidson_harel(&c_graph, &c_res, c_use_seed, c_maxiter, c_fineiter, c_cool_fact, c_weight_node_dist, c_weight_border, c_weight_edge_lengths, c_weight_edge_crossings, c_weight_node_edge_dist); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_similarity_jaccard / +/-------------------------------------------*/ +SEXP R_igraph_similarity_jaccard(SEXP graph, SEXP vids, SEXP mode, SEXP loops) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_bool_t c_loops; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_loops=LOGICAL(loops)[0]; + /* Call igraph */ + igraph_similarity_jaccard(&c_graph, &c_res, c_vids, c_mode, c_loops); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_similarity_dice / +/-------------------------------------------*/ +SEXP R_igraph_similarity_dice(SEXP graph, SEXP vids, SEXP mode, SEXP loops) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + igraph_bool_t c_loops; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + c_loops=LOGICAL(loops)[0]; + /* Call igraph */ + igraph_similarity_dice(&c_graph, &c_res, c_vids, c_mode, c_loops); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_similarity_inverse_log_weighted / +/-------------------------------------------*/ +SEXP R_igraph_similarity_inverse_log_weighted(SEXP graph, SEXP vids, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_vs_t c_vids; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_similarity_inverse_log_weighted(&c_graph, &c_res, c_vids, c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_compare_communities / +/-------------------------------------------*/ +SEXP R_igraph_compare_communities(SEXP comm1, SEXP comm2, SEXP method) { + /* Declarations */ + igraph_vector_t c_comm1; + igraph_vector_t c_comm2; + igraph_real_t c_res; + igraph_community_comparison_t c_method; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector(comm1, &c_comm1); + R_SEXP_to_vector(comm2, &c_comm2); + c_method = (igraph_community_comparison_t) Rf_asInteger(method); + /* Call igraph */ + igraph_compare_communities(&c_comm1, &c_comm2, &c_res, c_method); + + /* Convert output */ + PROTECT(res=NEW_NUMERIC(1)); + REAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_modularity / +/-------------------------------------------*/ +SEXP R_igraph_modularity(SEXP graph, SEXP membership, SEXP weights, SEXP resolution, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_membership; + igraph_vector_t c_weights; + igraph_real_t c_resolution; + igraph_bool_t c_directed; + igraph_real_t c_modularity; + SEXP modularity; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_vector(membership, &c_membership); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_resolution=REAL(resolution)[0]; + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_modularity(&c_graph, &c_membership, (isNull(weights) ? 0 : &c_weights), c_resolution, c_directed, &c_modularity); + + /* Convert output */ + PROTECT(modularity=NEW_NUMERIC(1)); + REAL(modularity)[0]=c_modularity; + r_result = modularity; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_modularity_matrix / +/-------------------------------------------*/ +SEXP R_igraph_modularity_matrix(SEXP graph, SEXP weights, SEXP resolution, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_real_t c_resolution; + igraph_matrix_t c_modmat; + igraph_bool_t c_directed; + SEXP modmat; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_resolution=REAL(resolution)[0]; + if (0 != igraph_matrix_init(&c_modmat, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_modmat); + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_modularity_matrix(&c_graph, (isNull(weights) ? 0 : &c_weights), c_resolution, &c_modmat, c_directed); + + /* Convert output */ + PROTECT(modmat=R_igraph_matrix_to_SEXP(&c_modmat)); + igraph_matrix_destroy(&c_modmat); + IGRAPH_FINALLY_CLEAN(1); + r_result = modmat; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_community_fluid_communities / +/-------------------------------------------*/ +SEXP R_igraph_community_fluid_communities(SEXP graph, SEXP no_of_communities) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_no_of_communities; + igraph_vector_t c_membership; + igraph_real_t c_modularity; + SEXP membership; + SEXP modularity; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_no_of_communities=INTEGER(no_of_communities)[0]; + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + /* Call igraph */ + igraph_community_fluid_communities(&c_graph, c_no_of_communities, &c_membership, &c_modularity); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(modularity=NEW_NUMERIC(1)); + REAL(modularity)[0]=c_modularity; + SET_VECTOR_ELT(r_result, 0, membership); + SET_VECTOR_ELT(r_result, 1, modularity); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("modularity")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_community_label_propagation / +/-------------------------------------------*/ +SEXP R_igraph_community_label_propagation(SEXP graph, SEXP weights, SEXP initial, SEXP fixed) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_membership; + igraph_vector_t c_weights; + igraph_vector_t c_initial; + igraph_vector_bool_t c_fixed; + igraph_real_t c_modularity; + SEXP membership; + SEXP modularity; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(initial)) { R_SEXP_to_vector(initial, &c_initial); } + if (!isNull(fixed)) { R_SEXP_to_vector_bool(fixed, &c_fixed); } + /* Call igraph */ + igraph_community_label_propagation(&c_graph, &c_membership, (isNull(weights) ? 0 : &c_weights), (isNull(initial) ? 0 : &c_initial), (isNull(fixed) ? 0 : &c_fixed), &c_modularity); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(modularity=NEW_NUMERIC(1)); + REAL(modularity)[0]=c_modularity; + SET_VECTOR_ELT(r_result, 0, membership); + SET_VECTOR_ELT(r_result, 1, modularity); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("modularity")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_community_multilevel / +/-------------------------------------------*/ +SEXP R_igraph_community_multilevel(SEXP graph, SEXP weights, SEXP resolution) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_real_t c_resolution; + igraph_vector_t c_membership; + igraph_matrix_t c_memberships; + igraph_vector_t c_modularity; + SEXP membership; + SEXP memberships; + SEXP modularity; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_resolution=REAL(resolution)[0]; + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + if (0 != igraph_matrix_init(&c_memberships, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_memberships); + memberships=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_modularity, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_modularity); + modularity=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_community_multilevel(&c_graph, (isNull(weights) ? 0 : &c_weights), c_resolution, &c_membership, (isNull(memberships) ? 0 : &c_memberships), (isNull(modularity) ? 0 : &c_modularity)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(memberships=R_igraph_0ormatrix_to_SEXP(&c_memberships)); + igraph_matrix_destroy(&c_memberships); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(modularity=R_igraph_0orvector_to_SEXP(&c_modularity)); + igraph_vector_destroy(&c_modularity); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, membership); + SET_VECTOR_ELT(r_result, 1, memberships); + SET_VECTOR_ELT(r_result, 2, modularity); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("memberships")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("modularity")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_community_optimal_modularity / +/-------------------------------------------*/ +SEXP R_igraph_community_optimal_modularity(SEXP graph, SEXP weights) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_modularity; + igraph_vector_t c_membership; + igraph_vector_t c_weights; + SEXP modularity; + SEXP membership; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + membership=R_GlobalEnv; /* hack to have a non-NULL value */ + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + /* Call igraph */ + igraph_community_optimal_modularity(&c_graph, &c_modularity, (isNull(membership) ? 0 : &c_membership), (isNull(weights) ? 0 : &c_weights)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(modularity=NEW_NUMERIC(1)); + REAL(modularity)[0]=c_modularity; + PROTECT(membership=R_igraph_0orvector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, modularity); + SET_VECTOR_ELT(r_result, 1, membership); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("modularity")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("membership")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_community_leiden / +/-------------------------------------------*/ +SEXP R_igraph_community_leiden(SEXP graph, SEXP weights, SEXP vertex_weights, SEXP resolution_parameter, SEXP beta, SEXP start, SEXP membership) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_weights; + igraph_vector_t c_vertex_weights; + igraph_real_t c_resolution_parameter; + igraph_real_t c_beta; + igraph_bool_t c_start; + igraph_vector_t c_membership; + igraph_integer_t c_nb_clusters; + igraph_real_t c_quality; + SEXP nb_clusters; + SEXP quality; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(vertex_weights)) { R_SEXP_to_vector(vertex_weights, &c_vertex_weights); } + c_resolution_parameter=REAL(resolution_parameter)[0]; + c_beta=REAL(beta)[0]; + c_start=LOGICAL(start)[0]; + if (!isNull(membership)) { + if (0 != R_SEXP_to_vector_copy(membership, &c_membership)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } else { + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + membership=NEW_NUMERIC(0); + c_nb_clusters=0; + /* Call igraph */ + igraph_community_leiden(&c_graph, (isNull(weights) ? 0 : &c_weights), (isNull(vertex_weights) ? 0 : &c_vertex_weights), c_resolution_parameter, c_beta, c_start, (isNull(membership) ? 0 : &c_membership), &c_nb_clusters, &c_quality); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(membership=R_igraph_0orvector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(nb_clusters=NEW_INTEGER(1)); + INTEGER(nb_clusters)[0]=c_nb_clusters; + PROTECT(quality=NEW_NUMERIC(1)); + REAL(quality)[0]=c_quality; + SET_VECTOR_ELT(r_result, 0, membership); + SET_VECTOR_ELT(r_result, 1, nb_clusters); + SET_VECTOR_ELT(r_result, 2, quality); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("nb_clusters")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("quality")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_split_join_distance / +/-------------------------------------------*/ +SEXP R_igraph_split_join_distance(SEXP comm1, SEXP comm2) { + /* Declarations */ + igraph_vector_t c_comm1; + igraph_vector_t c_comm2; + igraph_integer_t c_distance12; + igraph_integer_t c_distance21; + SEXP distance12; + SEXP distance21; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_vector(comm1, &c_comm1); + R_SEXP_to_vector(comm2, &c_comm2); + c_distance12=0; + c_distance21=0; + /* Call igraph */ + igraph_split_join_distance(&c_comm1, &c_comm2, &c_distance12, &c_distance21); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(distance12=NEW_INTEGER(1)); + INTEGER(distance12)[0]=c_distance12; + PROTECT(distance21=NEW_INTEGER(1)); + INTEGER(distance21)[0]=c_distance21; + SET_VECTOR_ELT(r_result, 0, distance12); + SET_VECTOR_ELT(r_result, 1, distance21); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("distance12")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("distance21")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hrg_fit / +/-------------------------------------------*/ +SEXP R_igraph_hrg_fit(SEXP graph, SEXP hrg, SEXP start, SEXP steps) { + /* Declarations */ + igraph_t c_graph; + igraph_hrg_t c_hrg; + igraph_bool_t c_start; + int c_steps; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != R_SEXP_to_hrg_copy(hrg, &c_hrg)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_hrg_destroy, &c_hrg); + c_start=LOGICAL(start)[0]; + c_steps=INTEGER(steps)[0]; + /* Call igraph */ + igraph_hrg_fit(&c_graph, &c_hrg, c_start, c_steps); + + /* Convert output */ + PROTECT(hrg=R_igraph_hrg_to_SEXP(&c_hrg)); + igraph_hrg_destroy(&c_hrg); + IGRAPH_FINALLY_CLEAN(1); + r_result = hrg; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hrg_game / +/-------------------------------------------*/ +SEXP R_igraph_hrg_game(SEXP hrg) { + /* Declarations */ + igraph_t c_graph; + igraph_hrg_t c_hrg; + SEXP graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_hrg(hrg, &c_hrg); + /* Call igraph */ + igraph_hrg_game(&c_graph, &c_hrg); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hrg_dendrogram / +/-------------------------------------------*/ +SEXP R_igraph_hrg_dendrogram(SEXP hrg) { + /* Declarations */ + igraph_t c_graph; + igraph_hrg_t c_hrg; + SEXP graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_hrg(hrg, &c_hrg); + /* Call igraph */ + igraph_hrg_dendrogram(&c_graph, &c_hrg); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hrg_consensus / +/-------------------------------------------*/ +SEXP R_igraph_hrg_consensus(SEXP graph, SEXP hrg, SEXP start, SEXP num_samples) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_parents; + igraph_vector_t c_weights; + igraph_hrg_t c_hrg; + igraph_bool_t c_start; + int c_num_samples; + SEXP parents; + SEXP weights; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_parents, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_parents); + if (0 != igraph_vector_init(&c_weights, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_weights); + if (0 != R_SEXP_to_hrg_copy(hrg, &c_hrg)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_hrg_destroy, &c_hrg); + c_start=LOGICAL(start)[0]; + c_num_samples=INTEGER(num_samples)[0]; + /* Call igraph */ + igraph_hrg_consensus(&c_graph, &c_parents, &c_weights, &c_hrg, c_start, c_num_samples); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(parents=R_igraph_vector_to_SEXP(&c_parents)); + igraph_vector_destroy(&c_parents); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(weights=R_igraph_vector_to_SEXP(&c_weights)); + igraph_vector_destroy(&c_weights); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(hrg=R_igraph_hrg_to_SEXP(&c_hrg)); + igraph_hrg_destroy(&c_hrg); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, parents); + SET_VECTOR_ELT(r_result, 1, weights); + SET_VECTOR_ELT(r_result, 2, hrg); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("parents")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("weights")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("hrg")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hrg_predict / +/-------------------------------------------*/ +SEXP R_igraph_hrg_predict(SEXP graph, SEXP hrg, SEXP start, SEXP num_samples, SEXP num_bins) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_edges; + igraph_vector_t c_prob; + igraph_hrg_t c_hrg; + igraph_bool_t c_start; + int c_num_samples; + int c_num_bins; + SEXP edges; + SEXP prob; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_edges, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_edges); + if (0 != igraph_vector_init(&c_prob, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_prob); + if (0 != R_SEXP_to_hrg_copy(hrg, &c_hrg)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_hrg_destroy, &c_hrg); + c_start=LOGICAL(start)[0]; + c_num_samples=INTEGER(num_samples)[0]; + c_num_bins=INTEGER(num_bins)[0]; + /* Call igraph */ + igraph_hrg_predict(&c_graph, &c_edges, &c_prob, &c_hrg, c_start, c_num_samples, c_num_bins); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(edges=R_igraph_vector_to_SEXPp1(&c_edges)); + igraph_vector_destroy(&c_edges); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(prob=R_igraph_vector_to_SEXP(&c_prob)); + igraph_vector_destroy(&c_prob); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(hrg=R_igraph_hrg_to_SEXP(&c_hrg)); + igraph_hrg_destroy(&c_hrg); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, edges); + SET_VECTOR_ELT(r_result, 1, prob); + SET_VECTOR_ELT(r_result, 2, hrg); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("edges")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("prob")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("hrg")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_hrg_create / +/-------------------------------------------*/ +SEXP R_igraph_hrg_create(SEXP graph, SEXP prob) { + /* Declarations */ + igraph_hrg_t c_hrg; + igraph_t c_graph; + igraph_vector_t c_prob; + SEXP hrg; + + SEXP r_result; + /* Convert input */ + if (0 != igraph_hrg_init(&c_hrg, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_hrg_destroy, &c_hrg); + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_vector(prob, &c_prob); + /* Call igraph */ + igraph_hrg_create(&c_hrg, &c_graph, &c_prob); + + /* Convert output */ + PROTECT(hrg=R_igraph_hrg_to_SEXP(&c_hrg)); + igraph_hrg_destroy(&c_hrg); + IGRAPH_FINALLY_CLEAN(1); + r_result = hrg; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_community_infomap / +/-------------------------------------------*/ +SEXP R_igraph_community_infomap(SEXP graph, SEXP e_weights, SEXP v_weights, SEXP nb_trials) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_e_weights; + igraph_vector_t c_v_weights; + int c_nb_trials; + igraph_vector_t c_membership; + igraph_real_t c_codelength; + SEXP membership; + SEXP codelength; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(e_weights)) { R_SEXP_to_vector(e_weights, &c_e_weights); } + if (!isNull(v_weights)) { R_SEXP_to_vector(v_weights, &c_v_weights); } + c_nb_trials=INTEGER(nb_trials)[0]; + if (0 != igraph_vector_init(&c_membership, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_membership); + /* Call igraph */ + igraph_community_infomap(&c_graph, (isNull(e_weights) ? 0 : &c_e_weights), (isNull(v_weights) ? 0 : &c_v_weights), c_nb_trials, &c_membership, &c_codelength); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(membership=R_igraph_vector_to_SEXP(&c_membership)); + igraph_vector_destroy(&c_membership); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(codelength=NEW_NUMERIC(1)); + REAL(codelength)[0]=c_codelength; + SET_VECTOR_ELT(r_result, 0, membership); + SET_VECTOR_ELT(r_result, 1, codelength); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("membership")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("codelength")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_to_directed / +/-------------------------------------------*/ +SEXP R_igraph_to_directed(SEXP graph, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_to_directed_t c_mode; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph_copy(graph, &c_graph); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + c_mode = (igraph_to_directed_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_to_directed(&c_graph, c_mode); + + /* Convert output */ + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_to_undirected / +/-------------------------------------------*/ +SEXP R_igraph_to_undirected(SEXP graph, SEXP mode, SEXP edge_attr_comb) { + /* Declarations */ + igraph_t c_graph; + igraph_to_undirected_t c_mode; + igraph_attribute_combination_t c_edge_attr_comb; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph_copy(graph, &c_graph); + IGRAPH_FINALLY(igraph_destroy, &c_graph); + c_mode = (igraph_to_undirected_t) Rf_asInteger(mode); + R_SEXP_to_attr_comb(edge_attr_comb, &c_edge_attr_comb); + /* Call igraph */ + igraph_to_undirected(&c_graph, c_mode, &c_edge_attr_comb); + + /* Convert output */ + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + igraph_attribute_combination_destroy(&c_edge_attr_comb); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_stochastic / +/-------------------------------------------*/ +SEXP R_igraph_get_stochastic(SEXP graph, SEXP column_wise) { + /* Declarations */ + igraph_t c_graph; + igraph_matrix_t c_res; + igraph_bool_t c_column_wise; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_matrix_init(&c_res, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_res); + c_column_wise=LOGICAL(column_wise)[0]; + /* Call igraph */ + igraph_get_stochastic(&c_graph, &c_res, c_column_wise); + + /* Convert output */ + PROTECT(res=R_igraph_matrix_to_SEXP(&c_res)); + igraph_matrix_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_stochastic_sparsemat / +/-------------------------------------------*/ +SEXP R_igraph_get_stochastic_sparsemat(SEXP graph, SEXP column_wise) { + /* Declarations */ + igraph_t c_graph; + igraph_sparsemat_t c_sparsemat; + igraph_bool_t c_column_wise; + SEXP sparsemat; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Don't need to init. */ + c_column_wise=LOGICAL(column_wise)[0]; + /* Call igraph */ + igraph_get_stochastic_sparsemat(&c_graph, &c_sparsemat, c_column_wise); + + /* Convert output */ + PROTECT(sparsemat=R_igraph_sparsemat_to_SEXP(&c_sparsemat)); + igraph_sparsemat_destroy(&c_sparsemat); + r_result = sparsemat; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_dyad_census / +/-------------------------------------------*/ +SEXP R_igraph_dyad_census(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_mut; + igraph_integer_t c_asym; + igraph_integer_t c_null; + SEXP mut; + SEXP asym; + SEXP null; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_mut=0; + c_asym=0; + c_null=0; + /* Call igraph */ + igraph_dyad_census(&c_graph, &c_mut, &c_asym, &c_null); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(mut=NEW_INTEGER(1)); + INTEGER(mut)[0]=c_mut; + PROTECT(asym=NEW_INTEGER(1)); + INTEGER(asym)[0]=c_asym; + PROTECT(null=NEW_INTEGER(1)); + INTEGER(null)[0]=c_null; + SET_VECTOR_ELT(r_result, 0, mut); + SET_VECTOR_ELT(r_result, 1, asym); + SET_VECTOR_ELT(r_result, 2, null); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("mut")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("asym")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("null")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_triad_census / +/-------------------------------------------*/ +SEXP R_igraph_triad_census(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + /* Call igraph */ + igraph_triad_census(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_adjacent_triangles / +/-------------------------------------------*/ +SEXP R_igraph_adjacent_triangles(SEXP graph, SEXP vids) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vs_t c_vids; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + R_SEXP_to_igraph_vs(vids, &c_graph, &c_vids); + /* Call igraph */ + igraph_adjacent_triangles(&c_graph, &c_res, c_vids); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + igraph_vs_destroy(&c_vids); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_0 / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_0(SEXP graph, SEXP weights, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vector_t c_weights; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_scan_0(&c_graph, &c_res, (isNull(weights) ? 0 : &c_weights), c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_0_them / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_0_them(SEXP us, SEXP them, SEXP weights_them, SEXP mode) { + /* Declarations */ + igraph_t c_us; + igraph_t c_them; + igraph_vector_t c_res; + igraph_vector_t c_weights_them; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(us, &c_us); + R_SEXP_to_igraph(them, &c_them); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights_them)) { R_SEXP_to_vector(weights_them, &c_weights_them); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_scan_0_them(&c_us, &c_them, &c_res, (isNull(weights_them) ? 0 : &c_weights_them), c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_1_ecount / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_1_ecount(SEXP graph, SEXP weights, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vector_t c_weights; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_scan_1_ecount(&c_graph, &c_res, (isNull(weights) ? 0 : &c_weights), c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_1_ecount_them / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_1_ecount_them(SEXP us, SEXP them, SEXP weights_them, SEXP mode) { + /* Declarations */ + igraph_t c_us; + igraph_t c_them; + igraph_vector_t c_res; + igraph_vector_t c_weights_them; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(us, &c_us); + R_SEXP_to_igraph(them, &c_them); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights_them)) { R_SEXP_to_vector(weights_them, &c_weights_them); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_scan_1_ecount_them(&c_us, &c_them, &c_res, (isNull(weights_them) ? 0 : &c_weights_them), c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_k_ecount / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_k_ecount(SEXP graph, SEXP k, SEXP weights, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + int c_k; + igraph_vector_t c_res; + igraph_vector_t c_weights; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_k=INTEGER(k)[0]; + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_scan_k_ecount(&c_graph, c_k, &c_res, (isNull(weights) ? 0 : &c_weights), c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_k_ecount_them / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_k_ecount_them(SEXP us, SEXP them, SEXP k, SEXP weights_them, SEXP mode) { + /* Declarations */ + igraph_t c_us; + igraph_t c_them; + int c_k; + igraph_vector_t c_res; + igraph_vector_t c_weights_them; + igraph_neimode_t c_mode; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(us, &c_us); + R_SEXP_to_igraph(them, &c_them); + c_k=INTEGER(k)[0]; + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights_them)) { R_SEXP_to_vector(weights_them, &c_weights_them); } + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_local_scan_k_ecount_them(&c_us, &c_them, c_k, &c_res, (isNull(weights_them) ? 0 : &c_weights_them), c_mode); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_local_scan_neighborhood_ecount / +/-------------------------------------------*/ +SEXP R_igraph_local_scan_neighborhood_ecount(SEXP graph, SEXP weights, SEXP neighborhoods) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_vector_t c_weights; + igraph_vector_ptr_t c_neighborhoods; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + if (!isNull(neighborhoods)) { R_igraph_SEXP_to_vectorlist_int(neighborhoods, &c_neighborhoods); } + /* Call igraph */ + igraph_local_scan_neighborhood_ecount(&c_graph, &c_res, (isNull(weights) ? 0 : &c_weights), &c_neighborhoods); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXP(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_list_triangles / +/-------------------------------------------*/ +SEXP R_igraph_list_triangles(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_int_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_int_destroy, &c_res); + /* Call igraph */ + igraph_list_triangles(&c_graph, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_vector_int_to_SEXPp1(&c_res)); + igraph_vector_int_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_maxflow / +/-------------------------------------------*/ +SEXP R_igraph_maxflow(SEXP graph, SEXP source, SEXP target, SEXP capacity) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_value; + igraph_vector_t c_flow; + igraph_vector_t c_cut; + igraph_vector_t c_partition1; + igraph_vector_t c_partition2; + igraph_integer_t c_source; + igraph_integer_t c_target; + igraph_vector_t c_capacity; + igraph_maxflow_stats_t c_stats; + SEXP value; + SEXP flow; + SEXP cut; + SEXP partition1; + SEXP partition2; + SEXP stats; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_flow, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_flow); + flow=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_cut, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_cut); + cut=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_partition1, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_partition1); + if (0 != igraph_vector_init(&c_partition2, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_partition2); + c_source = (igraph_integer_t) REAL(source)[0]; + c_target = (igraph_integer_t) REAL(target)[0]; + if (!isNull(capacity)) { R_SEXP_to_vector(capacity, &c_capacity); } + /* Call igraph */ + igraph_maxflow(&c_graph, &c_value, (isNull(flow) ? 0 : &c_flow), (isNull(cut) ? 0 : &c_cut), &c_partition1, &c_partition2, c_source, c_target, (isNull(capacity) ? 0 : &c_capacity), &c_stats); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(6)); + PROTECT(r_names=NEW_CHARACTER(6)); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(flow=R_igraph_0orvector_to_SEXP(&c_flow)); + igraph_vector_destroy(&c_flow); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(cut=R_igraph_0orvector_to_SEXPp1(&c_cut)); + igraph_vector_destroy(&c_cut); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(partition1=R_igraph_vector_to_SEXPp1(&c_partition1)); + igraph_vector_destroy(&c_partition1); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(partition2=R_igraph_vector_to_SEXPp1(&c_partition2)); + igraph_vector_destroy(&c_partition2); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(stats=R_igraph_maxflow_stats_to_SEXP(&c_stats)); + SET_VECTOR_ELT(r_result, 0, value); + SET_VECTOR_ELT(r_result, 1, flow); + SET_VECTOR_ELT(r_result, 2, cut); + SET_VECTOR_ELT(r_result, 3, partition1); + SET_VECTOR_ELT(r_result, 4, partition2); + SET_VECTOR_ELT(r_result, 5, stats); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("flow")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("cut")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("partition1")); + SET_STRING_ELT(r_names, 4, CREATE_STRING_VECTOR("partition2")); + SET_STRING_ELT(r_names, 5, CREATE_STRING_VECTOR("stats")); + SET_NAMES(r_result, r_names); + UNPROTECT(7); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_st_mincut / +/-------------------------------------------*/ +SEXP R_igraph_st_mincut(SEXP graph, SEXP source, SEXP target, SEXP capacity) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_value; + igraph_vector_t c_cut; + igraph_vector_t c_partition1; + igraph_vector_t c_partition2; + igraph_integer_t c_source; + igraph_integer_t c_target; + igraph_vector_t c_capacity; + SEXP value; + SEXP cut; + SEXP partition1; + SEXP partition2; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_cut, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_cut); + cut=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_partition1, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_partition1); + if (0 != igraph_vector_init(&c_partition2, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_partition2); + c_source = (igraph_integer_t) REAL(source)[0]; + c_target = (igraph_integer_t) REAL(target)[0]; + if (!isNull(capacity)) { R_SEXP_to_vector(capacity, &c_capacity); } + /* Call igraph */ + igraph_st_mincut(&c_graph, &c_value, (isNull(cut) ? 0 : &c_cut), &c_partition1, &c_partition2, c_source, c_target, (isNull(capacity) ? 0 : &c_capacity)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(4)); + PROTECT(r_names=NEW_CHARACTER(4)); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(cut=R_igraph_0orvector_to_SEXPp1(&c_cut)); + igraph_vector_destroy(&c_cut); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(partition1=R_igraph_vector_to_SEXPp1(&c_partition1)); + igraph_vector_destroy(&c_partition1); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(partition2=R_igraph_vector_to_SEXPp1(&c_partition2)); + igraph_vector_destroy(&c_partition2); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, value); + SET_VECTOR_ELT(r_result, 1, cut); + SET_VECTOR_ELT(r_result, 2, partition1); + SET_VECTOR_ELT(r_result, 3, partition2); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("cut")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("partition1")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("partition2")); + SET_NAMES(r_result, r_names); + UNPROTECT(5); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_dominator_tree / +/-------------------------------------------*/ +SEXP R_igraph_dominator_tree(SEXP graph, SEXP root, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_root; + igraph_vector_t c_dom; + igraph_t c_domtree; + igraph_vector_t c_leftout; + igraph_neimode_t c_mode; + SEXP dom; + SEXP domtree; + SEXP leftout; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_root = (igraph_integer_t) REAL(root)[0]; + if (0 != igraph_vector_init(&c_dom, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_dom); + domtree=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_leftout, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_leftout); + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_dominator_tree(&c_graph, c_root, &c_dom, (isNull(domtree) ? 0 : &c_domtree), &c_leftout, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(dom=R_igraph_vector_to_SEXPp1(&c_dom)); + igraph_vector_destroy(&c_dom); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, &c_domtree); + PROTECT(domtree=R_igraph_to_SEXP(&c_domtree)); + igraph_destroy(&c_domtree); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(leftout=R_igraph_vector_to_SEXPp1(&c_leftout)); + igraph_vector_destroy(&c_leftout); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, dom); + SET_VECTOR_ELT(r_result, 1, domtree); + SET_VECTOR_ELT(r_result, 2, leftout); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("dom")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("domtree")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("leftout")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_all_st_cuts / +/-------------------------------------------*/ +SEXP R_igraph_all_st_cuts(SEXP graph, SEXP source, SEXP target) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_cuts; + igraph_vector_ptr_t c_partition1s; + igraph_integer_t c_source; + igraph_integer_t c_target; + SEXP cuts; + SEXP partition1s; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_cuts, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_cuts); + if (0 != igraph_vector_ptr_init(&c_partition1s, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_partition1s); + c_source = (igraph_integer_t) REAL(source)[0]; + c_target = (igraph_integer_t) REAL(target)[0]; + /* Call igraph */ + igraph_all_st_cuts(&c_graph, &c_cuts, &c_partition1s, c_source, c_target); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(cuts=R_igraph_vectorlist_to_SEXP_p1(&c_cuts)); + R_igraph_vectorlist_destroy(&c_cuts); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(partition1s=R_igraph_vectorlist_to_SEXP_p1(&c_partition1s)); + R_igraph_vectorlist_destroy(&c_partition1s); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, cuts); + SET_VECTOR_ELT(r_result, 1, partition1s); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("cuts")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("partition1s")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_all_st_mincuts / +/-------------------------------------------*/ +SEXP R_igraph_all_st_mincuts(SEXP graph, SEXP source, SEXP target, SEXP capacity) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_value; + igraph_vector_ptr_t c_cuts; + igraph_vector_ptr_t c_partition1s; + igraph_integer_t c_source; + igraph_integer_t c_target; + igraph_vector_t c_capacity; + SEXP value; + SEXP cuts; + SEXP partition1s; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_cuts, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_cuts); + if (0 != igraph_vector_ptr_init(&c_partition1s, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_partition1s); + c_source = (igraph_integer_t) REAL(source)[0]; + c_target = (igraph_integer_t) REAL(target)[0]; + if (!isNull(capacity)) { R_SEXP_to_vector(capacity, &c_capacity); } + /* Call igraph */ + igraph_all_st_mincuts(&c_graph, &c_value, &c_cuts, &c_partition1s, c_source, c_target, (isNull(capacity) ? 0 : &c_capacity)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(value=NEW_NUMERIC(1)); + REAL(value)[0]=c_value; + PROTECT(cuts=R_igraph_vectorlist_to_SEXP_p1(&c_cuts)); + R_igraph_vectorlist_destroy(&c_cuts); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(partition1s=R_igraph_vectorlist_to_SEXP_p1(&c_partition1s)); + R_igraph_vectorlist_destroy(&c_partition1s); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, value); + SET_VECTOR_ELT(r_result, 1, cuts); + SET_VECTOR_ELT(r_result, 2, partition1s); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("value")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("cuts")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("partition1s")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_separator / +/-------------------------------------------*/ +SEXP R_igraph_is_separator(SEXP graph, SEXP candidate) { + /* Declarations */ + igraph_t c_graph; + igraph_vs_t c_candidate; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_igraph_vs(candidate, &c_graph, &c_candidate); + /* Call igraph */ + igraph_is_separator(&c_graph, c_candidate, &c_res); + + /* Convert output */ + igraph_vs_destroy(&c_candidate); + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_minimal_separator / +/-------------------------------------------*/ +SEXP R_igraph_is_minimal_separator(SEXP graph, SEXP candidate) { + /* Declarations */ + igraph_t c_graph; + igraph_vs_t c_candidate; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_igraph_vs(candidate, &c_graph, &c_candidate); + /* Call igraph */ + igraph_is_minimal_separator(&c_graph, c_candidate, &c_res); + + /* Convert output */ + igraph_vs_destroy(&c_candidate); + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_all_minimal_st_separators / +/-------------------------------------------*/ +SEXP R_igraph_all_minimal_st_separators(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_separators; + SEXP separators; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_separators, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_separators); + /* Call igraph */ + igraph_all_minimal_st_separators(&c_graph, &c_separators); + + /* Convert output */ + PROTECT(separators=R_igraph_vectorlist_to_SEXP_p1(&c_separators)); + R_igraph_vectorlist_destroy(&c_separators); + IGRAPH_FINALLY_CLEAN(1); + r_result = separators; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_minimum_size_separators / +/-------------------------------------------*/ +SEXP R_igraph_minimum_size_separators(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_ptr_t c_separators; + SEXP separators; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_ptr_init(&c_separators, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_separators); + /* Call igraph */ + igraph_minimum_size_separators(&c_graph, &c_separators); + + /* Convert output */ + PROTECT(separators=R_igraph_vectorlist_to_SEXP_p1(&c_separators)); + R_igraph_vectorlist_destroy(&c_separators); + IGRAPH_FINALLY_CLEAN(1); + r_result = separators; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isoclass / +/-------------------------------------------*/ +SEXP R_igraph_isoclass(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_isoclass; + SEXP isoclass; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_isoclass=0; + /* Call igraph */ + igraph_isoclass(&c_graph, &c_isoclass); + + /* Convert output */ + PROTECT(isoclass=NEW_INTEGER(1)); + INTEGER(isoclass)[0]=c_isoclass; + r_result = isoclass; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isomorphic / +/-------------------------------------------*/ +SEXP R_igraph_isomorphic(SEXP graph1, SEXP graph2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_bool_t c_iso; + SEXP iso; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + /* Call igraph */ + igraph_isomorphic(&c_graph1, &c_graph2, &c_iso); + + /* Convert output */ + PROTECT(iso=NEW_LOGICAL(1)); + LOGICAL(iso)[0]=c_iso; + r_result = iso; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isoclass_subgraph / +/-------------------------------------------*/ +SEXP R_igraph_isoclass_subgraph(SEXP graph, SEXP vids) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_vids; + igraph_integer_t c_isoclass; + SEXP isoclass; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_vector(vids, &c_vids); + c_isoclass=0; + /* Call igraph */ + igraph_isoclass_subgraph(&c_graph, &c_vids, &c_isoclass); + + /* Convert output */ + PROTECT(isoclass=NEW_INTEGER(1)); + INTEGER(isoclass)[0]=c_isoclass; + r_result = isoclass; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isoclass_create / +/-------------------------------------------*/ +SEXP R_igraph_isoclass_create(SEXP size, SEXP number, SEXP directed) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_size; + igraph_integer_t c_number; + igraph_bool_t c_directed; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_size=INTEGER(size)[0]; + c_number=INTEGER(number)[0]; + c_directed=LOGICAL(directed)[0]; + /* Call igraph */ + igraph_isoclass_create(&c_graph, c_size, c_number, c_directed); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isomorphic_vf2 / +/-------------------------------------------*/ +SEXP R_igraph_isomorphic_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_vertex_color1; + igraph_vector_int_t c_vertex_color2; + igraph_vector_int_t c_edge_color1; + igraph_vector_int_t c_edge_color2; + igraph_bool_t c_iso; + igraph_vector_t c_map12; + igraph_vector_t c_map21; + + + + SEXP iso; + SEXP map12; + SEXP map21; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } + if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } + if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } + if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } + if (0 != igraph_vector_init(&c_map12, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map12); + map12=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_map21, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map21); + map21=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_isomorphic_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_iso, (isNull(map12) ? 0 : &c_map12), (isNull(map21) ? 0 : &c_map21), 0, 0, 0); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(iso=NEW_LOGICAL(1)); + LOGICAL(iso)[0]=c_iso; + PROTECT(map12=R_igraph_0orvector_to_SEXPp1(&c_map12)); + igraph_vector_destroy(&c_map12); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(map21=R_igraph_0orvector_to_SEXPp1(&c_map21)); + igraph_vector_destroy(&c_map21); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, iso); + SET_VECTOR_ELT(r_result, 1, map12); + SET_VECTOR_ELT(r_result, 2, map21); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("iso")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("map12")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("map21")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_count_isomorphisms_vf2 / +/-------------------------------------------*/ +SEXP R_igraph_count_isomorphisms_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_vertex_color1; + igraph_vector_int_t c_vertex_color2; + igraph_vector_int_t c_edge_color1; + igraph_vector_int_t c_edge_color2; + igraph_integer_t c_count; + + + + SEXP count; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } + if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } + if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } + if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } + c_count=0; + /* Call igraph */ + igraph_count_isomorphisms_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_count, 0, 0, 0); + + /* Convert output */ + PROTECT(count=NEW_INTEGER(1)); + INTEGER(count)[0]=c_count; + r_result = count; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_isomorphisms_vf2 / +/-------------------------------------------*/ +SEXP R_igraph_get_isomorphisms_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_vertex_color1; + igraph_vector_int_t c_vertex_color2; + igraph_vector_int_t c_edge_color1; + igraph_vector_int_t c_edge_color2; + igraph_vector_ptr_t c_maps; + + + + SEXP maps; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } + if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } + if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } + if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } + if (0 != igraph_vector_ptr_init(&c_maps, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_maps); + /* Call igraph */ + igraph_get_isomorphisms_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_maps, 0, 0, 0); + + /* Convert output */ + PROTECT(maps=R_igraph_vectorlist_to_SEXP(&c_maps)); + R_igraph_vectorlist_destroy(&c_maps); + IGRAPH_FINALLY_CLEAN(1); + r_result = maps; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_subisomorphic_vf2 / +/-------------------------------------------*/ +SEXP R_igraph_subisomorphic_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_vertex_color1; + igraph_vector_int_t c_vertex_color2; + igraph_vector_int_t c_edge_color1; + igraph_vector_int_t c_edge_color2; + igraph_bool_t c_iso; + igraph_vector_t c_map12; + igraph_vector_t c_map21; + + + + SEXP iso; + SEXP map12; + SEXP map21; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } + if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } + if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } + if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } + if (0 != igraph_vector_init(&c_map12, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map12); + map12=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_map21, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map21); + map21=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_subisomorphic_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_iso, (isNull(map12) ? 0 : &c_map12), (isNull(map21) ? 0 : &c_map21), 0, 0, 0); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(iso=NEW_LOGICAL(1)); + LOGICAL(iso)[0]=c_iso; + PROTECT(map12=R_igraph_0orvector_to_SEXPp1(&c_map12)); + igraph_vector_destroy(&c_map12); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(map21=R_igraph_0orvector_to_SEXPp1(&c_map21)); + igraph_vector_destroy(&c_map21); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, iso); + SET_VECTOR_ELT(r_result, 1, map12); + SET_VECTOR_ELT(r_result, 2, map21); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("iso")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("map12")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("map21")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_count_subisomorphisms_vf2 / +/-------------------------------------------*/ +SEXP R_igraph_count_subisomorphisms_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_vertex_color1; + igraph_vector_int_t c_vertex_color2; + igraph_vector_int_t c_edge_color1; + igraph_vector_int_t c_edge_color2; + igraph_integer_t c_count; + + + + SEXP count; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } + if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } + if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } + if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } + c_count=0; + /* Call igraph */ + igraph_count_subisomorphisms_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_count, 0, 0, 0); + + /* Convert output */ + PROTECT(count=NEW_INTEGER(1)); + INTEGER(count)[0]=c_count; + r_result = count; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_get_subisomorphisms_vf2 / +/-------------------------------------------*/ +SEXP R_igraph_get_subisomorphisms_vf2(SEXP graph1, SEXP graph2, SEXP vertex_color1, SEXP vertex_color2, SEXP edge_color1, SEXP edge_color2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_vertex_color1; + igraph_vector_int_t c_vertex_color2; + igraph_vector_int_t c_edge_color1; + igraph_vector_int_t c_edge_color2; + igraph_vector_ptr_t c_maps; + + + + SEXP maps; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(vertex_color1)) { R_SEXP_to_vector_int(vertex_color1, &c_vertex_color1); } + if (!isNull(vertex_color2)) { R_SEXP_to_vector_int(vertex_color2, &c_vertex_color2); } + if (!isNull(edge_color1)) { R_SEXP_to_vector_int(edge_color1, &c_edge_color1); } + if (!isNull(edge_color2)) { R_SEXP_to_vector_int(edge_color2, &c_edge_color2); } + if (0 != igraph_vector_ptr_init(&c_maps, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_maps); + /* Call igraph */ + igraph_get_subisomorphisms_vf2(&c_graph1, &c_graph2, (isNull(vertex_color1) ? 0 : &c_vertex_color1), (isNull(vertex_color2) ? 0 : &c_vertex_color2), (isNull(edge_color1) ? 0 : &c_edge_color1), (isNull(edge_color2) ? 0 : &c_edge_color2), &c_maps, 0, 0, 0); + + /* Convert output */ + PROTECT(maps=R_igraph_vectorlist_to_SEXP(&c_maps)); + R_igraph_vectorlist_destroy(&c_maps); + IGRAPH_FINALLY_CLEAN(1); + r_result = maps; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isomorphic_34 / +/-------------------------------------------*/ +SEXP R_igraph_isomorphic_34(SEXP graph1, SEXP graph2) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_bool_t c_iso; + SEXP iso; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + /* Call igraph */ + igraph_isomorphic_34(&c_graph1, &c_graph2, &c_iso); + + /* Convert output */ + PROTECT(iso=NEW_LOGICAL(1)); + LOGICAL(iso)[0]=c_iso; + r_result = iso; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_canonical_permutation / +/-------------------------------------------*/ +SEXP R_igraph_canonical_permutation(SEXP graph, SEXP colors, SEXP sh) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_colors; + igraph_vector_t c_labeling; + igraph_bliss_sh_t c_sh; + igraph_bliss_info_t c_info; + SEXP labeling; + SEXP info; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(colors)) { R_SEXP_to_vector_int(colors, &c_colors); } + if (0 != igraph_vector_init(&c_labeling, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_labeling); + c_sh = (igraph_bliss_sh_t) Rf_asInteger(sh); + /* Call igraph */ + igraph_canonical_permutation(&c_graph, (isNull(colors) ? 0 : &c_colors), &c_labeling, c_sh, &c_info); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(labeling=R_igraph_vector_to_SEXPp1(&c_labeling)); + igraph_vector_destroy(&c_labeling); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(info=R_igraph_bliss_info_to_SEXP(&c_info)); + if (c_info.group_size) { free(c_info.group_size); } + SET_VECTOR_ELT(r_result, 0, labeling); + SET_VECTOR_ELT(r_result, 1, info); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("labeling")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("info")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_permute_vertices / +/-------------------------------------------*/ +SEXP R_igraph_permute_vertices(SEXP graph, SEXP permutation) { + /* Declarations */ + igraph_t c_graph; + igraph_t c_res; + igraph_vector_t c_permutation; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + R_SEXP_to_vector(permutation, &c_permutation); + /* Call igraph */ + igraph_permute_vertices(&c_graph, &c_res, &c_permutation); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_res); + PROTECT(res=R_igraph_to_SEXP(&c_res)); + igraph_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_isomorphic_bliss / +/-------------------------------------------*/ +SEXP R_igraph_isomorphic_bliss(SEXP graph1, SEXP graph2, SEXP colors1, SEXP colors2, SEXP sh) { + /* Declarations */ + igraph_t c_graph1; + igraph_t c_graph2; + igraph_vector_int_t c_colors1; + igraph_vector_int_t c_colors2; + igraph_bool_t c_iso; + igraph_vector_t c_map12; + igraph_vector_t c_map21; + igraph_bliss_sh_t c_sh; + igraph_bliss_info_t c_info1; + igraph_bliss_info_t c_info2; + SEXP iso; + SEXP map12; + SEXP map21; + SEXP info1; + SEXP info2; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph1, &c_graph1); + R_SEXP_to_igraph(graph2, &c_graph2); + if (!isNull(colors1)) { R_SEXP_to_vector_int(colors1, &c_colors1); } + if (!isNull(colors2)) { R_SEXP_to_vector_int(colors2, &c_colors2); } + if (0 != igraph_vector_init(&c_map12, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map12); + map12=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_vector_init(&c_map21, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_map21); + map21=R_GlobalEnv; /* hack to have a non-NULL value */ + c_sh = (igraph_bliss_sh_t) Rf_asInteger(sh); + /* Call igraph */ + igraph_isomorphic_bliss(&c_graph1, &c_graph2, (isNull(colors1) ? 0 : &c_colors1), (isNull(colors2) ? 0 : &c_colors2), &c_iso, (isNull(map12) ? 0 : &c_map12), (isNull(map21) ? 0 : &c_map21), c_sh, &c_info1, &c_info2); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(5)); + PROTECT(r_names=NEW_CHARACTER(5)); + PROTECT(iso=NEW_LOGICAL(1)); + LOGICAL(iso)[0]=c_iso; + PROTECT(map12=R_igraph_0orvector_to_SEXPp1(&c_map12)); + igraph_vector_destroy(&c_map12); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(map21=R_igraph_0orvector_to_SEXPp1(&c_map21)); + igraph_vector_destroy(&c_map21); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(info1=R_igraph_bliss_info_to_SEXP(&c_info1)); + if (c_info1.group_size) { free(c_info1.group_size); } + PROTECT(info2=R_igraph_bliss_info_to_SEXP(&c_info2)); + if (c_info2.group_size) { free(c_info2.group_size); } + SET_VECTOR_ELT(r_result, 0, iso); + SET_VECTOR_ELT(r_result, 1, map12); + SET_VECTOR_ELT(r_result, 2, map21); + SET_VECTOR_ELT(r_result, 3, info1); + SET_VECTOR_ELT(r_result, 4, info2); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("iso")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("map12")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("map21")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("info1")); + SET_STRING_ELT(r_names, 4, CREATE_STRING_VECTOR("info2")); + SET_NAMES(r_result, r_names); + UNPROTECT(6); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_automorphisms / +/-------------------------------------------*/ +SEXP R_igraph_automorphisms(SEXP graph, SEXP colors, SEXP sh) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_colors; + igraph_bliss_sh_t c_sh; + igraph_bliss_info_t c_info; + SEXP info; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(colors)) { R_SEXP_to_vector_int(colors, &c_colors); } + c_sh = (igraph_bliss_sh_t) Rf_asInteger(sh); + /* Call igraph */ + igraph_automorphisms(&c_graph, (isNull(colors) ? 0 : &c_colors), c_sh, &c_info); + + /* Convert output */ + PROTECT(info=R_igraph_bliss_info_to_SEXP(&c_info)); + if (c_info.group_size) { free(c_info.group_size); } + r_result = info; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_automorphism_group / +/-------------------------------------------*/ +SEXP R_igraph_automorphism_group(SEXP graph, SEXP colors, SEXP sh) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_colors; + igraph_vector_ptr_t c_generators; + igraph_bliss_sh_t c_sh; + igraph_bliss_info_t c_info; + SEXP generators; + SEXP info; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(colors)) { R_SEXP_to_vector_int(colors, &c_colors); } + if (0 != igraph_vector_ptr_init(&c_generators, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_vectorlist_destroy, &c_generators); + c_sh = (igraph_bliss_sh_t) Rf_asInteger(sh); + /* Call igraph */ + igraph_automorphism_group(&c_graph, (isNull(colors) ? 0 : &c_colors), &c_generators, c_sh, &c_info); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(generators=R_igraph_vectorlist_to_SEXP_p1(&c_generators)); + R_igraph_vectorlist_destroy(&c_generators); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(info=R_igraph_bliss_info_to_SEXP(&c_info)); + if (c_info.group_size) { free(c_info.group_size); } + SET_VECTOR_ELT(r_result, 0, generators); + SET_VECTOR_ELT(r_result, 1, info); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("generators")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("info")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_simplify_and_colorize / +/-------------------------------------------*/ +SEXP R_igraph_simplify_and_colorize(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_t c_res; + igraph_vector_int_t c_vertex_color; + igraph_vector_int_t c_edge_color; + SEXP res; + SEXP vertex_color; + SEXP edge_color; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_int_init(&c_vertex_color, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_int_destroy, &c_vertex_color); + if (0 != igraph_vector_int_init(&c_edge_color, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_int_destroy, &c_edge_color); + /* Call igraph */ + igraph_simplify_and_colorize(&c_graph, &c_res, &c_vertex_color, &c_edge_color); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + IGRAPH_FINALLY(igraph_destroy, &c_res); + PROTECT(res=R_igraph_to_SEXP(&c_res)); + igraph_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(vertex_color=R_igraph_vector_int_to_SEXP(&c_vertex_color)); + igraph_vector_int_destroy(&c_vertex_color); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(edge_color=R_igraph_vector_int_to_SEXP(&c_edge_color)); + igraph_vector_int_destroy(&c_edge_color); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, vertex_color); + SET_VECTOR_ELT(r_result, 2, edge_color); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("vertex_color")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("edge_color")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_scg_grouping / +/-------------------------------------------*/ +SEXP R_igraph_scg_grouping(SEXP V, SEXP nt, SEXP nt_vec, SEXP mtype, SEXP algo, SEXP p, SEXP maxiter) { + /* Declarations */ + igraph_matrix_t c_V; + igraph_vector_t c_groups; + igraph_integer_t c_nt; + igraph_vector_t c_nt_vec; + igraph_scg_matrix_t c_mtype; + igraph_scg_algorithm_t c_algo; + igraph_vector_t c_p; + igraph_integer_t c_maxiter; + SEXP groups; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_matrix(V, &c_V); + if (0 != igraph_vector_init(&c_groups, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_groups); + c_nt=INTEGER(nt)[0]; + if (!isNull(nt_vec)) { R_SEXP_to_vector(nt_vec, &c_nt_vec); } + c_mtype = (igraph_scg_matrix_t) Rf_asInteger(mtype); + c_algo = (igraph_scg_algorithm_t) Rf_asInteger(algo); + if (!isNull(p)) { R_SEXP_to_vector(p, &c_p); } + c_maxiter=INTEGER(maxiter)[0]; + /* Call igraph */ + igraph_scg_grouping(&c_V, &c_groups, c_nt, (isNull(nt_vec) ? 0 : &c_nt_vec), c_mtype, c_algo, (isNull(p) ? 0 : &c_p), c_maxiter); + + /* Convert output */ + PROTECT(groups=R_igraph_vector_to_SEXPp1(&c_groups)); + igraph_vector_destroy(&c_groups); + IGRAPH_FINALLY_CLEAN(1); + r_result = groups; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_scg_norm_eps / +/-------------------------------------------*/ +SEXP R_igraph_scg_norm_eps(SEXP V, SEXP groups, SEXP mtype, SEXP p, SEXP norm) { + /* Declarations */ + igraph_matrix_t c_V; + igraph_vector_t c_groups; + igraph_vector_t c_eps; + igraph_scg_matrix_t c_mtype; + igraph_vector_t c_p; + igraph_scg_norm_t c_norm; + SEXP eps; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_matrix(V, &c_V); + R_SEXP_to_vector(groups, &c_groups); + if (0 != igraph_vector_init(&c_eps, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_eps); + c_mtype = (igraph_scg_matrix_t) Rf_asInteger(mtype); + if (!isNull(p)) { R_SEXP_to_vector(p, &c_p); } + c_norm = (igraph_scg_norm_t) Rf_asInteger(norm); + /* Call igraph */ + igraph_scg_norm_eps(&c_V, &c_groups, &c_eps, c_mtype, (isNull(p) ? 0 : &c_p), c_norm); + + /* Convert output */ + PROTECT(eps=R_igraph_vector_to_SEXP(&c_eps)); + igraph_vector_destroy(&c_eps); + IGRAPH_FINALLY_CLEAN(1); + r_result = eps; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_matching / +/-------------------------------------------*/ +SEXP R_igraph_is_matching(SEXP graph, SEXP types, SEXP matching) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_vector_long_t c_matching; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + R_SEXP_to_vector_long_copy(matching, &c_matching); + /* Call igraph */ + igraph_is_matching(&c_graph, (isNull(types) ? 0 : &c_types), &c_matching, &c_res); + + /* Convert output */ + igraph_vector_long_destroy(&c_matching); + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_maximal_matching / +/-------------------------------------------*/ +SEXP R_igraph_is_maximal_matching(SEXP graph, SEXP types, SEXP matching) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_vector_long_t c_matching; + igraph_bool_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + R_SEXP_to_vector_long_copy(matching, &c_matching); + /* Call igraph */ + igraph_is_maximal_matching(&c_graph, (isNull(types) ? 0 : &c_types), &c_matching, &c_res); + + /* Convert output */ + igraph_vector_long_destroy(&c_matching); + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_maximum_bipartite_matching / +/-------------------------------------------*/ +SEXP R_igraph_maximum_bipartite_matching(SEXP graph, SEXP types, SEXP weights, SEXP eps) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_bool_t c_types; + igraph_integer_t c_matching_size; + igraph_real_t c_matching_weight; + igraph_vector_long_t c_matching; + igraph_vector_t c_weights; + igraph_real_t c_eps; + SEXP matching_size; + SEXP matching_weight; + SEXP matching; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (!isNull(types)) { R_SEXP_to_vector_bool(types, &c_types); } + c_matching_size=0; + if (0 != igraph_vector_long_init(&c_matching, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_long_destroy, &c_matching); + if (!isNull(weights)) { R_SEXP_to_vector(weights, &c_weights); } + c_eps=REAL(eps)[0]; + /* Call igraph */ + igraph_maximum_bipartite_matching(&c_graph, (isNull(types) ? 0 : &c_types), &c_matching_size, &c_matching_weight, &c_matching, (isNull(weights) ? 0 : &c_weights), c_eps); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(3)); + PROTECT(r_names=NEW_CHARACTER(3)); + PROTECT(matching_size=NEW_INTEGER(1)); + INTEGER(matching_size)[0]=c_matching_size; + PROTECT(matching_weight=NEW_NUMERIC(1)); + REAL(matching_weight)[0]=c_matching_weight; + PROTECT(matching=R_igraph_vector_long_to_SEXPp1(&c_matching)); + igraph_vector_long_destroy(&c_matching); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, matching_size); + SET_VECTOR_ELT(r_result, 1, matching_weight); + SET_VECTOR_ELT(r_result, 2, matching); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("matching_size")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("matching_weight")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("matching")); + SET_NAMES(r_result, r_names); + UNPROTECT(4); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_eigen_adjacency / +/-------------------------------------------*/ +SEXP R_igraph_eigen_adjacency(SEXP graph, SEXP algorithm, SEXP which, SEXP options) { + /* Declarations */ + igraph_t c_graph; + igraph_eigen_algorithm_t c_algorithm; + igraph_eigen_which_t c_which; + igraph_arpack_options_t c_options; + + igraph_vector_t c_values; + igraph_matrix_t c_vectors; + igraph_vector_complex_t c_cmplxvalues; + igraph_matrix_complex_t c_cmplxvectors; + SEXP values; + SEXP vectors; + SEXP cmplxvalues; + SEXP cmplxvectors; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_algorithm=REAL(algorithm)[0]; + R_SEXP_to_igraph_eigen_which(which, &c_which); + R_SEXP_to_igraph_arpack_options(options, &c_options); + if (0 != igraph_vector_init(&c_values, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_values); + if (0 != igraph_matrix_init(&c_vectors, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_vectors); + if (0 != igraph_vector_complex_init(&c_cmplxvalues, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_complex_destroy, &c_cmplxvalues); + cmplxvalues=R_GlobalEnv; /* hack to have a non-NULL value */ + if (0 != igraph_matrix_complex_init(&c_cmplxvectors, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_complex_destroy, &c_cmplxvectors); + cmplxvectors=R_GlobalEnv; /* hack to have a non-NULL value */ + /* Call igraph */ + igraph_eigen_adjacency(&c_graph, c_algorithm, &c_which, &c_options, 0, &c_values, &c_vectors, (isNull(cmplxvalues) ? 0 : &c_cmplxvalues), (isNull(cmplxvectors) ? 0 : &c_cmplxvectors)); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(5)); + PROTECT(r_names=NEW_CHARACTER(5)); + PROTECT(options=R_igraph_arpack_options_to_SEXP(&c_options)); + PROTECT(values=R_igraph_vector_to_SEXP(&c_values)); + igraph_vector_destroy(&c_values); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(vectors=R_igraph_matrix_to_SEXP(&c_vectors)); + igraph_matrix_destroy(&c_vectors); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(cmplxvalues=R_igraph_0orvector_complex_to_SEXP(&c_cmplxvalues)); + igraph_vector_complex_destroy(&c_cmplxvalues); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(cmplxvectors=R_igraph_0ormatrix_complex_to_SEXP(&c_cmplxvectors)); + igraph_matrix_complex_destroy(&c_cmplxvectors); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, options); + SET_VECTOR_ELT(r_result, 1, values); + SET_VECTOR_ELT(r_result, 2, vectors); + SET_VECTOR_ELT(r_result, 3, cmplxvalues); + SET_VECTOR_ELT(r_result, 4, cmplxvectors); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("options")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("values")); + SET_STRING_ELT(r_names, 2, CREATE_STRING_VECTOR("vectors")); + SET_STRING_ELT(r_names, 3, CREATE_STRING_VECTOR("cmplxvalues")); + SET_STRING_ELT(r_names, 4, CREATE_STRING_VECTOR("cmplxvectors")); + SET_NAMES(r_result, r_names); + UNPROTECT(6); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_power_law_fit / +/-------------------------------------------*/ +SEXP R_igraph_power_law_fit(SEXP data, SEXP xmin, SEXP force_continuous) { + /* Declarations */ + igraph_vector_t c_data; + igraph_plfit_result_t c_res; + igraph_real_t c_xmin; + igraph_bool_t c_force_continuous; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector(data, &c_data); + c_xmin=REAL(xmin)[0]; + c_force_continuous=LOGICAL(force_continuous)[0]; + /* Call igraph */ + igraph_power_law_fit(&c_data, &c_res, c_xmin, c_force_continuous); + + /* Convert output */ + PROTECT(res=R_igraph_plfit_result_to_SEXP(&c_res)); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_sir / +/-------------------------------------------*/ +SEXP R_igraph_sir(SEXP graph, SEXP beta, SEXP gamma, SEXP no_sim) { + /* Declarations */ + igraph_t c_graph; + igraph_real_t c_beta; + igraph_real_t c_gamma; + igraph_integer_t c_no_sim; + igraph_vector_ptr_t c_res; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_beta=REAL(beta)[0]; + c_gamma=REAL(gamma)[0]; + c_no_sim=INTEGER(no_sim)[0]; + if (0 != igraph_vector_ptr_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(R_igraph_sirlist_destroy, &c_res); + /* Call igraph */ + igraph_sir(&c_graph, c_beta, c_gamma, c_no_sim, &c_res); + + /* Convert output */ + PROTECT(res=R_igraph_sirlist_to_SEXP(&c_res)); + R_igraph_sirlist_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_convex_hull / +/-------------------------------------------*/ +SEXP R_igraph_convex_hull(SEXP data) { + /* Declarations */ + igraph_matrix_t c_data; + igraph_vector_t c_resverts; + igraph_matrix_t c_rescoords; + SEXP resverts; + SEXP rescoords; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_matrix(data, &c_data); + if (0 != igraph_vector_init(&c_resverts, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_resverts); + if (0 != igraph_matrix_init(&c_rescoords, 0, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_matrix_destroy, &c_rescoords); + /* Call igraph */ + igraph_convex_hull(&c_data, &c_resverts, &c_rescoords); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(resverts=R_igraph_vector_to_SEXP(&c_resverts)); + igraph_vector_destroy(&c_resverts); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(rescoords=R_igraph_matrix_to_SEXP(&c_rescoords)); + igraph_matrix_destroy(&c_rescoords); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, resverts); + SET_VECTOR_ELT(r_result, 1, rescoords); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("resverts")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("rescoords")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_dim_select / +/-------------------------------------------*/ +SEXP R_igraph_dim_select(SEXP sv) { + /* Declarations */ + igraph_vector_t c_sv; + igraph_integer_t c_dim; + SEXP dim; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector(sv, &c_sv); + c_dim=0; + /* Call igraph */ + igraph_dim_select(&c_sv, &c_dim); + + /* Convert output */ + PROTECT(dim=NEW_INTEGER(1)); + INTEGER(dim)[0]=c_dim; + r_result = dim; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_eulerian / +/-------------------------------------------*/ +SEXP R_igraph_is_eulerian(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_has_path; + igraph_bool_t c_has_cycle; + SEXP has_path; + SEXP has_cycle; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + /* Call igraph */ + igraph_is_eulerian(&c_graph, &c_has_path, &c_has_cycle); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(has_path=NEW_LOGICAL(1)); + LOGICAL(has_path)[0]=c_has_path; + PROTECT(has_cycle=NEW_LOGICAL(1)); + LOGICAL(has_cycle)[0]=c_has_cycle; + SET_VECTOR_ELT(r_result, 0, has_path); + SET_VECTOR_ELT(r_result, 1, has_cycle); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("has_path")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("has_cycle")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_eulerian_path / +/-------------------------------------------*/ +SEXP R_igraph_eulerian_path(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_edge_res; + igraph_vector_t c_vertex_res; + SEXP edge_res; + SEXP vertex_res; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_edge_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_edge_res); + if (0 != igraph_vector_init(&c_vertex_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vertex_res); + /* Call igraph */ + igraph_eulerian_path(&c_graph, &c_edge_res, &c_vertex_res); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(edge_res=R_igraph_vector_to_SEXPp1(&c_edge_res)); + igraph_vector_destroy(&c_edge_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(vertex_res=R_igraph_vector_to_SEXPp1(&c_vertex_res)); + igraph_vector_destroy(&c_vertex_res); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, edge_res); + SET_VECTOR_ELT(r_result, 1, vertex_res); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("epath")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("vpath")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_eulerian_cycle / +/-------------------------------------------*/ +SEXP R_igraph_eulerian_cycle(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_edge_res; + igraph_vector_t c_vertex_res; + SEXP edge_res; + SEXP vertex_res; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_edge_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_edge_res); + if (0 != igraph_vector_init(&c_vertex_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_vertex_res); + /* Call igraph */ + igraph_eulerian_cycle(&c_graph, &c_edge_res, &c_vertex_res); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(edge_res=R_igraph_vector_to_SEXPp1(&c_edge_res)); + igraph_vector_destroy(&c_edge_res); + IGRAPH_FINALLY_CLEAN(1); + PROTECT(vertex_res=R_igraph_vector_to_SEXPp1(&c_vertex_res)); + igraph_vector_destroy(&c_vertex_res); + IGRAPH_FINALLY_CLEAN(1); + SET_VECTOR_ELT(r_result, 0, edge_res); + SET_VECTOR_ELT(r_result, 1, vertex_res); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("epath")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("vpath")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_is_tree / +/-------------------------------------------*/ +SEXP R_igraph_is_tree(SEXP graph, SEXP mode) { + /* Declarations */ + igraph_t c_graph; + igraph_bool_t c_res; + igraph_integer_t c_root; + igraph_neimode_t c_mode; + SEXP res; + SEXP root; + + SEXP r_result, r_names; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + c_root = -1; + c_mode = (igraph_neimode_t) Rf_asInteger(mode); + /* Call igraph */ + igraph_is_tree(&c_graph, &c_res, &c_root, c_mode); + + /* Convert output */ + PROTECT(r_result=NEW_LIST(2)); + PROTECT(r_names=NEW_CHARACTER(2)); + PROTECT(res=NEW_LOGICAL(1)); + LOGICAL(res)[0]=c_res; + PROTECT(root = NEW_INTEGER(1)); + INTEGER(root)[0] = c_root + 1; + SET_VECTOR_ELT(r_result, 0, res); + SET_VECTOR_ELT(r_result, 1, root); + SET_STRING_ELT(r_names, 0, CREATE_STRING_VECTOR("res")); + SET_STRING_ELT(r_names, 1, CREATE_STRING_VECTOR("root")); + SET_NAMES(r_result, r_names); + UNPROTECT(3); + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_from_prufer / +/-------------------------------------------*/ +SEXP R_igraph_from_prufer(SEXP prufer) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_prufer; + SEXP graph; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_vector_int(prufer, &c_prufer); + /* Call igraph */ + igraph_from_prufer(&c_graph, &c_prufer); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_to_prufer / +/-------------------------------------------*/ +SEXP R_igraph_to_prufer(SEXP graph) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_prufer; + SEXP prufer; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_int_init(&c_prufer, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_int_destroy, &c_prufer); + /* Call igraph */ + igraph_to_prufer(&c_graph, &c_prufer); + + /* Convert output */ + PROTECT(prufer=R_igraph_vector_int_to_SEXP(&c_prufer)); + igraph_vector_int_destroy(&c_prufer); + IGRAPH_FINALLY_CLEAN(1); + r_result = prufer; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_random_spanning_tree / +/-------------------------------------------*/ +SEXP R_igraph_random_spanning_tree(SEXP graph, SEXP vid) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_t c_res; + igraph_integer_t c_vid; + SEXP res; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_init(&c_res, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_destroy, &c_res); + c_vid = (igraph_integer_t) REAL(vid)[0]; + /* Call igraph */ + igraph_random_spanning_tree(&c_graph, &c_res, c_vid); + + /* Convert output */ + PROTECT(res=R_igraph_vector_to_SEXPp1(&c_res)); + igraph_vector_destroy(&c_res); + IGRAPH_FINALLY_CLEAN(1); + r_result = res; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_tree_game / +/-------------------------------------------*/ +SEXP R_igraph_tree_game(SEXP n, SEXP directed, SEXP method) { + /* Declarations */ + igraph_t c_graph; + igraph_integer_t c_n; + igraph_bool_t c_directed; + igraph_random_tree_t c_method; + SEXP graph; + + SEXP r_result; + /* Convert input */ + c_n=INTEGER(n)[0]; + c_directed=LOGICAL(directed)[0]; + c_method = (igraph_random_tree_t) Rf_asInteger(method); + /* Call igraph */ + igraph_tree_game(&c_graph, c_n, c_directed, c_method); + + /* Convert output */ + IGRAPH_FINALLY(igraph_destroy, &c_graph); + PROTECT(graph=R_igraph_to_SEXP(&c_graph)); + igraph_destroy(&c_graph); + IGRAPH_FINALLY_CLEAN(1); + r_result = graph; + + UNPROTECT(1); + return(r_result); +} + +/*-------------------------------------------/ +/ igraph_vertex_coloring_greedy / +/-------------------------------------------*/ +SEXP R_igraph_vertex_coloring_greedy(SEXP graph, SEXP heuristic) { + /* Declarations */ + igraph_t c_graph; + igraph_vector_int_t c_colors; + igraph_coloring_greedy_t c_heuristic; + SEXP colors; + + SEXP r_result; + /* Convert input */ + R_SEXP_to_igraph(graph, &c_graph); + if (0 != igraph_vector_int_init(&c_colors, 0)) { + igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_vector_int_destroy, &c_colors); + c_heuristic = (igraph_coloring_greedy_t) Rf_asInteger(heuristic); + /* Call igraph */ + igraph_vertex_coloring_greedy(&c_graph, &c_colors, c_heuristic); + + /* Convert output */ + PROTECT(colors=R_igraph_vector_int_to_SEXP(&c_colors)); + igraph_vector_int_destroy(&c_colors); + IGRAPH_FINALLY_CLEAN(1); + r_result = colors; + + UNPROTECT(1); + return(r_result); +} diff --git a/src/rigraph/rinterface.h b/src/rigraph/rinterface.h new file mode 100644 index 0000000..d03f2f4 --- /dev/null +++ b/src/rigraph/rinterface.h @@ -0,0 +1,29 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010 Gabor Csardi + Rue de l'Industrie 5, Lausanne 1005, Switzerland + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "vendor/uuid/uuid.h" + +#define R_IGRAPH_TYPE_VERSION "0.8.0" +#define R_IGRAPH_VERSION_VAR ".__igraph_version__." + +SEXP R_igraph_add_env(SEXP graph); diff --git a/src/rigraph/rinterface_extra.c b/src/rigraph/rinterface_extra.c new file mode 100644 index 0000000..0b5e980 --- /dev/null +++ b/src/rigraph/rinterface_extra.c @@ -0,0 +1,334 @@ +/* -*- mode: C -*- */ +/* + IGraph library R interface. + Copyright (C) 2013 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph.h" + +#include +#include +#include + +#include "rinterface.h" + +#include + +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++C */ +/* C */ +/* Given a HIERARCHIC CLUSTERING, described as a sequence of C */ +/* agglomerations, prepare the seq. of aggloms. and "horiz." C */ +/* order of objects for plotting the dendrogram using S routine C */ +/* 'plclust'. C */ +/* C */ +/* Parameters: C */ +/* C */ +/* IA, IB: vectors of dimension N defining the agglomer- C */ +/* ations. C */ +/* IIA, IIB: used to store IA and IB values differently C */ +/* (in form needed for S command 'plclust' C */ +/* IORDER: "horiz." order of objects for dendrogram C */ +/* C */ +/* F. Murtagh, ESA/ESO/STECF, Garching, June 1991 C */ +/* C */ +/* HISTORY C */ +/* C */ +/* Adapted from routine HCASS, which additionally determines C */ +/* cluster assignments at all levels, at extra comput. expense C */ +/* C */ +/* ---------------------------------------------------------------C */ + +int igraphhcass2(int *n, int *ia, int *ib, + int *iorder, int *iia, + int *iib) { + + /* System generated locals */ + int i__1, i__2, i__3; + + /* Local variables */ + static int i__, j, k, k1, k2, loc; + + /* Args */ + /* Var */ + + /* Following bit is to get seq. of merges into format acceptable to + plclust I coded clusters as lowest seq. no. of constituents; S's + 'hclust' codes singletons as -ve numbers, and non-singletons with + their seq. nos. */ + + /* Parameter adjustments */ + --iib; + --iia; + --iorder; + --ib; + --ia; + + /* Function Body */ + i__1 = *n; + for (i__ = 1; i__ <= i__1; ++i__) { + iia[i__] = ia[i__]; + iib[i__] = ib[i__]; + } + i__1 = *n - 2; + for (i__ = 1; i__ <= i__1; ++i__) { + /* In the following, smallest (+ve or -ve) seq. no. wanted */ + /* Computing MIN */ + i__2 = ia[i__], i__3 = ib[i__]; + k = i__2 < i__3 ? i__2 : i__3; + i__2 = *n - 1; + for (j = i__ + 1; j <= i__2; ++j) { + if (ia[j] == k) { + iia[j] = -i__; + } + if (ib[j] == k) { + iib[j] = -i__; + } + } + } + i__1 = *n - 1; + for (i__ = 1; i__ <= i__1; ++i__) { + iia[i__] = -iia[i__]; + iib[i__] = -iib[i__]; + } + i__1 = *n - 1; + for (i__ = 1; i__ <= i__1; ++i__) { + if (iia[i__] > 0 && iib[i__] < 0) { + k = iia[i__]; + iia[i__] = iib[i__]; + iib[i__] = k; + } + if (iia[i__] > 0 && iib[i__] > 0) { + /* Computing MIN */ + i__2 = iia[i__], i__3 = iib[i__]; + k1 = i__2 < i__3 ? i__2 : i__3; + /* Computing MAX */ + i__2 = iia[i__], i__3 = iib[i__]; + k2 = i__2 > i__3 ? i__2 : i__3; + iia[i__] = k1; + iib[i__] = k2; + } + } + + /* NEW PART FOR 'ORDER' */ + + iorder[1] = iia[*n - 1]; + iorder[2] = iib[*n - 1]; + loc = 2; + for (i__ = *n - 2; i__ >= 1; --i__) { + i__1 = loc; + for (j = 1; j <= i__1; ++j) { + if (iorder[j] == i__) { + /* REPLACE IORDER(J) WITH IIA(I) AND IIB(I) */ + iorder[j] = iia[i__]; + if (j == loc) { + ++loc; + iorder[loc] = iib[i__]; + } else { + ++loc; + i__2 = j + 2; + for (k = loc; k >= i__2; --k) { + iorder[k] = iorder[k - 1]; + } + iorder[j + 1] = iib[i__]; + } + goto L171; + } + } + /* SHOULD NEVER REACH HERE */ + L171: + ; + } + + i__1 = *n; + for (i__ = 1; i__ <= i__1; ++i__) { + iorder[i__] = -iorder[i__]; + } + + return 0; +} /* hcass2_ */ + +SEXP R_igraph_psumtree_draw(SEXP plength, SEXP howmany, SEXP prob) { + SEXP result; + int length=INTEGER(plength)[0]; + int i, n=INTEGER(howmany)[0]; + igraph_psumtree_t tree; + igraph_real_t sum; + + PROTECT(result=NEW_INTEGER(n)); + + igraph_psumtree_init(&tree, length); + if (isNull(prob)) { + for (i=0; i + +#include "igraph_random.h" + +#include "igraph_nongraph.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_memory.h" + +#include "core/core/math.h" + +#include "config.h" +#include +#include + +double unif_rand(void); +double norm_rand(void); +double exp_rand(void); +double Rf_rgeom(double); +double Rf_rbinom(double, double); +double Rf_rgamma(double, double); + +static int igraph_rng_R_init(void **state) { + return IGRAPH_SUCCESS; +} + +static void igraph_rng_R_destroy(void *state) { +} + +static int igraph_rng_R_seed(void *state, unsigned long int seed) { + IGRAPH_ERROR("R RNG error, unsupported function called", + IGRAPH_EINTERNAL); + return IGRAPH_SUCCESS; +} + +static unsigned long int igraph_rng_R_get(void *state) { + return (unsigned long) (unif_rand() * 0x7FFFFFFFUL); +} + +static igraph_real_t igraph_rng_R_get_real(void *state) { + return unif_rand(); +} + +static igraph_real_t igraph_rng_R_get_norm(void *state) { + return norm_rand(); +} + +static igraph_real_t igraph_rng_R_get_geom(void *state, igraph_real_t p) { + return Rf_rgeom(p); +} + +static igraph_real_t igraph_rng_R_get_binom(void *state, long int n, + igraph_real_t p) { + return Rf_rbinom(n, p); +} + +static igraph_real_t igraph_rng_R_get_exp(void *state, igraph_real_t rate) { + igraph_real_t scale = 1.0 / rate; + if (!IGRAPH_FINITE(scale) || scale <= 0.0) { + if (scale == 0.0) { + return 0.0; + } + return IGRAPH_NAN; + } + return scale * exp_rand(); +} + +static igraph_rng_type_t igraph_rng_R_type = { + /* name= */ "GNU R", + /* min= */ 0, + /* max= */ 0x7FFFFFFFUL, + /* init= */ igraph_rng_R_init, + /* destroy= */ igraph_rng_R_destroy, + /* seed= */ igraph_rng_R_seed, + /* get= */ igraph_rng_R_get, + /* get_real= */ igraph_rng_R_get_real, + /* get_norm= */ igraph_rng_R_get_norm, + /* get_geom= */ igraph_rng_R_get_geom, + /* get_binom= */ igraph_rng_R_get_binom, + /* get_exp= */ igraph_rng_R_get_exp +}; + +igraph_rng_t igraph_rng_R_instance; + +void igraph_rng_R_install() { + igraph_rng_init(&igraph_rng_R_instance, &igraph_rng_R_type); + igraph_rng_set_default(&igraph_rng_R_instance); +} diff --git a/src/rigraph/rrandom.h b/src/rigraph/rrandom.h new file mode 100644 index 0000000..4d9be8e --- /dev/null +++ b/src/rigraph/rrandom.h @@ -0,0 +1,28 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2022 The igraph development team + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_RRANDOM_H +#define IGRAPH_RRANDOM_H + +void igraph_rng_R_install(); + +#endif diff --git a/src/rigraph/vendor/arpack/debug.h b/src/rigraph/vendor/arpack/debug.h new file mode 100644 index 0000000..5eb0bb1 --- /dev/null +++ b/src/rigraph/vendor/arpack/debug.h @@ -0,0 +1,16 @@ +c +c\SCCS Information: @(#) +c FILE: debug.h SID: 2.3 DATE OF SID: 11/16/95 RELEASE: 2 +c +c %---------------------------------% +c | See debug.doc for documentation | +c %---------------------------------% + integer logfil, ndigit, mgetv0, + & msaupd, msaup2, msaitr, mseigt, msapps, msgets, mseupd, + & mnaupd, mnaup2, mnaitr, mneigh, mnapps, mngets, mneupd, + & mcaupd, mcaup2, mcaitr, mceigh, mcapps, mcgets, mceupd + common /debug/ + & logfil, ndigit, mgetv0, + & msaupd, msaup2, msaitr, mseigt, msapps, msgets, mseupd, + & mnaupd, mnaup2, mnaitr, mneigh, mnapps, mngets, mneupd, + & mcaupd, mcaup2, mcaitr, mceigh, mcapps, mcgets, mceupd diff --git a/src/rigraph/vendor/arpack/dgetv0.f b/src/rigraph/vendor/arpack/dgetv0.f new file mode 100644 index 0000000..b64d47a --- /dev/null +++ b/src/rigraph/vendor/arpack/dgetv0.f @@ -0,0 +1,419 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdgetv0 +c +c\Description: +c Generate a random initial residual vector for the Arnoldi process. +c Force the residual vector to be in the range of the operator OP. +c +c\Usage: +c call igraphdgetv0 +c ( IDO, BMAT, ITRY, INITV, N, J, V, LDV, RESID, RNORM, +c IPNTR, WORKD, IERR ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. IDO must be zero on the first +c call to igraphdgetv0. +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c This is for the initialization phase to force the +c starting vector into the range of OP. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c IDO = 99: done +c ------------------------------------------------------------- +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of the matrix B in the (generalized) +c eigenvalue problem A*x = lambda*B*x. +c B = 'I' -> standard eigenvalue problem A*x = lambda*x +c B = 'G' -> generalized eigenvalue problem A*x = lambda*B*x +c +c ITRY Integer. (INPUT) +c ITRY counts the number of times that igraphdgetv0 is called. +c It should be set to 1 on the initial call to igraphdgetv0. +c +c INITV Logical variable. (INPUT) +c .TRUE. => the initial residual vector is given in RESID. +c .FALSE. => generate a random initial residual vector. +c +c N Integer. (INPUT) +c Dimension of the problem. +c +c J Integer. (INPUT) +c Index of the residual vector to be generated, with respect to +c the Arnoldi process. J > 1 in case of a "restart". +c +c V Double precision N by J array. (INPUT) +c The first J-1 columns of V contain the current Arnoldi basis +c if this is a "restart". +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c Initial residual vector to be generated. If RESID is +c provided, force RESID into the range of the operator OP. +c +c RNORM Double precision scalar. (OUTPUT) +c B-norm of the generated residual. +c +c IPNTR Integer array of length 3. (OUTPUT) +c +c WORKD Double precision work array of length 2*N. (REVERSE COMMUNICATION). +c On exit, WORK(1:N) = B*RESID to be used in SSAITR. +c +c IERR Integer. (OUTPUT) +c = 0: Normal exit. +c = -1: Cannot generate a nontrivial restarted residual vector +c in the range of the operator OP. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c +c\Routines called: +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine for vector output. +c dlarnv LAPACK routine for generating a random vector. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c dcopy Level 1 BLAS that copies one vector to another. +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: getv0.F SID: 2.6 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdgetv0 + & ( ido, bmat, itry, initv, n, j, v, ldv, resid, rnorm, + & ipntr, workd, ierr ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1 + logical initv + integer ido, ierr, itry, j, ldv, n + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(3) + Double precision + & resid(n), v(ldv,j), workd(2*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %------------------------% +c | Local Scalars & Arrays | +c %------------------------% +c + logical first, inits, orth + integer idist, iseed(4), iter, msglvl, jj + Double precision + & rnorm0 + save first, iseed, inits, iter, msglvl, orth, rnorm0 +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dlarnv, igraphdvout, dcopy, dgemv, igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2 + external ddot, dnrm2 +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs, sqrt +c +c %-----------------% +c | Data Statements | +c %-----------------% +c + data inits /.true./ +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c +c %-----------------------------------% +c | Initialize the seed of the LAPACK | +c | random number generator | +c %-----------------------------------% +c + if (inits) then + iseed(1) = 1 + iseed(2) = 3 + iseed(3) = 5 + iseed(4) = 7 + inits = .false. + end if +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mgetv0 +c + ierr = 0 + iter = 0 + first = .FALSE. + orth = .FALSE. +c +c %-----------------------------------------------------% +c | Possibly generate a random starting vector in RESID | +c | Use a LAPACK random number generator used by the | +c | matrix generation routines. | +c | idist = 1: uniform (0,1) distribution; | +c | idist = 2: uniform (-1,1) distribution; | +c | idist = 3: normal (0,1) distribution; | +c %-----------------------------------------------------% +c + if (.not.initv) then + idist = 2 + call dlarnv (idist, iseed, n, resid) + end if +c +c %----------------------------------------------------------% +c | Force the starting vector into the range of OP to handle | +c | the generalized problem when B is possibly (singular). | +c %----------------------------------------------------------% +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nopx = nopx + 1 + ipntr(1) = 1 + ipntr(2) = n + 1 + call dcopy (n, resid, 1, workd, 1) + ido = -1 + go to 9000 + end if + end if +c +c %-----------------------------------------% +c | Back from computing OP*(initial-vector) | +c %-----------------------------------------% +c + if (first) go to 20 +c +c %-----------------------------------------------% +c | Back from computing B*(orthogonalized-vector) | +c %-----------------------------------------------% +c + if (orth) go to 40 +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvopx = tmvopx + (t3 - t2) + end if +c +c %------------------------------------------------------% +c | Starting vector is now in the range of OP; r = OP*r; | +c | Compute B-norm of starting vector. | +c %------------------------------------------------------% +c + call igraphsecond (t2) + first = .TRUE. + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, workd(n+1), 1, resid, 1) + ipntr(1) = n + 1 + ipntr(2) = 1 + ido = 2 + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd, 1) + end if +c + 20 continue +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + first = .FALSE. + if (bmat .eq. 'G') then + rnorm0 = ddot (n, resid, 1, workd, 1) + rnorm0 = sqrt(abs(rnorm0)) + else if (bmat .eq. 'I') then + rnorm0 = dnrm2(n, resid, 1) + end if + rnorm = rnorm0 +c +c %---------------------------------------------% +c | Exit if this is the very first Arnoldi step | +c %---------------------------------------------% +c + if (j .eq. 1) go to 50 +c +c %---------------------------------------------------------------- +c | Otherwise need to B-orthogonalize the starting vector against | +c | the current Arnoldi basis using Gram-Schmidt with iter. ref. | +c | This is the case where an invariant subspace is encountered | +c | in the middle of the Arnoldi factorization. | +c | | +c | s = V^{T}*B*r; r = r - V*s; | +c | | +c | Stopping criteria used for iter. ref. is discussed in | +c | Parlett's book, page 107 and in Gragg & Reichel TOMS paper. | +c %---------------------------------------------------------------% +c + orth = .TRUE. + 30 continue +c + call dgemv ('T', n, j-1, one, v, ldv, workd, 1, + & zero, workd(n+1), 1) + call dgemv ('N', n, j-1, -one, v, ldv, workd(n+1), 1, + & one, resid, 1) +c +c %----------------------------------------------------------% +c | Compute the B-norm of the orthogonalized starting vector | +c %----------------------------------------------------------% +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(n+1), 1) + ipntr(1) = n + 1 + ipntr(2) = 1 + ido = 2 + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd, 1) + end if +c + 40 continue +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd, 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if +c +c %--------------------------------------% +c | Check for further orthogonalization. | +c %--------------------------------------% +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, 1, [rnorm0], ndigit, + & '_getv0: re-orthonalization ; rnorm0 is') + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_getv0: re-orthonalization ; rnorm is') + end if +c + if (rnorm .gt. 0.717*rnorm0) go to 50 +c + iter = iter + 1 + if (iter .le. 1) then +c +c %-----------------------------------% +c | Perform iterative refinement step | +c %-----------------------------------% +c + rnorm0 = rnorm + go to 30 + else +c +c %------------------------------------% +c | Iterative refinement step "failed" | +c %------------------------------------% +c + do 45 jj = 1, n + resid(jj) = zero + 45 continue + rnorm = zero + ierr = -1 + end if +c + 50 continue +c + if (msglvl .gt. 0) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_getv0: B-norm of initial / restarted starting vector') + end if + if (msglvl .gt. 2) then + call igraphdvout (logfil, n, resid, ndigit, + & '_getv0: initial / restarted starting vector') + end if + ido = 99 +c + call igraphsecond (t1) + tgetv0 = tgetv0 + (t1 - t0) +c + 9000 continue + return +c +c %---------------% +c | End of igraphdgetv0 | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dlaqrb.f b/src/rigraph/vendor/arpack/dlaqrb.f new file mode 100644 index 0000000..5fcefec --- /dev/null +++ b/src/rigraph/vendor/arpack/dlaqrb.f @@ -0,0 +1,521 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdlaqrb +c +c\Description: +c Compute the eigenvalues and the Schur decomposition of an upper +c Hessenberg submatrix in rows and columns ILO to IHI. Only the +c last component of the Schur vectors are computed. +c +c This is mostly a modification of the LAPACK routine dlahqr. +c +c\Usage: +c call igraphdlaqrb +c ( WANTT, N, ILO, IHI, H, LDH, WR, WI, Z, INFO ) +c +c\Arguments +c WANTT Logical variable. (INPUT) +c = .TRUE. : the full Schur form T is required; +c = .FALSE.: only eigenvalues are required. +c +c N Integer. (INPUT) +c The order of the matrix H. N >= 0. +c +c ILO Integer. (INPUT) +c IHI Integer. (INPUT) +c It is assumed that H is already upper quasi-triangular in +c rows and columns IHI+1:N, and that H(ILO,ILO-1) = 0 (unless +c ILO = 1). SLAQRB works primarily with the Hessenberg +c submatrix in rows and columns ILO to IHI, but applies +c transformations to all of H if WANTT is .TRUE.. +c 1 <= ILO <= max(1,IHI); IHI <= N. +c +c H Double precision array, dimension (LDH,N). (INPUT/OUTPUT) +c On entry, the upper Hessenberg matrix H. +c On exit, if WANTT is .TRUE., H is upper quasi-triangular in +c rows and columns ILO:IHI, with any 2-by-2 diagonal blocks in +c standard form. If WANTT is .FALSE., the contents of H are +c unspecified on exit. +c +c LDH Integer. (INPUT) +c The leading dimension of the array H. LDH >= max(1,N). +c +c WR Double precision array, dimension (N). (OUTPUT) +c WI Double precision array, dimension (N). (OUTPUT) +c The real and imaginary parts, respectively, of the computed +c eigenvalues ILO to IHI are stored in the corresponding +c elements of WR and WI. If two eigenvalues are computed as a +c complex conjugate pair, they are stored in consecutive +c elements of WR and WI, say the i-th and (i+1)th, with +c WI(i) > 0 and WI(i+1) < 0. If WANTT is .TRUE., the +c eigenvalues are stored in the same order as on the diagonal +c of the Schur form returned in H, with WR(i) = H(i,i), and, if +c H(i:i+1,i:i+1) is a 2-by-2 diagonal block, +c WI(i) = sqrt(H(i+1,i)*H(i,i+1)) and WI(i+1) = -WI(i). +c +c Z Double precision array, dimension (N). (OUTPUT) +c On exit Z contains the last components of the Schur vectors. +c +c INFO Integer. (OUPUT) +c = 0: successful exit +c > 0: SLAQRB failed to compute all the eigenvalues ILO to IHI +c in a total of 30*(IHI-ILO+1) iterations; if INFO = i, +c elements i+1:ihi of WR and WI contain those eigenvalues +c which have been successfully computed. +c +c\Remarks +c 1. None. +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c dlabad LAPACK routine that computes machine constants. +c dlamch LAPACK routine that determines machine constants. +c dlanhs LAPACK routine that computes various norms of a matrix. +c dlanv2 LAPACK routine that computes the Schur factorization of +c 2 by 2 nonsymmetric matrix in standard form. +c dlarfg LAPACK Householder reflection construction routine. +c dcopy Level 1 BLAS that copies one vector to another. +c drot Level 1 BLAS that applies a rotation to a 2 by 2 matrix. + +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.4' +c Modified from the LAPACK routine dlahqr so that only the +c last component of the Schur vectors are computed. +c +c\SCCS Information: @(#) +c FILE: laqrb.F SID: 2.2 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c 1. None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdlaqrb ( wantt, n, ilo, ihi, h, ldh, wr, wi, + & z, info ) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + logical wantt + integer ihi, ilo, info, ldh, n +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & h( ldh, * ), wi( * ), wr( * ), z( * ) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & zero, one, dat1, dat2 + parameter (zero = 0.0D+0, one = 1.0D+0, dat1 = 7.5D-1, + & dat2 = -4.375D-1) +c +c %------------------------% +c | Local Scalars & Arrays | +c %------------------------% +c + integer i, i1, i2, itn, its, j, k, l, m, nh, nr + Double precision + & cs, h00, h10, h11, h12, h21, h22, h33, h33s, + & h43h34, h44, h44s, ovfl, s, smlnum, sn, sum, + & t1, t2, t3, tst1, ulp, unfl, v1, v2, v3 + Double precision + & v( 3 ), work( 1 ) +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch, dlanhs + external dlamch, dlanhs +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, dlabad, dlanv2, dlarfg, drot +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + info = 0 +c +c %--------------------------% +c | Quick return if possible | +c %--------------------------% +c + if( n.eq.0 ) + & return + if( ilo.eq.ihi ) then + wr( ilo ) = h( ilo, ilo ) + wi( ilo ) = zero + return + end if +c +c %---------------------------------------------% +c | Initialize the vector of last components of | +c | the Schur vectors for accumulation. | +c %---------------------------------------------% +c + do 5 j = 1, n-1 + z(j) = zero + 5 continue + z(n) = one +c + nh = ihi - ilo + 1 +c +c %-------------------------------------------------------------% +c | Set machine-dependent constants for the stopping criterion. | +c | If norm(H) <= sqrt(OVFL), overflow should not occur. | +c %-------------------------------------------------------------% +c + unfl = dlamch( 'safe minimum' ) + ovfl = one / unfl + call dlabad( unfl, ovfl ) + ulp = dlamch( 'precision' ) + smlnum = unfl*( nh / ulp ) +c +c %---------------------------------------------------------------% +c | I1 and I2 are the indices of the first row and last column | +c | of H to which transformations must be applied. If eigenvalues | +c | only are computed, I1 and I2 are set inside the main loop. | +c | Zero out H(J+2,J) = ZERO for J=1:N if WANTT = .TRUE. | +c | else H(J+2,J) for J=ILO:IHI-ILO-1 if WANTT = .FALSE. | +c %---------------------------------------------------------------% +c + if( wantt ) then + i1 = 1 + i2 = n + do 8 i=1,i2-2 + h(i1+i+1,i) = zero + 8 continue + else + do 9 i=1, ihi-ilo-1 + h(ilo+i+1,ilo+i-1) = zero + 9 continue + end if +c +c %---------------------------------------------------% +c | ITN is the total number of QR iterations allowed. | +c %---------------------------------------------------% +c + itn = 30*nh +c +c ------------------------------------------------------------------ +c The main loop begins here. I is the loop index and decreases from +c IHI to ILO in steps of 1 or 2. Each iteration of the loop works +c with the active submatrix in rows and columns L to I. +c Eigenvalues I+1 to IHI have already converged. Either L = ILO or +c H(L,L-1) is negligible so that the matrix splits. +c ------------------------------------------------------------------ +c + i = ihi + 10 continue + l = ilo + if( i.lt.ilo ) + & go to 150 + +c %--------------------------------------------------------------% +c | Perform QR iterations on rows and columns ILO to I until a | +c | submatrix of order 1 or 2 splits off at the bottom because a | +c | subdiagonal element has become negligible. | +c %--------------------------------------------------------------% + + do 130 its = 0, itn +c +c %----------------------------------------------% +c | Look for a single small subdiagonal element. | +c %----------------------------------------------% +c + do 20 k = i, l + 1, -1 + tst1 = abs( h( k-1, k-1 ) ) + abs( h( k, k ) ) + if( tst1.eq.zero ) + & tst1 = dlanhs( '1', i-l+1, h( l, l ), ldh, work ) + if( abs( h( k, k-1 ) ).le.max( ulp*tst1, smlnum ) ) + & go to 30 + 20 continue + 30 continue + l = k + if( l.gt.ilo ) then +c +c %------------------------% +c | H(L,L-1) is negligible | +c %------------------------% +c + h( l, l-1 ) = zero + end if +c +c %-------------------------------------------------------------% +c | Exit from loop if a submatrix of order 1 or 2 has split off | +c %-------------------------------------------------------------% +c + if( l.ge.i-1 ) + & go to 140 +c +c %---------------------------------------------------------% +c | Now the active submatrix is in rows and columns L to I. | +c | If eigenvalues only are being computed, only the active | +c | submatrix need be transformed. | +c %---------------------------------------------------------% +c + if( .not.wantt ) then + i1 = l + i2 = i + end if +c + if( its.eq.10 .or. its.eq.20 ) then +c +c %-------------------% +c | Exceptional shift | +c %-------------------% +c + s = abs( h( i, i-1 ) ) + abs( h( i-1, i-2 ) ) + h44 = dat1*s + h33 = h44 + h43h34 = dat2*s*s +c + else +c +c %-----------------------------------------% +c | Prepare to use Wilkinson's double shift | +c %-----------------------------------------% +c + h44 = h( i, i ) + h33 = h( i-1, i-1 ) + h43h34 = h( i, i-1 )*h( i-1, i ) + end if +c +c %-----------------------------------------------------% +c | Look for two consecutive small subdiagonal elements | +c %-----------------------------------------------------% +c + do 40 m = i - 2, l, -1 +c +c %---------------------------------------------------------% +c | Determine the effect of starting the double-shift QR | +c | iteration at row M, and see if this would make H(M,M-1) | +c | negligible. | +c %---------------------------------------------------------% +c + h11 = h( m, m ) + h22 = h( m+1, m+1 ) + h21 = h( m+1, m ) + h12 = h( m, m+1 ) + h44s = h44 - h11 + h33s = h33 - h11 + v1 = ( h33s*h44s-h43h34 ) / h21 + h12 + v2 = h22 - h11 - h33s - h44s + v3 = h( m+2, m+1 ) + s = abs( v1 ) + abs( v2 ) + abs( v3 ) + v1 = v1 / s + v2 = v2 / s + v3 = v3 / s + v( 1 ) = v1 + v( 2 ) = v2 + v( 3 ) = v3 + if( m.eq.l ) + & go to 50 + h00 = h( m-1, m-1 ) + h10 = h( m, m-1 ) + tst1 = abs( v1 )*( abs( h00 )+abs( h11 )+abs( h22 ) ) + if( abs( h10 )*( abs( v2 )+abs( v3 ) ).le.ulp*tst1 ) + & go to 50 + 40 continue + 50 continue +c +c %----------------------% +c | Double-shift QR step | +c %----------------------% +c + do 120 k = m, i - 1 +c +c ------------------------------------------------------------ +c The first iteration of this loop determines a reflection G +c from the vector V and applies it from left and right to H, +c thus creating a nonzero bulge below the subdiagonal. +c +c Each subsequent iteration determines a reflection G to +c restore the Hessenberg form in the (K-1)th column, and thus +c chases the bulge one step toward the bottom of the active +c submatrix. NR is the order of G. +c ------------------------------------------------------------ +c + nr = min( 3, i-k+1 ) + if( k.gt.m ) + & call dcopy( nr, h( k, k-1 ), 1, v, 1 ) + call dlarfg( nr, v( 1 ), v( 2 ), 1, t1 ) + if( k.gt.m ) then + h( k, k-1 ) = v( 1 ) + h( k+1, k-1 ) = zero + if( k.lt.i-1 ) + & h( k+2, k-1 ) = zero + else if( m.gt.l ) then + h( k, k-1 ) = -h( k, k-1 ) + end if + v2 = v( 2 ) + t2 = t1*v2 + if( nr.eq.3 ) then + v3 = v( 3 ) + t3 = t1*v3 +c +c %------------------------------------------------% +c | Apply G from the left to transform the rows of | +c | the matrix in columns K to I2. | +c %------------------------------------------------% +c + do 60 j = k, i2 + sum = h( k, j ) + v2*h( k+1, j ) + v3*h( k+2, j ) + h( k, j ) = h( k, j ) - sum*t1 + h( k+1, j ) = h( k+1, j ) - sum*t2 + h( k+2, j ) = h( k+2, j ) - sum*t3 + 60 continue +c +c %----------------------------------------------------% +c | Apply G from the right to transform the columns of | +c | the matrix in rows I1 to min(K+3,I). | +c %----------------------------------------------------% +c + do 70 j = i1, min( k+3, i ) + sum = h( j, k ) + v2*h( j, k+1 ) + v3*h( j, k+2 ) + h( j, k ) = h( j, k ) - sum*t1 + h( j, k+1 ) = h( j, k+1 ) - sum*t2 + h( j, k+2 ) = h( j, k+2 ) - sum*t3 + 70 continue +c +c %----------------------------------% +c | Accumulate transformations for Z | +c %----------------------------------% +c + sum = z( k ) + v2*z( k+1 ) + v3*z( k+2 ) + z( k ) = z( k ) - sum*t1 + z( k+1 ) = z( k+1 ) - sum*t2 + z( k+2 ) = z( k+2 ) - sum*t3 + + else if( nr.eq.2 ) then +c +c %------------------------------------------------% +c | Apply G from the left to transform the rows of | +c | the matrix in columns K to I2. | +c %------------------------------------------------% +c + do 90 j = k, i2 + sum = h( k, j ) + v2*h( k+1, j ) + h( k, j ) = h( k, j ) - sum*t1 + h( k+1, j ) = h( k+1, j ) - sum*t2 + 90 continue +c +c %----------------------------------------------------% +c | Apply G from the right to transform the columns of | +c | the matrix in rows I1 to min(K+3,I). | +c %----------------------------------------------------% +c + do 100 j = i1, i + sum = h( j, k ) + v2*h( j, k+1 ) + h( j, k ) = h( j, k ) - sum*t1 + h( j, k+1 ) = h( j, k+1 ) - sum*t2 + 100 continue +c +c %----------------------------------% +c | Accumulate transformations for Z | +c %----------------------------------% +c + sum = z( k ) + v2*z( k+1 ) + z( k ) = z( k ) - sum*t1 + z( k+1 ) = z( k+1 ) - sum*t2 + end if + 120 continue + + 130 continue +c +c %-------------------------------------------------------% +c | Failure to converge in remaining number of iterations | +c %-------------------------------------------------------% +c + info = i + return + + 140 continue + + if( l.eq.i ) then +c +c %------------------------------------------------------% +c | H(I,I-1) is negligible: one eigenvalue has converged | +c %------------------------------------------------------% +c + wr( i ) = h( i, i ) + wi( i ) = zero + + else if( l.eq.i-1 ) then +c +c %--------------------------------------------------------% +c | H(I-1,I-2) is negligible; | +c | a pair of eigenvalues have converged. | +c | | +c | Transform the 2-by-2 submatrix to standard Schur form, | +c | and compute and store the eigenvalues. | +c %--------------------------------------------------------% +c + call dlanv2( h( i-1, i-1 ), h( i-1, i ), h( i, i-1 ), + & h( i, i ), wr( i-1 ), wi( i-1 ), wr( i ), wi( i ), + & cs, sn ) + + if( wantt ) then +c +c %-----------------------------------------------------% +c | Apply the transformation to the rest of H and to Z, | +c | as required. | +c %-----------------------------------------------------% +c + if( i2.gt.i ) + & call drot( i2-i, h( i-1, i+1 ), ldh, h( i, i+1 ), ldh, + & cs, sn ) + call drot( i-i1-1, h( i1, i-1 ), 1, h( i1, i ), 1, cs, sn ) + sum = cs*z( i-1 ) + sn*z( i ) + z( i ) = cs*z( i ) - sn*z( i-1 ) + z( i-1 ) = sum + end if + end if +c +c %---------------------------------------------------------% +c | Decrement number of remaining iterations, and return to | +c | start of the main loop with new value of I. | +c %---------------------------------------------------------% +c + itn = itn - its + i = l - 1 + go to 10 + + 150 continue + return +c +c %---------------% +c | End of igraphdlaqrb | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dmout.f b/src/rigraph/vendor/arpack/dmout.f new file mode 100644 index 0000000..6204d64 --- /dev/null +++ b/src/rigraph/vendor/arpack/dmout.f @@ -0,0 +1,167 @@ +*----------------------------------------------------------------------- +* Routine: DMOUT +* +* Purpose: Real matrix output routine. +* +* Usage: CALL DMOUT (LOUT, M, N, A, LDA, IDIGIT, IFMT) +* +* Arguments +* M - Number of rows of A. (Input) +* N - Number of columns of A. (Input) +* A - Real M by N matrix to be printed. (Input) +* LDA - Leading dimension of A exactly as specified in the +* dimension statement of the calling program. (Input) +* IFMT - Format to be used in printing matrix A. (Input) +* IDIGIT - Print up to IABS(IDIGIT) decimal digits per number. (In) +* If IDIGIT .LT. 0, printing is done with 72 columns. +* If IDIGIT .GT. 0, printing is done with 132 columns. +* +*----------------------------------------------------------------------- +* + SUBROUTINE IGRAPHDMOUT( LOUT, M, N, A, LDA, IDIGIT, IFMT ) +* ... +* ... SPECIFICATIONS FOR ARGUMENTS +* ... +* ... SPECIFICATIONS FOR LOCAL VARIABLES +* .. Scalar Arguments .. + CHARACTER*( * ) IFMT + INTEGER IDIGIT, LDA, LOUT, M, N +* .. +* .. Array Arguments .. + DOUBLE PRECISION A( LDA, * ) +* .. +* .. Local Scalars .. + CHARACTER*80 LINE + INTEGER I, J, K1, K2, LLL, NDIGIT +* .. +* .. Local Arrays .. + CHARACTER ICOL( 3 ) +* .. +* .. Intrinsic Functions .. + INTRINSIC LEN, MIN, MIN0 +* .. +* .. Data statements .. + DATA ICOL( 1 ), ICOL( 2 ), ICOL( 3 ) / 'C', 'o', + $ 'l' / +* .. +* .. Executable Statements .. +* ... +* ... FIRST EXECUTABLE STATEMENT +* +c$$$ LLL = MIN( LEN( IFMT ), 80 ) +c$$$ DO 10 I = 1, LLL +c$$$ LINE( I: I ) = '-' +c$$$ 10 CONTINUE +c$$$* +c$$$ DO 20 I = LLL + 1, 80 +c$$$ LINE( I: I ) = ' ' +c$$$ 20 CONTINUE +c$$$* +c$$$ WRITE( LOUT, FMT = 9999 )IFMT, LINE( 1: LLL ) +c$$$ 9999 FORMAT( / 1X, A, / 1X, A ) +c$$$* +c$$$ IF( M.LE.0 .OR. N.LE.0 .OR. LDA.LE.0 ) +c$$$ $ RETURN +c$$$ NDIGIT = IDIGIT +c$$$ IF( IDIGIT.EQ.0 ) +c$$$ $ NDIGIT = 4 +c$$$* +c$$$*======================================================================= +c$$$* CODE FOR OUTPUT USING 72 COLUMNS FORMAT +c$$$*======================================================================= +c$$$* +c$$$ IF( IDIGIT.LT.0 ) THEN +c$$$ NDIGIT = -IDIGIT +c$$$ IF( NDIGIT.LE.4 ) THEN +c$$$ DO 40 K1 = 1, N, 5 +c$$$ K2 = MIN0( N, K1+4 ) +c$$$ WRITE( LOUT, FMT = 9998 )( ICOL, I, I = K1, K2 ) +c$$$ DO 30 I = 1, M +c$$$ WRITE( LOUT, FMT = 9994 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 30 CONTINUE +c$$$ 40 CONTINUE +c$$$* +c$$$ ELSE IF( NDIGIT.LE.6 ) THEN +c$$$ DO 60 K1 = 1, N, 4 +c$$$ K2 = MIN0( N, K1+3 ) +c$$$ WRITE( LOUT, FMT = 9997 )( ICOL, I, I = K1, K2 ) +c$$$ DO 50 I = 1, M +c$$$ WRITE( LOUT, FMT = 9993 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 50 CONTINUE +c$$$ 60 CONTINUE +c$$$* +c$$$ ELSE IF( NDIGIT.LE.10 ) THEN +c$$$ DO 80 K1 = 1, N, 3 +c$$$ K2 = MIN0( N, K1+2 ) +c$$$ WRITE( LOUT, FMT = 9996 )( ICOL, I, I = K1, K2 ) +c$$$ DO 70 I = 1, M +c$$$ WRITE( LOUT, FMT = 9992 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 70 CONTINUE +c$$$ 80 CONTINUE +c$$$* +c$$$ ELSE +c$$$ DO 100 K1 = 1, N, 2 +c$$$ K2 = MIN0( N, K1+1 ) +c$$$ WRITE( LOUT, FMT = 9995 )( ICOL, I, I = K1, K2 ) +c$$$ DO 90 I = 1, M +c$$$ WRITE( LOUT, FMT = 9991 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 90 CONTINUE +c$$$ 100 CONTINUE +c$$$ END IF +c$$$* +c$$$*======================================================================= +c$$$* CODE FOR OUTPUT USING 132 COLUMNS FORMAT +c$$$*======================================================================= +c$$$* +c$$$ ELSE +c$$$ IF( NDIGIT.LE.4 ) THEN +c$$$ DO 120 K1 = 1, N, 10 +c$$$ K2 = MIN0( N, K1+9 ) +c$$$ WRITE( LOUT, FMT = 9998 )( ICOL, I, I = K1, K2 ) +c$$$ DO 110 I = 1, M +c$$$ WRITE( LOUT, FMT = 9994 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 110 CONTINUE +c$$$ 120 CONTINUE +c$$$* +c$$$ ELSE IF( NDIGIT.LE.6 ) THEN +c$$$ DO 140 K1 = 1, N, 8 +c$$$ K2 = MIN0( N, K1+7 ) +c$$$ WRITE( LOUT, FMT = 9997 )( ICOL, I, I = K1, K2 ) +c$$$ DO 130 I = 1, M +c$$$ WRITE( LOUT, FMT = 9993 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 130 CONTINUE +c$$$ 140 CONTINUE +c$$$* +c$$$ ELSE IF( NDIGIT.LE.10 ) THEN +c$$$ DO 160 K1 = 1, N, 6 +c$$$ K2 = MIN0( N, K1+5 ) +c$$$ WRITE( LOUT, FMT = 9996 )( ICOL, I, I = K1, K2 ) +c$$$ DO 150 I = 1, M +c$$$ WRITE( LOUT, FMT = 9992 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 150 CONTINUE +c$$$ 160 CONTINUE +c$$$* +c$$$ ELSE +c$$$ DO 180 K1 = 1, N, 5 +c$$$ K2 = MIN0( N, K1+4 ) +c$$$ WRITE( LOUT, FMT = 9995 )( ICOL, I, I = K1, K2 ) +c$$$ DO 170 I = 1, M +c$$$ WRITE( LOUT, FMT = 9991 )I, ( A( I, J ), J = K1, K2 ) +c$$$ 170 CONTINUE +c$$$ 180 CONTINUE +c$$$ END IF +c$$$ END IF +c$$$ WRITE( LOUT, FMT = 9990 ) +c$$$* +c$$$ 9998 FORMAT( 10X, 10( 4X, 3A1, I4, 1X ) ) +c$$$ 9997 FORMAT( 10X, 8( 5X, 3A1, I4, 2X ) ) +c$$$ 9996 FORMAT( 10X, 6( 7X, 3A1, I4, 4X ) ) +c$$$ 9995 FORMAT( 10X, 5( 9X, 3A1, I4, 6X ) ) +c$$$ 9994 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 10D12.3 ) +c$$$ 9993 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 8D14.5 ) +c$$$ 9992 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 6D18.9 ) +c$$$ 9991 FORMAT( 1X, ' Row', I4, ':', 1X, 1P, 5D22.13 ) +c$$$ 9990 FORMAT( 1X, ' ' ) +* + RETURN + END diff --git a/src/rigraph/vendor/arpack/dnaitr.f b/src/rigraph/vendor/arpack/dnaitr.f new file mode 100644 index 0000000..8ec2569 --- /dev/null +++ b/src/rigraph/vendor/arpack/dnaitr.f @@ -0,0 +1,840 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdnaitr +c +c\Description: +c Reverse communication interface for applying NP additional steps to +c a K step nonsymmetric Arnoldi factorization. +c +c Input: OP*V_{k} - V_{k}*H = r_{k}*e_{k}^T +c +c with (V_{k}^T)*B*V_{k} = I, (V_{k}^T)*B*r_{k} = 0. +c +c Output: OP*V_{k+p} - V_{k+p}*H = r_{k+p}*e_{k+p}^T +c +c with (V_{k+p}^T)*B*V_{k+p} = I, (V_{k+p}^T)*B*r_{k+p} = 0. +c +c where OP and B are as in igraphdnaupd. The B-norm of r_{k+p} is also +c computed and returned. +c +c\Usage: +c call igraphdnaitr +c ( IDO, BMAT, N, K, NP, NB, RESID, RNORM, V, LDV, H, LDH, +c IPNTR, WORKD, INFO ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y. +c This is for the restart phase to force the new +c starting vector into the range of OP. +c IDO = 1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y, +c IPNTR(3) is the pointer into WORK for B * X. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y. +c IDO = 99: done +c ------------------------------------------------------------- +c When the routine is used in the "shift-and-invert" mode, the +c vector B * Q is already available and do not need to be +c recompute in forming OP * Q. +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of the matrix B that defines the +c semi-inner product for the operator OP. See igraphdnaupd. +c B = 'I' -> standard eigenvalue problem A*x = lambda*x +c B = 'G' -> generalized eigenvalue problem A*x = lambda*M**x +c +c N Integer. (INPUT) +c Dimension of the eigenproblem. +c +c K Integer. (INPUT) +c Current size of V and H. +c +c NP Integer. (INPUT) +c Number of additional Arnoldi steps to take. +c +c NB Integer. (INPUT) +c Blocksize to be used in the recurrence. +c Only work for NB = 1 right now. The goal is to have a +c program that implement both the block and non-block method. +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT: RESID contains the residual vector r_{k}. +c On OUTPUT: RESID contains the residual vector r_{k+p}. +c +c RNORM Double precision scalar. (INPUT/OUTPUT) +c B-norm of the starting residual on input. +c B-norm of the updated residual r_{k+p} on output. +c +c V Double precision N by K+NP array. (INPUT/OUTPUT) +c On INPUT: V contains the Arnoldi vectors in the first K +c columns. +c On OUTPUT: V contains the new NP Arnoldi vectors in the next +c NP columns. The first K columns are unchanged. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (K+NP) by (K+NP) array. (INPUT/OUTPUT) +c H is used to store the generated upper Hessenberg matrix. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c IPNTR Integer array of length 3. (OUTPUT) +c Pointer to mark the starting locations in the WORK for +c vectors used by the Arnoldi iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X. +c IPNTR(2): pointer to the current result vector Y. +c IPNTR(3): pointer to the vector B * X when used in the +c shift-and-invert mode. X is the current operand. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The calling program should not +c use WORKD as temporary workspace during the iteration !!!!!! +c On input, WORKD(1:N) = B*RESID and is used to save some +c computation at the first step. +c +c INFO Integer. (OUTPUT) +c = 0: Normal exit. +c > 0: Size of the spanning invariant subspace of OP found. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c +c\Routines called: +c igraphdgetv0 ARPACK routine to generate the initial vector. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdmout ARPACK utility routine that prints matrices +c igraphdvout ARPACK utility routine that prints vectors. +c dlabad LAPACK routine that computes machine constants. +c dlamch LAPACK routine that determines machine constants. +c dlascl LAPACK routine for careful scaling of a matrix. +c dlanhs LAPACK routine that computes various norms of a matrix. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c daxpy Level 1 BLAS that computes a vector triad. +c dscal Level 1 BLAS that scales a vector. +c dcopy Level 1 BLAS that copies one vector to another . +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.4' +c +c\SCCS Information: @(#) +c FILE: naitr.F SID: 2.4 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c The algorithm implemented is: +c +c restart = .false. +c Given V_{k} = [v_{1}, ..., v_{k}], r_{k}; +c r_{k} contains the initial residual vector even for k = 0; +c Also assume that rnorm = || B*r_{k} || and B*r_{k} are already +c computed by the calling program. +c +c betaj = rnorm ; p_{k+1} = B*r_{k} ; +c For j = k+1, ..., k+np Do +c 1) if ( betaj < tol ) stop or restart depending on j. +c ( At present tol is zero ) +c if ( restart ) generate a new starting vector. +c 2) v_{j} = r(j-1)/betaj; V_{j} = [V_{j-1}, v_{j}]; +c p_{j} = p_{j}/betaj +c 3) r_{j} = OP*v_{j} where OP is defined as in igraphdnaupd +c For shift-invert mode p_{j} = B*v_{j} is already available. +c wnorm = || OP*v_{j} || +c 4) Compute the j-th step residual vector. +c w_{j} = V_{j}^T * B * OP * v_{j} +c r_{j} = OP*v_{j} - V_{j} * w_{j} +c H(:,j) = w_{j}; +c H(j,j-1) = rnorm +c rnorm = || r_(j) || +c If (rnorm > 0.717*wnorm) accept step and go back to 1) +c 5) Re-orthogonalization step: +c s = V_{j}'*B*r_{j} +c r_{j} = r_{j} - V_{j}*s; rnorm1 = || r_{j} || +c alphaj = alphaj + s_{j}; +c 6) Iterative refinement step: +c If (rnorm1 > 0.717*rnorm) then +c rnorm = rnorm1 +c accept step and go back to 1) +c Else +c rnorm = rnorm1 +c If this is the first time in step 6), go to 5) +c Else r_{j} lies in the span of V_{j} numerically. +c Set r_{j} = 0 and rnorm = 0; go to 1) +c EndIf +c End Do +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnaitr + & (ido, bmat, n, k, np, nb, resid, rnorm, v, ldv, h, ldh, + & ipntr, workd, info) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1 + integer ido, info, k, ldh, ldv, n, nb, np + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(3) + Double precision + & h(ldh,k+np), resid(n), v(ldv,k+np), workd(3*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + logical first, orth1, orth2, rstart, step3, step4 + integer ierr, i, infol, ipj, irj, ivj, iter, itry, j, msglvl, + & jj + Double precision + & betaj, ovfl, temp1, rnorm1, smlnum, tst1, ulp, unfl, + & wnorm + save first, orth1, orth2, rstart, step3, step4, + & ierr, ipj, irj, ivj, iter, itry, j, msglvl, ovfl, + & betaj, rnorm1, smlnum, ulp, unfl, wnorm +c +c %-----------------------% +c | Local Array Arguments | +c %-----------------------% +c + Double precision + & xtemp(2) +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external daxpy, dcopy, dscal, dgemv, igraphdgetv0, dlabad, + & igraphdvout, igraphdmout, igraphivout, igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2, dlanhs, dlamch + external ddot, dnrm2, dlanhs, dlamch +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs, sqrt +c +c %-----------------% +c | Data statements | +c %-----------------% +c + data first / .true. / +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (first) then +c +c %-----------------------------------------% +c | Set machine-dependent constants for the | +c | the splitting and deflation criterion. | +c | If norm(H) <= sqrt(OVFL), | +c | overflow should not occur. | +c | REFERENCE: LAPACK subroutine dlahqr | +c %-----------------------------------------% +c + unfl = dlamch( 'safe minimum' ) + ovfl = one / unfl + call dlabad( unfl, ovfl ) + ulp = dlamch( 'precision' ) + smlnum = unfl*( n / ulp ) + first = .false. + end if +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mnaitr +c +c %------------------------------% +c | Initial call to this routine | +c %------------------------------% +c + info = 0 + step3 = .false. + step4 = .false. + rstart = .false. + orth1 = .false. + orth2 = .false. + j = k + 1 + ipj = 1 + irj = ipj + n + ivj = irj + n + end if +c +c %-------------------------------------------------% +c | When in reverse communication mode one of: | +c | STEP3, STEP4, ORTH1, ORTH2, RSTART | +c | will be .true. when .... | +c | STEP3: return from computing OP*v_{j}. | +c | STEP4: return from computing B-norm of OP*v_{j} | +c | ORTH1: return from computing B-norm of r_{j+1} | +c | ORTH2: return from computing B-norm of | +c | correction to the residual vector. | +c | RSTART: return from OP computations needed by | +c | igraphdgetv0. | +c %-------------------------------------------------% +c + if (step3) go to 50 + if (step4) go to 60 + if (orth1) go to 70 + if (orth2) go to 90 + if (rstart) go to 30 +c +c %-----------------------------% +c | Else this is the first step | +c %-----------------------------% +c +c %--------------------------------------------------------------% +c | | +c | A R N O L D I I T E R A T I O N L O O P | +c | | +c | Note: B*r_{j-1} is already in WORKD(1:N)=WORKD(IPJ:IPJ+N-1) | +c %--------------------------------------------------------------% + + 1000 continue +c + if (msglvl .gt. 1) then + call igraphivout (logfil, 1, [j], ndigit, + & '_naitr: generating Arnoldi vector number') + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_naitr: B-norm of the current residual is') + end if +c +c %---------------------------------------------------% +c | STEP 1: Check if the B norm of j-th residual | +c | vector is zero. Equivalent to determing whether | +c | an exact j-step Arnoldi factorization is present. | +c %---------------------------------------------------% +c + betaj = rnorm + if (rnorm .gt. zero) go to 40 +c +c %---------------------------------------------------% +c | Invariant subspace found, generate a new starting | +c | vector which is orthogonal to the current Arnoldi | +c | basis and continue the iteration. | +c %---------------------------------------------------% +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [j], ndigit, + & '_naitr: ****** RESTART AT STEP ******') + end if +c +c %---------------------------------------------% +c | ITRY is the loop variable that controls the | +c | maximum amount of times that a restart is | +c | attempted. NRSTRT is used by stat.h | +c %---------------------------------------------% +c + betaj = zero + nrstrt = nrstrt + 1 + itry = 1 + 20 continue + rstart = .true. + ido = 0 + 30 continue +c +c %--------------------------------------% +c | If in reverse communication mode and | +c | RSTART = .true. flow returns here. | +c %--------------------------------------% +c + call igraphdgetv0 (ido, bmat, itry, .false., n, j, v, ldv, + & resid, rnorm, ipntr, workd, ierr) + if (ido .ne. 99) go to 9000 + if (ierr .lt. 0) then + itry = itry + 1 + if (itry .le. 3) go to 20 +c +c %------------------------------------------------% +c | Give up after several restart attempts. | +c | Set INFO to the size of the invariant subspace | +c | which spans OP and exit. | +c %------------------------------------------------% +c + info = j - 1 + call igraphsecond (t1) + tnaitr = tnaitr + (t1 - t0) + ido = 99 + go to 9000 + end if +c + 40 continue +c +c %---------------------------------------------------------% +c | STEP 2: v_{j} = r_{j-1}/rnorm and p_{j} = p_{j}/rnorm | +c | Note that p_{j} = B*r_{j-1}. In order to avoid overflow | +c | when reciprocating a small RNORM, test against lower | +c | machine bound. | +c %---------------------------------------------------------% +c + call dcopy (n, resid, 1, v(1,j), 1) + if (rnorm .ge. unfl) then + temp1 = one / rnorm + call dscal (n, temp1, v(1,j), 1) + call dscal (n, temp1, workd(ipj), 1) + else +c +c %-----------------------------------------% +c | To scale both v_{j} and p_{j} carefully | +c | use LAPACK routine SLASCL | +c %-----------------------------------------% +c + call dlascl ('General', i, i, rnorm, one, n, 1, + & v(1,j), n, infol) + call dlascl ('General', i, i, rnorm, one, n, 1, + & workd(ipj), n, infol) + end if +c +c %------------------------------------------------------% +c | STEP 3: r_{j} = OP*v_{j}; Note that p_{j} = B*v_{j} | +c | Note that this is not quite yet r_{j}. See STEP 4 | +c %------------------------------------------------------% +c + step3 = .true. + nopx = nopx + 1 + call igraphsecond (t2) + call dcopy (n, v(1,j), 1, workd(ivj), 1) + ipntr(1) = ivj + ipntr(2) = irj + ipntr(3) = ipj + ido = 1 +c +c %-----------------------------------% +c | Exit in order to compute OP*v_{j} | +c %-----------------------------------% +c + go to 9000 + 50 continue +c +c %----------------------------------% +c | Back from reverse communication; | +c | WORKD(IRJ:IRJ+N-1) := OP*v_{j} | +c | if step3 = .true. | +c %----------------------------------% +c + call igraphsecond (t3) + tmvopx = tmvopx + (t3 - t2) + + step3 = .false. +c +c %------------------------------------------% +c | Put another copy of OP*v_{j} into RESID. | +c %------------------------------------------% +c + call dcopy (n, workd(irj), 1, resid, 1) +c +c %---------------------------------------% +c | STEP 4: Finish extending the Arnoldi | +c | factorization to length j. | +c %---------------------------------------% +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + step4 = .true. + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %-------------------------------------% +c | Exit in order to compute B*OP*v_{j} | +c %-------------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 60 continue +c +c %----------------------------------% +c | Back from reverse communication; | +c | WORKD(IPJ:IPJ+N-1) := B*OP*v_{j} | +c | if step4 = .true. | +c %----------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + step4 = .false. +c +c %-------------------------------------% +c | The following is needed for STEP 5. | +c | Compute the B-norm of OP*v_{j}. | +c %-------------------------------------% +c + if (bmat .eq. 'G') then + wnorm = ddot (n, resid, 1, workd(ipj), 1) + wnorm = sqrt(abs(wnorm)) + else if (bmat .eq. 'I') then + wnorm = dnrm2(n, resid, 1) + end if +c +c %-----------------------------------------% +c | Compute the j-th residual corresponding | +c | to the j step factorization. | +c | Use Classical Gram Schmidt and compute: | +c | w_{j} <- V_{j}^T * B * OP * v_{j} | +c | r_{j} <- OP*v_{j} - V_{j} * w_{j} | +c %-----------------------------------------% +c +c +c %------------------------------------------% +c | Compute the j Fourier coefficients w_{j} | +c | WORKD(IPJ:IPJ+N-1) contains B*OP*v_{j}. | +c %------------------------------------------% +c + call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, + & zero, h(1,j), 1) +c +c %--------------------------------------% +c | Orthogonalize r_{j} against V_{j}. | +c | RESID contains OP*v_{j}. See STEP 3. | +c %--------------------------------------% +c + call dgemv ('N', n, j, -one, v, ldv, h(1,j), 1, + & one, resid, 1) +c + if (j .gt. 1) h(j,j-1) = betaj +c + call igraphsecond (t4) +c + orth1 = .true. +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(irj), 1) + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %----------------------------------% +c | Exit in order to compute B*r_{j} | +c %----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 70 continue +c +c %---------------------------------------------------% +c | Back from reverse communication if ORTH1 = .true. | +c | WORKD(IPJ:IPJ+N-1) := B*r_{j}. | +c %---------------------------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + orth1 = .false. +c +c %------------------------------% +c | Compute the B-norm of r_{j}. | +c %------------------------------% +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd(ipj), 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if +c +c %-----------------------------------------------------------% +c | STEP 5: Re-orthogonalization / Iterative refinement phase | +c | Maximum NITER_ITREF tries. | +c | | +c | s = V_{j}^T * B * r_{j} | +c | r_{j} = r_{j} - V_{j}*s | +c | alphaj = alphaj + s_{j} | +c | | +c | The stopping criteria used for iterative refinement is | +c | discussed in Parlett's book SEP, page 107 and in Gragg & | +c | Reichel ACM TOMS paper; Algorithm 686, Dec. 1990. | +c | Determine if we need to correct the residual. The goal is | +c | to enforce ||v(:,1:j)^T * r_{j}|| .le. eps * || r_{j} || | +c | The following test determines whether the sine of the | +c | angle between OP*x and the computed residual is less | +c | than or equal to 0.717. | +c %-----------------------------------------------------------% +c + if (rnorm .gt. 0.717*wnorm) go to 100 + iter = 0 + nrorth = nrorth + 1 +c +c %---------------------------------------------------% +c | Enter the Iterative refinement phase. If further | +c | refinement is necessary, loop back here. The loop | +c | variable is ITER. Perform a step of Classical | +c | Gram-Schmidt using all the Arnoldi vectors V_{j} | +c %---------------------------------------------------% +c + 80 continue +c + if (msglvl .gt. 2) then + xtemp(1) = wnorm + xtemp(2) = rnorm + call igraphdvout (logfil, 2, xtemp, ndigit, + & '_naitr: re-orthonalization; wnorm and rnorm are') + call igraphdvout (logfil, j, h(1,j), ndigit, + & '_naitr: j-th column of H') + end if +c +c %----------------------------------------------------% +c | Compute V_{j}^T * B * r_{j}. | +c | WORKD(IRJ:IRJ+J-1) = v(:,1:J)'*WORKD(IPJ:IPJ+N-1). | +c %----------------------------------------------------% +c + call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, + & zero, workd(irj), 1) +c +c %---------------------------------------------% +c | Compute the correction to the residual: | +c | r_{j} = r_{j} - V_{j} * WORKD(IRJ:IRJ+J-1). | +c | The correction to H is v(:,1:J)*H(1:J,1:J) | +c | + v(:,1:J)*WORKD(IRJ:IRJ+J-1)*e'_j. | +c %---------------------------------------------% +c + call dgemv ('N', n, j, -one, v, ldv, workd(irj), 1, + & one, resid, 1) + call daxpy (j, one, workd(irj), 1, h(1,j), 1) +c + orth2 = .true. + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(irj), 1) + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %-----------------------------------% +c | Exit in order to compute B*r_{j}. | +c | r_{j} is the corrected residual. | +c %-----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 90 continue +c +c %---------------------------------------------------% +c | Back from reverse communication if ORTH2 = .true. | +c %---------------------------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c +c %-----------------------------------------------------% +c | Compute the B-norm of the corrected residual r_{j}. | +c %-----------------------------------------------------% +c + if (bmat .eq. 'G') then + rnorm1 = ddot (n, resid, 1, workd(ipj), 1) + rnorm1 = sqrt(abs(rnorm1)) + else if (bmat .eq. 'I') then + rnorm1 = dnrm2(n, resid, 1) + end if +c + if (msglvl .gt. 0 .and. iter .gt. 0) then + call igraphivout (logfil, 1, [j], ndigit, + & '_naitr: Iterative refinement for Arnoldi residual') + if (msglvl .gt. 2) then + xtemp(1) = rnorm + xtemp(2) = rnorm1 + call igraphdvout (logfil, 2, xtemp, ndigit, + & '_naitr: iterative refinement ; rnorm and rnorm1 are') + end if + end if +c +c %-----------------------------------------% +c | Determine if we need to perform another | +c | step of re-orthogonalization. | +c %-----------------------------------------% +c + if (rnorm1 .gt. 0.717*rnorm) then +c +c %---------------------------------------% +c | No need for further refinement. | +c | The cosine of the angle between the | +c | corrected residual vector and the old | +c | residual vector is greater than 0.717 | +c | In other words the corrected residual | +c | and the old residual vector share an | +c | angle of less than arcCOS(0.717) | +c %---------------------------------------% +c + rnorm = rnorm1 +c + else +c +c %-------------------------------------------% +c | Another step of iterative refinement step | +c | is required. NITREF is used by stat.h | +c %-------------------------------------------% +c + nitref = nitref + 1 + rnorm = rnorm1 + iter = iter + 1 + if (iter .le. 1) go to 80 +c +c %-------------------------------------------------% +c | Otherwise RESID is numerically in the span of V | +c %-------------------------------------------------% +c + do 95 jj = 1, n + resid(jj) = zero + 95 continue + rnorm = zero + end if +c +c %----------------------------------------------% +c | Branch here directly if iterative refinement | +c | wasn't necessary or after at most NITER_REF | +c | steps of iterative refinement. | +c %----------------------------------------------% +c + 100 continue +c + rstart = .false. + orth2 = .false. +c + call igraphsecond (t5) + titref = titref + (t5 - t4) +c +c %------------------------------------% +c | STEP 6: Update j = j+1; Continue | +c %------------------------------------% +c + j = j + 1 + if (j .gt. k+np) then + call igraphsecond (t1) + tnaitr = tnaitr + (t1 - t0) + ido = 99 + do 110 i = max(1,k), k+np-1 +c +c %--------------------------------------------% +c | Check for splitting and deflation. | +c | Use a standard test as in the QR algorithm | +c | REFERENCE: LAPACK subroutine dlahqr | +c %--------------------------------------------% +c + tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) + if( tst1.eq.zero ) + & tst1 = dlanhs( '1', k+np, h, ldh, workd(n+1) ) + if( abs( h( i+1,i ) ).le.max( ulp*tst1, smlnum ) ) + & h(i+1,i) = zero + 110 continue +c + if (msglvl .gt. 2) then + call igraphdmout (logfil, k+np, k+np, h, ldh, ndigit, + & '_naitr: Final upper Hessenberg matrix H of order K+NP') + end if +c + go to 9000 + end if +c +c %--------------------------------------------------------% +c | Loop back to extend the factorization by another step. | +c %--------------------------------------------------------% +c + go to 1000 +c +c %---------------------------------------------------------------% +c | | +c | E N D O F M A I N I T E R A T I O N L O O P | +c | | +c %---------------------------------------------------------------% +c + 9000 continue + return +c +c %---------------% +c | End of igraphdnaitr | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dnapps.f b/src/rigraph/vendor/arpack/dnapps.f new file mode 100644 index 0000000..247e66b --- /dev/null +++ b/src/rigraph/vendor/arpack/dnapps.f @@ -0,0 +1,647 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdnapps +c +c\Description: +c Given the Arnoldi factorization +c +c A*V_{k} - V_{k}*H_{k} = r_{k+p}*e_{k+p}^T, +c +c apply NP implicit shifts resulting in +c +c A*(V_{k}*Q) - (V_{k}*Q)*(Q^T* H_{k}*Q) = r_{k+p}*e_{k+p}^T * Q +c +c where Q is an orthogonal matrix which is the product of rotations +c and reflections resulting from the NP bulge chage sweeps. +c The updated Arnoldi factorization becomes: +c +c A*VNEW_{k} - VNEW_{k}*HNEW_{k} = rnew_{k}*e_{k}^T. +c +c\Usage: +c call igraphdnapps +c ( N, KEV, NP, SHIFTR, SHIFTI, V, LDV, H, LDH, RESID, Q, LDQ, +c WORKL, WORKD ) +c +c\Arguments +c N Integer. (INPUT) +c Problem size, i.e. size of matrix A. +c +c KEV Integer. (INPUT/OUTPUT) +c KEV+NP is the size of the input matrix H. +c KEV is the size of the updated matrix HNEW. KEV is only +c updated on ouput when fewer than NP shifts are applied in +c order to keep the conjugate pair together. +c +c NP Integer. (INPUT) +c Number of implicit shifts to be applied. +c +c SHIFTR, Double precision array of length NP. (INPUT) +c SHIFTI Real and imaginary part of the shifts to be applied. +c Upon, entry to igraphdnapps, the shifts must be sorted so that the +c conjugate pairs are in consecutive locations. +c +c V Double precision N by (KEV+NP) array. (INPUT/OUTPUT) +c On INPUT, V contains the current KEV+NP Arnoldi vectors. +c On OUTPUT, V contains the updated KEV Arnoldi vectors +c in the first KEV columns of V. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (KEV+NP) by (KEV+NP) array. (INPUT/OUTPUT) +c On INPUT, H contains the current KEV+NP by KEV+NP upper +c Hessenber matrix of the Arnoldi factorization. +c On OUTPUT, H contains the updated KEV by KEV upper Hessenberg +c matrix in the KEV leading submatrix. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT, RESID contains the the residual vector r_{k+p}. +c On OUTPUT, RESID is the update residual vector rnew_{k} +c in the first KEV locations. +c +c Q Double precision KEV+NP by KEV+NP work array. (WORKSPACE) +c Work array used to accumulate the rotations and reflections +c during the bulge chase sweep. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKL Double precision work array of length (KEV+NP). (WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. +c +c WORKD Double precision work array of length 2*N. (WORKSPACE) +c Distributed array used in the application of the accumulated +c orthogonal matrix Q. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c +c\Routines called: +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdmout ARPACK utility routine that prints matrices. +c igraphdvout ARPACK utility routine that prints vectors. +c dlabad LAPACK routine that computes machine constants. +c dlacpy LAPACK matrix copy routine. +c dlamch LAPACK routine that determines machine constants. +c dlanhs LAPACK routine that computes various norms of a matrix. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dlarf LAPACK routine that applies Householder reflection to +c a matrix. +c dlarfg LAPACK Householder reflection construction routine. +c dlartg LAPACK Givens rotation construction routine. +c dlaset LAPACK matrix initialization routine. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c daxpy Level 1 BLAS that computes a vector triad. +c dcopy Level 1 BLAS that copies one vector to another . +c dscal Level 1 BLAS that scales a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: napps.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\Remarks +c 1. In this version, each shift is applied to all the sublocks of +c the Hessenberg matrix H and not just to the submatrix that it +c comes from. Deflation as in LAPACK routine dlahqr (QR algorithm +c for upper Hessenberg matrices ) is used. +c The subdiagonals of H are enforced to be non-negative. +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnapps + & ( n, kev, np, shiftr, shifti, v, ldv, h, ldh, resid, q, ldq, + & workl, workd ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer kev, ldh, ldq, ldv, n, np +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & h(ldh,kev+np), resid(n), shifti(np), shiftr(np), + & v(ldv,kev+np), q(ldq,kev+np), workd(2*n), workl(kev+np) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %------------------------% +c | Local Scalars & Arrays | +c %------------------------% +c + integer i, iend, ir, istart, j, jj, kplusp, msglvl, nr + logical cconj, first + Double precision + & c, f, g, h11, h12, h21, h22, h32, ovfl, r, s, sigmai, + & sigmar, smlnum, ulp, unfl, u(3), t, tau, tst1 + save first, ovfl, smlnum, ulp, unfl +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external daxpy, dcopy, dscal, dlacpy, dlarfg, dlarf, + & dlaset, dlabad, igraphsecond, dlartg +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch, dlanhs, dlapy2 + external dlamch, dlanhs, dlapy2 +c +c %----------------------% +c | Intrinsics Functions | +c %----------------------% +c + intrinsic abs, max, min +c +c %----------------% +c | Data statments | +c %----------------% +c + data first / .true. / +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (first) then +c +c %-----------------------------------------------% +c | Set machine-dependent constants for the | +c | stopping criterion. If norm(H) <= sqrt(OVFL), | +c | overflow should not occur. | +c | REFERENCE: LAPACK subroutine dlahqr | +c %-----------------------------------------------% +c + unfl = dlamch( 'safe minimum' ) + ovfl = one / unfl + call dlabad( unfl, ovfl ) + ulp = dlamch( 'precision' ) + smlnum = unfl*( n / ulp ) + first = .false. + end if +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mnapps + kplusp = kev + np +c +c %--------------------------------------------% +c | Initialize Q to the identity to accumulate | +c | the rotations and reflections | +c %--------------------------------------------% +c + call dlaset ('All', kplusp, kplusp, zero, one, q, ldq) +c +c %----------------------------------------------% +c | Quick return if there are no shifts to apply | +c %----------------------------------------------% +c + if (np .eq. 0) go to 9000 +c +c %----------------------------------------------% +c | Chase the bulge with the application of each | +c | implicit shift. Each shift is applied to the | +c | whole matrix including each block. | +c %----------------------------------------------% +c + cconj = .false. + do 110 jj = 1, np + sigmar = shiftr(jj) + sigmai = shifti(jj) +c + if (msglvl .gt. 2 ) then + call igraphivout (logfil, 1, [jj], ndigit, + & '_napps: shift number.') + call igraphdvout (logfil, 1, [sigmar], ndigit, + & '_napps: The real part of the shift ') + call igraphdvout (logfil, 1, [sigmai], ndigit, + & '_napps: The imaginary part of the shift ') + end if +c +c %-------------------------------------------------% +c | The following set of conditionals is necessary | +c | in order that complex conjugate pairs of shifts | +c | are applied together or not at all. | +c %-------------------------------------------------% +c + if ( cconj ) then +c +c %-----------------------------------------% +c | cconj = .true. means the previous shift | +c | had non-zero imaginary part. | +c %-----------------------------------------% +c + cconj = .false. + go to 110 + else if ( jj .lt. np .and. abs( sigmai ) .gt. zero ) then +c +c %------------------------------------% +c | Start of a complex conjugate pair. | +c %------------------------------------% +c + cconj = .true. + else if ( jj .eq. np .and. abs( sigmai ) .gt. zero ) then +c +c %----------------------------------------------% +c | The last shift has a nonzero imaginary part. | +c | Don't apply it; thus the order of the | +c | compressed H is order KEV+1 since only np-1 | +c | were applied. | +c %----------------------------------------------% +c + kev = kev + 1 + go to 110 + end if + istart = 1 + 20 continue +c +c %--------------------------------------------------% +c | if sigmai = 0 then | +c | Apply the jj-th shift ... | +c | else | +c | Apply the jj-th and (jj+1)-th together ... | +c | (Note that jj < np at this point in the code) | +c | end | +c | to the current block of H. The next do loop | +c | determines the current block ; | +c %--------------------------------------------------% +c + do 30 i = istart, kplusp-1 +c +c %----------------------------------------% +c | Check for splitting and deflation. Use | +c | a standard test as in the QR algorithm | +c | REFERENCE: LAPACK subroutine dlahqr | +c %----------------------------------------% +c + tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) + if( tst1.eq.zero ) + & tst1 = dlanhs( '1', kplusp-jj+1, h, ldh, workl ) + if( abs( h( i+1,i ) ).le.max( ulp*tst1, smlnum ) ) then + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [i], ndigit, + & '_napps: matrix splitting at row/column no.') + call igraphivout (logfil, 1, [jj], ndigit, + & '_napps: matrix splitting with shift number.') + call igraphdvout (logfil, 1, h(i+1,i), ndigit, + & '_napps: off diagonal element.') + end if + iend = i + h(i+1,i) = zero + go to 40 + end if + 30 continue + iend = kplusp + 40 continue +c + if (msglvl .gt. 2) then + call igraphivout (logfil, 1, [istart], ndigit, + & '_napps: Start of current block ') + call igraphivout (logfil, 1, [iend], ndigit, + & '_napps: End of current block ') + end if +c +c %------------------------------------------------% +c | No reason to apply a shift to block of order 1 | +c %------------------------------------------------% +c + if ( istart .eq. iend ) go to 100 +c +c %------------------------------------------------------% +c | If istart + 1 = iend then no reason to apply a | +c | complex conjugate pair of shifts on a 2 by 2 matrix. | +c %------------------------------------------------------% +c + if ( istart + 1 .eq. iend .and. abs( sigmai ) .gt. zero ) + & go to 100 +c + h11 = h(istart,istart) + h21 = h(istart+1,istart) + if ( abs( sigmai ) .le. zero ) then +c +c %---------------------------------------------% +c | Real-valued shift ==> apply single shift QR | +c %---------------------------------------------% +c + f = h11 - sigmar + g = h21 +c + do 80 i = istart, iend-1 +c +c %-----------------------------------------------------% +c | Contruct the plane rotation G to zero out the bulge | +c %-----------------------------------------------------% +c + call dlartg (f, g, c, s, r) + if (i .gt. istart) then +c +c %-------------------------------------------% +c | The following ensures that h(1:iend-1,1), | +c | the first iend-2 off diagonal of elements | +c | H, remain non negative. | +c %-------------------------------------------% +c + if (r .lt. zero) then + r = -r + c = -c + s = -s + end if + h(i,i-1) = r + h(i+1,i-1) = zero + end if +c +c %---------------------------------------------% +c | Apply rotation to the left of H; H <- G'*H | +c %---------------------------------------------% +c + do 50 j = i, kplusp + t = c*h(i,j) + s*h(i+1,j) + h(i+1,j) = -s*h(i,j) + c*h(i+1,j) + h(i,j) = t + 50 continue +c +c %---------------------------------------------% +c | Apply rotation to the right of H; H <- H*G | +c %---------------------------------------------% +c + do 60 j = 1, min(i+2,iend) + t = c*h(j,i) + s*h(j,i+1) + h(j,i+1) = -s*h(j,i) + c*h(j,i+1) + h(j,i) = t + 60 continue +c +c %----------------------------------------------------% +c | Accumulate the rotation in the matrix Q; Q <- Q*G | +c %----------------------------------------------------% +c + do 70 j = 1, min( j+jj, kplusp ) + t = c*q(j,i) + s*q(j,i+1) + q(j,i+1) = - s*q(j,i) + c*q(j,i+1) + q(j,i) = t + 70 continue +c +c %---------------------------% +c | Prepare for next rotation | +c %---------------------------% +c + if (i .lt. iend-1) then + f = h(i+1,i) + g = h(i+2,i) + end if + 80 continue +c +c %-----------------------------------% +c | Finished applying the real shift. | +c %-----------------------------------% +c + else +c +c %----------------------------------------------------% +c | Complex conjugate shifts ==> apply double shift QR | +c %----------------------------------------------------% +c + h12 = h(istart,istart+1) + h22 = h(istart+1,istart+1) + h32 = h(istart+2,istart+1) +c +c %---------------------------------------------------------% +c | Compute 1st column of (H - shift*I)*(H - conj(shift)*I) | +c %---------------------------------------------------------% +c + s = 2.0*sigmar + t = dlapy2 ( sigmar, sigmai ) + u(1) = ( h11 * (h11 - s) + t * t ) / h21 + h12 + u(2) = h11 + h22 - s + u(3) = h32 +c + do 90 i = istart, iend-1 +c + nr = min ( 3, iend-i+1 ) +c +c %-----------------------------------------------------% +c | Construct Householder reflector G to zero out u(1). | +c | G is of the form I - tau*( 1 u )' * ( 1 u' ). | +c %-----------------------------------------------------% +c + call dlarfg ( nr, u(1), u(2), 1, tau ) +c + if (i .gt. istart) then + h(i,i-1) = u(1) + h(i+1,i-1) = zero + if (i .lt. iend-1) h(i+2,i-1) = zero + end if + u(1) = one +c +c %--------------------------------------% +c | Apply the reflector to the left of H | +c %--------------------------------------% +c + call dlarf ('Left', nr, kplusp-i+1, u, 1, tau, + & h(i,i), ldh, workl) +c +c %---------------------------------------% +c | Apply the reflector to the right of H | +c %---------------------------------------% +c + ir = min ( i+3, iend ) + call dlarf ('Right', ir, nr, u, 1, tau, + & h(1,i), ldh, workl) +c +c %-----------------------------------------------------% +c | Accumulate the reflector in the matrix Q; Q <- Q*G | +c %-----------------------------------------------------% +c + call dlarf ('Right', kplusp, nr, u, 1, tau, + & q(1,i), ldq, workl) +c +c %----------------------------% +c | Prepare for next reflector | +c %----------------------------% +c + if (i .lt. iend-1) then + u(1) = h(i+1,i) + u(2) = h(i+2,i) + if (i .lt. iend-2) u(3) = h(i+3,i) + end if +c + 90 continue +c +c %--------------------------------------------% +c | Finished applying a complex pair of shifts | +c | to the current block | +c %--------------------------------------------% +c + end if +c + 100 continue +c +c %---------------------------------------------------------% +c | Apply the same shift to the next block if there is any. | +c %---------------------------------------------------------% +c + istart = iend + 1 + if (iend .lt. kplusp) go to 20 +c +c %---------------------------------------------% +c | Loop back to the top to get the next shift. | +c %---------------------------------------------% +c + 110 continue +c +c %--------------------------------------------------% +c | Perform a similarity transformation that makes | +c | sure that H will have non negative sub diagonals | +c %--------------------------------------------------% +c + do 120 j=1,kev + if ( h(j+1,j) .lt. zero ) then + call dscal( kplusp-j+1, -one, h(j+1,j), ldh ) + call dscal( min(j+2, kplusp), -one, h(1,j+1), 1 ) + call dscal( min(j+np+1,kplusp), -one, q(1,j+1), 1 ) + end if + 120 continue +c + do 130 i = 1, kev +c +c %--------------------------------------------% +c | Final check for splitting and deflation. | +c | Use a standard test as in the QR algorithm | +c | REFERENCE: LAPACK subroutine dlahqr | +c %--------------------------------------------% +c + tst1 = abs( h( i, i ) ) + abs( h( i+1, i+1 ) ) + if( tst1.eq.zero ) + & tst1 = dlanhs( '1', kev, h, ldh, workl ) + if( h( i+1,i ) .le. max( ulp*tst1, smlnum ) ) + & h(i+1,i) = zero + 130 continue +c +c %-------------------------------------------------% +c | Compute the (kev+1)-st column of (V*Q) and | +c | temporarily store the result in WORKD(N+1:2*N). | +c | This is needed in the residual update since we | +c | cannot GUARANTEE that the corresponding entry | +c | of H would be zero as in exact arithmetic. | +c %-------------------------------------------------% +c + if (h(kev+1,kev) .gt. zero) + & call dgemv ('N', n, kplusp, one, v, ldv, q(1,kev+1), 1, zero, + & workd(n+1), 1) +c +c %----------------------------------------------------------% +c | Compute column 1 to kev of (V*Q) in backward order | +c | taking advantage of the upper Hessenberg structure of Q. | +c %----------------------------------------------------------% +c + do 140 i = 1, kev + call dgemv ('N', n, kplusp-i+1, one, v, ldv, + & q(1,kev-i+1), 1, zero, workd, 1) + call dcopy (n, workd, 1, v(1,kplusp-i+1), 1) + 140 continue +c +c %-------------------------------------------------% +c | Move v(:,kplusp-kev+1:kplusp) into v(:,1:kev). | +c %-------------------------------------------------% +c + call dlacpy ('A', n, kev, v(1,kplusp-kev+1), ldv, v, ldv) +c +c %--------------------------------------------------------------% +c | Copy the (kev+1)-st column of (V*Q) in the appropriate place | +c %--------------------------------------------------------------% +c + if (h(kev+1,kev) .gt. zero) + & call dcopy (n, workd(n+1), 1, v(1,kev+1), 1) +c +c %-------------------------------------% +c | Update the residual vector: | +c | r <- sigmak*r + betak*v(:,kev+1) | +c | where | +c | sigmak = (e_{kplusp}'*Q)*e_{kev} | +c | betak = e_{kev+1}'*H*e_{kev} | +c %-------------------------------------% +c + call dscal (n, q(kplusp,kev), resid, 1) + if (h(kev+1,kev) .gt. zero) + & call daxpy (n, h(kev+1,kev), v(1,kev+1), 1, resid, 1) +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, 1, q(kplusp,kev), ndigit, + & '_napps: sigmak = (e_{kev+p}^T*Q)*e_{kev}') + call igraphdvout (logfil, 1, h(kev+1,kev), ndigit, + & '_napps: betak = e_{kev+1}^T*H*e_{kev}') + call igraphivout (logfil, 1, [kev], ndigit, + & '_napps: Order of the final Hessenberg matrix ') + if (msglvl .gt. 2) then + call igraphdmout (logfil, kev, kev, h, ldh, ndigit, + & '_napps: updated Hessenberg matrix H for next iteration') + end if +c + end if +c + 9000 continue + call igraphsecond (t1) + tnapps = tnapps + (t1 - t0) +c + return +c +c %---------------% +c | End of igraphdnapps | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dnaup2.f b/src/rigraph/vendor/arpack/dnaup2.f new file mode 100644 index 0000000..e060689 --- /dev/null +++ b/src/rigraph/vendor/arpack/dnaup2.f @@ -0,0 +1,838 @@ +c\BeginDoc +c +c\Name: igraphdnaup2 +c +c\Description: +c Intermediate level interface called by igraphdnaupd. +c +c\Usage: +c call igraphdnaup2 +c ( IDO, BMAT, N, WHICH, NEV, NP, TOL, RESID, MODE, IUPD, +c ISHIFT, MXITER, V, LDV, H, LDH, RITZR, RITZI, BOUNDS, +c Q, LDQ, WORKL, IPNTR, WORKD, INFO ) +c +c\Arguments +c +c IDO, BMAT, N, WHICH, NEV, TOL, RESID: same as defined in igraphdnaupd. +c MODE, ISHIFT, MXITER: see the definition of IPARAM in igraphdnaupd. +c +c NP Integer. (INPUT/OUTPUT) +c Contains the number of implicit shifts to apply during +c each Arnoldi iteration. +c If ISHIFT=1, NP is adjusted dynamically at each iteration +c to accelerate convergence and prevent stagnation. +c This is also roughly equal to the number of matrix-vector +c products (involving the operator OP) per Arnoldi iteration. +c The logic for adjusting is contained within the current +c subroutine. +c If ISHIFT=0, NP is the number of shifts the user needs +c to provide via reverse comunication. 0 < NP < NCV-NEV. +c NP may be less than NCV-NEV for two reasons. The first, is +c to keep complex conjugate pairs of "wanted" Ritz values +c together. The igraphsecond, is that a leading block of the current +c upper Hessenberg matrix has split off and contains "unwanted" +c Ritz values. +c Upon termination of the IRA iteration, NP contains the number +c of "converged" wanted Ritz values. +c +c IUPD Integer. (INPUT) +c IUPD .EQ. 0: use explicit restart instead implicit update. +c IUPD .NE. 0: use implicit update. +c +c V Double precision N by (NEV+NP) array. (INPUT/OUTPUT) +c The Arnoldi basis vectors are returned in the first NEV +c columns of V. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (NEV+NP) by (NEV+NP) array. (OUTPUT) +c H is used to store the generated upper Hessenberg matrix +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RITZR, Double precision arrays of length NEV+NP. (OUTPUT) +c RITZI RITZR(1:NEV) (resp. RITZI(1:NEV)) contains the real (resp. +c imaginary) part of the computed Ritz values of OP. +c +c BOUNDS Double precision array of length NEV+NP. (OUTPUT) +c BOUNDS(1:NEV) contain the error bounds corresponding to +c the computed Ritz values. +c +c Q Double precision (NEV+NP) by (NEV+NP) array. (WORKSPACE) +c Private (replicated) work array used to accumulate the +c rotation in the shift application step. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKL Double precision work array of length at least +c (NEV+NP)**2 + 3*(NEV+NP). (INPUT/WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. It is used in shifts calculation, shifts +c application and convergence checking. +c +c On exit, the last 3*(NEV+NP) locations of WORKL contain +c the Ritz values (real,imaginary) and associated Ritz +c estimates of the current Hessenberg matrix. They are +c listed in the same order as returned from igraphdneigh. +c +c If ISHIFT .EQ. O and IDO .EQ. 3, the first 2*NP locations +c of WORKL are used in reverse communication to hold the user +c supplied shifts. +c +c IPNTR Integer array of length 3. (OUTPUT) +c Pointer to mark the starting locations in the WORKD for +c vectors used by the Arnoldi iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X. +c IPNTR(2): pointer to the current result vector Y. +c IPNTR(3): pointer to the vector B * X when used in the +c shift-and-invert mode. X is the current operand. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (WORKSPACE) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The user should not use WORKD +c as temporary workspace during the iteration !!!!!!!!!! +c See Data Distribution Note in DNAUPD. +c +c INFO Integer. (INPUT/OUTPUT) +c If INFO .EQ. 0, a randomly initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c Error flag on output. +c = 0: Normal return. +c = 1: Maximum number of iterations taken. +c All possible eigenvalues of OP has been found. +c NP returns the number of converged Ritz values. +c = 2: No shifts could be applied. +c = -8: Error return from LAPACK eigenvalue calculation; +c This should never happen. +c = -9: Starting vector is zero. +c = -9999: Could not build an Arnoldi factorization. +c Size that was built in returned in NP. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c +c\Routines called: +c igraphdgetv0 ARPACK initial vector generation routine. +c igraphdnaitr ARPACK Arnoldi factorization routine. +c igraphdnapps ARPACK application of implicit shifts routine. +c igraphdnconv ARPACK convergence of Ritz values routine. +c igraphdneigh ARPACK compute Ritz values and error bounds routine. +c igraphdngets ARPACK reorder Ritz values and error bounds routine. +c igraphdsortc ARPACK sorting routine. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdmout ARPACK utility routine that prints matrices +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dcopy Level 1 BLAS that copies one vector to another . +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dswap Level 1 BLAS that swaps two vectors. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: naup2.F SID: 2.4 DATE OF SID: 7/30/96 RELEASE: 2 +c +c\Remarks +c 1. None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnaup2 + & ( ido, bmat, n, which, nev, np, tol, resid, mode, iupd, + & ishift, mxiter, v, ldv, h, ldh, ritzr, ritzi, bounds, + & q, ldq, workl, ipntr, workd, info ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1, which*2 + integer ido, info, ishift, iupd, mode, ldh, ldq, ldv, mxiter, + & n, nev, np + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(13) + Double precision + & bounds(nev+np), h(ldh,nev+np), q(ldq,nev+np), resid(n), + & ritzi(nev+np), ritzr(nev+np), v(ldv,nev+np), + & workd(3*n), workl( (nev+np)*(nev+np+3) ) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + character wprime*2 + logical cnorm, getv0, initv, update, ushift + integer ierr, iter, j, kplusp, msglvl, nconv, nevbef, nev0, + & np0, nptemp, numcnv + Double precision + & rnorm, temp, eps23 +c +c %-----------------------% +c | Local array arguments | +c %-----------------------% +c + integer kp(4) + save +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, igraphdgetv0, igraphdnaitr, igraphdnconv, + & igraphdneigh, igraphdngets, igraphdnapps, + & igraphdvout, igraphivout, igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2, dlapy2, dlamch + external ddot, dnrm2, dlapy2, dlamch +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic min, max, abs, sqrt +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (ido .eq. 0) then +c + call igraphsecond (t0) +c + msglvl = mnaup2 +c +c %-------------------------------------% +c | Get the machine dependent constant. | +c %-------------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c + nev0 = nev + np0 = np +c +c %-------------------------------------% +c | kplusp is the bound on the largest | +c | Lanczos factorization built. | +c | nconv is the current number of | +c | "converged" eigenvlues. | +c | iter is the counter on the current | +c | iteration step. | +c %-------------------------------------% +c + kplusp = nev + np + nconv = 0 + iter = 0 +c +c %---------------------------------------% +c | Set flags for computing the first NEV | +c | steps of the Arnoldi factorization. | +c %---------------------------------------% +c + getv0 = .true. + update = .false. + ushift = .false. + cnorm = .false. +c + if (info .ne. 0) then +c +c %--------------------------------------------% +c | User provides the initial residual vector. | +c %--------------------------------------------% +c + initv = .true. + info = 0 + else + initv = .false. + end if + end if +c +c %---------------------------------------------% +c | Get a possibly random starting vector and | +c | force it into the range of the operator OP. | +c %---------------------------------------------% +c + 10 continue +c + if (getv0) then + call igraphdgetv0 (ido, bmat, 1, initv, n, 1, v, ldv, resid, + & rnorm, ipntr, workd, info) +c + if (ido .ne. 99) go to 9000 +c + if (rnorm .eq. zero) then +c +c %-----------------------------------------% +c | The initial vector is zero. Error exit. | +c %-----------------------------------------% +c + info = -9 + go to 1100 + end if + getv0 = .false. + ido = 0 + end if +c +c %-----------------------------------% +c | Back from reverse communication : | +c | continue with update step | +c %-----------------------------------% +c + if (update) go to 20 +c +c %-------------------------------------------% +c | Back from computing user specified shifts | +c %-------------------------------------------% +c + if (ushift) go to 50 +c +c %-------------------------------------% +c | Back from computing residual norm | +c | at the end of the current iteration | +c %-------------------------------------% +c + if (cnorm) go to 100 +c +c %----------------------------------------------------------% +c | Compute the first NEV steps of the Arnoldi factorization | +c %----------------------------------------------------------% +c + call igraphdnaitr (ido, bmat, n, 0, nev, mode, resid, rnorm, v, + & ldv, h, ldh, ipntr, workd, info) +c +c %---------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP and possibly B | +c %---------------------------------------------------% +c + if (ido .ne. 99) go to 9000 +c + if (info .gt. 0) then + np = info + mxiter = iter + info = -9999 + go to 1200 + end if +c +c %--------------------------------------------------------------% +c | | +c | M A I N ARNOLDI I T E R A T I O N L O O P | +c | Each iteration implicitly restarts the Arnoldi | +c | factorization in place. | +c | | +c %--------------------------------------------------------------% +c + 1000 continue +c + iter = iter + 1 +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [iter], ndigit, + & '_naup2: **** Start of major iteration number ****') + end if +c +c %-----------------------------------------------------------% +c | Compute NP additional steps of the Arnoldi factorization. | +c | Adjust NP since NEV might have been updated by last call | +c | to the shift application routine igraphdnapps. | +c %-----------------------------------------------------------% +c + np = kplusp - nev +c + if (msglvl .gt. 1) then + call igraphivout (logfil, 1, [nev], ndigit, + & '_naup2: The length of the current Arnoldi factorization') + call igraphivout (logfil, 1, [np], ndigit, + & '_naup2: Extend the Arnoldi factorization by') + end if +c +c %-----------------------------------------------------------% +c | Compute NP additional steps of the Arnoldi factorization. | +c %-----------------------------------------------------------% +c + ido = 0 + 20 continue + update = .true. +c + call igraphdnaitr (ido, bmat, n, nev, np, mode, resid, rnorm, + & v, ldv, h, ldh, ipntr, workd, info) +c +c %---------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP and possibly B | +c %---------------------------------------------------% +c + if (ido .ne. 99) go to 9000 +c + if (info .gt. 0) then + np = info + mxiter = iter + info = -9999 + go to 1200 + end if + update = .false. +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_naup2: Corresponding B-norm of the residual') + end if +c +c %--------------------------------------------------------% +c | Compute the eigenvalues and corresponding error bounds | +c | of the current upper Hessenberg matrix. | +c %--------------------------------------------------------% +c + call igraphdneigh (rnorm, kplusp, h, ldh, ritzr, ritzi, bounds, + & q, ldq, workl, ierr) +c + if (ierr .ne. 0) then + info = -8 + go to 1200 + end if +c +c %----------------------------------------------------% +c | Make a copy of eigenvalues and corresponding error | +c | bounds obtained from igraphdneigh. | +c %----------------------------------------------------% +c + call dcopy(kplusp, ritzr, 1, workl(kplusp**2+1), 1) + call dcopy(kplusp, ritzi, 1, workl(kplusp**2+kplusp+1), 1) + call dcopy(kplusp, bounds, 1, workl(kplusp**2+2*kplusp+1), 1) +c +c %---------------------------------------------------% +c | Select the wanted Ritz values and their bounds | +c | to be used in the convergence test. | +c | The wanted part of the spectrum and corresponding | +c | error bounds are in the last NEV loc. of RITZR, | +c | RITZI and BOUNDS respectively. The variables NEV | +c | and NP may be updated if the NEV-th wanted Ritz | +c | value has a non zero imaginary part. In this case | +c | NEV is increased by one and NP decreased by one. | +c | NOTE: The last two arguments of igraphdngets are no | +c | longer used as of version 2.1. | +c %---------------------------------------------------% +c + nev = nev0 + np = np0 + numcnv = nev + call igraphdngets (ishift, which, nev, np, ritzr, ritzi, + & bounds, workl, workl(np+1)) + if (nev .eq. nev0+1) numcnv = nev0+1 +c +c %-------------------% +c | Convergence test. | +c %-------------------% +c + call dcopy (nev, bounds(np+1), 1, workl(2*np+1), 1) + call igraphdnconv (nev, ritzr(np+1), ritzi(np+1), + & workl(2*np+1), tol, nconv) +c + if (msglvl .gt. 2) then + kp(1) = nev + kp(2) = np + kp(3) = numcnv + kp(4) = nconv + call igraphivout (logfil, 4, kp, ndigit, + & '_naup2: NEV, NP, NUMCNV, NCONV are') + call igraphdvout (logfil, kplusp, ritzr, ndigit, + & '_naup2: Real part of the eigenvalues of H') + call igraphdvout (logfil, kplusp, ritzi, ndigit, + & '_naup2: Imaginary part of the eigenvalues of H') + call igraphdvout (logfil, kplusp, bounds, ndigit, + & '_naup2: Ritz estimates of the current NCV Ritz values') + end if +c +c %---------------------------------------------------------% +c | Count the number of unwanted Ritz values that have zero | +c | Ritz estimates. If any Ritz estimates are equal to zero | +c | then a leading block of H of order equal to at least | +c | the number of Ritz values with zero Ritz estimates has | +c | split off. None of these Ritz values may be removed by | +c | shifting. Decrease NP the number of shifts to apply. If | +c | no shifts may be applied, then prepare to exit | +c %---------------------------------------------------------% +c + nptemp = np + do 30 j=1, nptemp + if (bounds(j) .eq. zero) then + np = np - 1 + nev = nev + 1 + end if + 30 continue +c + if ( (nconv .ge. numcnv) .or. + & (iter .gt. mxiter) .or. + & (np .eq. 0) ) then +c + if (msglvl .gt. 4) then + call igraphdvout(logfil, kplusp, workl(kplusp**2+1), + & ndigit, + & '_naup2: Real part of the eig computed by _neigh:') + call igraphdvout(logfil, kplusp, + & workl(kplusp**2+kplusp+1), ndigit, + & '_naup2: Imag part of the eig computed by _neigh:') + call igraphdvout(logfil, kplusp, + & workl(kplusp**2+kplusp*2+1), ndigit, + & '_naup2: Ritz eistmates computed by _neigh:') + end if +c +c %------------------------------------------------% +c | Prepare to exit. Put the converged Ritz values | +c | and corresponding bounds in RITZ(1:NCONV) and | +c | BOUNDS(1:NCONV) respectively. Then sort. Be | +c | careful when NCONV > NP | +c %------------------------------------------------% +c +c %------------------------------------------% +c | Use h( 3,1 ) as storage to communicate | +c | rnorm to _neupd if needed | +c %------------------------------------------% + + h(3,1) = rnorm +c +c %----------------------------------------------% +c | To be consistent with igraphdngets, we first do a | +c | pre-processing sort in order to keep complex | +c | conjugate pairs together. This is similar | +c | to the pre-processing sort used in igraphdngets | +c | except that the sort is done in the opposite | +c | order. | +c %----------------------------------------------% +c + if (which .eq. 'LM') wprime = 'SR' + if (which .eq. 'SM') wprime = 'LR' + if (which .eq. 'LR') wprime = 'SM' + if (which .eq. 'SR') wprime = 'LM' + if (which .eq. 'LI') wprime = 'SM' + if (which .eq. 'SI') wprime = 'LM' +c + call igraphdsortc (wprime, .true., kplusp, ritzr, ritzi, + & bounds) +c +c %----------------------------------------------% +c | Now sort Ritz values so that converged Ritz | +c | values appear within the first NEV locations | +c | of ritzr, ritzi and bounds, and the most | +c | desired one appears at the front. | +c %----------------------------------------------% +c + if (which .eq. 'LM') wprime = 'SM' + if (which .eq. 'SM') wprime = 'LM' + if (which .eq. 'LR') wprime = 'SR' + if (which .eq. 'SR') wprime = 'LR' + if (which .eq. 'LI') wprime = 'SI' + if (which .eq. 'SI') wprime = 'LI' +c + call igraphdsortc(wprime, .true., kplusp, ritzr, ritzi, + & bounds) +c +c %--------------------------------------------------% +c | Scale the Ritz estimate of each Ritz value | +c | by 1 / max(eps23,magnitude of the Ritz value). | +c %--------------------------------------------------% +c + do 35 j = 1, nev0 + temp = max(eps23,dlapy2(ritzr(j), + & ritzi(j))) + bounds(j) = bounds(j)/temp + 35 continue +c +c %----------------------------------------------------% +c | Sort the Ritz values according to the scaled Ritz | +c | esitmates. This will push all the converged ones | +c | towards the front of ritzr, ritzi, bounds | +c | (in the case when NCONV < NEV.) | +c %----------------------------------------------------% +c + wprime = 'LR' + call igraphdsortc(wprime, .true., nev0, bounds, ritzr, + & ritzi) +c +c %----------------------------------------------% +c | Scale the Ritz estimate back to its original | +c | value. | +c %----------------------------------------------% +c + do 40 j = 1, nev0 + temp = max(eps23, dlapy2(ritzr(j), + & ritzi(j))) + bounds(j) = bounds(j)*temp + 40 continue +c +c %------------------------------------------------% +c | Sort the converged Ritz values again so that | +c | the "threshold" value appears at the front of | +c | ritzr, ritzi and bound. | +c %------------------------------------------------% +c + call igraphdsortc(which, .true., nconv, ritzr, ritzi, + & bounds) +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, kplusp, ritzr, ndigit, + & '_naup2: Sorted real part of the eigenvalues') + call igraphdvout (logfil, kplusp, ritzi, ndigit, + & '_naup2: Sorted imaginary part of the eigenvalues') + call igraphdvout (logfil, kplusp, bounds, ndigit, + & '_naup2: Sorted ritz estimates.') + end if +c +c %------------------------------------% +c | Max iterations have been exceeded. | +c %------------------------------------% +c + if (iter .gt. mxiter .and. nconv .lt. numcnv) info = 1 +c +c %---------------------% +c | No shifts to apply. | +c %---------------------% +c + if (np .eq. 0 .and. nconv .lt. numcnv) info = 2 +c + np = nconv + go to 1100 +c + else if ( (nconv .lt. numcnv) .and. (ishift .eq. 1) ) then +c +c %-------------------------------------------------% +c | Do not have all the requested eigenvalues yet. | +c | To prevent possible stagnation, adjust the size | +c | of NEV. | +c %-------------------------------------------------% +c + nevbef = nev + nev = nev + min(nconv, np/2) + if (nev .eq. 1 .and. kplusp .ge. 6) then + nev = kplusp / 2 + else if (nev .eq. 1 .and. kplusp .gt. 3) then + nev = 2 + end if + np = kplusp - nev +c +c %---------------------------------------% +c | If the size of NEV was just increased | +c | resort the eigenvalues. | +c %---------------------------------------% +c + if (nevbef .lt. nev) + & call igraphdngets (ishift, which, nev, np, ritzr, ritzi, + & bounds, workl, workl(np+1)) +c + end if +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [nconv], ndigit, + & '_naup2: no. of "converged" Ritz values at this iter.') + if (msglvl .gt. 1) then + kp(1) = nev + kp(2) = np + call igraphivout (logfil, 2, kp, ndigit, + & '_naup2: NEV and NP are') + call igraphdvout (logfil, nev, ritzr(np+1), ndigit, + & '_naup2: "wanted" Ritz values -- real part') + call igraphdvout (logfil, nev, ritzi(np+1), ndigit, + & '_naup2: "wanted" Ritz values -- imag part') + call igraphdvout (logfil, nev, bounds(np+1), ndigit, + & '_naup2: Ritz estimates of the "wanted" values ') + end if + end if +c + if (ishift .eq. 0) then +c +c %-------------------------------------------------------% +c | User specified shifts: reverse comminucation to | +c | compute the shifts. They are returned in the first | +c | 2*NP locations of WORKL. | +c %-------------------------------------------------------% +c + ushift = .true. + ido = 3 + go to 9000 + end if +c + 50 continue +c +c %------------------------------------% +c | Back from reverse communication; | +c | User specified shifts are returned | +c | in WORKL(1:2*NP) | +c %------------------------------------% +c + ushift = .false. +c + if ( ishift .eq. 0 ) then +c +c %----------------------------------% +c | Move the NP shifts from WORKL to | +c | RITZR, RITZI to free up WORKL | +c | for non-exact shift case. | +c %----------------------------------% +c + call dcopy (np, workl, 1, ritzr, 1) + call dcopy (np, workl(np+1), 1, ritzi, 1) + end if +c + if (msglvl .gt. 2) then + call igraphivout (logfil, 1, [np], ndigit, + & '_naup2: The number of shifts to apply ') + call igraphdvout (logfil, np, ritzr, ndigit, + & '_naup2: Real part of the shifts') + call igraphdvout (logfil, np, ritzi, ndigit, + & '_naup2: Imaginary part of the shifts') + if ( ishift .eq. 1 ) + & call igraphdvout (logfil, np, bounds, ndigit, + & '_naup2: Ritz estimates of the shifts') + end if +c +c %---------------------------------------------------------% +c | Apply the NP implicit shifts by QR bulge chasing. | +c | Each shift is applied to the whole upper Hessenberg | +c | matrix H. | +c | The first 2*N locations of WORKD are used as workspace. | +c %---------------------------------------------------------% +c + call igraphdnapps (n, nev, np, ritzr, ritzi, v, ldv, + & h, ldh, resid, q, ldq, workl, workd) +c +c %---------------------------------------------% +c | Compute the B-norm of the updated residual. | +c | Keep B*RESID in WORKD(1:N) to be used in | +c | the first step of the next call to igraphdnaitr. | +c %---------------------------------------------% +c + cnorm = .true. + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(n+1), 1) + ipntr(1) = n + 1 + ipntr(2) = 1 + ido = 2 +c +c %----------------------------------% +c | Exit in order to compute B*RESID | +c %----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd, 1) + end if +c + 100 continue +c +c %----------------------------------% +c | Back from reverse communication; | +c | WORKD(1:N) := B*RESID | +c %----------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd, 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if + cnorm = .false. +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_naup2: B-norm of residual for compressed factorization') + call igraphdmout (logfil, nev, nev, h, ldh, ndigit, + & '_naup2: Compressed upper Hessenberg matrix H') + end if +c + go to 1000 +c +c %---------------------------------------------------------------% +c | | +c | E N D O F M A I N I T E R A T I O N L O O P | +c | | +c %---------------------------------------------------------------% +c + 1100 continue +c + mxiter = iter + nev = numcnv +c + 1200 continue + ido = 99 +c +c %------------% +c | Error Exit | +c %------------% +c + call igraphsecond (t1) + tnaup2 = t1 - t0 +c + 9000 continue +c +c %---------------% +c | End of igraphdnaup2 | +c %---------------% +c + return + end diff --git a/src/rigraph/vendor/arpack/dnaupd.f b/src/rigraph/vendor/arpack/dnaupd.f new file mode 100644 index 0000000..4133022 --- /dev/null +++ b/src/rigraph/vendor/arpack/dnaupd.f @@ -0,0 +1,655 @@ +c\BeginDoc +c +c\Name: igraphdnaupd +c +c\Description: +c Reverse communication interface for the Implicitly Restarted Arnoldi +c iteration. This subroutine computes approximations to a few eigenpairs +c of a linear operator "OP" with respect to a semi-inner product defined by +c a symmetric positive semi-definite real matrix B. B may be the identity +c matrix. NOTE: If the linear operator "OP" is real and symmetric +c with respect to the real positive semi-definite symmetric matrix B, +c i.e. B*OP = (OP')*B, then subroutine ssaupd should be used instead. +c +c The computed approximate eigenvalues are called Ritz values and +c the corresponding approximate eigenvectors are called Ritz vectors. +c +c igraphdnaupd is usually called iteratively to solve one of the +c following problems: +c +c Mode 1: A*x = lambda*x. +c ===> OP = A and B = I. +c +c Mode 2: A*x = lambda*M*x, M symmetric positive definite +c ===> OP = inv[M]*A and B = M. +c ===> (If M can be factored see remark 3 below) +c +c Mode 3: A*x = lambda*M*x, M symmetric semi-definite +c ===> OP = Real_Part{ inv[A - sigma*M]*M } and B = M. +c ===> shift-and-invert mode (in real arithmetic) +c If OP*x = amu*x, then +c amu = 1/2 * [ 1/(lambda-sigma) + 1/(lambda-conjg(sigma)) ]. +c Note: If sigma is real, i.e. imaginary part of sigma is zero; +c Real_Part{ inv[A - sigma*M]*M } == inv[A - sigma*M]*M +c amu == 1/(lambda-sigma). +c +c Mode 4: A*x = lambda*M*x, M symmetric semi-definite +c ===> OP = Imaginary_Part{ inv[A - sigma*M]*M } and B = M. +c ===> shift-and-invert mode (in real arithmetic) +c If OP*x = amu*x, then +c amu = 1/2i * [ 1/(lambda-sigma) - 1/(lambda-conjg(sigma)) ]. +c +c Both mode 3 and 4 give the same enhancement to eigenvalues close to +c the (complex) shift sigma. However, as lambda goes to infinity, +c the operator OP in mode 4 dampens the eigenvalues more strongly than +c does OP defined in mode 3. +c +c NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v +c should be accomplished either by a direct method +c using a sparse matrix factorization and solving +c +c [A - sigma*M]*w = v or M*w = v, +c +c or through an iterative method for solving these +c systems. If an iterative method is used, the +c convergence test must be more stringent than +c the accuracy requirements for the eigenvalue +c approximations. +c +c\Usage: +c call igraphdnaupd +c ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, +c IPNTR, WORKD, WORKL, LWORKL, INFO ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. IDO must be zero on the first +c call to igraphdnaupd. IDO will be set internally to +c indicate the type of operation to be performed. Control is +c then given back to the calling routine which has the +c responsibility to carry out the requested operation and call +c igraphdnaupd with the result. The operand is given in +c WORKD(IPNTR(1)), the result must be put in WORKD(IPNTR(2)). +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c This is for the initialization phase to force the +c starting vector into the range of OP. +c IDO = 1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c In mode 3 and 4, the vector B * X is already +c available in WORKD(ipntr(3)). It does not +c need to be recomputed in forming OP * X. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c IDO = 3: compute the IPARAM(8) real and imaginary parts +c of the shifts where INPTR(14) is the pointer +c into WORKL for placing the shifts. See Remark +c 5 below. +c IDO = 99: done +c ------------------------------------------------------------- +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of the matrix B that defines the +c semi-inner product for the operator OP. +c BMAT = 'I' -> standard eigenvalue problem A*x = lambda*x +c BMAT = 'G' -> generalized eigenvalue problem A*x = lambda*B*x +c +c N Integer. (INPUT) +c Dimension of the eigenproblem. +c +c WHICH Character*2. (INPUT) +c 'LM' -> want the NEV eigenvalues of largest magnitude. +c 'SM' -> want the NEV eigenvalues of smallest magnitude. +c 'LR' -> want the NEV eigenvalues of largest real part. +c 'SR' -> want the NEV eigenvalues of smallest real part. +c 'LI' -> want the NEV eigenvalues of largest imaginary part. +c 'SI' -> want the NEV eigenvalues of smallest imaginary part. +c +c NEV Integer. (INPUT) +c Number of eigenvalues of OP to be computed. 0 < NEV < N-1. +c +c TOL Double precision scalar. (INPUT) +c Stopping criterion: the relative accuracy of the Ritz value +c is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I)) +c where ABS(RITZ(I)) is the magnitude when RITZ(I) is complex. +c DEFAULT = DLAMCH('EPS') (machine precision as computed +c by the LAPACK auxiliary subroutine DLAMCH). +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT: +c If INFO .EQ. 0, a random initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c On OUTPUT: +c RESID contains the final residual vector. +c +c NCV Integer. (INPUT) +c Number of columns of the matrix V. NCV must satisfy the two +c inequalities 2 <= NCV-NEV and NCV <= N. +c This will indicate how many Arnoldi vectors are generated +c at each iteration. After the startup phase in which NEV +c Arnoldi vectors are generated, the algorithm generates +c approximately NCV-NEV Arnoldi vectors at each subsequent update +c iteration. Most of the cost in generating each Arnoldi vector is +c in the matrix-vector operation OP*x. +c NOTE: 2 <= NCV-NEV in order that complex conjugate pairs of Ritz +c values are kept together. (See remark 4 below) +c +c V Double precision array N by NCV. (OUTPUT) +c Contains the final set of Arnoldi basis vectors. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling program. +c +c IPARAM Integer array of length 11. (INPUT/OUTPUT) +c IPARAM(1) = ISHIFT: method for selecting the implicit shifts. +c The shifts selected at each iteration are used to restart +c the Arnoldi iteration in an implicit fashion. +c ------------------------------------------------------------- +c ISHIFT = 0: the shifts are provided by the user via +c reverse communication. The real and imaginary +c parts of the NCV eigenvalues of the Hessenberg +c matrix H are returned in the part of the WORKL +c array corresponding to RITZR and RITZI. See remark +c 5 below. +c ISHIFT = 1: exact shifts with respect to the current +c Hessenberg matrix H. This is equivalent to +c restarting the iteration with a starting vector +c that is a linear combination of approximate Schur +c vectors associated with the "wanted" Ritz values. +c ------------------------------------------------------------- +c +c IPARAM(2) = No longer referenced. +c +c IPARAM(3) = MXITER +c On INPUT: maximum number of Arnoldi update iterations allowed. +c On OUTPUT: actual number of Arnoldi update iterations taken. +c +c IPARAM(4) = NB: blocksize to be used in the recurrence. +c The code currently works only for NB = 1. +c +c IPARAM(5) = NCONV: number of "converged" Ritz values. +c This represents the number of Ritz values that satisfy +c the convergence criterion. +c +c IPARAM(6) = IUPD +c No longer referenced. Implicit restarting is ALWAYS used. +c +c IPARAM(7) = MODE +c On INPUT determines what type of eigenproblem is being solved. +c Must be 1,2,3,4; See under \Description of igraphdnaupd for the +c four modes available. +c +c IPARAM(8) = NP +c When ido = 3 and the user provides shifts through reverse +c communication (IPARAM(1)=0), igraphdnaupd returns NP, the number +c of shifts the user is to provide. 0 < NP <=NCV-NEV. See Remark +c 5 below. +c +c IPARAM(9) = NUMOP, IPARAM(10) = NUMOPB, IPARAM(11) = NUMREO, +c OUTPUT: NUMOP = total number of OP*x operations, +c NUMOPB = total number of B*x operations if BMAT='G', +c NUMREO = total number of steps of re-orthogonalization. +c +c IPNTR Integer array of length 14. (OUTPUT) +c Pointer to mark the starting locations in the WORKD and WORKL +c arrays for matrices/vectors used by the Arnoldi iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X in WORKD. +c IPNTR(2): pointer to the current result vector Y in WORKD. +c IPNTR(3): pointer to the vector B * X in WORKD when used in +c the shift-and-invert mode. +c IPNTR(4): pointer to the next available location in WORKL +c that is untouched by the program. +c IPNTR(5): pointer to the NCV by NCV upper Hessenberg matrix +c H in WORKL. +c IPNTR(6): pointer to the real part of the ritz value array +c RITZR in WORKL. +c IPNTR(7): pointer to the imaginary part of the ritz value array +c RITZI in WORKL. +c IPNTR(8): pointer to the Ritz estimates in array WORKL associated +c with the Ritz values located in RITZR and RITZI in WORKL. +c +c IPNTR(14): pointer to the NP shifts in WORKL. See Remark 5 below. +c +c Note: IPNTR(9:13) is only referenced by igraphdneupd. See Remark 2 below. +c +c IPNTR(9): pointer to the real part of the NCV RITZ values of the +c original system. +c IPNTR(10): pointer to the imaginary part of the NCV RITZ values of +c the original system. +c IPNTR(11): pointer to the NCV corresponding error bounds. +c IPNTR(12): pointer to the NCV by NCV upper quasi-triangular +c Schur matrix for H. +c IPNTR(13): pointer to the NCV by NCV matrix of eigenvectors +c of the upper Hessenberg matrix H. Only referenced by +c igraphdneupd if RVEC = .TRUE. See Remark 2 below. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The user should not use WORKD +c as temporary workspace during the iteration. Upon termination +c WORKD(1:N) contains B*RESID(1:N). If an invariant subspace +c associated with the converged Ritz values is desired, see remark +c 2 below, subroutine igraphdneupd uses this output. +c See Data Distribution Note below. +c +c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. See Data Distribution Note below. +c +c LWORKL Integer. (INPUT) +c LWORKL must be at least 3*NCV**2 + 6*NCV. +c +c INFO Integer. (INPUT/OUTPUT) +c If INFO .EQ. 0, a randomly initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c Error flag on output. +c = 0: Normal exit. +c = 1: Maximum number of iterations taken. +c All possible eigenvalues of OP has been found. IPARAM(5) +c returns the number of wanted converged Ritz values. +c = 2: No longer an informational error. Deprecated starting +c with release 2 of ARPACK. +c = 3: No shifts could be applied during a cycle of the +c Implicitly restarted Arnoldi iteration. One possibility +c is to increase the size of NCV relative to NEV. +c See remark 4 below. +c = -1: N must be positive. +c = -2: NEV must be positive. +c = -3: NCV-NEV >= 2 and less than or equal to N. +c = -4: The maximum number of Arnoldi update iteration +c must be greater than zero. +c = -5: WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI' +c = -6: BMAT must be one of 'I' or 'G'. +c = -7: Length of private work array is not sufficient. +c = -8: Error return from LAPACK eigenvalue calculation; +c = -9: Starting vector is zero. +c = -10: IPARAM(7) must be 1,2,3,4. +c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatable. +c = -12: IPARAM(1) must be equal to 0 or 1. +c = -9999: Could not build an Arnoldi factorization. +c IPARAM(5) returns the size of the current Arnoldi +c factorization. +c +c\Remarks +c 1. The computed Ritz values are approximate eigenvalues of OP. The +c selection of WHICH should be made with this in mind when +c Mode = 3 and 4. After convergence, approximate eigenvalues of the +c original problem may be obtained with the ARPACK subroutine igraphdneupd. +c +c 2. If a basis for the invariant subspace corresponding to the converged Ritz +c values is needed, the user must call igraphdneupd immediately following +c completion of igraphdnaupd. This is new starting with release 2 of ARPACK. +c +c 3. If M can be factored into a Cholesky factorization M = LL' +c then Mode = 2 should not be selected. Instead one should use +c Mode = 1 with OP = inv(L)*A*inv(L'). Appropriate triangular +c linear systems should be solved with L and L' rather +c than computing inverses. After convergence, an approximate +c eigenvector z of the original problem is recovered by solving +c L'z = x where x is a Ritz vector of OP. +c +c 4. At present there is no a-priori analysis to guide the selection +c of NCV relative to NEV. The only formal requrement is that NCV > NEV + 2. +c However, it is recommended that NCV .ge. 2*NEV+1. If many problems of +c the same type are to be solved, one should experiment with increasing +c NCV while keeping NEV fixed for a given test problem. This will +c usually decrease the required number of OP*x operations but it +c also increases the work and storage required to maintain the orthogonal +c basis vectors. The optimal "cross-over" with respect to CPU time +c is problem dependent and must be determined empirically. +c See Chapter 8 of Reference 2 for further information. +c +c 5. When IPARAM(1) = 0, and IDO = 3, the user needs to provide the +c NP = IPARAM(8) real and imaginary parts of the shifts in locations +c real part imaginary part +c ----------------------- -------------- +c 1 WORKL(IPNTR(14)) WORKL(IPNTR(14)+NP) +c 2 WORKL(IPNTR(14)+1) WORKL(IPNTR(14)+NP+1) +c . . +c . . +c . . +c NP WORKL(IPNTR(14)+NP-1) WORKL(IPNTR(14)+2*NP-1). +c +c Only complex conjugate pairs of shifts may be applied and the pairs +c must be placed in consecutive locations. The real part of the +c eigenvalues of the current upper Hessenberg matrix are located in +c WORKL(IPNTR(6)) through WORKL(IPNTR(6)+NCV-1) and the imaginary part +c in WORKL(IPNTR(7)) through WORKL(IPNTR(7)+NCV-1). They are ordered +c according to the order defined by WHICH. The complex conjugate +c pairs are kept together and the associated Ritz estimates are located in +c WORKL(IPNTR(8)), WORKL(IPNTR(8)+1), ... , WORKL(IPNTR(8)+NCV-1). +c +c----------------------------------------------------------------------- +c +c\Data Distribution Note: +c +c Fortran-D syntax: +c ================ +c Double precision resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) +c decompose d1(n), d2(n,ncv) +c align resid(i) with d1(i) +c align v(i,j) with d2(i,j) +c align workd(i) with d1(i) range (1:n) +c align workd(i) with d1(i-n) range (n+1:2*n) +c align workd(i) with d1(i-2*n) range (2*n+1:3*n) +c distribute d1(block), d2(block,:) +c replicated workl(lworkl) +c +c Cray MPP syntax: +c =============== +c Double precision resid(n), v(ldv,ncv), workd(n,3), workl(lworkl) +c shared resid(block), v(block,:), workd(block,:) +c replicated workl(lworkl) +c +c CM2/CM5 syntax: +c ============== +c +c----------------------------------------------------------------------- +c +c include 'ex-nonsym.doc' +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett & Y. Saad, "Complex Shift and Invert Strategies for +c Real Matrices", Linear Algebra and its Applications, vol 88/89, +c pp 575-595, (1987). +c +c\Routines called: +c igraphdnaup2 ARPACK routine that implements the Implicitly Restarted +c Arnoldi Iteration. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/16/93: Version '1.1' +c +c\SCCS Information: @(#) +c FILE: naupd.F SID: 2.5 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnaupd + & ( ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1, which*2 + integer ido, info, ldv, lworkl, n, ncv, nev + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer iparam(11), ipntr(14) + Double precision + & resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer bounds, ierr, ih, iq, ishift, iupd, iw, + & ldh, ldq, levec, mode, msglvl, mxiter, nb, + & nev0, next, np, ritzi, ritzr, j + save bounds, ih, iq, ishift, iupd, iw, ldh, ldq, + & levec, mode, msglvl, mxiter, nb, nev0, next, + & np, ritzi, ritzr +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external igraphdnaup2, igraphdvout, igraphivout, + & igraphsecond, igraphdstatn +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch + external dlamch +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphdstatn + call igraphsecond (t0) + msglvl = mnaupd +c +c %----------------% +c | Error checking | +c %----------------% +c + ierr = 0 + ishift = iparam(1) + levec = iparam(2) + mxiter = iparam(3) + nb = iparam(4) +c +c %--------------------------------------------% +c | Revision 2 performs only implicit restart. | +c %--------------------------------------------% +c + iupd = 1 + mode = iparam(7) +c + if (n .le. 0) then + ierr = -1 + else if (nev .le. 0) then + ierr = -2 + else if (ncv .le. nev+1 .or. ncv .gt. n) then + ierr = -3 + else if (mxiter .le. 0) then + ierr = -4 + else if (which .ne. 'LM' .and. + & which .ne. 'SM' .and. + & which .ne. 'LR' .and. + & which .ne. 'SR' .and. + & which .ne. 'LI' .and. + & which .ne. 'SI') then + ierr = -5 + else if (bmat .ne. 'I' .and. bmat .ne. 'G') then + ierr = -6 + else if (lworkl .lt. 3*ncv**2 + 6*ncv) then + ierr = -7 + else if (mode .lt. 1 .or. mode .gt. 5) then + ierr = -10 + else if (mode .eq. 1 .and. bmat .eq. 'G') then + ierr = -11 + else if (ishift .lt. 0 .or. ishift .gt. 1) then + ierr = -12 + end if +c +c %------------% +c | Error Exit | +c %------------% +c + if (ierr .ne. 0) then + info = ierr + ido = 99 + go to 9000 + end if +c +c %------------------------% +c | Set default parameters | +c %------------------------% +c + if (nb .le. 0) nb = 1 + if (tol .le. zero) tol = dlamch('EpsMach') +c +c %----------------------------------------------% +c | NP is the number of additional steps to | +c | extend the length NEV Lanczos factorization. | +c | NEV0 is the local variable designating the | +c | size of the invariant subspace desired. | +c %----------------------------------------------% +c + np = ncv - nev + nev0 = nev +c +c %-----------------------------% +c | Zero out internal workspace | +c %-----------------------------% +c + do 10 j = 1, 3*ncv**2 + 6*ncv + workl(j) = zero + 10 continue +c +c %-------------------------------------------------------------% +c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | +c | etc... and the remaining workspace. | +c | Also update pointer to be used on output. | +c | Memory is laid out as follows: | +c | workl(1:ncv*ncv) := generated Hessenberg matrix | +c | workl(ncv*ncv+1:ncv*ncv+2*ncv) := real and imaginary | +c | parts of ritz values | +c | workl(ncv*ncv+2*ncv+1:ncv*ncv+3*ncv) := error bounds | +c | workl(ncv*ncv+3*ncv+1:2*ncv*ncv+3*ncv) := rotation matrix Q | +c | workl(2*ncv*ncv+3*ncv+1:3*ncv*ncv+6*ncv) := workspace | +c | The final workspace is needed by subroutine igraphdneigh called | +c | by igraphdnaup2. Subroutine igraphdneigh calls LAPACK routines for | +c | calculating eigenvalues and the last row of the eigenvector | +c | matrix. | +c %-------------------------------------------------------------% +c + ldh = ncv + ldq = ncv + ih = 1 + ritzr = ih + ldh*ncv + ritzi = ritzr + ncv + bounds = ritzi + ncv + iq = bounds + ncv + iw = iq + ldq*ncv + next = iw + ncv**2 + 3*ncv +c + ipntr(4) = next + ipntr(5) = ih + ipntr(6) = ritzr + ipntr(7) = ritzi + ipntr(8) = bounds + ipntr(14) = iw +c + end if +c +c %-------------------------------------------------------% +c | Carry out the Implicitly restarted Arnoldi Iteration. | +c %-------------------------------------------------------% +c + call igraphdnaup2 + & ( ido, bmat, n, which, nev0, np, tol, resid, mode, iupd, + & ishift, mxiter, v, ldv, workl(ih), ldh, workl(ritzr), + & workl(ritzi), workl(bounds), workl(iq), ldq, workl(iw), + & ipntr, workd, info ) +c +c %--------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP or shifts. | +c %--------------------------------------------------% +c + if (ido .eq. 3) iparam(8) = np + if (ido .ne. 99) go to 9000 +c + iparam(3) = mxiter + iparam(5) = np + iparam(9) = nopx + iparam(10) = nbx + iparam(11) = nrorth +c +c %------------------------------------% +c | Exit if there was an informational | +c | error within igraphdnaup2. | +c %------------------------------------% +c + if (info .lt. 0) go to 9000 + if (info .eq. 2) info = 3 +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [mxiter], ndigit, + & '_naupd: Number of update iterations taken') + call igraphivout (logfil, 1, [np], ndigit, + & '_naupd: Number of wanted "converged" Ritz values') + call igraphdvout (logfil, np, workl(ritzr), ndigit, + & '_naupd: Real part of the final Ritz values') + call igraphdvout (logfil, np, workl(ritzi), ndigit, + & '_naupd: Imaginary part of the final Ritz values') + call igraphdvout (logfil, np, workl(bounds), ndigit, + & '_naupd: Associated Ritz estimates') + end if +c + call igraphsecond (t1) + tnaupd = t1 - t0 +c +c + 9000 continue +c + return +c +c %---------------% +c | End of igraphdnaupd | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dnconv.f b/src/rigraph/vendor/arpack/dnconv.f new file mode 100644 index 0000000..4735159 --- /dev/null +++ b/src/rigraph/vendor/arpack/dnconv.f @@ -0,0 +1,146 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdnconv +c +c\Description: +c Convergence testing for the nonsymmetric Arnoldi eigenvalue routine. +c +c\Usage: +c call igraphdnconv +c ( N, RITZR, RITZI, BOUNDS, TOL, NCONV ) +c +c\Arguments +c N Integer. (INPUT) +c Number of Ritz values to check for convergence. +c +c RITZR, Double precision arrays of length N. (INPUT) +c RITZI Real and imaginary parts of the Ritz values to be checked +c for convergence. + +c BOUNDS Double precision array of length N. (INPUT) +c Ritz estimates for the Ritz values in RITZR and RITZI. +c +c TOL Double precision scalar. (INPUT) +c Desired backward error for a Ritz value to be considered +c "converged". +c +c NCONV Integer scalar. (OUTPUT) +c Number of "converged" Ritz values. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphsecond ARPACK utility routine for timing. +c dlamch LAPACK routine that determines machine constants. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: nconv.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\Remarks +c 1. xxxx +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdnconv (n, ritzr, ritzi, bounds, tol, nconv) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer n, nconv + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% + + Double precision + & ritzr(n), ritzi(n), bounds(n) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i + Double precision + & temp, eps23 +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlapy2, dlamch + external dlapy2, dlamch + +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %-------------------------------------------------------------% +c | Convergence test: unlike in the symmetric code, I am not | +c | using things like refined error bounds and gap condition | +c | because I don't know the exact equivalent concept. | +c | | +c | Instead the i-th Ritz value is considered "converged" when: | +c | | +c | bounds(i) .le. ( TOL * | ritz | ) | +c | | +c | for some appropriate choice of norm. | +c %-------------------------------------------------------------% +c + call igraphsecond (t0) +c +c %---------------------------------% +c | Get machine dependent constant. | +c %---------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c + nconv = 0 + do 20 i = 1, n + temp = max( eps23, dlapy2( ritzr(i), ritzi(i) ) ) + if (bounds(i) .le. tol*temp) nconv = nconv + 1 + 20 continue +c + call igraphsecond (t1) + tnconv = tnconv + (t1 - t0) +c + return +c +c %---------------% +c | End of igraphdnconv | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dneigh.f b/src/rigraph/vendor/arpack/dneigh.f new file mode 100644 index 0000000..53c7c89 --- /dev/null +++ b/src/rigraph/vendor/arpack/dneigh.f @@ -0,0 +1,315 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdneigh +c +c\Description: +c Compute the eigenvalues of the current upper Hessenberg matrix +c and the corresponding Ritz estimates given the current residual norm. +c +c\Usage: +c call igraphdneigh +c ( RNORM, N, H, LDH, RITZR, RITZI, BOUNDS, Q, LDQ, WORKL, IERR ) +c +c\Arguments +c RNORM Double precision scalar. (INPUT) +c Residual norm corresponding to the current upper Hessenberg +c matrix H. +c +c N Integer. (INPUT) +c Size of the matrix H. +c +c H Double precision N by N array. (INPUT) +c H contains the current upper Hessenberg matrix. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RITZR, Double precision arrays of length N. (OUTPUT) +c RITZI On output, RITZR(1:N) (resp. RITZI(1:N)) contains the real +c (respectively imaginary) parts of the eigenvalues of H. +c +c BOUNDS Double precision array of length N. (OUTPUT) +c On output, BOUNDS contains the Ritz estimates associated with +c the eigenvalues RITZR and RITZI. This is equal to RNORM +c times the last components of the eigenvectors corresponding +c to the eigenvalues in RITZR and RITZI. +c +c Q Double precision N by N array. (WORKSPACE) +c Workspace needed to store the eigenvectors of H. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKL Double precision work array of length N**2 + 3*N. (WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. This is needed to keep the full Schur form +c of H and also in the calculation of the eigenvectors of H. +c +c IERR Integer. (OUTPUT) +c Error exit flag from igraphdlaqrb or dtrevc. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdlaqrb ARPACK routine to compute the real Schur form of an +c upper Hessenberg matrix and last row of the Schur vectors. +c igraphsecond ARPACK utility routine for timing. +c igraphdmout ARPACK utility routine that prints matrices +c igraphdvout ARPACK utility routine that prints vectors. +c dlacpy LAPACK matrix copy routine. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dtrevc LAPACK routine to compute the eigenvectors of a matrix +c in upper quasi-triangular form +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c dcopy Level 1 BLAS that copies one vector to another . +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dscal Level 1 BLAS that scales a vector. +c +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: neigh.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\Remarks +c None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdneigh (rnorm, n, h, ldh, ritzr, ritzi, bounds, + & q, ldq, workl, ierr) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer ierr, n, ldh, ldq + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & bounds(n), h(ldh,n), q(ldq,n), ritzi(n), ritzr(n), + & workl(n*(n+3)) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %------------------------% +c | Local Scalars & Arrays | +c %------------------------% +c + logical select(1) + integer i, iconj, msglvl + Double precision + & temp, vl(1) +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, dlacpy, igraphdlaqrb, dtrevc, igraphdvout, + & igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlapy2, dnrm2 + external dlapy2, dnrm2 +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mneigh +c + if (msglvl .gt. 2) then + call igraphdmout (logfil, n, n, h, ldh, ndigit, + & '_neigh: Entering upper Hessenberg matrix H ') + end if +c +c %-----------------------------------------------------------% +c | 1. Compute the eigenvalues, the last components of the | +c | corresponding Schur vectors and the full Schur form T | +c | of the current upper Hessenberg matrix H. | +c | igraphdlaqrb returns the full Schur form of H in WORKL(1:N**2) | +c | and the last components of the Schur vectors in BOUNDS. | +c %-----------------------------------------------------------% +c + call dlacpy ('All', n, n, h, ldh, workl, n) + call igraphdlaqrb (.true., n, 1, n, workl, n, ritzr, ritzi, + & bounds, ierr) + if (ierr .ne. 0) go to 9000 +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, n, bounds, ndigit, + & '_neigh: last row of the Schur matrix for H') + end if +c +c %-----------------------------------------------------------% +c | 2. Compute the eigenvectors of the full Schur form T and | +c | apply the last components of the Schur vectors to get | +c | the last components of the corresponding eigenvectors. | +c | Remember that if the i-th and (i+1)-st eigenvalues are | +c | complex conjugate pairs, then the real & imaginary part | +c | of the eigenvector components are split across adjacent | +c | columns of Q. | +c %-----------------------------------------------------------% +c + call dtrevc ('R', 'A', select, n, workl, n, vl, n, q, ldq, + & n, n, workl(n*n+1), ierr) +c + if (ierr .ne. 0) go to 9000 +c +c %------------------------------------------------% +c | Scale the returning eigenvectors so that their | +c | euclidean norms are all one. LAPACK subroutine | +c | dtrevc returns each eigenvector normalized so | +c | that the element of largest magnitude has | +c | magnitude 1; here the magnitude of a complex | +c | number (x,y) is taken to be |x| + |y|. | +c %------------------------------------------------% +c + iconj = 0 + do 10 i=1, n + if ( abs( ritzi(i) ) .le. zero ) then +c +c %----------------------% +c | Real eigenvalue case | +c %----------------------% +c + temp = dnrm2( n, q(1,i), 1 ) + call dscal ( n, one / temp, q(1,i), 1 ) + else +c +c %-------------------------------------------% +c | Complex conjugate pair case. Note that | +c | since the real and imaginary part of | +c | the eigenvector are stored in consecutive | +c | columns, we further normalize by the | +c | square root of two. | +c %-------------------------------------------% +c + if (iconj .eq. 0) then + temp = dlapy2( dnrm2( n, q(1,i), 1 ), + & dnrm2( n, q(1,i+1), 1 ) ) + call dscal ( n, one / temp, q(1,i), 1 ) + call dscal ( n, one / temp, q(1,i+1), 1 ) + iconj = 1 + else + iconj = 0 + end if + end if + 10 continue +c + call dgemv ('T', n, n, one, q, ldq, bounds, 1, zero, workl, 1) +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, n, workl, ndigit, + & '_neigh: Last row of the eigenvector matrix for H') + end if +c +c %----------------------------% +c | Compute the Ritz estimates | +c %----------------------------% +c + iconj = 0 + do 20 i = 1, n + if ( abs( ritzi(i) ) .le. zero ) then +c +c %----------------------% +c | Real eigenvalue case | +c %----------------------% +c + bounds(i) = rnorm * abs( workl(i) ) + else +c +c %-------------------------------------------% +c | Complex conjugate pair case. Note that | +c | since the real and imaginary part of | +c | the eigenvector are stored in consecutive | +c | columns, we need to take the magnitude | +c | of the last components of the two vectors | +c %-------------------------------------------% +c + if (iconj .eq. 0) then + bounds(i) = rnorm * dlapy2( workl(i), workl(i+1) ) + bounds(i+1) = bounds(i) + iconj = 1 + else + iconj = 0 + end if + end if + 20 continue +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, n, ritzr, ndigit, + & '_neigh: Real part of the eigenvalues of H') + call igraphdvout (logfil, n, ritzi, ndigit, + & '_neigh: Imaginary part of the eigenvalues of H') + call igraphdvout (logfil, n, bounds, ndigit, + & '_neigh: Ritz estimates for the eigenvalues of H') + end if +c + call igraphsecond (t1) + tneigh = tneigh + (t1 - t0) +c + 9000 continue + return +c +c %---------------% +c | End of igraphdneigh | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dneupd.f b/src/rigraph/vendor/arpack/dneupd.f new file mode 100644 index 0000000..92b4bc3 --- /dev/null +++ b/src/rigraph/vendor/arpack/dneupd.f @@ -0,0 +1,1044 @@ +c\BeginDoc +c +c\Name: igraphdneupd +c +c\Description: +c +c This subroutine returns the converged approximations to eigenvalues +c of A*z = lambda*B*z and (optionally): +c +c (1) The corresponding approximate eigenvectors; +c +c (2) An orthonormal basis for the associated approximate +c invariant subspace; +c +c (3) Both. +c +c There is negligible additional cost to obtain eigenvectors. An orthonormal +c basis is always computed. There is an additional storage cost of n*nev +c if both are requested (in this case a separate array Z must be supplied). +c +c The approximate eigenvalues and eigenvectors of A*z = lambda*B*z +c are derived from approximate eigenvalues and eigenvectors of +c of the linear operator OP prescribed by the MODE selection in the +c call to DNAUPD. DNAUPD must be called before this routine is called. +c These approximate eigenvalues and vectors are commonly called Ritz +c values and Ritz vectors respectively. They are referred to as such +c in the comments that follow. The computed orthonormal basis for the +c invariant subspace corresponding to these Ritz values is referred to as a +c Schur basis. +c +c See documentation in the header of the subroutine DNAUPD for +c definition of OP as well as other terms and the relation of computed +c Ritz values and Ritz vectors of OP with respect to the given problem +c A*z = lambda*B*z. For a brief description, see definitions of +c IPARAM(7), MODE and WHICH in the documentation of DNAUPD. +c +c\Usage: +c call igraphdneupd +c ( RVEC, HOWMNY, SELECT, DR, DI, Z, LDZ, SIGMAR, SIGMAI, WORKEV, BMAT, +c N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, WORKD, WORKL, +c LWORKL, INFO ) +c +c\Arguments: +c RVEC LOGICAL (INPUT) +c Specifies whether a basis for the invariant subspace corresponding +c to the converged Ritz value approximations for the eigenproblem +c A*z = lambda*B*z is computed. +c +c RVEC = .FALSE. Compute Ritz values only. +c +c RVEC = .TRUE. Compute the Ritz vectors or Schur vectors. +c See Remarks below. +c +c HOWMNY Character*1 (INPUT) +c Specifies the form of the basis for the invariant subspace +c corresponding to the converged Ritz values that is to be computed. +c +c = 'A': Compute NEV Ritz vectors; +c = 'P': Compute NEV Schur vectors; +c = 'S': compute some of the Ritz vectors, specified +c by the logical array SELECT. +c +c SELECT Logical array of dimension NCV. (INPUT) +c If HOWMNY = 'S', SELECT specifies the Ritz vectors to be +c computed. To select the Ritz vector corresponding to a +c Ritz value (DR(j), DI(j)), SELECT(j) must be set to .TRUE.. +c If HOWMNY = 'A' or 'P', SELECT is used as internal workspace. +c +c DR Double precision array of dimension NEV+1. (OUTPUT) +c If IPARAM(7) = 1,2 or 3 and SIGMAI=0.0 then on exit: DR contains +c the real part of the Ritz approximations to the eigenvalues of +c A*z = lambda*B*z. +c If IPARAM(7) = 3, 4 and SIGMAI is not equal to zero, then on exit: +c DR contains the real part of the Ritz values of OP computed by +c DNAUPD. A further computation must be performed by the user +c to transform the Ritz values computed for OP by DNAUPD to those +c of the original system A*z = lambda*B*z. See remark 3 below. +c +c DI Double precision array of dimension NEV+1. (OUTPUT) +c On exit, DI contains the imaginary part of the Ritz value +c approximations to the eigenvalues of A*z = lambda*B*z associated +c with DR. +c +c NOTE: When Ritz values are complex, they will come in complex +c conjugate pairs. If eigenvectors are requested, the +c corresponding Ritz vectors will also come in conjugate +c pairs and the real and imaginary parts of these are +c represented in two consecutive columns of the array Z +c (see below). +c +c Z Double precision N by NEV+1 array if RVEC = .TRUE. and HOWMNY = 'A'. (OUTPUT) +c On exit, if RVEC = .TRUE. and HOWMNY = 'A', then the columns of +c Z represent approximate eigenvectors (Ritz vectors) corresponding +c to the NCONV=IPARAM(5) Ritz values for eigensystem +c A*z = lambda*B*z. +c +c The complex Ritz vector associated with the Ritz value +c with positive imaginary part is stored in two consecutive +c columns. The first column holds the real part of the Ritz +c vector and the igraphsecond column holds the imaginary part. The +c Ritz vector associated with the Ritz value with negative +c imaginary part is simply the complex conjugate of the Ritz vector +c associated with the positive imaginary part. +c +c If RVEC = .FALSE. or HOWMNY = 'P', then Z is not referenced. +c +c NOTE: If if RVEC = .TRUE. and a Schur basis is not required, +c the array Z may be set equal to first NEV+1 columns of the Arnoldi +c basis array V computed by DNAUPD. In this case the Arnoldi basis +c will be destroyed and overwritten with the eigenvector basis. +c +c LDZ Integer. (INPUT) +c The leading dimension of the array Z. If Ritz vectors are +c desired, then LDZ >= max( 1, N ). In any case, LDZ >= 1. +c +c SIGMAR Double precision (INPUT) +c If IPARAM(7) = 3 or 4, represents the real part of the shift. +c Not referenced if IPARAM(7) = 1 or 2. +c +c SIGMAI Double precision (INPUT) +c If IPARAM(7) = 3 or 4, represents the imaginary part of the shift. +c Not referenced if IPARAM(7) = 1 or 2. See remark 3 below. +c +c WORKEV Double precision work array of dimension 3*NCV. (WORKSPACE) +c +c **** The remaining arguments MUST be the same as for the **** +c **** call to DNAUPD that was just completed. **** +c +c NOTE: The remaining arguments +c +c BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, +c WORKD, WORKL, LWORKL, INFO +c +c must be passed directly to DNEUPD following the last call +c to DNAUPD. These arguments MUST NOT BE MODIFIED between +c the the last call to DNAUPD and the call to DNEUPD. +c +c Three of these parameters (V, WORKL, INFO) are also output parameters: +c +c V Double precision N by NCV array. (INPUT/OUTPUT) +c +c Upon INPUT: the NCV columns of V contain the Arnoldi basis +c vectors for OP as constructed by DNAUPD . +c +c Upon OUTPUT: If RVEC = .TRUE. the first NCONV=IPARAM(5) columns +c contain approximate Schur vectors that span the +c desired invariant subspace. See Remark 2 below. +c +c NOTE: If the array Z has been set equal to first NEV+1 columns +c of the array V and RVEC=.TRUE. and HOWMNY= 'A', then the +c Arnoldi basis held by V has been overwritten by the desired +c Ritz vectors. If a separate array Z has been passed then +c the first NCONV=IPARAM(5) columns of V will contain approximate +c Schur vectors that span the desired invariant subspace. +c +c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) +c WORKL(1:ncv*ncv+3*ncv) contains information obtained in +c igraphdnaupd. They are not changed by igraphdneupd. +c WORKL(ncv*ncv+3*ncv+1:3*ncv*ncv+6*ncv) holds the +c real and imaginary part of the untransformed Ritz values, +c the upper quasi-triangular matrix for H, and the +c associated matrix representation of the invariant subspace for H. +c +c Note: IPNTR(9:13) contains the pointer into WORKL for addresses +c of the above information computed by igraphdneupd. +c ------------------------------------------------------------- +c IPNTR(9): pointer to the real part of the NCV RITZ values of the +c original system. +c IPNTR(10): pointer to the imaginary part of the NCV RITZ values of +c the original system. +c IPNTR(11): pointer to the NCV corresponding error bounds. +c IPNTR(12): pointer to the NCV by NCV upper quasi-triangular +c Schur matrix for H. +c IPNTR(13): pointer to the NCV by NCV matrix of eigenvectors +c of the upper Hessenberg matrix H. Only referenced by +c igraphdneupd if RVEC = .TRUE. See Remark 2 below. +c ------------------------------------------------------------- +c +c INFO Integer. (OUTPUT) +c Error flag on output. +c +c = 0: Normal exit. +c +c = 1: The Schur form computed by LAPACK routine dlahqr +c could not be reordered by LAPACK routine dtrsen. +c Re-enter subroutine igraphdneupd with IPARAM(5)=NCV and +c increase the size of the arrays DR and DI to have +c dimension at least dimension NCV and allocate at least NCV +c columns for Z. NOTE: Not necessary if Z and V share +c the same space. Please notify the authors if this error +c occurs. +c +c = -1: N must be positive. +c = -2: NEV must be positive. +c = -3: NCV-NEV >= 2 and less than or equal to N. +c = -5: WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI' +c = -6: BMAT must be one of 'I' or 'G'. +c = -7: Length of private work WORKL array is not sufficient. +c = -8: Error return from calculation of a real Schur form. +c Informational error from LAPACK routine dlahqr. +c = -9: Error return from calculation of eigenvectors. +c Informational error from LAPACK routine dtrevc. +c = -10: IPARAM(7) must be 1,2,3,4. +c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatible. +c = -12: HOWMNY = 'S' not yet implemented +c = -13: HOWMNY must be one of 'A' or 'P' if RVEC = .true. +c = -14: DNAUPD did not find any eigenvalues to sufficient +c accuracy. +c +c\BeginLib +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett & Y. Saad, "Complex Shift and Invert Strategies for +c Real Matrices", Linear Algebra and its Applications, vol 88/89, +c pp 575-595, (1987). +c +c\Routines called: +c igraphivout ARPACK utility routine that prints integers. +c igraphdmout ARPACK utility routine that prints matrices +c igraphdvout ARPACK utility routine that prints vectors. +c dgeqr2 LAPACK routine that computes the QR factorization of +c a matrix. +c dlacpy LAPACK matrix copy routine. +c dlahqr LAPACK routine to compute the real Schur form of an +c upper Hessenberg matrix. +c dlamch LAPACK routine that determines machine constants. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dlaset LAPACK matrix initialization routine. +c dorm2r LAPACK routine that applies an orthogonal matrix in +c factored form. +c dtrevc LAPACK routine to compute the eigenvectors of a matrix +c in upper quasi-triangular form. +c dtrsen LAPACK routine that re-orders the Schur form. +c dtrmm Level 3 BLAS matrix times an upper triangular matrix. +c dger Level 2 BLAS rank one update to a matrix. +c dcopy Level 1 BLAS that copies one vector to another . +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dscal Level 1 BLAS that scales a vector. +c +c\Remarks +c +c 1. Currently only HOWMNY = 'A' and 'P' are implemented. +c +c Let X' denote the transpose of X. +c +c 2. Schur vectors are an orthogonal representation for the basis of +c Ritz vectors. Thus, their numerical properties are often superior. +c If RVEC = .TRUE. then the relationship +c A * V(:,1:IPARAM(5)) = V(:,1:IPARAM(5)) * T, and +c V(:,1:IPARAM(5))' * V(:,1:IPARAM(5)) = I are approximately satisfied. +c Here T is the leading submatrix of order IPARAM(5) of the real +c upper quasi-triangular matrix stored workl(ipntr(12)). That is, +c T is block upper triangular with 1-by-1 and 2-by-2 diagonal blocks; +c each 2-by-2 diagonal block has its diagonal elements equal and its +c off-diagonal elements of opposite sign. Corresponding to each 2-by-2 +c diagonal block is a complex conjugate pair of Ritz values. The real +c Ritz values are stored on the diagonal of T. +c +c 3. If IPARAM(7) = 3 or 4 and SIGMAI is not equal zero, then the user must +c form the IPARAM(5) Rayleigh quotients in order to transform the Ritz +c values computed by DNAUPD for OP to those of A*z = lambda*B*z. +c Set RVEC = .true. and HOWMNY = 'A', and +c compute +c Z(:,I)' * A * Z(:,I) if DI(I) = 0. +c If DI(I) is not equal to zero and DI(I+1) = - D(I), +c then the desired real and imaginary parts of the Ritz value are +c Z(:,I)' * A * Z(:,I) + Z(:,I+1)' * A * Z(:,I+1), +c Z(:,I)' * A * Z(:,I+1) - Z(:,I+1)' * A * Z(:,I), respectively. +c Another possibility is to set RVEC = .true. and HOWMNY = 'P' and +c compute V(:,1:IPARAM(5))' * A * V(:,1:IPARAM(5)) and then an upper +c quasi-triangular matrix of order IPARAM(5) is computed. See remark +c 2 above. +c +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Chao Yang Houston, Texas +c Dept. of Computational & +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: neupd.F SID: 2.5 DATE OF SID: 7/31/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- + subroutine igraphdneupd (rvec, howmny, select, dr, di, z, ldz, + & sigmar, sigmai, workev, bmat, n, which, nev, tol, + & resid, ncv, v, ldv, iparam, ipntr, workd, + & workl, lworkl, info) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat, howmny, which*2 + logical rvec + integer info, ldz, ldv, lworkl, n, ncv, nev + Double precision + & sigmar, sigmai, tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer iparam(11), ipntr(14) + logical select(ncv) + Double precision + & dr(nev+1), di(nev+1), resid(n), v(ldv,ncv), z(ldz,*), + & workd(3*n), workl(lworkl), workev(3*ncv) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + character type*6 + integer bounds, ierr, ih, ihbds, iheigr, iheigi, iconj, nconv, + & invsub, iuptri, iwev, iwork(1), j, k, ktrord, + & ldh, ldq, mode, msglvl, outncv, ritzr, ritzi, wri, wrr, + & irr, iri, ibd + logical reord + Double precision + & conds, rnorm, sep, temp, thres, vl(1,1), temp1, eps23 +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, dger, dgeqr2, dlacpy, dlahqr, dlaset, + & igraphdmout, dorm2r, dtrevc, dtrmm, dtrsen, dscal, + & igraphdvout, igraphivout +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlapy2, dnrm2, dlamch, ddot + external dlapy2, dnrm2, dlamch, ddot +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs, min, sqrt +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %------------------------% +c | Set default parameters | +c %------------------------% +c + msglvl = mneupd + mode = iparam(7) + nconv = iparam(5) + info = 0 +c +c %---------------------------------% +c | Get machine dependent constant. | +c %---------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c +c %--------------% +c | Quick return | +c %--------------% +c + ierr = 0 +c + if (nconv .le. 0) then + ierr = -14 + else if (n .le. 0) then + ierr = -1 + else if (nev .le. 0) then + ierr = -2 + else if (ncv .le. nev+1 .or. ncv .gt. n) then + ierr = -3 + else if (which .ne. 'LM' .and. + & which .ne. 'SM' .and. + & which .ne. 'LR' .and. + & which .ne. 'SR' .and. + & which .ne. 'LI' .and. + & which .ne. 'SI') then + ierr = -5 + else if (bmat .ne. 'I' .and. bmat .ne. 'G') then + ierr = -6 + else if (lworkl .lt. 3*ncv**2 + 6*ncv) then + ierr = -7 + else if ( (howmny .ne. 'A' .and. + & howmny .ne. 'P' .and. + & howmny .ne. 'S') .and. rvec ) then + ierr = -13 + else if (howmny .eq. 'S' ) then + ierr = -12 + end if +c + if (mode .eq. 1 .or. mode .eq. 2) then + type = 'REGULR' + else if (mode .eq. 3 .and. sigmai .eq. zero) then + type = 'SHIFTI' + else if (mode .eq. 3 ) then + type = 'REALPT' + else if (mode .eq. 4 ) then + type = 'IMAGPT' + else + ierr = -10 + end if + if (mode .eq. 1 .and. bmat .eq. 'G') ierr = -11 +c +c %------------% +c | Error Exit | +c %------------% +c + if (ierr .ne. 0) then + info = ierr + go to 9000 + end if +c +c %--------------------------------------------------------% +c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | +c | etc... and the remaining workspace. | +c | Also update pointer to be used on output. | +c | Memory is laid out as follows: | +c | workl(1:ncv*ncv) := generated Hessenberg matrix | +c | workl(ncv*ncv+1:ncv*ncv+2*ncv) := real and imaginary | +c | parts of ritz values | +c | workl(ncv*ncv+2*ncv+1:ncv*ncv+3*ncv) := error bounds | +c %--------------------------------------------------------% +c +c %-----------------------------------------------------------% +c | The following is used and set by DNEUPD. | +c | workl(ncv*ncv+3*ncv+1:ncv*ncv+4*ncv) := The untransformed | +c | real part of the Ritz values. | +c | workl(ncv*ncv+4*ncv+1:ncv*ncv+5*ncv) := The untransformed | +c | imaginary part of the Ritz values. | +c | workl(ncv*ncv+5*ncv+1:ncv*ncv+6*ncv) := The untransformed | +c | error bounds of the Ritz values | +c | workl(ncv*ncv+6*ncv+1:2*ncv*ncv+6*ncv) := Holds the upper | +c | quasi-triangular matrix for H | +c | workl(2*ncv*ncv+6*ncv+1: 3*ncv*ncv+6*ncv) := Holds the | +c | associated matrix representation of the invariant | +c | subspace for H. | +c | GRAND total of NCV * ( 3 * NCV + 6 ) locations. | +c %-----------------------------------------------------------% +c + ih = ipntr(5) + ritzr = ipntr(6) + ritzi = ipntr(7) + bounds = ipntr(8) + ldh = ncv + ldq = ncv + iheigr = bounds + ldh + iheigi = iheigr + ldh + ihbds = iheigi + ldh + iuptri = ihbds + ldh + invsub = iuptri + ldh*ncv + ipntr(9) = iheigr + ipntr(10) = iheigi + ipntr(11) = ihbds + ipntr(12) = iuptri + ipntr(13) = invsub + wrr = 1 + wri = ncv + 1 + iwev = wri + ncv +c +c %-----------------------------------------% +c | irr points to the REAL part of the Ritz | +c | values computed by _neigh before | +c | exiting _naup2. | +c | iri points to the IMAGINARY part of the | +c | Ritz values computed by _neigh | +c | before exiting _naup2. | +c | ibd points to the Ritz estimates | +c | computed by _neigh before exiting | +c | _naup2. | +c %-----------------------------------------% +c + irr = ipntr(14)+ncv*ncv + iri = irr+ncv + ibd = iri+ncv +c +c %------------------------------------% +c | RNORM is B-norm of the RESID(1:N). | +c %------------------------------------% +c + rnorm = workl(ih+2) + workl(ih+2) = zero +c + if (rvec) then +c +c %-------------------------------------------% +c | Get converged Ritz value on the boundary. | +c | Note: converged Ritz values have been | +c | placed in the first NCONV locations in | +c | workl(ritzr) and workl(ritzi). They have | +c | been sorted (in _naup2) according to the | +c | WHICH selection criterion. | +c %-------------------------------------------% +c + if (which .eq. 'LM' .or. which .eq. 'SM') then + thres = dlapy2( workl(ritzr), workl(ritzi) ) + else if (which .eq. 'LR' .or. which .eq. 'SR') then + thres = workl(ritzr) + else if (which .eq. 'LI' .or. which .eq. 'SI') then + thres = abs( workl(ritzi) ) + end if +c + if (msglvl .gt. 2) then + call igraphdvout(logfil, 1, [thres], ndigit, + & '_neupd: Threshold eigenvalue used for re-ordering') + end if +c +c %----------------------------------------------------------% +c | Check to see if all converged Ritz values appear at the | +c | top of the upper quasi-triangular matrix computed by | +c | _neigh in _naup2. This is done in the following way: | +c | | +c | 1) For each Ritz value obtained from _neigh, compare it | +c | with the threshold Ritz value computed above to | +c | determine whether it is a wanted one. | +c | | +c | 2) If it is wanted, then check the corresponding Ritz | +c | estimate to see if it has converged. If it has, set | +c | correponding entry in the logical array SELECT to | +c | .TRUE.. | +c | | +c | If SELECT(j) = .TRUE. and j > NCONV, then there is a | +c | converged Ritz value that does not appear at the top of | +c | the upper quasi-triangular matrix computed by _neigh in | +c | _naup2. Reordering is needed. | +c %----------------------------------------------------------% +c + reord = .false. + ktrord = 0 + do 10 j = 0, ncv-1 + select(j+1) = .false. + if (which .eq. 'LM') then + if (dlapy2(workl(irr+j), workl(iri+j)) + & .ge. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'SM') then + if (dlapy2(workl(irr+j), workl(iri+j)) + & .le. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'LR') then + if (workl(irr+j) .ge. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'SR') then + if (workl(irr+j) .le. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'LI') then + if (abs(workl(iri+j)) .ge. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + else if (which .eq. 'SI') then + if (abs(workl(iri+j)) .le. thres) then + temp1 = max( eps23, + & dlapy2( workl(irr+j), workl(iri+j) ) ) + if (workl(ibd+j) .le. tol*temp1) + & select(j+1) = .true. + end if + end if + if (j+1 .gt. nconv ) reord = ( select(j+1) .or. reord ) + if (select(j+1)) ktrord = ktrord + 1 + 10 continue +c + if (msglvl .gt. 2) then + call igraphivout(logfil, 1, [ktrord], ndigit, + & '_neupd: Number of specified eigenvalues') + call igraphivout(logfil, 1, [nconv], ndigit, + & '_neupd: Number of "converged" eigenvalues') + end if +c +c %-----------------------------------------------------------% +c | Call LAPACK routine dlahqr to compute the real Schur form | +c | of the upper Hessenberg matrix returned by DNAUPD. | +c | Make a copy of the upper Hessenberg matrix. | +c | Initialize the Schur vector matrix Q to the identity. | +c %-----------------------------------------------------------% +c + call dcopy (ldh*ncv, workl(ih), 1, workl(iuptri), 1) + call dlaset ('All', ncv, ncv, zero, one, workl(invsub), ldq) + call dlahqr (.true., .true., ncv, 1, ncv, workl(iuptri), ldh, + & workl(iheigr), workl(iheigi), 1, ncv, + & workl(invsub), ldq, ierr) + call dcopy (ncv, workl(invsub+ncv-1), ldq, workl(ihbds), 1) +c + if (ierr .ne. 0) then + info = -8 + go to 9000 + end if +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, ncv, workl(iheigr), ndigit, + & '_neupd: Real part of the eigenvalues of H') + call igraphdvout (logfil, ncv, workl(iheigi), ndigit, + & '_neupd: Imaginary part of the Eigenvalues of H') + call igraphdvout (logfil, ncv, workl(ihbds), ndigit, + & '_neupd: Last row of the Schur vector matrix') + if (msglvl .gt. 3) then + call igraphdmout (logfil, ncv, ncv, workl(iuptri), ldh, + & ndigit, + & '_neupd: The upper quasi-triangular matrix ') + end if + end if +c + if (reord) then +c +c %-----------------------------------------------------% +c | Reorder the computed upper quasi-triangular matrix. | +c %-----------------------------------------------------% +c + call dtrsen ('None', 'V', select, ncv, workl(iuptri), ldh, + & workl(invsub), ldq, workl(iheigr), workl(iheigi), + & nconv, conds, sep, workl(ihbds), ncv, iwork, 1, ierr) +c + if (ierr .eq. 1) then + info = 1 + go to 9000 + end if +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, ncv, workl(iheigr), ndigit, + & '_neupd: Real part of the eigenvalues of H--reordered') + call igraphdvout (logfil, ncv, workl(iheigi), ndigit, + & '_neupd: Imag part of the eigenvalues of H--reordered') + if (msglvl .gt. 3) then + call igraphdmout (logfil, ncv, ncv, workl(iuptri), + & ldq, ndigit, + & '_neupd: Quasi-triangular matrix after re-ordering') + end if + end if +c + end if +c +c %---------------------------------------% +c | Copy the last row of the Schur vector | +c | into workl(ihbds). This will be used | +c | to compute the Ritz estimates of | +c | converged Ritz values. | +c %---------------------------------------% +c + call dcopy(ncv, workl(invsub+ncv-1), ldq, workl(ihbds), 1) +c +c %----------------------------------------------------% +c | Place the computed eigenvalues of H into DR and DI | +c | if a spectral transformation was not used. | +c %----------------------------------------------------% +c + if (type .eq. 'REGULR') then + call dcopy (nconv, workl(iheigr), 1, dr, 1) + call dcopy (nconv, workl(iheigi), 1, di, 1) + end if +c +c %----------------------------------------------------------% +c | Compute the QR factorization of the matrix representing | +c | the wanted invariant subspace located in the first NCONV | +c | columns of workl(invsub,ldq). | +c %----------------------------------------------------------% +c + call dgeqr2 (ncv, nconv, workl(invsub), ldq, workev, + & workev(ncv+1), ierr) +c +c %---------------------------------------------------------% +c | * Postmultiply V by Q using dorm2r. | +c | * Copy the first NCONV columns of VQ into Z. | +c | * Postmultiply Z by R. | +c | The N by NCONV matrix Z is now a matrix representation | +c | of the approximate invariant subspace associated with | +c | the Ritz values in workl(iheigr) and workl(iheigi) | +c | The first NCONV columns of V are now approximate Schur | +c | vectors associated with the real upper quasi-triangular | +c | matrix of order NCONV in workl(iuptri) | +c %---------------------------------------------------------% +c + call dorm2r ('Right', 'Notranspose', n, ncv, nconv, + & workl(invsub), ldq, workev, v, ldv, workd(n+1), ierr) + call dlacpy ('All', n, nconv, v, ldv, z, ldz) +c + do 20 j=1, nconv +c +c %---------------------------------------------------% +c | Perform both a column and row scaling if the | +c | diagonal element of workl(invsub,ldq) is negative | +c | I'm lazy and don't take advantage of the upper | +c | quasi-triangular form of workl(iuptri,ldq) | +c | Note that since Q is orthogonal, R is a diagonal | +c | matrix consisting of plus or minus ones | +c %---------------------------------------------------% +c + if (workl(invsub+(j-1)*ldq+j-1) .lt. zero) then + call dscal (nconv, -one, workl(iuptri+j-1), ldq) + call dscal (nconv, -one, workl(iuptri+(j-1)*ldq), 1) + end if +c + 20 continue +c + if (howmny .eq. 'A') then +c +c %--------------------------------------------% +c | Compute the NCONV wanted eigenvectors of T | +c | located in workl(iuptri,ldq). | +c %--------------------------------------------% +c + do 30 j=1, ncv + if (j .le. nconv) then + select(j) = .true. + else + select(j) = .false. + end if + 30 continue +c + call dtrevc ('Right', 'Select', select, ncv, workl(iuptri), + & ldq, vl, 1, workl(invsub), ldq, ncv, outncv, workev, + & ierr) +c + if (ierr .ne. 0) then + info = -9 + go to 9000 + end if +c +c %------------------------------------------------% +c | Scale the returning eigenvectors so that their | +c | Euclidean norms are all one. LAPACK subroutine | +c | dtrevc returns each eigenvector normalized so | +c | that the element of largest magnitude has | +c | magnitude 1; | +c %------------------------------------------------% +c + iconj = 0 + do 40 j=1, nconv +c + if ( workl(iheigi+j-1) .eq. zero ) then +c +c %----------------------% +c | real eigenvalue case | +c %----------------------% +c + temp = dnrm2( ncv, workl(invsub+(j-1)*ldq), 1 ) + call dscal ( ncv, one / temp, + & workl(invsub+(j-1)*ldq), 1 ) +c + else +c +c %-------------------------------------------% +c | Complex conjugate pair case. Note that | +c | since the real and imaginary part of | +c | the eigenvector are stored in consecutive | +c | columns, we further normalize by the | +c | square root of two. | +c %-------------------------------------------% +c + if (iconj .eq. 0) then + temp = dlapy2( dnrm2( ncv, workl(invsub+(j-1)*ldq), + & 1 ), dnrm2( ncv, workl(invsub+j*ldq), 1) ) + call dscal ( ncv, one / temp, + & workl(invsub+(j-1)*ldq), 1 ) + call dscal ( ncv, one / temp, + & workl(invsub+j*ldq), 1 ) + iconj = 1 + else + iconj = 0 + end if +c + end if +c + 40 continue +c + call dgemv('T', ncv, nconv, one, workl(invsub), + & ldq, workl(ihbds), 1, zero, workev, 1) +c + iconj = 0 + do 45 j=1, nconv + if (workl(iheigi+j-1) .ne. zero) then +c +c %-------------------------------------------% +c | Complex conjugate pair case. Note that | +c | since the real and imaginary part of | +c | the eigenvector are stored in consecutive | +c %-------------------------------------------% +c + if (iconj .eq. 0) then + workev(j) = dlapy2(workev(j), workev(j+1)) + workev(j+1) = workev(j) + iconj = 1 + else + iconj = 0 + end if + end if + 45 continue +c + if (msglvl .gt. 2) then + call dcopy(ncv, workl(invsub+ncv-1), ldq, + & workl(ihbds), 1) + call igraphdvout (logfil, ncv, workl(ihbds), ndigit, + & '_neupd: Last row of the eigenvector matrix for T') + if (msglvl .gt. 3) then + call igraphdmout (logfil, ncv, ncv, workl(invsub), + & ldq, ndigit, + & '_neupd: The eigenvector matrix for T') + end if + end if +c +c %---------------------------------------% +c | Copy Ritz estimates into workl(ihbds) | +c %---------------------------------------% +c + call dcopy(nconv, workev, 1, workl(ihbds), 1) +c +c %---------------------------------------------------------% +c | Compute the QR factorization of the eigenvector matrix | +c | associated with leading portion of T in the first NCONV | +c | columns of workl(invsub,ldq). | +c %---------------------------------------------------------% +c + call dgeqr2 (ncv, nconv, workl(invsub), ldq, workev, + & workev(ncv+1), ierr) +c +c %----------------------------------------------% +c | * Postmultiply Z by Q. | +c | * Postmultiply Z by R. | +c | The N by NCONV matrix Z is now contains the | +c | Ritz vectors associated with the Ritz values | +c | in workl(iheigr) and workl(iheigi). | +c %----------------------------------------------% +c + call dorm2r ('Right', 'Notranspose', n, ncv, nconv, + & workl(invsub), ldq, workev, z, ldz, workd(n+1), ierr) +c + call dtrmm ('Right', 'Upper', 'No transpose', 'Non-unit', + & n, nconv, one, workl(invsub), ldq, z, ldz) +c + end if +c + else +c +c %------------------------------------------------------% +c | An approximate invariant subspace is not needed. | +c | Place the Ritz values computed DNAUPD into DR and DI | +c %------------------------------------------------------% +c + call dcopy (nconv, workl(ritzr), 1, dr, 1) + call dcopy (nconv, workl(ritzi), 1, di, 1) + call dcopy (nconv, workl(ritzr), 1, workl(iheigr), 1) + call dcopy (nconv, workl(ritzi), 1, workl(iheigi), 1) + call dcopy (nconv, workl(bounds), 1, workl(ihbds), 1) + end if +c +c %------------------------------------------------% +c | Transform the Ritz values and possibly vectors | +c | and corresponding error bounds of OP to those | +c | of A*x = lambda*B*x. | +c %------------------------------------------------% +c + if (type .eq. 'REGULR') then +c + if (rvec) + & call dscal (ncv, rnorm, workl(ihbds), 1) +c + else +c +c %---------------------------------------% +c | A spectral transformation was used. | +c | * Determine the Ritz estimates of the | +c | Ritz values in the original system. | +c %---------------------------------------% +c + if (type .eq. 'SHIFTI') then +c + if (rvec) + & call dscal (ncv, rnorm, workl(ihbds), 1) +c + do 50 k=1, ncv + temp = dlapy2( workl(iheigr+k-1), + & workl(iheigi+k-1) ) + workl(ihbds+k-1) = abs( workl(ihbds+k-1) ) + & / temp / temp + 50 continue +c + else if (type .eq. 'REALPT') then +c + do 60 k=1, ncv + 60 continue +c + else if (type .eq. 'IMAGPT') then +c + do 70 k=1, ncv + 70 continue +c + end if +c +c %-----------------------------------------------------------% +c | * Transform the Ritz values back to the original system. | +c | For TYPE = 'SHIFTI' the transformation is | +c | lambda = 1/theta + sigma | +c | For TYPE = 'REALPT' or 'IMAGPT' the user must from | +c | Rayleigh quotients or a projection. See remark 3 above.| +c | NOTES: | +c | *The Ritz vectors are not affected by the transformation. | +c %-----------------------------------------------------------% +c + if (type .eq. 'SHIFTI') then +c + do 80 k=1, ncv + temp = dlapy2( workl(iheigr+k-1), + & workl(iheigi+k-1) ) + workl(iheigr+k-1) = workl(iheigr+k-1) / temp / temp + & + sigmar + workl(iheigi+k-1) = -workl(iheigi+k-1) / temp / temp + & + sigmai + 80 continue +c + call dcopy (nconv, workl(iheigr), 1, dr, 1) + call dcopy (nconv, workl(iheigi), 1, di, 1) +c + else if (type .eq. 'REALPT' .or. type .eq. 'IMAGPT') then +c + call dcopy (nconv, workl(iheigr), 1, dr, 1) + call dcopy (nconv, workl(iheigi), 1, di, 1) +c + end if +c + end if +c + if (type .eq. 'SHIFTI' .and. msglvl .gt. 1) then + call igraphdvout (logfil, nconv, dr, ndigit, + & '_neupd: Untransformed real part of the Ritz valuess.') + call igraphdvout (logfil, nconv, di, ndigit, + & '_neupd: Untransformed imag part of the Ritz valuess.') + call igraphdvout (logfil, nconv, workl(ihbds), ndigit, + & '_neupd: Ritz estimates of untransformed Ritz values.') + else if (type .eq. 'REGULR' .and. msglvl .gt. 1) then + call igraphdvout (logfil, nconv, dr, ndigit, + & '_neupd: Real parts of converged Ritz values.') + call igraphdvout (logfil, nconv, di, ndigit, + & '_neupd: Imag parts of converged Ritz values.') + call igraphdvout (logfil, nconv, workl(ihbds), ndigit, + & '_neupd: Associated Ritz estimates.') + end if +c +c %-------------------------------------------------% +c | Eigenvector Purification step. Formally perform | +c | one of inverse subspace iteration. Only used | +c | for MODE = 2. | +c %-------------------------------------------------% +c + if (rvec .and. howmny .eq. 'A' .and. type .eq. 'SHIFTI') then +c +c %------------------------------------------------% +c | Purify the computed Ritz vectors by adding a | +c | little bit of the residual vector: | +c | T | +c | resid(:)*( e s ) / theta | +c | NCV | +c | where H s = s theta. Remember that when theta | +c | has nonzero imaginary part, the corresponding | +c | Ritz vector is stored across two columns of Z. | +c %------------------------------------------------% +c + iconj = 0 + do 110 j=1, nconv + if (workl(iheigi+j-1) .eq. zero) then + workev(j) = workl(invsub+(j-1)*ldq+ncv-1) / + & workl(iheigr+j-1) + else if (iconj .eq. 0) then + temp = dlapy2( workl(iheigr+j-1), workl(iheigi+j-1) ) + workev(j) = ( workl(invsub+(j-1)*ldq+ncv-1) * + & workl(iheigr+j-1) + + & workl(invsub+j*ldq+ncv-1) * + & workl(iheigi+j-1) ) / temp / temp + workev(j+1) = ( workl(invsub+j*ldq+ncv-1) * + & workl(iheigr+j-1) - + & workl(invsub+(j-1)*ldq+ncv-1) * + & workl(iheigi+j-1) ) / temp / temp + iconj = 1 + else + iconj = 0 + end if + 110 continue +c +c %---------------------------------------% +c | Perform a rank one update to Z and | +c | purify all the Ritz vectors together. | +c %---------------------------------------% +c + call dger (n, nconv, one, resid, 1, workev, 1, z, ldz) +c + end if +c + 9000 continue +c + return +c +c %---------------% +c | End of DNEUPD | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dngets.f b/src/rigraph/vendor/arpack/dngets.f new file mode 100644 index 0000000..0202090 --- /dev/null +++ b/src/rigraph/vendor/arpack/dngets.f @@ -0,0 +1,231 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdngets +c +c\Description: +c Given the eigenvalues of the upper Hessenberg matrix H, +c computes the NP shifts AMU that are zeros of the polynomial of +c degree NP which filters out components of the unwanted eigenvectors +c corresponding to the AMU's based on some given criteria. +c +c NOTE: call this even in the case of user specified shifts in order +c to sort the eigenvalues, and error bounds of H for later use. +c +c\Usage: +c call igraphdngets +c ( ISHIFT, WHICH, KEV, NP, RITZR, RITZI, BOUNDS, SHIFTR, SHIFTI ) +c +c\Arguments +c ISHIFT Integer. (INPUT) +c Method for selecting the implicit shifts at each iteration. +c ISHIFT = 0: user specified shifts +c ISHIFT = 1: exact shift with respect to the matrix H. +c +c WHICH Character*2. (INPUT) +c Shift selection criteria. +c 'LM' -> want the KEV eigenvalues of largest magnitude. +c 'SM' -> want the KEV eigenvalues of smallest magnitude. +c 'LR' -> want the KEV eigenvalues of largest real part. +c 'SR' -> want the KEV eigenvalues of smallest real part. +c 'LI' -> want the KEV eigenvalues of largest imaginary part. +c 'SI' -> want the KEV eigenvalues of smallest imaginary part. +c +c KEV Integer. (INPUT/OUTPUT) +c INPUT: KEV+NP is the size of the matrix H. +c OUTPUT: Possibly increases KEV by one to keep complex conjugate +c pairs together. +c +c NP Integer. (INPUT/OUTPUT) +c Number of implicit shifts to be computed. +c OUTPUT: Possibly decreases NP by one to keep complex conjugate +c pairs together. +c +c RITZR, Double precision array of length KEV+NP. (INPUT/OUTPUT) +c RITZI On INPUT, RITZR and RITZI contain the real and imaginary +c parts of the eigenvalues of H. +c On OUTPUT, RITZR and RITZI are sorted so that the unwanted +c eigenvalues are in the first NP locations and the wanted +c portion is in the last KEV locations. When exact shifts are +c selected, the unwanted part corresponds to the shifts to +c be applied. Also, if ISHIFT .eq. 1, the unwanted eigenvalues +c are further sorted so that the ones with largest Ritz values +c are first. +c +c BOUNDS Double precision array of length KEV+NP. (INPUT/OUTPUT) +c Error bounds corresponding to the ordering in RITZ. +c +c SHIFTR, SHIFTI *** USE deprecated as of version 2.1. *** +c +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdsortc ARPACK sorting routine. +c dcopy Level 1 BLAS that copies one vector to another . +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: ngets.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\Remarks +c 1. xxxx +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdngets ( ishift, which, kev, np, ritzr, ritzi, + & bounds, shiftr, shifti ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + integer ishift, kev, np +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & bounds(kev+np), ritzr(kev+np), ritzi(kev+np), + & shiftr(1), shifti(1) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0, zero = 0.0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer msglvl +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, igraphdsortc, igraphsecond +c +c %----------------------% +c | Intrinsics Functions | +c %----------------------% +c + intrinsic abs +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mngets +c +c %----------------------------------------------------% +c | LM, SM, LR, SR, LI, SI case. | +c | Sort the eigenvalues of H into the desired order | +c | and apply the resulting order to BOUNDS. | +c | The eigenvalues are sorted so that the wanted part | +c | are always in the last KEV locations. | +c | We first do a pre-processing sort in order to keep | +c | complex conjugate pairs together | +c %----------------------------------------------------% +c + if (which .eq. 'LM') then + call igraphdsortc ('LR', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'SM') then + call igraphdsortc ('SR', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'LR') then + call igraphdsortc ('LM', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'SR') then + call igraphdsortc ('SM', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'LI') then + call igraphdsortc ('LM', .true., kev+np, ritzr, ritzi, bounds) + else if (which .eq. 'SI') then + call igraphdsortc ('SM', .true., kev+np, ritzr, ritzi, bounds) + end if +c + call igraphdsortc (which, .true., kev+np, ritzr, ritzi, bounds) +c +c %-------------------------------------------------------% +c | Increase KEV by one if the ( ritzr(np),ritzi(np) ) | +c | = ( ritzr(np+1),-ritzi(np+1) ) and ritz(np) .ne. zero | +c | Accordingly decrease NP by one. In other words keep | +c | complex conjugate pairs together. | +c %-------------------------------------------------------% +c + if ( ( ritzr(np+1) - ritzr(np) ) .eq. zero + & .and. ( ritzi(np+1) + ritzi(np) ) .eq. zero ) then + np = np - 1 + kev = kev + 1 + end if +c + if ( ishift .eq. 1 ) then +c +c %-------------------------------------------------------% +c | Sort the unwanted Ritz values used as shifts so that | +c | the ones with largest Ritz estimates are first | +c | This will tend to minimize the effects of the | +c | forward instability of the iteration when they shifts | +c | are applied in subroutine igraphdnapps. | +c | Be careful and use 'SR' since we want to sort BOUNDS! | +c %-------------------------------------------------------% +c + call igraphdsortc ( 'SR', .true., np, bounds, ritzr, ritzi ) + end if +c + call igraphsecond (t1) + tngets = tngets + (t1 - t0) +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [kev], ndigit, '_ngets: KEV is') + call igraphivout (logfil, 1, [np], ndigit, '_ngets: NP is') + call igraphdvout (logfil, kev+np, ritzr, ndigit, + & '_ngets: Eigenvalues of current H matrix -- real part') + call igraphdvout (logfil, kev+np, ritzi, ndigit, + & '_ngets: Eigenvalues of current H matrix -- imag part') + call igraphdvout (logfil, kev+np, bounds, ndigit, + & '_ngets: Ritz estimates of the current KEV+NP Ritz values') + end if +c + return +c +c %---------------% +c | End of igraphdngets | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsaitr.f b/src/rigraph/vendor/arpack/dsaitr.f new file mode 100644 index 0000000..4a4698c --- /dev/null +++ b/src/rigraph/vendor/arpack/dsaitr.f @@ -0,0 +1,854 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsaitr +c +c\Description: +c Reverse communication interface for applying NP additional steps to +c a K step symmetric Arnoldi factorization. +c +c Input: OP*V_{k} - V_{k}*H = r_{k}*e_{k}^T +c +c with (V_{k}^T)*B*V_{k} = I, (V_{k}^T)*B*r_{k} = 0. +c +c Output: OP*V_{k+p} - V_{k+p}*H = r_{k+p}*e_{k+p}^T +c +c with (V_{k+p}^T)*B*V_{k+p} = I, (V_{k+p}^T)*B*r_{k+p} = 0. +c +c where OP and B are as in igraphdsaupd. The B-norm of r_{k+p} is also +c computed and returned. +c +c\Usage: +c call igraphdsaitr +c ( IDO, BMAT, N, K, NP, MODE, RESID, RNORM, V, LDV, H, LDH, +c IPNTR, WORKD, INFO ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y. +c This is for the restart phase to force the new +c starting vector into the range of OP. +c IDO = 1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y, +c IPNTR(3) is the pointer into WORK for B * X. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORK for X, +c IPNTR(2) is the pointer into WORK for Y. +c IDO = 99: done +c ------------------------------------------------------------- +c When the routine is used in the "shift-and-invert" mode, the +c vector B * Q is already available and does not need to be +c recomputed in forming OP * Q. +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of matrix B that defines the +c semi-inner product for the operator OP. See igraphdsaupd. +c B = 'I' -> standard eigenvalue problem A*x = lambda*x +c B = 'G' -> generalized eigenvalue problem A*x = lambda*M*x +c +c N Integer. (INPUT) +c Dimension of the eigenproblem. +c +c K Integer. (INPUT) +c Current order of H and the number of columns of V. +c +c NP Integer. (INPUT) +c Number of additional Arnoldi steps to take. +c +c MODE Integer. (INPUT) +c Signifies which form for "OP". If MODE=2 then +c a reduction in the number of B matrix vector multiplies +c is possible since the B-norm of OP*x is equivalent to +c the inv(B)-norm of A*x. +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT: RESID contains the residual vector r_{k}. +c On OUTPUT: RESID contains the residual vector r_{k+p}. +c +c RNORM Double precision scalar. (INPUT/OUTPUT) +c On INPUT the B-norm of r_{k}. +c On OUTPUT the B-norm of the updated residual r_{k+p}. +c +c V Double precision N by K+NP array. (INPUT/OUTPUT) +c On INPUT: V contains the Arnoldi vectors in the first K +c columns. +c On OUTPUT: V contains the new NP Arnoldi vectors in the next +c NP columns. The first K columns are unchanged. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (K+NP) by 2 array. (INPUT/OUTPUT) +c H is used to store the generated symmetric tridiagonal matrix +c with the subdiagonal in the first column starting at H(2,1) +c and the main diagonal in the igraphsecond column. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c IPNTR Integer array of length 3. (OUTPUT) +c Pointer to mark the starting locations in the WORK for +c vectors used by the Arnoldi iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X. +c IPNTR(2): pointer to the current result vector Y. +c IPNTR(3): pointer to the vector B * X when used in the +c shift-and-invert mode. X is the current operand. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The calling program should not +c use WORKD as temporary workspace during the iteration !!!!!! +c On INPUT, WORKD(1:N) = B*RESID where RESID is associated +c with the K step Arnoldi factorization. Used to save some +c computation at the first step. +c On OUTPUT, WORKD(1:N) = B*RESID where RESID is associated +c with the K+NP step Arnoldi factorization. +c +c INFO Integer. (OUTPUT) +c = 0: Normal exit. +c > 0: Size of an invariant subspace of OP is found that is +c less than K + NP. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdgetv0 ARPACK routine to generate the initial vector. +c igraphivout ARPACK utility routine that prints integers. +c igraphdmout ARPACK utility routine that prints matrices. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c dlascl LAPACK routine for careful scaling of a matrix. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c daxpy Level 1 BLAS that computes a vector triad. +c dscal Level 1 BLAS that scales a vector. +c dcopy Level 1 BLAS that copies one vector to another . +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/93: Version ' 2.4' +c +c\SCCS Information: @(#) +c FILE: saitr.F SID: 2.6 DATE OF SID: 8/28/96 RELEASE: 2 +c +c\Remarks +c The algorithm implemented is: +c +c restart = .false. +c Given V_{k} = [v_{1}, ..., v_{k}], r_{k}; +c r_{k} contains the initial residual vector even for k = 0; +c Also assume that rnorm = || B*r_{k} || and B*r_{k} are already +c computed by the calling program. +c +c betaj = rnorm ; p_{k+1} = B*r_{k} ; +c For j = k+1, ..., k+np Do +c 1) if ( betaj < tol ) stop or restart depending on j. +c if ( restart ) generate a new starting vector. +c 2) v_{j} = r(j-1)/betaj; V_{j} = [V_{j-1}, v_{j}]; +c p_{j} = p_{j}/betaj +c 3) r_{j} = OP*v_{j} where OP is defined as in igraphdsaupd +c For shift-invert mode p_{j} = B*v_{j} is already available. +c wnorm = || OP*v_{j} || +c 4) Compute the j-th step residual vector. +c w_{j} = V_{j}^T * B * OP * v_{j} +c r_{j} = OP*v_{j} - V_{j} * w_{j} +c alphaj <- j-th component of w_{j} +c rnorm = || r_{j} || +c betaj+1 = rnorm +c If (rnorm > 0.717*wnorm) accept step and go back to 1) +c 5) Re-orthogonalization step: +c s = V_{j}'*B*r_{j} +c r_{j} = r_{j} - V_{j}*s; rnorm1 = || r_{j} || +c alphaj = alphaj + s_{j}; +c 6) Iterative refinement step: +c If (rnorm1 > 0.717*rnorm) then +c rnorm = rnorm1 +c accept step and go back to 1) +c Else +c rnorm = rnorm1 +c If this is the first time in step 6), go to 5) +c Else r_{j} lies in the span of V_{j} numerically. +c Set r_{j} = 0 and rnorm = 0; go to 1) +c EndIf +c End Do +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsaitr + & (ido, bmat, n, k, np, mode, resid, rnorm, v, ldv, h, ldh, + & ipntr, workd, info) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1 + integer ido, info, k, ldh, ldv, n, mode, np + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(3) + Double precision + & h(ldh,2), resid(n), v(ldv,k+np), workd(3*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + logical first, orth1, orth2, rstart, step3, step4 + integer i, ierr, ipj, irj, ivj, iter, itry, j, msglvl, + & infol, jj + Double precision + & rnorm1, wnorm, safmin, temp1 + save orth1, orth2, rstart, step3, step4, + & ierr, ipj, irj, ivj, iter, itry, j, msglvl, + & rnorm1, safmin, wnorm +c +c %-----------------------% +c | Local Array Arguments | +c %-----------------------% +c + Double precision + & xtemp(2) +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external daxpy, dcopy, dscal, dgemv, igraphdgetv0, + & igraphdvout, igraphdmout, + & dlascl, igraphivout, igraphsecond +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2, dlamch + external ddot, dnrm2, dlamch +c +c %-----------------% +c | Data statements | +c %-----------------% +c + data first / .true. / +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (first) then + first = .false. +c +c %--------------------------------% +c | safmin = safe minimum is such | +c | that 1/sfmin does not overflow | +c %--------------------------------% +c + safmin = dlamch('safmin') + end if +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = msaitr +c +c %------------------------------% +c | Initial call to this routine | +c %------------------------------% +c + info = 0 + step3 = .false. + step4 = .false. + rstart = .false. + orth1 = .false. + orth2 = .false. +c +c %--------------------------------% +c | Pointer to the current step of | +c | the factorization to build | +c %--------------------------------% +c + j = k + 1 +c +c %------------------------------------------% +c | Pointers used for reverse communication | +c | when using WORKD. | +c %------------------------------------------% +c + ipj = 1 + irj = ipj + n + ivj = irj + n + end if +c +c %-------------------------------------------------% +c | When in reverse communication mode one of: | +c | STEP3, STEP4, ORTH1, ORTH2, RSTART | +c | will be .true. | +c | STEP3: return from computing OP*v_{j}. | +c | STEP4: return from computing B-norm of OP*v_{j} | +c | ORTH1: return from computing B-norm of r_{j+1} | +c | ORTH2: return from computing B-norm of | +c | correction to the residual vector. | +c | RSTART: return from OP computations needed by | +c | igraphdgetv0. | +c %-------------------------------------------------% +c + if (step3) go to 50 + if (step4) go to 60 + if (orth1) go to 70 + if (orth2) go to 90 + if (rstart) go to 30 +c +c %------------------------------% +c | Else this is the first step. | +c %------------------------------% +c +c %--------------------------------------------------------------% +c | | +c | A R N O L D I I T E R A T I O N L O O P | +c | | +c | Note: B*r_{j-1} is already in WORKD(1:N)=WORKD(IPJ:IPJ+N-1) | +c %--------------------------------------------------------------% +c + 1000 continue +c + if (msglvl .gt. 2) then + call igraphivout (logfil, 1, [j], ndigit, + & '_saitr: generating Arnoldi vector no.') + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_saitr: B-norm of the current residual =') + end if +c +c %---------------------------------------------------------% +c | Check for exact zero. Equivalent to determing whether a | +c | j-step Arnoldi factorization is present. | +c %---------------------------------------------------------% +c + if (rnorm .gt. zero) go to 40 +c +c %---------------------------------------------------% +c | Invariant subspace found, generate a new starting | +c | vector which is orthogonal to the current Arnoldi | +c | basis and continue the iteration. | +c %---------------------------------------------------% +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [j], ndigit, + & '_saitr: ****** restart at step ******') + end if +c +c %---------------------------------------------% +c | ITRY is the loop variable that controls the | +c | maximum amount of times that a restart is | +c | attempted. NRSTRT is used by stat.h | +c %---------------------------------------------% +c + nrstrt = nrstrt + 1 + itry = 1 + 20 continue + rstart = .true. + ido = 0 + 30 continue +c +c %--------------------------------------% +c | If in reverse communication mode and | +c | RSTART = .true. flow returns here. | +c %--------------------------------------% +c + call igraphdgetv0 (ido, bmat, itry, .false., n, j, v, ldv, + & resid, rnorm, ipntr, workd, ierr) + if (ido .ne. 99) go to 9000 + if (ierr .lt. 0) then + itry = itry + 1 + if (itry .le. 3) go to 20 +c +c %------------------------------------------------% +c | Give up after several restart attempts. | +c | Set INFO to the size of the invariant subspace | +c | which spans OP and exit. | +c %------------------------------------------------% +c + info = j - 1 + call igraphsecond (t1) + tsaitr = tsaitr + (t1 - t0) + ido = 99 + go to 9000 + end if +c + 40 continue +c +c %---------------------------------------------------------% +c | STEP 2: v_{j} = r_{j-1}/rnorm and p_{j} = p_{j}/rnorm | +c | Note that p_{j} = B*r_{j-1}. In order to avoid overflow | +c | when reciprocating a small RNORM, test against lower | +c | machine bound. | +c %---------------------------------------------------------% +c + call dcopy (n, resid, 1, v(1,j), 1) + if (rnorm .ge. safmin) then + temp1 = one / rnorm + call dscal (n, temp1, v(1,j), 1) + call dscal (n, temp1, workd(ipj), 1) + else +c +c %-----------------------------------------% +c | To scale both v_{j} and p_{j} carefully | +c | use LAPACK routine SLASCL | +c %-----------------------------------------% +c + call dlascl ('General', i, i, rnorm, one, n, 1, + & v(1,j), n, infol) + call dlascl ('General', i, i, rnorm, one, n, 1, + & workd(ipj), n, infol) + end if +c +c %------------------------------------------------------% +c | STEP 3: r_{j} = OP*v_{j}; Note that p_{j} = B*v_{j} | +c | Note that this is not quite yet r_{j}. See STEP 4 | +c %------------------------------------------------------% +c + step3 = .true. + nopx = nopx + 1 + call igraphsecond (t2) + call dcopy (n, v(1,j), 1, workd(ivj), 1) + ipntr(1) = ivj + ipntr(2) = irj + ipntr(3) = ipj + ido = 1 +c +c %-----------------------------------% +c | Exit in order to compute OP*v_{j} | +c %-----------------------------------% +c + go to 9000 + 50 continue +c +c %-----------------------------------% +c | Back from reverse communication; | +c | WORKD(IRJ:IRJ+N-1) := OP*v_{j}. | +c %-----------------------------------% +c + call igraphsecond (t3) + tmvopx = tmvopx + (t3 - t2) +c + step3 = .false. +c +c %------------------------------------------% +c | Put another copy of OP*v_{j} into RESID. | +c %------------------------------------------% +c + call dcopy (n, workd(irj), 1, resid, 1) +c +c %-------------------------------------------% +c | STEP 4: Finish extending the symmetric | +c | Arnoldi to length j. If MODE = 2 | +c | then B*OP = B*inv(B)*A = A and | +c | we don't need to compute B*OP. | +c | NOTE: If MODE = 2 WORKD(IVJ:IVJ+N-1) is | +c | assumed to have A*v_{j}. | +c %-------------------------------------------% +c + if (mode .eq. 2) go to 65 + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + step4 = .true. + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %-------------------------------------% +c | Exit in order to compute B*OP*v_{j} | +c %-------------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy(n, resid, 1 , workd(ipj), 1) + end if + 60 continue +c +c %-----------------------------------% +c | Back from reverse communication; | +c | WORKD(IPJ:IPJ+N-1) := B*OP*v_{j}. | +c %-----------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + step4 = .false. +c +c %-------------------------------------% +c | The following is needed for STEP 5. | +c | Compute the B-norm of OP*v_{j}. | +c %-------------------------------------% +c + 65 continue + if (mode .eq. 2) then +c +c %----------------------------------% +c | Note that the B-norm of OP*v_{j} | +c | is the inv(B)-norm of A*v_{j}. | +c %----------------------------------% +c + wnorm = ddot (n, resid, 1, workd(ivj), 1) + wnorm = sqrt(abs(wnorm)) + else if (bmat .eq. 'G') then + wnorm = ddot (n, resid, 1, workd(ipj), 1) + wnorm = sqrt(abs(wnorm)) + else if (bmat .eq. 'I') then + wnorm = dnrm2(n, resid, 1) + end if +c +c %-----------------------------------------% +c | Compute the j-th residual corresponding | +c | to the j step factorization. | +c | Use Classical Gram Schmidt and compute: | +c | w_{j} <- V_{j}^T * B * OP * v_{j} | +c | r_{j} <- OP*v_{j} - V_{j} * w_{j} | +c %-----------------------------------------% +c +c +c %------------------------------------------% +c | Compute the j Fourier coefficients w_{j} | +c | WORKD(IPJ:IPJ+N-1) contains B*OP*v_{j}. | +c %------------------------------------------% +c + if (mode .ne. 2 ) then + call dgemv('T', n, j, one, v, ldv, workd(ipj), 1, zero, + & workd(irj), 1) + else if (mode .eq. 2) then + call dgemv('T', n, j, one, v, ldv, workd(ivj), 1, zero, + & workd(irj), 1) + end if +c +c %--------------------------------------% +c | Orthgonalize r_{j} against V_{j}. | +c | RESID contains OP*v_{j}. See STEP 3. | +c %--------------------------------------% +c + call dgemv('N', n, j, -one, v, ldv, workd(irj), 1, one, + & resid, 1) +c +c %--------------------------------------% +c | Extend H to have j rows and columns. | +c %--------------------------------------% +c + h(j,2) = workd(irj + j - 1) + if (j .eq. 1 .or. rstart) then + h(j,1) = zero + else + h(j,1) = rnorm + end if + call igraphsecond (t4) +c + orth1 = .true. + iter = 0 +c + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(irj), 1) + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %----------------------------------% +c | Exit in order to compute B*r_{j} | +c %----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 70 continue +c +c %---------------------------------------------------% +c | Back from reverse communication if ORTH1 = .true. | +c | WORKD(IPJ:IPJ+N-1) := B*r_{j}. | +c %---------------------------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + orth1 = .false. +c +c %------------------------------% +c | Compute the B-norm of r_{j}. | +c %------------------------------% +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd(ipj), 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if +c +c %-----------------------------------------------------------% +c | STEP 5: Re-orthogonalization / Iterative refinement phase | +c | Maximum NITER_ITREF tries. | +c | | +c | s = V_{j}^T * B * r_{j} | +c | r_{j} = r_{j} - V_{j}*s | +c | alphaj = alphaj + s_{j} | +c | | +c | The stopping criteria used for iterative refinement is | +c | discussed in Parlett's book SEP, page 107 and in Gragg & | +c | Reichel ACM TOMS paper; Algorithm 686, Dec. 1990. | +c | Determine if we need to correct the residual. The goal is | +c | to enforce ||v(:,1:j)^T * r_{j}|| .le. eps * || r_{j} || | +c %-----------------------------------------------------------% +c + if (rnorm .gt. 0.717*wnorm) go to 100 + nrorth = nrorth + 1 +c +c %---------------------------------------------------% +c | Enter the Iterative refinement phase. If further | +c | refinement is necessary, loop back here. The loop | +c | variable is ITER. Perform a step of Classical | +c | Gram-Schmidt using all the Arnoldi vectors V_{j} | +c %---------------------------------------------------% +c + 80 continue +c + if (msglvl .gt. 2) then + xtemp(1) = wnorm + xtemp(2) = rnorm + call igraphdvout (logfil, 2, xtemp, ndigit, + & '_saitr: re-orthonalization ; wnorm and rnorm are') + end if +c +c %----------------------------------------------------% +c | Compute V_{j}^T * B * r_{j}. | +c | WORKD(IRJ:IRJ+J-1) = v(:,1:J)'*WORKD(IPJ:IPJ+N-1). | +c %----------------------------------------------------% +c + call dgemv ('T', n, j, one, v, ldv, workd(ipj), 1, + & zero, workd(irj), 1) +c +c %----------------------------------------------% +c | Compute the correction to the residual: | +c | r_{j} = r_{j} - V_{j} * WORKD(IRJ:IRJ+J-1). | +c | The correction to H is v(:,1:J)*H(1:J,1:J) + | +c | v(:,1:J)*WORKD(IRJ:IRJ+J-1)*e'_j, but only | +c | H(j,j) is updated. | +c %----------------------------------------------% +c + call dgemv ('N', n, j, -one, v, ldv, workd(irj), 1, + & one, resid, 1) +c + if (j .eq. 1 .or. rstart) h(j,1) = zero + h(j,2) = h(j,2) + workd(irj + j - 1) +c + orth2 = .true. + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(irj), 1) + ipntr(1) = irj + ipntr(2) = ipj + ido = 2 +c +c %-----------------------------------% +c | Exit in order to compute B*r_{j}. | +c | r_{j} is the corrected residual. | +c %-----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd(ipj), 1) + end if + 90 continue +c +c %---------------------------------------------------% +c | Back from reverse communication if ORTH2 = .true. | +c %---------------------------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c +c %-----------------------------------------------------% +c | Compute the B-norm of the corrected residual r_{j}. | +c %-----------------------------------------------------% +c + if (bmat .eq. 'G') then + rnorm1 = ddot (n, resid, 1, workd(ipj), 1) + rnorm1 = sqrt(abs(rnorm1)) + else if (bmat .eq. 'I') then + rnorm1 = dnrm2(n, resid, 1) + end if +c + if (msglvl .gt. 0 .and. iter .gt. 0) then + call igraphivout (logfil, 1, [j], ndigit, + & '_saitr: Iterative refinement for Arnoldi residual') + if (msglvl .gt. 2) then + xtemp(1) = rnorm + xtemp(2) = rnorm1 + call igraphdvout (logfil, 2, xtemp, ndigit, + & '_saitr: iterative refinement ; rnorm and rnorm1 are') + end if + end if +c +c %-----------------------------------------% +c | Determine if we need to perform another | +c | step of re-orthogonalization. | +c %-----------------------------------------% +c + if (rnorm1 .gt. 0.717*rnorm) then +c +c %--------------------------------% +c | No need for further refinement | +c %--------------------------------% +c + rnorm = rnorm1 +c + else +c +c %-------------------------------------------% +c | Another step of iterative refinement step | +c | is required. NITREF is used by stat.h | +c %-------------------------------------------% +c + nitref = nitref + 1 + rnorm = rnorm1 + iter = iter + 1 + if (iter .le. 1) go to 80 +c +c %-------------------------------------------------% +c | Otherwise RESID is numerically in the span of V | +c %-------------------------------------------------% +c + do 95 jj = 1, n + resid(jj) = zero + 95 continue + rnorm = zero + end if +c +c %----------------------------------------------% +c | Branch here directly if iterative refinement | +c | wasn't necessary or after at most NITER_REF | +c | steps of iterative refinement. | +c %----------------------------------------------% +c + 100 continue +c + rstart = .false. + orth2 = .false. +c + call igraphsecond (t5) + titref = titref + (t5 - t4) +c +c %----------------------------------------------------------% +c | Make sure the last off-diagonal element is non negative | +c | If not perform a similarity transformation on H(1:j,1:j) | +c | and scale v(:,j) by -1. | +c %----------------------------------------------------------% +c + if (h(j,1) .lt. zero) then + h(j,1) = -h(j,1) + if ( j .lt. k+np) then + call dscal(n, -one, v(1,j+1), 1) + else + call dscal(n, -one, resid, 1) + end if + end if +c +c %------------------------------------% +c | STEP 6: Update j = j+1; Continue | +c %------------------------------------% +c + j = j + 1 + if (j .gt. k+np) then + call igraphsecond (t1) + tsaitr = tsaitr + (t1 - t0) + ido = 99 +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, k+np, h(1,2), ndigit, + & '_saitr: main diagonal of matrix H of step K+NP.') + if (k+np .gt. 1) then + call igraphdvout (logfil, k+np-1, h(2,1), ndigit, + & '_saitr: sub diagonal of matrix H of step K+NP.') + end if + end if +c + go to 9000 + end if +c +c %--------------------------------------------------------% +c | Loop back to extend the factorization by another step. | +c %--------------------------------------------------------% +c + go to 1000 +c +c %---------------------------------------------------------------% +c | | +c | E N D O F M A I N I T E R A T I O N L O O P | +c | | +c %---------------------------------------------------------------% +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsaitr | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsapps.f b/src/rigraph/vendor/arpack/dsapps.f new file mode 100644 index 0000000..850e3fd --- /dev/null +++ b/src/rigraph/vendor/arpack/dsapps.f @@ -0,0 +1,516 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsapps +c +c\Description: +c Given the Arnoldi factorization +c +c A*V_{k} - V_{k}*H_{k} = r_{k+p}*e_{k+p}^T, +c +c apply NP shifts implicitly resulting in +c +c A*(V_{k}*Q) - (V_{k}*Q)*(Q^T* H_{k}*Q) = r_{k+p}*e_{k+p}^T * Q +c +c where Q is an orthogonal matrix of order KEV+NP. Q is the product of +c rotations resulting from the NP bulge chasing sweeps. The updated Arnoldi +c factorization becomes: +c +c A*VNEW_{k} - VNEW_{k}*HNEW_{k} = rnew_{k}*e_{k}^T. +c +c\Usage: +c call igraphdsapps +c ( N, KEV, NP, SHIFT, V, LDV, H, LDH, RESID, Q, LDQ, WORKD ) +c +c\Arguments +c N Integer. (INPUT) +c Problem size, i.e. dimension of matrix A. +c +c KEV Integer. (INPUT) +c INPUT: KEV+NP is the size of the input matrix H. +c OUTPUT: KEV is the size of the updated matrix HNEW. +c +c NP Integer. (INPUT) +c Number of implicit shifts to be applied. +c +c SHIFT Double precision array of length NP. (INPUT) +c The shifts to be applied. +c +c V Double precision N by (KEV+NP) array. (INPUT/OUTPUT) +c INPUT: V contains the current KEV+NP Arnoldi vectors. +c OUTPUT: VNEW = V(1:n,1:KEV); the updated Arnoldi vectors +c are in the first KEV columns of V. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (KEV+NP) by 2 array. (INPUT/OUTPUT) +c INPUT: H contains the symmetric tridiagonal matrix of the +c Arnoldi factorization with the subdiagonal in the 1st column +c starting at H(2,1) and the main diagonal in the 2nd column. +c OUTPUT: H contains the updated tridiagonal matrix in the +c KEV leading submatrix. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RESID Double precision array of length (N). (INPUT/OUTPUT) +c INPUT: RESID contains the the residual vector r_{k+p}. +c OUTPUT: RESID is the updated residual vector rnew_{k}. +c +c Q Double precision KEV+NP by KEV+NP work array. (WORKSPACE) +c Work array used to accumulate the rotations during the bulge +c chase sweep. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKD Double precision work array of length 2*N. (WORKSPACE) +c Distributed array used in the application of the accumulated +c orthogonal matrix Q. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c +c\Routines called: +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c dlartg LAPACK Givens rotation construction routine. +c dlacpy LAPACK matrix copy routine. +c dlaset LAPACK matrix initialization routine. +c dgemv Level 2 BLAS routine for matrix vector multiplication. +c daxpy Level 1 BLAS that computes a vector triad. +c dcopy Level 1 BLAS that copies one vector to another. +c dscal Level 1 BLAS that scales a vector. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/16/93: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: sapps.F SID: 2.5 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\Remarks +c 1. In this version, each shift is applied to all the subblocks of +c the tridiagonal matrix H and not just to the submatrix that it +c comes from. This routine assumes that the subdiagonal elements +c of H that are stored in h(1:kev+np,1) are nonegative upon input +c and enforce this condition upon output. This version incorporates +c deflation. See code for documentation. +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsapps + & ( n, kev, np, shift, v, ldv, h, ldh, resid, q, ldq, workd ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer kev, ldh, ldq, ldv, n, np +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & h(ldh,2), q(ldq,kev+np), resid(n), shift(np), + & v(ldv,kev+np), workd(2*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, iend, istart, itop, j, jj, kplusp, msglvl + logical first + Double precision + & a1, a2, a3, a4, big, c, epsmch, f, g, r, s + save epsmch, first +c +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external daxpy, dcopy, dscal, dlacpy, dlartg, dlaset, + & igraphdvout, igraphivout, igraphsecond, dgemv +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch + external dlamch +c +c %----------------------% +c | Intrinsics Functions | +c %----------------------% +c + intrinsic abs +c +c %----------------% +c | Data statments | +c %----------------% +c + data first / .true. / +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (first) then + epsmch = dlamch('Epsilon-Machine') + first = .false. + end if + itop = 1 +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = msapps +c + kplusp = kev + np +c +c %----------------------------------------------% +c | Initialize Q to the identity matrix of order | +c | kplusp used to accumulate the rotations. | +c %----------------------------------------------% +c + call dlaset ('All', kplusp, kplusp, zero, one, q, ldq) +c +c %----------------------------------------------% +c | Quick return if there are no shifts to apply | +c %----------------------------------------------% +c + if (np .eq. 0) go to 9000 +c +c %----------------------------------------------------------% +c | Apply the np shifts implicitly. Apply each shift to the | +c | whole matrix and not just to the submatrix from which it | +c | comes. | +c %----------------------------------------------------------% +c + do 90 jj = 1, np +c + istart = itop +c +c %----------------------------------------------------------% +c | Check for splitting and deflation. Currently we consider | +c | an off-diagonal element h(i+1,1) negligible if | +c | h(i+1,1) .le. epsmch*( |h(i,2)| + |h(i+1,2)| ) | +c | for i=1:KEV+NP-1. | +c | If above condition tests true then we set h(i+1,1) = 0. | +c | Note that h(1:KEV+NP,1) are assumed to be non negative. | +c %----------------------------------------------------------% +c + 20 continue +c +c %------------------------------------------------% +c | The following loop exits early if we encounter | +c | a negligible off diagonal element. | +c %------------------------------------------------% +c + do 30 i = istart, kplusp-1 + big = abs(h(i,2)) + abs(h(i+1,2)) + if (h(i+1,1) .le. epsmch*big) then + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, i, ndigit, + & '_sapps: deflation at row/column no.') + call igraphivout (logfil, 1, jj, ndigit, + & '_sapps: occured before shift number.') + call igraphdvout (logfil, 1, h(i+1,1), ndigit, + & '_sapps: the corresponding off diagonal element') + end if + h(i+1,1) = zero + iend = i + go to 40 + end if + 30 continue + iend = kplusp + 40 continue +c + if (istart .lt. iend) then +c +c %--------------------------------------------------------% +c | Construct the plane rotation G'(istart,istart+1,theta) | +c | that attempts to drive h(istart+1,1) to zero. | +c %--------------------------------------------------------% +c + f = h(istart,2) - shift(jj) + g = h(istart+1,1) + call dlartg (f, g, c, s, r) +c +c %-------------------------------------------------------% +c | Apply rotation to the left and right of H; | +c | H <- G' * H * G, where G = G(istart,istart+1,theta). | +c | This will create a "bulge". | +c %-------------------------------------------------------% +c + a1 = c*h(istart,2) + s*h(istart+1,1) + a2 = c*h(istart+1,1) + s*h(istart+1,2) + a4 = c*h(istart+1,2) - s*h(istart+1,1) + a3 = c*h(istart+1,1) - s*h(istart,2) + h(istart,2) = c*a1 + s*a2 + h(istart+1,2) = c*a4 - s*a3 + h(istart+1,1) = c*a3 + s*a4 +c +c %----------------------------------------------------% +c | Accumulate the rotation in the matrix Q; Q <- Q*G | +c %----------------------------------------------------% +c + do 60 j = 1, min(istart+jj,kplusp) + a1 = c*q(j,istart) + s*q(j,istart+1) + q(j,istart+1) = - s*q(j,istart) + c*q(j,istart+1) + q(j,istart) = a1 + 60 continue +c +c +c %----------------------------------------------% +c | The following loop chases the bulge created. | +c | Note that the previous rotation may also be | +c | done within the following loop. But it is | +c | kept separate to make the distinction among | +c | the bulge chasing sweeps and the first plane | +c | rotation designed to drive h(istart+1,1) to | +c | zero. | +c %----------------------------------------------% +c + do 70 i = istart+1, iend-1 +c +c %----------------------------------------------% +c | Construct the plane rotation G'(i,i+1,theta) | +c | that zeros the i-th bulge that was created | +c | by G(i-1,i,theta). g represents the bulge. | +c %----------------------------------------------% +c + f = h(i,1) + g = s*h(i+1,1) +c +c %----------------------------------% +c | Final update with G(i-1,i,theta) | +c %----------------------------------% +c + h(i+1,1) = c*h(i+1,1) + call dlartg (f, g, c, s, r) +c +c %-------------------------------------------% +c | The following ensures that h(1:iend-1,1), | +c | the first iend-2 off diagonal of elements | +c | H, remain non negative. | +c %-------------------------------------------% +c + if (r .lt. zero) then + r = -r + c = -c + s = -s + end if +c +c %--------------------------------------------% +c | Apply rotation to the left and right of H; | +c | H <- G * H * G', where G = G(i,i+1,theta) | +c %--------------------------------------------% +c + h(i,1) = r +c + a1 = c*h(i,2) + s*h(i+1,1) + a2 = c*h(i+1,1) + s*h(i+1,2) + a3 = c*h(i+1,1) - s*h(i,2) + a4 = c*h(i+1,2) - s*h(i+1,1) +c + h(i,2) = c*a1 + s*a2 + h(i+1,2) = c*a4 - s*a3 + h(i+1,1) = c*a3 + s*a4 +c +c %----------------------------------------------------% +c | Accumulate the rotation in the matrix Q; Q <- Q*G | +c %----------------------------------------------------% +c + do 50 j = 1, min( j+jj, kplusp ) + a1 = c*q(j,i) + s*q(j,i+1) + q(j,i+1) = - s*q(j,i) + c*q(j,i+1) + q(j,i) = a1 + 50 continue +c + 70 continue +c + end if +c +c %--------------------------% +c | Update the block pointer | +c %--------------------------% +c + istart = iend + 1 +c +c %------------------------------------------% +c | Make sure that h(iend,1) is non-negative | +c | If not then set h(iend,1) <-- -h(iend,1) | +c | and negate the last column of Q. | +c | We have effectively carried out a | +c | similarity on transformation H | +c %------------------------------------------% +c + if (h(iend,1) .lt. zero) then + h(iend,1) = -h(iend,1) + call dscal(kplusp, -one, q(1,iend), 1) + end if +c +c %--------------------------------------------------------% +c | Apply the same shift to the next block if there is any | +c %--------------------------------------------------------% +c + if (iend .lt. kplusp) go to 20 +c +c %-----------------------------------------------------% +c | Check if we can increase the the start of the block | +c %-----------------------------------------------------% +c + do 80 i = itop, kplusp-1 + if (h(i+1,1) .gt. zero) go to 90 + itop = itop + 1 + 80 continue +c +c %-----------------------------------% +c | Finished applying the jj-th shift | +c %-----------------------------------% +c + 90 continue +c +c %------------------------------------------% +c | All shifts have been applied. Check for | +c | more possible deflation that might occur | +c | after the last shift is applied. | +c %------------------------------------------% +c + do 100 i = itop, kplusp-1 + big = abs(h(i,2)) + abs(h(i+1,2)) + if (h(i+1,1) .le. epsmch*big) then + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, i, ndigit, + & '_sapps: deflation at row/column no.') + call igraphdvout (logfil, 1, h(i+1,1), ndigit, + & '_sapps: the corresponding off diagonal element') + end if + h(i+1,1) = zero + end if + 100 continue +c +c %-------------------------------------------------% +c | Compute the (kev+1)-st column of (V*Q) and | +c | temporarily store the result in WORKD(N+1:2*N). | +c | This is not necessary if h(kev+1,1) = 0. | +c %-------------------------------------------------% +c + if ( h(kev+1,1) .gt. zero ) + & call dgemv ('N', n, kplusp, one, v, ldv, + & q(1,kev+1), 1, zero, workd(n+1), 1) +c +c %-------------------------------------------------------% +c | Compute column 1 to kev of (V*Q) in backward order | +c | taking advantage that Q is an upper triangular matrix | +c | with lower bandwidth np. | +c | Place results in v(:,kplusp-kev:kplusp) temporarily. | +c %-------------------------------------------------------% +c + do 130 i = 1, kev + call dgemv ('N', n, kplusp-i+1, one, v, ldv, + & q(1,kev-i+1), 1, zero, workd, 1) + call dcopy (n, workd, 1, v(1,kplusp-i+1), 1) + 130 continue +c +c %-------------------------------------------------% +c | Move v(:,kplusp-kev+1:kplusp) into v(:,1:kev). | +c %-------------------------------------------------% +c + call dlacpy ('All', n, kev, v(1,np+1), ldv, v, ldv) +c +c %--------------------------------------------% +c | Copy the (kev+1)-st column of (V*Q) in the | +c | appropriate place if h(kev+1,1) .ne. zero. | +c %--------------------------------------------% +c + if ( h(kev+1,1) .gt. zero ) + & call dcopy (n, workd(n+1), 1, v(1,kev+1), 1) +c +c %-------------------------------------% +c | Update the residual vector: | +c | r <- sigmak*r + betak*v(:,kev+1) | +c | where | +c | sigmak = (e_{kev+p}'*Q)*e_{kev} | +c | betak = e_{kev+1}'*H*e_{kev} | +c %-------------------------------------% +c + call dscal (n, q(kplusp,kev), resid, 1) + if (h(kev+1,1) .gt. zero) + & call daxpy (n, h(kev+1,1), v(1,kev+1), 1, resid, 1) +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, 1, q(kplusp,kev), ndigit, + & '_sapps: sigmak of the updated residual vector') + call igraphdvout (logfil, 1, h(kev+1,1), ndigit, + & '_sapps: betak of the updated residual vector') + call igraphdvout (logfil, kev, h(1,2), ndigit, + & '_sapps: updated main diagonal of H for next iteration') + if (kev .gt. 1) then + call igraphdvout (logfil, kev-1, h(2,1), ndigit, + & '_sapps: updated sub diagonal of H for next iteration') + end if + end if +c + call igraphsecond (t1) + tsapps = tsapps + (t1 - t0) +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsapps | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsaup2.f b/src/rigraph/vendor/arpack/dsaup2.f new file mode 100644 index 0000000..116dd31 --- /dev/null +++ b/src/rigraph/vendor/arpack/dsaup2.f @@ -0,0 +1,853 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsaup2 +c +c\Description: +c Intermediate level interface called by igraphdsaupd. +c +c\Usage: +c call igraphdsaup2 +c ( IDO, BMAT, N, WHICH, NEV, NP, TOL, RESID, MODE, IUPD, +c ISHIFT, MXITER, V, LDV, H, LDH, RITZ, BOUNDS, Q, LDQ, WORKL, +c IPNTR, WORKD, INFO ) +c +c\Arguments +c +c IDO, BMAT, N, WHICH, NEV, TOL, RESID: same as defined in igraphdsaupd. +c MODE, ISHIFT, MXITER: see the definition of IPARAM in igraphdsaupd. +c +c NP Integer. (INPUT/OUTPUT) +c Contains the number of implicit shifts to apply during +c each Arnoldi/Lanczos iteration. +c If ISHIFT=1, NP is adjusted dynamically at each iteration +c to accelerate convergence and prevent stagnation. +c This is also roughly equal to the number of matrix-vector +c products (involving the operator OP) per Arnoldi iteration. +c The logic for adjusting is contained within the current +c subroutine. +c If ISHIFT=0, NP is the number of shifts the user needs +c to provide via reverse comunication. 0 < NP < NCV-NEV. +c NP may be less than NCV-NEV since a leading block of the current +c upper Tridiagonal matrix has split off and contains "unwanted" +c Ritz values. +c Upon termination of the IRA iteration, NP contains the number +c of "converged" wanted Ritz values. +c +c IUPD Integer. (INPUT) +c IUPD .EQ. 0: use explicit restart instead implicit update. +c IUPD .NE. 0: use implicit update. +c +c V Double precision N by (NEV+NP) array. (INPUT/OUTPUT) +c The Lanczos basis vectors. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c H Double precision (NEV+NP) by 2 array. (OUTPUT) +c H is used to store the generated symmetric tridiagonal matrix +c The subdiagonal is stored in the first column of H starting +c at H(2,1). The main diagonal is stored in the igraphsecond column +c of H starting at H(1,2). If igraphdsaup2 converges store the +c B-norm of the final residual vector in H(1,1). +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c RITZ Double precision array of length NEV+NP. (OUTPUT) +c RITZ(1:NEV) contains the computed Ritz values of OP. +c +c BOUNDS Double precision array of length NEV+NP. (OUTPUT) +c BOUNDS(1:NEV) contain the error bounds corresponding to RITZ. +c +c Q Double precision (NEV+NP) by (NEV+NP) array. (WORKSPACE) +c Private (replicated) work array used to accumulate the +c rotation in the shift application step. +c +c LDQ Integer. (INPUT) +c Leading dimension of Q exactly as declared in the calling +c program. +c +c WORKL Double precision array of length at least 3*(NEV+NP). (INPUT/WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. It is used in the computation of the +c tridiagonal eigenvalue problem, the calculation and +c application of the shifts and convergence checking. +c If ISHIFT .EQ. O and IDO .EQ. 3, the first NP locations +c of WORKL are used in reverse communication to hold the user +c supplied shifts. +c +c IPNTR Integer array of length 3. (OUTPUT) +c Pointer to mark the starting locations in the WORKD for +c vectors used by the Lanczos iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X. +c IPNTR(2): pointer to the current result vector Y. +c IPNTR(3): pointer to the vector B * X when used in one of +c the spectral transformation modes. X is the current +c operand. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Lanczos iteration +c for reverse communication. The user should not use WORKD +c as temporary workspace during the iteration !!!!!!!!!! +c See Data Distribution Note in igraphdsaupd. +c +c INFO Integer. (INPUT/OUTPUT) +c If INFO .EQ. 0, a randomly initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c Error flag on output. +c = 0: Normal return. +c = 1: All possible eigenvalues of OP has been found. +c NP returns the size of the invariant subspace +c spanning the operator OP. +c = 2: No shifts could be applied. +c = -8: Error return from trid. eigenvalue calculation; +c This should never happen. +c = -9: Starting vector is zero. +c = -9999: Could not build an Lanczos factorization. +c Size that was built in returned in NP. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, +c 1980. +c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", +c Computer Physics Communications, 53 (1989), pp 169-179. +c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to +c Implement the Spectral Transformation", Math. Comp., 48 (1987), +c pp 663-673. +c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos +c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", +c SIAM J. Matr. Anal. Apps., January (1993). +c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines +c for Updating the QR decomposition", ACM TOMS, December 1990, +c Volume 16 Number 4, pp 369-377. +c +c\Routines called: +c igraphdgetv0 ARPACK initial vector generation routine. +c igraphdsaitr ARPACK Lanczos factorization routine. +c igraphdsapps ARPACK application of implicit shifts routine. +c igraphdsconv ARPACK convergence of Ritz values routine. +c igraphdseigt ARPACK compute Ritz values and error bounds routine. +c igraphdsgets ARPACK reorder Ritz values and error bounds routine. +c igraphdsortr ARPACK sorting routine. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c dcopy Level 1 BLAS that copies one vector to another. +c ddot Level 1 BLAS that computes the scalar product of two vectors. +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dscal Level 1 BLAS that scales a vector. +c dswap Level 1 BLAS that swaps two vectors. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/15/93: Version ' 2.4' +c xx/xx/95: Version ' 2.4'. (R.B. Lehoucq) +c +c\SCCS Information: @(#) +c FILE: saup2.F SID: 2.6 DATE OF SID: 8/16/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsaup2 + & ( ido, bmat, n, which, nev, np, tol, resid, mode, iupd, + & ishift, mxiter, v, ldv, h, ldh, ritz, bounds, + & q, ldq, workl, ipntr, workd, info ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1, which*2 + integer ido, info, ishift, iupd, ldh, ldq, ldv, mxiter, + & n, mode, nev, np + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer ipntr(3) + Double precision + & bounds(nev+np), h(ldh,2), q(ldq,nev+np), resid(n), + & ritz(nev+np), v(ldv,nev+np), workd(3*n), + & workl(3*(nev+np)) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + character wprime*2 + logical cnorm, getv0, initv, update, ushift + integer ierr, iter, j, kplusp, msglvl, nconv, nevbef, nev0, + & np0, nptemp, nevd2, nevm2, kp(3) + Double precision + & rnorm, temp, eps23 + save cnorm, getv0, initv, update, ushift, + & iter, kplusp, msglvl, nconv, nev0, np0, + & rnorm, eps23 +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, igraphdgetv0, igraphdsaitr, dscal, + & igraphdsconv, igraphdseigt, igraphdsgets, + & igraphdsapps, igraphdsortr, igraphdvout, igraphivout, + & igraphsecond, dswap +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & ddot, dnrm2, dlamch + external ddot, dnrm2, dlamch +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic min +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = msaup2 +c +c %---------------------------------% +c | Set machine dependent constant. | +c %---------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0/3.0D+0) +c +c %-------------------------------------% +c | nev0 and np0 are integer variables | +c | hold the initial values of NEV & NP | +c %-------------------------------------% +c + nev0 = nev + np0 = np +c +c %-------------------------------------% +c | kplusp is the bound on the largest | +c | Lanczos factorization built. | +c | nconv is the current number of | +c | "converged" eigenvlues. | +c | iter is the counter on the current | +c | iteration step. | +c %-------------------------------------% +c + kplusp = nev0 + np0 + nconv = 0 + iter = 0 +c +c %--------------------------------------------% +c | Set flags for computing the first NEV steps | +c | of the Lanczos factorization. | +c %--------------------------------------------% +c + getv0 = .true. + update = .false. + ushift = .false. + cnorm = .false. +c + if (info .ne. 0) then +c +c %--------------------------------------------% +c | User provides the initial residual vector. | +c %--------------------------------------------% +c + initv = .true. + info = 0 + else + initv = .false. + end if + end if +c +c %---------------------------------------------% +c | Get a possibly random starting vector and | +c | force it into the range of the operator OP. | +c %---------------------------------------------% +c + 10 continue +c + if (getv0) then + call igraphdgetv0 (ido, bmat, 1, initv, n, 1, v, ldv, resid, + & rnorm, ipntr, workd, info) +c + if (ido .ne. 99) go to 9000 +c + if (rnorm .eq. zero) then +c +c %-----------------------------------------% +c | The initial vector is zero. Error exit. | +c %-----------------------------------------% +c + info = -9 + go to 1200 + end if + getv0 = .false. + ido = 0 + end if +c +c %------------------------------------------------------------% +c | Back from reverse communication: continue with update step | +c %------------------------------------------------------------% +c + if (update) go to 20 +c +c %-------------------------------------------% +c | Back from computing user specified shifts | +c %-------------------------------------------% +c + if (ushift) go to 50 +c +c %-------------------------------------% +c | Back from computing residual norm | +c | at the end of the current iteration | +c %-------------------------------------% +c + if (cnorm) go to 100 +c +c %----------------------------------------------------------% +c | Compute the first NEV steps of the Lanczos factorization | +c %----------------------------------------------------------% +c + call igraphdsaitr (ido, bmat, n, 0, nev0, mode, resid, rnorm, v, + & ldv, h, ldh, ipntr, workd, info) +c +c %---------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP and possibly B | +c %---------------------------------------------------% +c + if (ido .ne. 99) go to 9000 +c + if (info .gt. 0) then +c +c %-----------------------------------------------------% +c | igraphdsaitr was unable to build an Lanczos factorization | +c | of length NEV0. INFO is returned with the size of | +c | the factorization built. Exit main loop. | +c %-----------------------------------------------------% +c + np = info + mxiter = iter + info = -9999 + go to 1200 + end if +c +c %--------------------------------------------------------------% +c | | +c | M A I N LANCZOS I T E R A T I O N L O O P | +c | Each iteration implicitly restarts the Lanczos | +c | factorization in place. | +c | | +c %--------------------------------------------------------------% +c + 1000 continue +c + iter = iter + 1 +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [iter], ndigit, + & '_saup2: **** Start of major iteration number ****') + end if + if (msglvl .gt. 1) then + call igraphivout (logfil, 1, [nev], ndigit, + & '_saup2: The length of the current Lanczos factorization') + call igraphivout (logfil, 1, [np], ndigit, + & '_saup2: Extend the Lanczos factorization by') + end if +c +c %------------------------------------------------------------% +c | Compute NP additional steps of the Lanczos factorization. | +c %------------------------------------------------------------% +c + ido = 0 + 20 continue + update = .true. +c + call igraphdsaitr (ido, bmat, n, nev, np, mode, resid, rnorm, + & v, ldv, h, ldh, ipntr, workd, info) +c +c %---------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP and possibly B | +c %---------------------------------------------------% +c + if (ido .ne. 99) go to 9000 +c + if (info .gt. 0) then +c +c %-----------------------------------------------------% +c | igraphdsaitr was unable to build an Lanczos factorization | +c | of length NEV0+NP0. INFO is returned with the size | +c | of the factorization built. Exit main loop. | +c %-----------------------------------------------------% +c + np = info + mxiter = iter + info = -9999 + go to 1200 + end if + update = .false. +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_saup2: Current B-norm of residual for factorization') + end if +c +c %--------------------------------------------------------% +c | Compute the eigenvalues and corresponding error bounds | +c | of the current symmetric tridiagonal matrix. | +c %--------------------------------------------------------% +c + call igraphdseigt (rnorm, kplusp, h, ldh, ritz, bounds, workl, + & ierr) +c + if (ierr .ne. 0) then + info = -8 + go to 1200 + end if +c +c %----------------------------------------------------% +c | Make a copy of eigenvalues and corresponding error | +c | bounds obtained from _seigt. | +c %----------------------------------------------------% +c + call dcopy(kplusp, ritz, 1, workl(kplusp+1), 1) + call dcopy(kplusp, bounds, 1, workl(2*kplusp+1), 1) +c +c %---------------------------------------------------% +c | Select the wanted Ritz values and their bounds | +c | to be used in the convergence test. | +c | The selection is based on the requested number of | +c | eigenvalues instead of the current NEV and NP to | +c | prevent possible misconvergence. | +c | * Wanted Ritz values := RITZ(NP+1:NEV+NP) | +c | * Shifts := RITZ(1:NP) := WORKL(1:NP) | +c %---------------------------------------------------% +c + nev = nev0 + np = np0 + call igraphdsgets (ishift, which, nev, np, ritz, bounds, workl) +c +c %-------------------% +c | Convergence test. | +c %-------------------% +c + call dcopy (nev, bounds(np+1), 1, workl(np+1), 1) + call igraphdsconv (nev, ritz(np+1), workl(np+1), tol, nconv) +c + if (msglvl .gt. 2) then + kp(1) = nev + kp(2) = np + kp(3) = nconv + call igraphivout (logfil, 3, kp, ndigit, + & '_saup2: NEV, NP, NCONV are') + call igraphdvout (logfil, kplusp, ritz, ndigit, + & '_saup2: The eigenvalues of H') + call igraphdvout (logfil, kplusp, bounds, ndigit, + & '_saup2: Ritz estimates of the current NCV Ritz values') + end if +c +c %---------------------------------------------------------% +c | Count the number of unwanted Ritz values that have zero | +c | Ritz estimates. If any Ritz estimates are equal to zero | +c | then a leading block of H of order equal to at least | +c | the number of Ritz values with zero Ritz estimates has | +c | split off. None of these Ritz values may be removed by | +c | shifting. Decrease NP the number of shifts to apply. If | +c | no shifts may be applied, then prepare to exit | +c %---------------------------------------------------------% +c + nptemp = np + do 30 j=1, nptemp + if (bounds(j) .eq. zero) then + np = np - 1 + nev = nev + 1 + end if + 30 continue +c + if ( (nconv .ge. nev0) .or. + & (iter .gt. mxiter) .or. + & (np .eq. 0) ) then +c +c %------------------------------------------------% +c | Prepare to exit. Put the converged Ritz values | +c | and corresponding bounds in RITZ(1:NCONV) and | +c | BOUNDS(1:NCONV) respectively. Then sort. Be | +c | careful when NCONV > NP since we don't want to | +c | swap overlapping locations. | +c %------------------------------------------------% +c + if (which .eq. 'BE') then +c +c %-----------------------------------------------------% +c | Both ends of the spectrum are requested. | +c | Sort the eigenvalues into algebraically decreasing | +c | order first then swap low end of the spectrum next | +c | to high end in appropriate locations. | +c | NOTE: when np < floor(nev/2) be careful not to swap | +c | overlapping locations. | +c %-----------------------------------------------------% +c + wprime = 'SA' + call igraphdsortr (wprime, .true., kplusp, ritz, bounds) + nevd2 = nev / 2 + nevm2 = nev - nevd2 + if ( nev .gt. 1 ) then + call dswap ( min(nevd2,np), ritz(nevm2+1), 1, + & ritz( max(kplusp-nevd2+1,kplusp-np+1) ), 1) + call dswap ( min(nevd2,np), bounds(nevm2+1), 1, + & bounds( max(kplusp-nevd2+1,kplusp-np)+1 ), 1) + end if +c + else +c +c %--------------------------------------------------% +c | LM, SM, LA, SA case. | +c | Sort the eigenvalues of H into the an order that | +c | is opposite to WHICH, and apply the resulting | +c | order to BOUNDS. The eigenvalues are sorted so | +c | that the wanted part are always within the first | +c | NEV locations. | +c %--------------------------------------------------% +c + if (which .eq. 'LM') wprime = 'SM' + if (which .eq. 'SM') wprime = 'LM' + if (which .eq. 'LA') wprime = 'SA' + if (which .eq. 'SA') wprime = 'LA' +c + call igraphdsortr (wprime, .true., kplusp, ritz, bounds) +c + end if +c +c %--------------------------------------------------% +c | Scale the Ritz estimate of each Ritz value | +c | by 1 / max(eps23,magnitude of the Ritz value). | +c %--------------------------------------------------% +c + do 35 j = 1, nev0 + temp = max( eps23, abs(ritz(j)) ) + bounds(j) = bounds(j)/temp + 35 continue +c +c %----------------------------------------------------% +c | Sort the Ritz values according to the scaled Ritz | +c | esitmates. This will push all the converged ones | +c | towards the front of ritzr, ritzi, bounds | +c | (in the case when NCONV < NEV.) | +c %----------------------------------------------------% +c + wprime = 'LA' + call igraphdsortr(wprime, .true., nev0, bounds, ritz) +c +c %----------------------------------------------% +c | Scale the Ritz estimate back to its original | +c | value. | +c %----------------------------------------------% +c + do 40 j = 1, nev0 + temp = max( eps23, abs(ritz(j)) ) + bounds(j) = bounds(j)*temp + 40 continue +c +c %--------------------------------------------------% +c | Sort the "converged" Ritz values again so that | +c | the "threshold" values and their associated Ritz | +c | estimates appear at the appropriate position in | +c | ritz and bound. | +c %--------------------------------------------------% +c + if (which .eq. 'BE') then +c +c %------------------------------------------------% +c | Sort the "converged" Ritz values in increasing | +c | order. The "threshold" values are in the | +c | middle. | +c %------------------------------------------------% +c + wprime = 'LA' + call igraphdsortr(wprime, .true., nconv, ritz, bounds) +c + else +c +c %----------------------------------------------% +c | In LM, SM, LA, SA case, sort the "converged" | +c | Ritz values according to WHICH so that the | +c | "threshold" value appears at the front of | +c | ritz. | +c %----------------------------------------------% + + call igraphdsortr(which, .true., nconv, ritz, bounds) +c + end if +c +c %------------------------------------------% +c | Use h( 1,1 ) as storage to communicate | +c | rnorm to _seupd if needed | +c %------------------------------------------% +c + h(1,1) = rnorm +c + if (msglvl .gt. 1) then + call igraphdvout (logfil, kplusp, ritz, ndigit, + & '_saup2: Sorted Ritz values.') + call igraphdvout (logfil, kplusp, bounds, ndigit, + & '_saup2: Sorted ritz estimates.') + end if +c +c %------------------------------------% +c | Max iterations have been exceeded. | +c %------------------------------------% +c + if (iter .gt. mxiter .and. nconv .lt. nev) info = 1 +c +c %---------------------% +c | No shifts to apply. | +c %---------------------% +c + if (np .eq. 0 .and. nconv .lt. nev0) info = 2 +c + np = nconv + go to 1100 +c + else if (nconv .lt. nev .and. ishift .eq. 1) then +c +c %---------------------------------------------------% +c | Do not have all the requested eigenvalues yet. | +c | To prevent possible stagnation, adjust the number | +c | of Ritz values and the shifts. | +c %---------------------------------------------------% +c + nevbef = nev + nev = nev + min (nconv, np/2) + if (nev .eq. 1 .and. kplusp .ge. 6) then + nev = kplusp / 2 + else if (nev .eq. 1 .and. kplusp .gt. 2) then + nev = 2 + end if + np = kplusp - nev +c +c %---------------------------------------% +c | If the size of NEV was just increased | +c | resort the eigenvalues. | +c %---------------------------------------% +c + if (nevbef .lt. nev) + & call igraphdsgets (ishift, which, nev, np, ritz, bounds, + & workl) +c + end if +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, [nconv], ndigit, + & '_saup2: no. of "converged" Ritz values at this iter.') + if (msglvl .gt. 1) then + kp(1) = nev + kp(2) = np + call igraphivout (logfil, 2, kp, ndigit, + & '_saup2: NEV and NP are') + call igraphdvout (logfil, nev, ritz(np+1), ndigit, + & '_saup2: "wanted" Ritz values.') + call igraphdvout (logfil, nev, bounds(np+1), ndigit, + & '_saup2: Ritz estimates of the "wanted" values ') + end if + end if + +c + if (ishift .eq. 0) then +c +c %-----------------------------------------------------% +c | User specified shifts: reverse communication to | +c | compute the shifts. They are returned in the first | +c | NP locations of WORKL. | +c %-----------------------------------------------------% +c + ushift = .true. + ido = 3 + go to 9000 + end if +c + 50 continue +c +c %------------------------------------% +c | Back from reverse communication; | +c | User specified shifts are returned | +c | in WORKL(1:*NP) | +c %------------------------------------% +c + ushift = .false. +c +c +c %---------------------------------------------------------% +c | Move the NP shifts to the first NP locations of RITZ to | +c | free up WORKL. This is for the non-exact shift case; | +c | in the exact shift case, igraphdsgets already handles this. | +c %---------------------------------------------------------% +c + if (ishift .eq. 0) call dcopy (np, workl, 1, ritz, 1) +c + if (msglvl .gt. 2) then + call igraphivout (logfil, 1, [np], ndigit, + & '_saup2: The number of shifts to apply ') + call igraphdvout (logfil, np, workl, ndigit, + & '_saup2: shifts selected') + if (ishift .eq. 1) then + call igraphdvout (logfil, np, bounds, ndigit, + & '_saup2: corresponding Ritz estimates') + end if + end if +c +c %---------------------------------------------------------% +c | Apply the NP0 implicit shifts by QR bulge chasing. | +c | Each shift is applied to the entire tridiagonal matrix. | +c | The first 2*N locations of WORKD are used as workspace. | +c | After igraphdsapps is done, we have a Lanczos | +c | factorization of length NEV. | +c %---------------------------------------------------------% +c + call igraphdsapps (n, nev, np, ritz, v, ldv, h, ldh, resid, + & q, ldq, workd) +c +c %---------------------------------------------% +c | Compute the B-norm of the updated residual. | +c | Keep B*RESID in WORKD(1:N) to be used in | +c | the first step of the next call to igraphdsaitr. | +c %---------------------------------------------% +c + cnorm = .true. + call igraphsecond (t2) + if (bmat .eq. 'G') then + nbx = nbx + 1 + call dcopy (n, resid, 1, workd(n+1), 1) + ipntr(1) = n + 1 + ipntr(2) = 1 + ido = 2 +c +c %----------------------------------% +c | Exit in order to compute B*RESID | +c %----------------------------------% +c + go to 9000 + else if (bmat .eq. 'I') then + call dcopy (n, resid, 1, workd, 1) + end if +c + 100 continue +c +c %----------------------------------% +c | Back from reverse communication; | +c | WORKD(1:N) := B*RESID | +c %----------------------------------% +c + if (bmat .eq. 'G') then + call igraphsecond (t3) + tmvbx = tmvbx + (t3 - t2) + end if +c + if (bmat .eq. 'G') then + rnorm = ddot (n, resid, 1, workd, 1) + rnorm = sqrt(abs(rnorm)) + else if (bmat .eq. 'I') then + rnorm = dnrm2(n, resid, 1) + end if + cnorm = .false. + 130 continue +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, 1, [rnorm], ndigit, + & '_saup2: B-norm of residual for NEV factorization') + call igraphdvout (logfil, nev, h(1,2), ndigit, + & '_saup2: main diagonal of compressed H matrix') + call igraphdvout (logfil, nev-1, h(2,1), ndigit, + & '_saup2: subdiagonal of compressed H matrix') + end if +c + go to 1000 +c +c %---------------------------------------------------------------% +c | | +c | E N D O F M A I N I T E R A T I O N L O O P | +c | | +c %---------------------------------------------------------------% +c + 1100 continue +c + mxiter = iter + nev = nconv +c + 1200 continue + ido = 99 +c +c %------------% +c | Error exit | +c %------------% +c + call igraphsecond (t1) + tsaup2 = t1 - t0 +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsaup2 | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsaupd.f b/src/rigraph/vendor/arpack/dsaupd.f new file mode 100644 index 0000000..7e85781 --- /dev/null +++ b/src/rigraph/vendor/arpack/dsaupd.f @@ -0,0 +1,653 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsaupd +c +c\Description: +c +c Reverse communication interface for the Implicitly Restarted Arnoldi +c Iteration. For symmetric problems this reduces to a variant of the Lanczos +c method. This method has been designed to compute approximations to a +c few eigenpairs of a linear operator OP that is real and symmetric +c with respect to a real positive semi-definite symmetric matrix B, +c i.e. +c +c B*OP = (OP')*B. +c +c Another way to express this condition is +c +c < x,OPy > = < OPx,y > where < z,w > = z'Bw . +c +c In the standard eigenproblem B is the identity matrix. +c ( A' denotes transpose of A) +c +c The computed approximate eigenvalues are called Ritz values and +c the corresponding approximate eigenvectors are called Ritz vectors. +c +c igraphdsaupd is usually called iteratively to solve one of the +c following problems: +c +c Mode 1: A*x = lambda*x, A symmetric +c ===> OP = A and B = I. +c +c Mode 2: A*x = lambda*M*x, A symmetric, M symmetric positive definite +c ===> OP = inv[M]*A and B = M. +c ===> (If M can be factored see remark 3 below) +c +c Mode 3: K*x = lambda*M*x, K symmetric, M symmetric positive semi-definite +c ===> OP = (inv[K - sigma*M])*M and B = M. +c ===> Shift-and-Invert mode +c +c Mode 4: K*x = lambda*KG*x, K symmetric positive semi-definite, +c KG symmetric indefinite +c ===> OP = (inv[K - sigma*KG])*K and B = K. +c ===> Buckling mode +c +c Mode 5: A*x = lambda*M*x, A symmetric, M symmetric positive semi-definite +c ===> OP = inv[A - sigma*M]*[A + sigma*M] and B = M. +c ===> Cayley transformed mode +c +c NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v +c should be accomplished either by a direct method +c using a sparse matrix factorization and solving +c +c [A - sigma*M]*w = v or M*w = v, +c +c or through an iterative method for solving these +c systems. If an iterative method is used, the +c convergence test must be more stringent than +c the accuracy requirements for the eigenvalue +c approximations. +c +c\Usage: +c call igraphdsaupd +c ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, +c IPNTR, WORKD, WORKL, LWORKL, INFO ) +c +c\Arguments +c IDO Integer. (INPUT/OUTPUT) +c Reverse communication flag. IDO must be zero on the first +c call to igraphdsaupd. IDO will be set internally to +c indicate the type of operation to be performed. Control is +c then given back to the calling routine which has the +c responsibility to carry out the requested operation and call +c igraphdsaupd with the result. The operand is given in +c WORKD(IPNTR(1)), the result must be put in WORKD(IPNTR(2)). +c (If Mode = 2 see remark 5 below) +c ------------------------------------------------------------- +c IDO = 0: first call to the reverse communication interface +c IDO = -1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c This is for the initialization phase to force the +c starting vector into the range of OP. +c IDO = 1: compute Y = OP * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c In mode 3,4 and 5, the vector B * X is already +c available in WORKD(ipntr(3)). It does not +c need to be recomputed in forming OP * X. +c IDO = 2: compute Y = B * X where +c IPNTR(1) is the pointer into WORKD for X, +c IPNTR(2) is the pointer into WORKD for Y. +c IDO = 3: compute the IPARAM(8) shifts where +c IPNTR(11) is the pointer into WORKL for +c placing the shifts. See remark 6 below. +c IDO = 99: done +c ------------------------------------------------------------- +c +c BMAT Character*1. (INPUT) +c BMAT specifies the type of the matrix B that defines the +c semi-inner product for the operator OP. +c B = 'I' -> standard eigenvalue problem A*x = lambda*x +c B = 'G' -> generalized eigenvalue problem A*x = lambda*B*x +c +c N Integer. (INPUT) +c Dimension of the eigenproblem. +c +c WHICH Character*2. (INPUT) +c Specify which of the Ritz values of OP to compute. +c +c 'LA' - compute the NEV largest (algebraic) eigenvalues. +c 'SA' - compute the NEV smallest (algebraic) eigenvalues. +c 'LM' - compute the NEV largest (in magnitude) eigenvalues. +c 'SM' - compute the NEV smallest (in magnitude) eigenvalues. +c 'BE' - compute NEV eigenvalues, half from each end of the +c spectrum. When NEV is odd, compute one more from the +c high end than from the low end. +c (see remark 1 below) +c +c NEV Integer. (INPUT) +c Number of eigenvalues of OP to be computed. 0 < NEV < N. +c +c TOL Double precision scalar. (INPUT) +c Stopping criterion: the relative accuracy of the Ritz value +c is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I)). +c If TOL .LE. 0. is passed a default is set: +c DEFAULT = DLAMCH('EPS') (machine precision as computed +c by the LAPACK auxiliary subroutine DLAMCH). +c +c RESID Double precision array of length N. (INPUT/OUTPUT) +c On INPUT: +c If INFO .EQ. 0, a random initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c On OUTPUT: +c RESID contains the final residual vector. +c +c NCV Integer. (INPUT) +c Number of columns of the matrix V (less than or equal to N). +c This will indicate how many Lanczos vectors are generated +c at each iteration. After the startup phase in which NEV +c Lanczos vectors are generated, the algorithm generates +c NCV-NEV Lanczos vectors at each subsequent update iteration. +c Most of the cost in generating each Lanczos vector is in the +c matrix-vector product OP*x. (See remark 4 below). +c +c V Double precision N by NCV array. (OUTPUT) +c The NCV columns of V contain the Lanczos basis vectors. +c +c LDV Integer. (INPUT) +c Leading dimension of V exactly as declared in the calling +c program. +c +c IPARAM Integer array of length 11. (INPUT/OUTPUT) +c IPARAM(1) = ISHIFT: method for selecting the implicit shifts. +c The shifts selected at each iteration are used to restart +c the Arnoldi iteration in an implicit fashion. +c ------------------------------------------------------------- +c ISHIFT = 0: the shifts are provided by the user via +c reverse communication. The NCV eigenvalues of +c the current tridiagonal matrix T are returned in +c the part of WORKL array corresponding to RITZ. +c See remark 6 below. +c ISHIFT = 1: exact shifts with respect to the reduced +c tridiagonal matrix T. This is equivalent to +c restarting the iteration with a starting vector +c that is a linear combination of Ritz vectors +c associated with the "wanted" Ritz values. +c ------------------------------------------------------------- +c +c IPARAM(2) = LEVEC +c No longer referenced. See remark 2 below. +c +c IPARAM(3) = MXITER +c On INPUT: maximum number of Arnoldi update iterations allowed. +c On OUTPUT: actual number of Arnoldi update iterations taken. +c +c IPARAM(4) = NB: blocksize to be used in the recurrence. +c The code currently works only for NB = 1. +c +c IPARAM(5) = NCONV: number of "converged" Ritz values. +c This represents the number of Ritz values that satisfy +c the convergence criterion. +c +c IPARAM(6) = IUPD +c No longer referenced. Implicit restarting is ALWAYS used. +c +c IPARAM(7) = MODE +c On INPUT determines what type of eigenproblem is being solved. +c Must be 1,2,3,4,5; See under \Description of igraphdsaupd for the +c five modes available. +c +c IPARAM(8) = NP +c When ido = 3 and the user provides shifts through reverse +c communication (IPARAM(1)=0), igraphdsaupd returns NP, the number +c of shifts the user is to provide. 0 < NP <=NCV-NEV. See Remark +c 6 below. +c +c IPARAM(9) = NUMOP, IPARAM(10) = NUMOPB, IPARAM(11) = NUMREO, +c OUTPUT: NUMOP = total number of OP*x operations, +c NUMOPB = total number of B*x operations if BMAT='G', +c NUMREO = total number of steps of re-orthogonalization. +c +c IPNTR Integer array of length 11. (OUTPUT) +c Pointer to mark the starting locations in the WORKD and WORKL +c arrays for matrices/vectors used by the Lanczos iteration. +c ------------------------------------------------------------- +c IPNTR(1): pointer to the current operand vector X in WORKD. +c IPNTR(2): pointer to the current result vector Y in WORKD. +c IPNTR(3): pointer to the vector B * X in WORKD when used in +c the shift-and-invert mode. +c IPNTR(4): pointer to the next available location in WORKL +c that is untouched by the program. +c IPNTR(5): pointer to the NCV by 2 tridiagonal matrix T in WORKL. +c IPNTR(6): pointer to the NCV RITZ values array in WORKL. +c IPNTR(7): pointer to the Ritz estimates in array WORKL associated +c with the Ritz values located in RITZ in WORKL. +c IPNTR(11): pointer to the NP shifts in WORKL. See Remark 6 below. +c +c Note: IPNTR(8:10) is only referenced by igraphdseupd. See Remark 2. +c IPNTR(8): pointer to the NCV RITZ values of the original system. +c IPNTR(9): pointer to the NCV corresponding error bounds. +c IPNTR(10): pointer to the NCV by NCV matrix of eigenvectors +c of the tridiagonal matrix T. Only referenced by +c igraphdseupd if RVEC = .TRUE. See Remarks. +c ------------------------------------------------------------- +c +c WORKD Double precision work array of length 3*N. (REVERSE COMMUNICATION) +c Distributed array to be used in the basic Arnoldi iteration +c for reverse communication. The user should not use WORKD +c as temporary workspace during the iteration. Upon termination +c WORKD(1:N) contains B*RESID(1:N). If the Ritz vectors are desired +c subroutine igraphdseupd uses this output. +c See Data Distribution Note below. +c +c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. See Data Distribution Note below. +c +c LWORKL Integer. (INPUT) +c LWORKL must be at least NCV**2 + 8*NCV . +c +c INFO Integer. (INPUT/OUTPUT) +c If INFO .EQ. 0, a randomly initial residual vector is used. +c If INFO .NE. 0, RESID contains the initial residual vector, +c possibly from a previous run. +c Error flag on output. +c = 0: Normal exit. +c = 1: Maximum number of iterations taken. +c All possible eigenvalues of OP has been found. IPARAM(5) +c returns the number of wanted converged Ritz values. +c = 2: No longer an informational error. Deprecated starting +c with release 2 of ARPACK. +c = 3: No shifts could be applied during a cycle of the +c Implicitly restarted Arnoldi iteration. One possibility +c is to increase the size of NCV relative to NEV. +c See remark 4 below. +c = -1: N must be positive. +c = -2: NEV must be positive. +c = -3: NCV must be greater than NEV and less than or equal to N. +c = -4: The maximum number of Arnoldi update iterations allowed +c must be greater than zero. +c = -5: WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'. +c = -6: BMAT must be one of 'I' or 'G'. +c = -7: Length of private work array WORKL is not sufficient. +c = -8: Error return from trid. eigenvalue calculation; +c Informatinal error from LAPACK routine dsteqr. +c = -9: Starting vector is zero. +c = -10: IPARAM(7) must be 1,2,3,4,5. +c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatable. +c = -12: IPARAM(1) must be equal to 0 or 1. +c = -13: NEV and WHICH = 'BE' are incompatable. +c = -9999: Could not build an Arnoldi factorization. +c IPARAM(5) returns the size of the current Arnoldi +c factorization. The user is advised to check that +c enough workspace and array storage has been allocated. +c +c +c\Remarks +c 1. The converged Ritz values are always returned in ascending +c algebraic order. The computed Ritz values are approximate +c eigenvalues of OP. The selection of WHICH should be made +c with this in mind when Mode = 3,4,5. After convergence, +c approximate eigenvalues of the original problem may be obtained +c with the ARPACK subroutine igraphdseupd. +c +c 2. If the Ritz vectors corresponding to the converged Ritz values +c are needed, the user must call igraphdseupd immediately following completion +c of igraphdsaupd. This is new starting with version 2.1 of ARPACK. +c +c 3. If M can be factored into a Cholesky factorization M = LL' +c then Mode = 2 should not be selected. Instead one should use +c Mode = 1 with OP = inv(L)*A*inv(L'). Appropriate triangular +c linear systems should be solved with L and L' rather +c than computing inverses. After convergence, an approximate +c eigenvector z of the original problem is recovered by solving +c L'z = x where x is a Ritz vector of OP. +c +c 4. At present there is no a-priori analysis to guide the selection +c of NCV relative to NEV. The only formal requrement is that NCV > NEV. +c However, it is recommended that NCV .ge. 2*NEV. If many problems of +c the same type are to be solved, one should experiment with increasing +c NCV while keeping NEV fixed for a given test problem. This will +c usually decrease the required number of OP*x operations but it +c also increases the work and storage required to maintain the orthogonal +c basis vectors. The optimal "cross-over" with respect to CPU time +c is problem dependent and must be determined empirically. +c +c 5. If IPARAM(7) = 2 then in the Reverse commuication interface the user +c must do the following. When IDO = 1, Y = OP * X is to be computed. +c When IPARAM(7) = 2 OP = inv(B)*A. After computing A*X the user +c must overwrite X with A*X. Y is then the solution to the linear set +c of equations B*Y = A*X. +c +c 6. When IPARAM(1) = 0, and IDO = 3, the user needs to provide the +c NP = IPARAM(8) shifts in locations: +c 1 WORKL(IPNTR(11)) +c 2 WORKL(IPNTR(11)+1) +c . +c . +c . +c NP WORKL(IPNTR(11)+NP-1). +c +c The eigenvalues of the current tridiagonal matrix are located in +c WORKL(IPNTR(6)) through WORKL(IPNTR(6)+NCV-1). They are in the +c order defined by WHICH. The associated Ritz estimates are located in +c WORKL(IPNTR(8)), WORKL(IPNTR(8)+1), ... , WORKL(IPNTR(8)+NCV-1). +c +c----------------------------------------------------------------------- +c +c\Data Distribution Note: +c +c Fortran-D syntax: +c ================ +c REAL RESID(N), V(LDV,NCV), WORKD(3*N), WORKL(LWORKL) +c DECOMPOSE D1(N), D2(N,NCV) +c ALIGN RESID(I) with D1(I) +c ALIGN V(I,J) with D2(I,J) +c ALIGN WORKD(I) with D1(I) range (1:N) +c ALIGN WORKD(I) with D1(I-N) range (N+1:2*N) +c ALIGN WORKD(I) with D1(I-2*N) range (2*N+1:3*N) +c DISTRIBUTE D1(BLOCK), D2(BLOCK,:) +c REPLICATED WORKL(LWORKL) +c +c Cray MPP syntax: +c =============== +c REAL RESID(N), V(LDV,NCV), WORKD(N,3), WORKL(LWORKL) +c SHARED RESID(BLOCK), V(BLOCK,:), WORKD(BLOCK,:) +c REPLICATED WORKL(LWORKL) +c +c +c\BeginLib +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, +c 1980. +c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", +c Computer Physics Communications, 53 (1989), pp 169-179. +c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to +c Implement the Spectral Transformation", Math. Comp., 48 (1987), +c pp 663-673. +c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos +c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", +c SIAM J. Matr. Anal. Apps., January (1993). +c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines +c for Updating the QR decomposition", ACM TOMS, December 1990, +c Volume 16 Number 4, pp 369-377. +c 8. R.B. Lehoucq, D.C. Sorensen, "Implementation of Some Spectral +c Transformations in a k-Step Arnoldi Method". In Preparation. +c +c\Routines called: +c igraphdsaup2 ARPACK routine that implements the Implicitly Restarted +c Arnoldi Iteration. +c igraphdstats ARPACK routine that initialize timing and other statistics +c variables. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dlamch LAPACK routine that determines machine constants. +c +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/15/93: Version ' 2.4' +c +c\SCCS Information: @(#) +c FILE: saupd.F SID: 2.7 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c 1. None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsaupd + & ( ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat*1, which*2 + integer ido, info, ldv, lworkl, n, ncv, nev + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer iparam(11), ipntr(11) + Double precision + & resid(n), v(ldv,ncv), workd(3*n), workl(lworkl) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer bounds, ierr, ih, iq, ishift, iupd, iw, + & ldh, ldq, msglvl, mxiter, mode, nb, + & nev0, next, np, ritz, j + save bounds, ierr, ih, iq, ishift, iupd, iw, + & ldh, ldq, msglvl, mxiter, mode, nb, + & nev0, next, np, ritz +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external igraphdsaup2, igraphdvout, igraphivout, + & igraphsecond, igraphdstats +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlamch + external dlamch +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + if (ido .eq. 0) then +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphdstats + call igraphsecond (t0) + msglvl = msaupd +c + ierr = 0 + ishift = iparam(1) + mxiter = iparam(3) + nb = iparam(4) +c +c %--------------------------------------------% +c | Revision 2 performs only implicit restart. | +c %--------------------------------------------% +c + iupd = 1 + mode = iparam(7) +c +c %----------------% +c | Error checking | +c %----------------% +c + if (n .le. 0) then + ierr = -1 + else if (nev .le. 0) then + ierr = -2 + else if (ncv .le. nev .or. ncv .gt. n) then + ierr = -3 + end if +c +c %----------------------------------------------% +c | NP is the number of additional steps to | +c | extend the length NEV Lanczos factorization. | +c %----------------------------------------------% +c + np = ncv - nev +c + if (mxiter .le. 0) ierr = -4 + if (which .ne. 'LM' .and. + & which .ne. 'SM' .and. + & which .ne. 'LA' .and. + & which .ne. 'SA' .and. + & which .ne. 'BE') ierr = -5 + if (bmat .ne. 'I' .and. bmat .ne. 'G') ierr = -6 +c + if (lworkl .lt. ncv**2 + 8*ncv) ierr = -7 + if (mode .lt. 1 .or. mode .gt. 5) then + ierr = -10 + else if (mode .eq. 1 .and. bmat .eq. 'G') then + ierr = -11 + else if (ishift .lt. 0 .or. ishift .gt. 1) then + ierr = -12 + else if (nev .eq. 1 .and. which .eq. 'BE') then + ierr = -13 + end if +c +c %------------% +c | Error Exit | +c %------------% +c + if (ierr .ne. 0) then + info = ierr + ido = 99 + go to 9000 + end if +c +c %------------------------% +c | Set default parameters | +c %------------------------% +c + if (nb .le. 0) nb = 1 + if (tol .le. zero) tol = dlamch('EpsMach') +c +c %----------------------------------------------% +c | NP is the number of additional steps to | +c | extend the length NEV Lanczos factorization. | +c | NEV0 is the local variable designating the | +c | size of the invariant subspace desired. | +c %----------------------------------------------% +c + np = ncv - nev + nev0 = nev +c +c %-----------------------------% +c | Zero out internal workspace | +c %-----------------------------% +c + do 10 j = 1, ncv**2 + 8*ncv + workl(j) = zero + 10 continue +c +c %-------------------------------------------------------% +c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | +c | etc... and the remaining workspace. | +c | Also update pointer to be used on output. | +c | Memory is laid out as follows: | +c | workl(1:2*ncv) := generated tridiagonal matrix | +c | workl(2*ncv+1:2*ncv+ncv) := ritz values | +c | workl(3*ncv+1:3*ncv+ncv) := computed error bounds | +c | workl(4*ncv+1:4*ncv+ncv*ncv) := rotation matrix Q | +c | workl(4*ncv+ncv*ncv+1:7*ncv+ncv*ncv) := workspace | +c %-------------------------------------------------------% +c + ldh = ncv + ldq = ncv + ih = 1 + ritz = ih + 2*ldh + bounds = ritz + ncv + iq = bounds + ncv + iw = iq + ncv**2 + next = iw + 3*ncv +c + ipntr(4) = next + ipntr(5) = ih + ipntr(6) = ritz + ipntr(7) = bounds + ipntr(11) = iw + end if +c +c %-------------------------------------------------------% +c | Carry out the Implicitly restarted Lanczos Iteration. | +c %-------------------------------------------------------% +c + call igraphdsaup2 + & ( ido, bmat, n, which, nev0, np, tol, resid, mode, iupd, + & ishift, mxiter, v, ldv, workl(ih), ldh, workl(ritz), + & workl(bounds), workl(iq), ldq, workl(iw), ipntr, workd, + & info ) +c +c %--------------------------------------------------% +c | ido .ne. 99 implies use of reverse communication | +c | to compute operations involving OP or shifts. | +c %--------------------------------------------------% +c + if (ido .eq. 3) iparam(8) = np + if (ido .ne. 99) go to 9000 +c + iparam(3) = mxiter + iparam(5) = np + iparam(9) = nopx + iparam(10) = nbx + iparam(11) = nrorth +c +c %------------------------------------% +c | Exit if there was an informational | +c | error within igraphdsaup2. | +c %------------------------------------% +c + if (info .lt. 0) go to 9000 + if (info .eq. 2) info = 3 +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, mxiter, ndigit, + & '_saupd: number of update iterations taken') + call igraphivout (logfil, 1, np, ndigit, + & '_saupd: number of "converged" Ritz values') + call igraphdvout (logfil, np, workl(Ritz), ndigit, + & '_saupd: final Ritz values') + call igraphdvout (logfil, np, workl(Bounds), ndigit, + & '_saupd: corresponding error bounds') + end if +c + call igraphsecond (t1) + tsaupd = t1 - t0 +c +c + 9000 continue +c + return +c +c %---------------% +c | End of igraphdsaupd | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsconv.f b/src/rigraph/vendor/arpack/dsconv.f new file mode 100644 index 0000000..d8bac2e --- /dev/null +++ b/src/rigraph/vendor/arpack/dsconv.f @@ -0,0 +1,138 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsconv +c +c\Description: +c Convergence testing for the symmetric Arnoldi eigenvalue routine. +c +c\Usage: +c call igraphdsconv +c ( N, RITZ, BOUNDS, TOL, NCONV ) +c +c\Arguments +c N Integer. (INPUT) +c Number of Ritz values to check for convergence. +c +c RITZ Double precision array of length N. (INPUT) +c The Ritz values to be checked for convergence. +c +c BOUNDS Double precision array of length N. (INPUT) +c Ritz estimates associated with the Ritz values in RITZ. +c +c TOL Double precision scalar. (INPUT) +c Desired relative accuracy for a Ritz value to be considered +c "converged". +c +c NCONV Integer scalar. (OUTPUT) +c Number of "converged" Ritz values. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Routines called: +c igraphsecond ARPACK utility routine for timing. +c dlamch LAPACK routine that determines machine constants. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: sconv.F SID: 2.4 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\Remarks +c 1. Starting with version 2.4, this routine no longer uses the +c Parlett strategy using the gap conditions. +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsconv (n, ritz, bounds, tol, nconv) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer n, nconv + Double precision + & tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & ritz(n), bounds(n) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i + Double precision + & temp, eps23 +c +c %-------------------% +c | External routines | +c %-------------------% +c + Double precision + & dlamch + external dlamch + +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic abs +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + call igraphsecond (t0) +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c + nconv = 0 + do 10 i = 1, n +c +c %-----------------------------------------------------% +c | The i-th Ritz value is considered "converged" | +c | when: bounds(i) .le. TOL*max(eps23, abs(ritz(i))) | +c %-----------------------------------------------------% +c + temp = max( eps23, abs(ritz(i)) ) + if ( bounds(i) .le. tol*temp ) then + nconv = nconv + 1 + end if +c + 10 continue +c + call igraphsecond (t1) + tsconv = tsconv + (t1 - t0) +c + return +c +c %---------------% +c | End of igraphdsconv | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dseigt.f b/src/rigraph/vendor/arpack/dseigt.f new file mode 100644 index 0000000..dc5dccd --- /dev/null +++ b/src/rigraph/vendor/arpack/dseigt.f @@ -0,0 +1,181 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdseigt +c +c\Description: +c Compute the eigenvalues of the current symmetric tridiagonal matrix +c and the corresponding error bounds given the current residual norm. +c +c\Usage: +c call igraphdseigt +c ( RNORM, N, H, LDH, EIG, BOUNDS, WORKL, IERR ) +c +c\Arguments +c RNORM Double precision scalar. (INPUT) +c RNORM contains the residual norm corresponding to the current +c symmetric tridiagonal matrix H. +c +c N Integer. (INPUT) +c Size of the symmetric tridiagonal matrix H. +c +c H Double precision N by 2 array. (INPUT) +c H contains the symmetric tridiagonal matrix with the +c subdiagonal in the first column starting at H(2,1) and the +c main diagonal in igraphsecond column. +c +c LDH Integer. (INPUT) +c Leading dimension of H exactly as declared in the calling +c program. +c +c EIG Double precision array of length N. (OUTPUT) +c On output, EIG contains the N eigenvalues of H possibly +c unsorted. The BOUNDS arrays are returned in the +c same sorted order as EIG. +c +c BOUNDS Double precision array of length N. (OUTPUT) +c On output, BOUNDS contains the error estimates corresponding +c to the eigenvalues EIG. This is equal to RNORM times the +c last components of the eigenvectors corresponding to the +c eigenvalues in EIG. +c +c WORKL Double precision work array of length 3*N. (WORKSPACE) +c Private (replicated) array on each PE or array allocated on +c the front end. +c +c IERR Integer. (OUTPUT) +c Error exit flag from igraphdstqrb. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdstqrb ARPACK routine that computes the eigenvalues and the +c last components of the eigenvectors of a symmetric +c and tridiagonal matrix. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dcopy Level 1 BLAS that copies one vector to another. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.4' +c +c\SCCS Information: @(#) +c FILE: seigt.F SID: 2.4 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c None +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdseigt + & ( rnorm, n, h, ldh, eig, bounds, workl, ierr ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer ierr, ldh, n + Double precision + & rnorm +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & eig(n), bounds(n), h(ldh,2), workl(3*n) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & zero + parameter (zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, k, msglvl +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, igraphdstqrb, igraphdvout, igraphsecond +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = mseigt +c + if (msglvl .gt. 0) then + call igraphdvout (logfil, n, h(1,2), ndigit, + & '_seigt: main diagonal of matrix H') + if (n .gt. 1) then + call igraphdvout (logfil, n-1, h(2,1), ndigit, + & '_seigt: sub diagonal of matrix H') + end if + end if +c + call dcopy (n, h(1,2), 1, eig, 1) + call dcopy (n-1, h(2,1), 1, workl, 1) + call igraphdstqrb (n, eig, workl, bounds, workl(n+1), ierr) + if (ierr .ne. 0) go to 9000 + if (msglvl .gt. 1) then + call igraphdvout (logfil, n, bounds, ndigit, + & '_seigt: last row of the eigenvector matrix for H') + end if +c +c %-----------------------------------------------% +c | Finally determine the error bounds associated | +c | with the n Ritz values of H. | +c %-----------------------------------------------% +c + do 30 k = 1, n + bounds(k) = rnorm*abs(bounds(k)) + 30 continue +c + call igraphsecond (t1) + tseigt = tseigt + (t1 - t0) +c + 9000 continue + return +c +c %---------------% +c | End of igraphdseigt | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsesrt.f b/src/rigraph/vendor/arpack/dsesrt.f new file mode 100644 index 0000000..05e2c36 --- /dev/null +++ b/src/rigraph/vendor/arpack/dsesrt.f @@ -0,0 +1,217 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsesrt +c +c\Description: +c Sort the array X in the order specified by WHICH and optionally +c apply the permutation to the columns of the matrix A. +c +c\Usage: +c call igraphdsesrt +c ( WHICH, APPLY, N, X, NA, A, LDA) +c +c\Arguments +c WHICH Character*2. (Input) +c 'LM' -> X is sorted into increasing order of magnitude. +c 'SM' -> X is sorted into decreasing order of magnitude. +c 'LA' -> X is sorted into increasing order of algebraic. +c 'SA' -> X is sorted into decreasing order of algebraic. +c +c APPLY Logical. (Input) +c APPLY = .TRUE. -> apply the sorted order to A. +c APPLY = .FALSE. -> do not apply the sorted order to A. +c +c N Integer. (INPUT) +c Dimension of the array X. +c +c X Double precision array of length N. (INPUT/OUTPUT) +c The array to be sorted. +c +c NA Integer. (INPUT) +c Number of rows of the matrix A. +c +c A Double precision array of length NA by N. (INPUT/OUTPUT) +c +c LDA Integer. (INPUT) +c Leading dimension of A. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Routines +c dswap Level 1 BLAS that swaps the contents of two vectors. +c +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/15/93: Version ' 2.1'. +c Adapted from the sort routine in LANSO and +c the ARPACK code igraphdsortr +c +c\SCCS Information: @(#) +c FILE: sesrt.F SID: 2.3 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsesrt (which, apply, n, x, na, a, lda) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + logical apply + integer lda, n, na +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & x(0:n-1), a(lda, 0:n-1) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, igap, j + Double precision + & temp +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dswap +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + igap = n / 2 +c + if (which .eq. 'SA') then +c +c X is sorted into decreasing order of algebraic. +c + 10 continue + if (igap .eq. 0) go to 9000 + do 30 i = igap, n-1 + j = i-igap + 20 continue +c + if (j.lt.0) go to 30 +c + if (x(j).lt.x(j+igap)) then + temp = x(j) + x(j) = x(j+igap) + x(j+igap) = temp + if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) + else + go to 30 + endif + j = j-igap + go to 20 + 30 continue + igap = igap / 2 + go to 10 +c + else if (which .eq. 'SM') then +c +c X is sorted into decreasing order of magnitude. +c + 40 continue + if (igap .eq. 0) go to 9000 + do 60 i = igap, n-1 + j = i-igap + 50 continue +c + if (j.lt.0) go to 60 +c + if (abs(x(j)).lt.abs(x(j+igap))) then + temp = x(j) + x(j) = x(j+igap) + x(j+igap) = temp + if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) + else + go to 60 + endif + j = j-igap + go to 50 + 60 continue + igap = igap / 2 + go to 40 +c + else if (which .eq. 'LA') then +c +c X is sorted into increasing order of algebraic. +c + 70 continue + if (igap .eq. 0) go to 9000 + do 90 i = igap, n-1 + j = i-igap + 80 continue +c + if (j.lt.0) go to 90 +c + if (x(j).gt.x(j+igap)) then + temp = x(j) + x(j) = x(j+igap) + x(j+igap) = temp + if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) + else + go to 90 + endif + j = j-igap + go to 80 + 90 continue + igap = igap / 2 + go to 70 +c + else if (which .eq. 'LM') then +c +c X is sorted into increasing order of magnitude. +c + 100 continue + if (igap .eq. 0) go to 9000 + do 120 i = igap, n-1 + j = i-igap + 110 continue +c + if (j.lt.0) go to 120 +c + if (abs(x(j)).gt.abs(x(j+igap))) then + temp = x(j) + x(j) = x(j+igap) + x(j+igap) = temp + if (apply) call dswap( na, a(1, j), 1, a(1,j+igap), 1) + else + go to 120 + endif + j = j-igap + go to 110 + 120 continue + igap = igap / 2 + go to 100 + end if +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsesrt | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dseupd.f b/src/rigraph/vendor/arpack/dseupd.f new file mode 100644 index 0000000..0336c3a --- /dev/null +++ b/src/rigraph/vendor/arpack/dseupd.f @@ -0,0 +1,905 @@ +c\BeginDoc +c +c\Name: igraphdseupd +c +c\Description: +c +c This subroutine returns the converged approximations to eigenvalues +c of A*z = lambda*B*z and (optionally): +c +c (1) the corresponding approximate eigenvectors, +c +c (2) an orthonormal (Lanczos) basis for the associated approximate +c invariant subspace, +c +c (3) Both. +c +c There is negligible additional cost to obtain eigenvectors. An orthonormal +c (Lanczos) basis is always computed. There is an additional storage cost +c of n*nev if both are requested (in this case a separate array Z must be +c supplied). +c +c These quantities are obtained from the Lanczos factorization computed +c by DSAUPD for the linear operator OP prescribed by the MODE selection +c (see IPARAM(7) in DSAUPD documentation.) DSAUPD must be called before +c this routine is called. These approximate eigenvalues and vectors are +c commonly called Ritz values and Ritz vectors respectively. They are +c referred to as such in the comments that follow. The computed orthonormal +c basis for the invariant subspace corresponding to these Ritz values is +c referred to as a Lanczos basis. +c +c See documentation in the header of the subroutine DSAUPD for a definition +c of OP as well as other terms and the relation of computed Ritz values +c and vectors of OP with respect to the given problem A*z = lambda*B*z. +c +c The approximate eigenvalues of the original problem are returned in +c ascending algebraic order. The user may elect to call this routine +c once for each desired Ritz vector and store it peripherally if desired. +c There is also the option of computing a selected set of these vectors +c with a single call. +c +c\Usage: +c call igraphdseupd +c ( RVEC, HOWMNY, SELECT, D, Z, LDZ, SIGMA, BMAT, N, WHICH, NEV, TOL, +c RESID, NCV, V, LDV, IPARAM, IPNTR, WORKD, WORKL, LWORKL, INFO ) +c +c RVEC LOGICAL (INPUT) +c Specifies whether Ritz vectors corresponding to the Ritz value +c approximations to the eigenproblem A*z = lambda*B*z are computed. +c +c RVEC = .FALSE. Compute Ritz values only. +c +c RVEC = .TRUE. Compute Ritz vectors. +c +c HOWMNY Character*1 (INPUT) +c Specifies how many Ritz vectors are wanted and the form of Z +c the matrix of Ritz vectors. See remark 1 below. +c = 'A': compute NEV Ritz vectors; +c = 'S': compute some of the Ritz vectors, specified +c by the logical array SELECT. +c +c SELECT Logical array of dimension NEV. (INPUT) +c If HOWMNY = 'S', SELECT specifies the Ritz vectors to be +c computed. To select the Ritz vector corresponding to a +c Ritz value D(j), SELECT(j) must be set to .TRUE.. +c If HOWMNY = 'A' , SELECT is not referenced. +c +c D Double precision array of dimension NEV. (OUTPUT) +c On exit, D contains the Ritz value approximations to the +c eigenvalues of A*z = lambda*B*z. The values are returned +c in ascending order. If IPARAM(7) = 3,4,5 then D represents +c the Ritz values of OP computed by igraphdsaupd transformed to +c those of the original eigensystem A*z = lambda*B*z. If +c IPARAM(7) = 1,2 then the Ritz values of OP are the same +c as the those of A*z = lambda*B*z. +c +c Z Double precision N by NEV array if HOWMNY = 'A'. (OUTPUT) +c On exit, Z contains the B-orthonormal Ritz vectors of the +c eigensystem A*z = lambda*B*z corresponding to the Ritz +c value approximations. +c If RVEC = .FALSE. then Z is not referenced. +c NOTE: The array Z may be set equal to first NEV columns of the +c Arnoldi/Lanczos basis array V computed by DSAUPD. +c +c LDZ Integer. (INPUT) +c The leading dimension of the array Z. If Ritz vectors are +c desired, then LDZ .ge. max( 1, N ). In any case, LDZ .ge. 1. +c +c SIGMA Double precision (INPUT) +c If IPARAM(7) = 3,4,5 represents the shift. Not referenced if +c IPARAM(7) = 1 or 2. +c +c +c **** The remaining arguments MUST be the same as for the **** +c **** call to DNAUPD that was just completed. **** +c +c NOTE: The remaining arguments +c +c BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, +c WORKD, WORKL, LWORKL, INFO +c +c must be passed directly to DSEUPD following the last call +c to DSAUPD. These arguments MUST NOT BE MODIFIED between +c the the last call to DSAUPD and the call to DSEUPD. +c +c Two of these parameters (WORKL, INFO) are also output parameters: +c +c WORKL Double precision work array of length LWORKL. (OUTPUT/WORKSPACE) +c WORKL(1:4*ncv) contains information obtained in +c igraphdsaupd. They are not changed by igraphdseupd. +c WORKL(4*ncv+1:ncv*ncv+8*ncv) holds the +c untransformed Ritz values, the computed error estimates, +c and the associated eigenvector matrix of H. +c +c Note: IPNTR(8:10) contains the pointer into WORKL for addresses +c of the above information computed by igraphdseupd. +c ------------------------------------------------------------- +c IPNTR(8): pointer to the NCV RITZ values of the original system. +c IPNTR(9): pointer to the NCV corresponding error bounds. +c IPNTR(10): pointer to the NCV by NCV matrix of eigenvectors +c of the tridiagonal matrix T. Only referenced by +c igraphdseupd if RVEC = .TRUE. See Remarks. +c ------------------------------------------------------------- +c +c INFO Integer. (OUTPUT) +c Error flag on output. +c = 0: Normal exit. +c = -1: N must be positive. +c = -2: NEV must be positive. +c = -3: NCV must be greater than NEV and less than or equal to N. +c = -5: WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'. +c = -6: BMAT must be one of 'I' or 'G'. +c = -7: Length of private work WORKL array is not sufficient. +c = -8: Error return from trid. eigenvalue calculation; +c Information error from LAPACK routine dsteqr. +c = -9: Starting vector is zero. +c = -10: IPARAM(7) must be 1,2,3,4,5. +c = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatible. +c = -12: NEV and WHICH = 'BE' are incompatible. +c = -14: DSAUPD did not find any eigenvalues to sufficient +c accuracy. +c = -15: HOWMNY must be one of 'A' or 'S' if RVEC = .true. +c = -16: HOWMNY = 'S' not yet implemented +c +c\BeginLib +c +c\References: +c 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in +c a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), +c pp 357-385. +c 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly +c Restarted Arnoldi Iteration", Rice University Technical Report +c TR95-13, Department of Computational and Applied Mathematics. +c 3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall, +c 1980. +c 4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program", +c Computer Physics Communications, 53 (1989), pp 169-179. +c 5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to +c Implement the Spectral Transformation", Math. Comp., 48 (1987), +c pp 663-673. +c 6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos +c Algorithm for Solving Sparse Symmetric Generalized Eigenproblems", +c SIAM J. Matr. Anal. Apps., January (1993). +c 7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines +c for Updating the QR decomposition", ACM TOMS, December 1990, +c Volume 16 Number 4, pp 369-377. +c +c\Remarks +c 1. The converged Ritz values are always returned in increasing +c (algebraic) order. +c +c 2. Currently only HOWMNY = 'A' is implemented. It is included at this +c stage for the user who wants to incorporate it. +c +c\Routines called: +c igraphdsesrt ARPACK routine that sorts an array X, and applies the +c corresponding permutation to a matrix A. +c igraphdsortr igraphdsortr ARPACK sorting routine. +c igraphivout ARPACK utility routine that prints integers. +c igraphdvout ARPACK utility routine that prints vectors. +c dgeqr2 LAPACK routine that computes the QR factorization of +c a matrix. +c dlacpy LAPACK matrix copy routine. +c dlamch LAPACK routine that determines machine constants. +c dorm2r LAPACK routine that applies an orthogonal matrix in +c factored form. +c dsteqr LAPACK routine that computes eigenvalues and eigenvectors +c of a tridiagonal matrix. +c dger Level 2 BLAS rank one update to a matrix. +c dcopy Level 1 BLAS that copies one vector to another . +c dnrm2 Level 1 BLAS that computes the norm of a vector. +c dscal Level 1 BLAS that scales a vector. +c dswap Level 1 BLAS that swaps the contents of two vectors. + +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Chao Yang Houston, Texas +c Dept. of Computational & +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/15/93: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: seupd.F SID: 2.7 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- + subroutine igraphdseupd (rvec, howmny, select, d, z, ldz, + & sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character bmat, howmny, which*2 + logical rvec, select(ncv) + integer info, ldz, ldv, lworkl, n, ncv, nev + Double precision + & sigma, tol +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + integer iparam(7), ipntr(11) + Double precision + & d(nev), resid(n), v(ldv,ncv), z(ldz, nev), + & workd(2*n), workl(lworkl) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + character type*6 + integer bounds, ierr, ih, ihb, ihd, iq, iw, j, k, + & ldh, ldq, mode, msglvl, nconv, next, ritz, + & irz, ibd, ktrord, leftptr, rghtptr, ism, ilg + Double precision + & bnorm2, rnorm, temp, thres1, thres2, tempbnd, eps23 + logical reord +c +c %--------------% +c | Local Arrays | +c %--------------% +c + Double precision + & kv(2) +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dcopy, dger, dgeqr2, dlacpy, dorm2r, dscal, + & igraphdsesrt, dsteqr, dswap, igraphdvout, + & igraphivout, igraphdsortr +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dnrm2, dlamch + external dnrm2, dlamch +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic min +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %------------------------% +c | Set default parameters | +c %------------------------% +c + msglvl = mseupd + mode = iparam(7) + nconv = iparam(5) + info = 0 +c +c %--------------% +c | Quick return | +c %--------------% +c + if (nconv .eq. 0) go to 9000 + ierr = 0 +c + if (nconv .le. 0) ierr = -14 + if (n .le. 0) ierr = -1 + if (nev .le. 0) ierr = -2 + if (ncv .le. nev .or. ncv .gt. n) ierr = -3 + if (which .ne. 'LM' .and. + & which .ne. 'SM' .and. + & which .ne. 'LA' .and. + & which .ne. 'SA' .and. + & which .ne. 'BE') ierr = -5 + if (bmat .ne. 'I' .and. bmat .ne. 'G') ierr = -6 + if ( (howmny .ne. 'A' .and. + & howmny .ne. 'P' .and. + & howmny .ne. 'S') .and. rvec ) + & ierr = -15 + if (rvec .and. howmny .eq. 'S') ierr = -16 +c + if (rvec .and. lworkl .lt. ncv**2+8*ncv) ierr = -7 +c + if (mode .eq. 1 .or. mode .eq. 2) then + type = 'REGULR' + else if (mode .eq. 3 ) then + type = 'SHIFTI' + else if (mode .eq. 4 ) then + type = 'BUCKLE' + else if (mode .eq. 5 ) then + type = 'CAYLEY' + else + ierr = -10 + end if + if (mode .eq. 1 .and. bmat .eq. 'G') ierr = -11 + if (nev .eq. 1 .and. which .eq. 'BE') ierr = -12 +c +c %------------% +c | Error Exit | +c %------------% +c + if (ierr .ne. 0) then + info = ierr + go to 9000 + end if +c +c %-------------------------------------------------------% +c | Pointer into WORKL for address of H, RITZ, BOUNDS, Q | +c | etc... and the remaining workspace. | +c | Also update pointer to be used on output. | +c | Memory is laid out as follows: | +c | workl(1:2*ncv) := generated tridiagonal matrix H | +c | The subdiagonal is stored in workl(2:ncv). | +c | The dead spot is workl(1) but upon exiting | +c | igraphdsaupd stores the B-norm of the last residual | +c | vector in workl(1). We use this !!! | +c | workl(2*ncv+1:2*ncv+ncv) := ritz values | +c | The wanted values are in the first NCONV spots. | +c | workl(3*ncv+1:3*ncv+ncv) := computed Ritz estimates | +c | The wanted values are in the first NCONV spots. | +c | NOTE: workl(1:4*ncv) is set by igraphdsaupd and is not | +c | modified by igraphdseupd. | +c %-------------------------------------------------------% +c +c %-------------------------------------------------------% +c | The following is used and set by igraphdseupd. | +c | workl(4*ncv+1:4*ncv+ncv) := used as workspace during | +c | computation of the eigenvectors of H. Stores | +c | the diagonal of H. Upon EXIT contains the NCV | +c | Ritz values of the original system. The first | +c | NCONV spots have the wanted values. If MODE = | +c | 1 or 2 then will equal workl(2*ncv+1:3*ncv). | +c | workl(5*ncv+1:5*ncv+ncv) := used as workspace during | +c | computation of the eigenvectors of H. Stores | +c | the subdiagonal of H. Upon EXIT contains the | +c | NCV corresponding Ritz estimates of the | +c | original system. The first NCONV spots have the | +c | wanted values. If MODE = 1,2 then will equal | +c | workl(3*ncv+1:4*ncv). | +c | workl(6*ncv+1:6*ncv+ncv*ncv) := orthogonal Q that is | +c | the eigenvector matrix for H as returned by | +c | dsteqr. Not referenced if RVEC = .False. | +c | Ordering follows that of workl(4*ncv+1:5*ncv) | +c | workl(6*ncv+ncv*ncv+1:6*ncv+ncv*ncv+2*ncv) := | +c | Workspace. Needed by dsteqr and by igraphdseupd. | +c | GRAND total of NCV*(NCV+8) locations. | +c %-------------------------------------------------------% +c +c + ih = ipntr(5) + ritz = ipntr(6) + bounds = ipntr(7) + ldh = ncv + ldq = ncv + ihd = bounds + ldh + ihb = ihd + ldh + iq = ihb + ldh + iw = iq + ldh*ncv + next = iw + 2*ncv + ipntr(4) = next + ipntr(8) = ihd + ipntr(9) = ihb + ipntr(10) = iq +c +c %----------------------------------------% +c | irz points to the Ritz values computed | +c | by _seigt before exiting _saup2. | +c | ibd points to the Ritz estimates | +c | computed by _seigt before exiting | +c | _saup2. | +c %----------------------------------------% +c + irz = ipntr(11)+ncv + ibd = irz+ncv +c +c +c %---------------------------------% +c | Set machine dependent constant. | +c %---------------------------------% +c + eps23 = dlamch('Epsilon-Machine') + eps23 = eps23**(2.0D+0 / 3.0D+0) +c +c %---------------------------------------% +c | RNORM is B-norm of the RESID(1:N). | +c | BNORM2 is the 2 norm of B*RESID(1:N). | +c | Upon exit of igraphdsaupd WORKD(1:N) has | +c | B*RESID(1:N). | +c %---------------------------------------% +c + rnorm = workl(ih) + if (bmat .eq. 'I') then + bnorm2 = rnorm + else if (bmat .eq. 'G') then + bnorm2 = dnrm2(n, workd, 1) + end if +c + if (rvec) then +c +c %------------------------------------------------% +c | Get the converged Ritz value on the boundary. | +c | This value will be used to dermine whether we | +c | need to reorder the eigenvalues and | +c | eigenvectors comupted by _steqr, and is | +c | referred to as the "threshold" value. | +c | | +c | A Ritz value gamma is said to be a wanted | +c | one, if | +c | abs(gamma) .ge. threshold, when WHICH = 'LM'; | +c | abs(gamma) .le. threshold, when WHICH = 'SM'; | +c | gamma .ge. threshold, when WHICH = 'LA'; | +c | gamma .le. threshold, when WHICH = 'SA'; | +c | gamma .le. thres1 .or. gamma .ge. thres2 | +c | when WHICH = 'BE'; | +c | | +c | Note: converged Ritz values and associated | +c | Ritz estimates have been placed in the first | +c | NCONV locations in workl(ritz) and | +c | workl(bounds) respectively. They have been | +c | sorted (in _saup2) according to the WHICH | +c | selection criterion. (Except in the case | +c | WHICH = 'BE', they are sorted in an increasing | +c | order.) | +c %------------------------------------------------% +c + if (which .eq. 'LM' .or. which .eq. 'SM' + & .or. which .eq. 'LA' .or. which .eq. 'SA' ) then +c + thres1 = workl(ritz) +c + if (msglvl .gt. 2) then + call igraphdvout(logfil, 1, [thres1], ndigit, + & '_seupd: Threshold eigenvalue used for re-ordering') + end if +c + else if (which .eq. 'BE') then +c +c %------------------------------------------------% +c | Ritz values returned from _saup2 have been | +c | sorted in increasing order. Thus two | +c | "threshold" values (one for the small end, one | +c | for the large end) are in the middle. | +c %------------------------------------------------% +c + ism = max(nev,nconv) / 2 + ilg = ism + 1 + thres1 = workl(ism) + thres2 = workl(ilg) +c + if (msglvl .gt. 2) then + kv(1) = thres1 + kv(2) = thres2 + call igraphdvout(logfil, 2, kv, ndigit, + & '_seupd: Threshold eigenvalues used for re-ordering') + end if +c + end if +c +c %----------------------------------------------------------% +c | Check to see if all converged Ritz values appear within | +c | the first NCONV diagonal elements returned from _seigt. | +c | This is done in the following way: | +c | | +c | 1) For each Ritz value obtained from _seigt, compare it | +c | with the threshold Ritz value computed above to | +c | determine whether it is a wanted one. | +c | | +c | 2) If it is wanted, then check the corresponding Ritz | +c | estimate to see if it has converged. If it has, set | +c | correponding entry in the logical array SELECT to | +c | .TRUE.. | +c | | +c | If SELECT(j) = .TRUE. and j > NCONV, then there is a | +c | converged Ritz value that does not appear at the top of | +c | the diagonal matrix computed by _seigt in _saup2. | +c | Reordering is needed. | +c %----------------------------------------------------------% +c + reord = .false. + ktrord = 0 + do 10 j = 0, ncv-1 + select(j+1) = .false. + if (which .eq. 'LM') then + if (abs(workl(irz+j)) .ge. abs(thres1)) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + else if (which .eq. 'SM') then + if (abs(workl(irz+j)) .le. abs(thres1)) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + else if (which .eq. 'LA') then + if (workl(irz+j) .ge. thres1) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + else if (which .eq. 'SA') then + if (workl(irz+j) .le. thres1) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + else if (which .eq. 'BE') then + if ( workl(irz+j) .le. thres1 .or. + & workl(irz+j) .ge. thres2 ) then + tempbnd = max( eps23, abs(workl(irz+j)) ) + if (workl(ibd+j) .le. tol*tempbnd) then + select(j+1) = .true. + end if + end if + end if + if (j+1 .gt. nconv ) reord = select(j+1) .or. reord + if (select(j+1)) ktrord = ktrord + 1 + 10 continue + +c %-------------------------------------------% +c | If KTRORD .ne. NCONV, something is wrong. | +c %-------------------------------------------% +c + if (msglvl .gt. 2) then + call igraphivout(logfil, 1, [ktrord], ndigit, + & '_seupd: Number of specified eigenvalues') + call igraphivout(logfil, 1, [nconv], ndigit, + & '_seupd: Number of "converged" eigenvalues') + end if +c +c %-----------------------------------------------------------% +c | Call LAPACK routine _steqr to compute the eigenvalues and | +c | eigenvectors of the final symmetric tridiagonal matrix H. | +c | Initialize the eigenvector matrix Q to the identity. | +c %-----------------------------------------------------------% +c + call dcopy (ncv-1, workl(ih+1), 1, workl(ihb), 1) + call dcopy (ncv, workl(ih+ldh), 1, workl(ihd), 1) +c + call dsteqr ('Identity', ncv, workl(ihd), workl(ihb), + & workl(iq), ldq, workl(iw), ierr) +c + if (ierr .ne. 0) then + info = -8 + go to 9000 + end if +c + if (msglvl .gt. 1) then + call dcopy (ncv, workl(iq+ncv-1), ldq, workl(iw), 1) + call igraphdvout (logfil, ncv, workl(ihd), ndigit, + & '_seupd: NCV Ritz values of the final H matrix') + call igraphdvout (logfil, ncv, workl(iw), ndigit, + & '_seupd: last row of the eigenvector matrix for H') + end if +c + if (reord) then +c +c %---------------------------------------------% +c | Reordered the eigenvalues and eigenvectors | +c | computed by _steqr so that the "converged" | +c | eigenvalues appear in the first NCONV | +c | positions of workl(ihd), and the associated | +c | eigenvectors appear in the first NCONV | +c | columns. | +c %---------------------------------------------% +c + leftptr = 1 + rghtptr = ncv +c + if (ncv .eq. 1) go to 30 +c + 20 if (select(leftptr)) then +c +c %-------------------------------------------% +c | Search, from the left, for the first Ritz | +c | value that has not converged. | +c %-------------------------------------------% +c + leftptr = leftptr + 1 +c + else if ( .not. select(rghtptr)) then +c +c %----------------------------------------------% +c | Search, from the right, the first Ritz value | +c | that has converged. | +c %----------------------------------------------% +c + rghtptr = rghtptr - 1 +c + else +c +c %----------------------------------------------% +c | Swap the Ritz value on the left that has not | +c | converged with the Ritz value on the right | +c | that has converged. Swap the associated | +c | eigenvector of the tridiagonal matrix H as | +c | well. | +c %----------------------------------------------% +c + temp = workl(ihd+leftptr-1) + workl(ihd+leftptr-1) = workl(ihd+rghtptr-1) + workl(ihd+rghtptr-1) = temp + call dcopy(ncv, workl(iq+ncv*(leftptr-1)), 1, + & workl(iw), 1) + call dcopy(ncv, workl(iq+ncv*(rghtptr-1)), 1, + & workl(iq+ncv*(leftptr-1)), 1) + call dcopy(ncv, workl(iw), 1, + & workl(iq+ncv*(rghtptr-1)), 1) + leftptr = leftptr + 1 + rghtptr = rghtptr - 1 +c + end if +c + if (leftptr .lt. rghtptr) go to 20 +c + 30 end if +c + if (msglvl .gt. 2) then + call igraphdvout (logfil, ncv, workl(ihd), ndigit, + & '_seupd: The eigenvalues of H--reordered') + end if +c +c %----------------------------------------% +c | Load the converged Ritz values into D. | +c %----------------------------------------% +c + call dcopy(nconv, workl(ihd), 1, d, 1) +c + else +c +c %-----------------------------------------------------% +c | Ritz vectors not required. Load Ritz values into D. | +c %-----------------------------------------------------% +c + call dcopy (nconv, workl(ritz), 1, d, 1) + call dcopy (ncv, workl(ritz), 1, workl(ihd), 1) +c + end if +c +c %------------------------------------------------------------------% +c | Transform the Ritz values and possibly vectors and corresponding | +c | Ritz estimates of OP to those of A*x=lambda*B*x. The Ritz values | +c | (and corresponding data) are returned in ascending order. | +c %------------------------------------------------------------------% +c + if (type .eq. 'REGULR') then +c +c %---------------------------------------------------------% +c | Ascending sort of wanted Ritz values, vectors and error | +c | bounds. Not necessary if only Ritz values are desired. | +c %---------------------------------------------------------% +c + if (rvec) then + call igraphdsesrt ('LA', rvec , nconv, d, ncv, workl(iq), + & ldq) + else + call dcopy (ncv, workl(bounds), 1, workl(ihb), 1) + end if +c + else +c +c %-------------------------------------------------------------% +c | * Make a copy of all the Ritz values. | +c | * Transform the Ritz values back to the original system. | +c | For TYPE = 'SHIFTI' the transformation is | +c | lambda = 1/theta + sigma | +c | For TYPE = 'BUCKLE' the transformation is | +c | lambda = sigma * theta / ( theta - 1 ) | +c | For TYPE = 'CAYLEY' the transformation is | +c | lambda = sigma * (theta + 1) / (theta - 1 ) | +c | where the theta are the Ritz values returned by igraphdsaupd. | +c | NOTES: | +c | *The Ritz vectors are not affected by the transformation. | +c | They are only reordered. | +c %-------------------------------------------------------------% +c + call dcopy (ncv, workl(ihd), 1, workl(iw), 1) + if (type .eq. 'SHIFTI') then + do 40 k=1, ncv + workl(ihd+k-1) = one / workl(ihd+k-1) + sigma + 40 continue + else if (type .eq. 'BUCKLE') then + do 50 k=1, ncv + workl(ihd+k-1) = sigma * workl(ihd+k-1) / + & (workl(ihd+k-1) - one) + 50 continue + else if (type .eq. 'CAYLEY') then + do 60 k=1, ncv + workl(ihd+k-1) = sigma * (workl(ihd+k-1) + one) / + & (workl(ihd+k-1) - one) + 60 continue + end if +c +c %-------------------------------------------------------------% +c | * Store the wanted NCONV lambda values into D. | +c | * Sort the NCONV wanted lambda in WORKL(IHD:IHD+NCONV-1) | +c | into ascending order and apply sort to the NCONV theta | +c | values in the transformed system. We'll need this to | +c | compute Ritz estimates in the original system. | +c | * Finally sort the lambda's into ascending order and apply | +c | to Ritz vectors if wanted. Else just sort lambda's into | +c | ascending order. | +c | NOTES: | +c | *workl(iw:iw+ncv-1) contain the theta ordered so that they | +c | match the ordering of the lambda. We'll use them again for | +c | Ritz vector purification. | +c %-------------------------------------------------------------% +c + call dcopy (nconv, workl(ihd), 1, d, 1) + call igraphdsortr ('LA', .true., nconv, workl(ihd), workl(iw)) + if (rvec) then + call igraphdsesrt ('LA', rvec , nconv, d, ncv, workl(iq), + & ldq) + else + call dcopy (ncv, workl(bounds), 1, workl(ihb), 1) + call dscal (ncv, bnorm2/rnorm, workl(ihb), 1) + call igraphdsortr ('LA', .true., nconv, d, workl(ihb)) + end if +c + end if +c +c %------------------------------------------------% +c | Compute the Ritz vectors. Transform the wanted | +c | eigenvectors of the symmetric tridiagonal H by | +c | the Lanczos basis matrix V. | +c %------------------------------------------------% +c + if (rvec .and. howmny .eq. 'A') then +c +c %----------------------------------------------------------% +c | Compute the QR factorization of the matrix representing | +c | the wanted invariant subspace located in the first NCONV | +c | columns of workl(iq,ldq). | +c %----------------------------------------------------------% +c + call dgeqr2 (ncv, nconv, workl(iq), ldq, workl(iw+ncv), + & workl(ihb), ierr) +c +c +c %--------------------------------------------------------% +c | * Postmultiply V by Q. | +c | * Copy the first NCONV columns of VQ into Z. | +c | The N by NCONV matrix Z is now a matrix representation | +c | of the approximate invariant subspace associated with | +c | the Ritz values in workl(ihd). | +c %--------------------------------------------------------% +c + call dorm2r ('Right', 'Notranspose', n, ncv, nconv, workl(iq), + & ldq, workl(iw+ncv), v, ldv, workd(n+1), ierr) + call dlacpy ('All', n, nconv, v, ldv, z, ldz) +c +c %-----------------------------------------------------% +c | In order to compute the Ritz estimates for the Ritz | +c | values in both systems, need the last row of the | +c | eigenvector matrix. Remember, it's in factored form | +c %-----------------------------------------------------% +c + do 65 j = 1, ncv-1 + workl(ihb+j-1) = zero + 65 continue + workl(ihb+ncv-1) = one + call dorm2r ('Left', 'Transpose', ncv, 1, nconv, workl(iq), + & ldq, workl(iw+ncv), workl(ihb), ncv, temp, ierr) +c + else if (rvec .and. howmny .eq. 'S') then +c +c Not yet implemented. See remark 2 above. +c + end if +c + if (type .eq. 'REGULR' .and. rvec) then +c + do 70 j=1, ncv + workl(ihb+j-1) = rnorm * abs( workl(ihb+j-1) ) + 70 continue +c + else if (type .ne. 'REGULR' .and. rvec) then +c +c %-------------------------------------------------% +c | * Determine Ritz estimates of the theta. | +c | If RVEC = .true. then compute Ritz estimates | +c | of the theta. | +c | If RVEC = .false. then copy Ritz estimates | +c | as computed by igraphdsaupd. | +c | * Determine Ritz estimates of the lambda. | +c %-------------------------------------------------% +c + call dscal (ncv, bnorm2, workl(ihb), 1) + if (type .eq. 'SHIFTI') then +c + do 80 k=1, ncv + workl(ihb+k-1) = abs( workl(ihb+k-1) ) / workl(iw+k-1)**2 + 80 continue +c + else if (type .eq. 'BUCKLE') then +c + do 90 k=1, ncv + workl(ihb+k-1) = sigma * abs( workl(ihb+k-1) ) / + & ( workl(iw+k-1)-one )**2 + 90 continue +c + else if (type .eq. 'CAYLEY') then +c + do 100 k=1, ncv + workl(ihb+k-1) = abs( workl(ihb+k-1) / + & workl(iw+k-1)*(workl(iw+k-1)-one) ) + 100 continue +c + end if +c + end if +c + if (type .ne. 'REGULR' .and. msglvl .gt. 1) then + call igraphdvout (logfil, nconv, d, ndigit, + & '_seupd: Untransformed converged Ritz values') + call igraphdvout (logfil, nconv, workl(ihb), ndigit, + & '_seupd: Ritz estimates of the untransformed Ritz values') + else if (msglvl .gt. 1) then + call igraphdvout (logfil, nconv, d, ndigit, + & '_seupd: Converged Ritz values') + call igraphdvout (logfil, nconv, workl(ihb), ndigit, + & '_seupd: Associated Ritz estimates') + end if +c +c %-------------------------------------------------% +c | Ritz vector purification step. Formally perform | +c | one of inverse subspace iteration. Only used | +c | for MODE = 3,4,5. See reference 7 | +c %-------------------------------------------------% +c + if (rvec .and. (type .eq. 'SHIFTI' .or. type .eq. 'CAYLEY')) then +c + do 110 k=0, nconv-1 + workl(iw+k) = workl(iq+k*ldq+ncv-1) / workl(iw+k) + 110 continue +c + else if (rvec .and. type .eq. 'BUCKLE') then +c + do 120 k=0, nconv-1 + workl(iw+k) = workl(iq+k*ldq+ncv-1) / (workl(iw+k)-one) + 120 continue +c + end if +c + if (type .ne. 'REGULR') + & call dger (n, nconv, one, resid, 1, workl(iw), 1, z, ldz) +c + 9000 continue +c + return +c +c %---------------% +c | End of igraphdseupd | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsgets.f b/src/rigraph/vendor/arpack/dsgets.f new file mode 100644 index 0000000..2b0794f --- /dev/null +++ b/src/rigraph/vendor/arpack/dsgets.f @@ -0,0 +1,220 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsgets +c +c\Description: +c Given the eigenvalues of the symmetric tridiagonal matrix H, +c computes the NP shifts AMU that are zeros of the polynomial of +c degree NP which filters out components of the unwanted eigenvectors +c corresponding to the AMU's based on some given criteria. +c +c NOTE: This is called even in the case of user specified shifts in +c order to sort the eigenvalues, and error bounds of H for later use. +c +c\Usage: +c call igraphdsgets +c ( ISHIFT, WHICH, KEV, NP, RITZ, BOUNDS, SHIFTS ) +c +c\Arguments +c ISHIFT Integer. (INPUT) +c Method for selecting the implicit shifts at each iteration. +c ISHIFT = 0: user specified shifts +c ISHIFT = 1: exact shift with respect to the matrix H. +c +c WHICH Character*2. (INPUT) +c Shift selection criteria. +c 'LM' -> KEV eigenvalues of largest magnitude are retained. +c 'SM' -> KEV eigenvalues of smallest magnitude are retained. +c 'LA' -> KEV eigenvalues of largest value are retained. +c 'SA' -> KEV eigenvalues of smallest value are retained. +c 'BE' -> KEV eigenvalues, half from each end of the spectrum. +c If KEV is odd, compute one more from the high end. +c +c KEV Integer. (INPUT) +c KEV+NP is the size of the matrix H. +c +c NP Integer. (INPUT) +c Number of implicit shifts to be computed. +c +c RITZ Double precision array of length KEV+NP. (INPUT/OUTPUT) +c On INPUT, RITZ contains the eigenvalues of H. +c On OUTPUT, RITZ are sorted so that the unwanted eigenvalues +c are in the first NP locations and the wanted part is in +c the last KEV locations. When exact shifts are selected, the +c unwanted part corresponds to the shifts to be applied. +c +c BOUNDS Double precision array of length KEV+NP. (INPUT/OUTPUT) +c Error bounds corresponding to the ordering in RITZ. +c +c SHIFTS Double precision array of length NP. (INPUT/OUTPUT) +c On INPUT: contains the user specified shifts if ISHIFT = 0. +c On OUTPUT: contains the shifts sorted into decreasing order +c of magnitude with respect to the Ritz estimates contained in +c BOUNDS. If ISHIFT = 0, SHIFTS is not modified on exit. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c igraphdsortr ARPACK utility sorting routine. +c igraphivout ARPACK utility routine that prints integers. +c igraphsecond ARPACK utility routine for timing. +c igraphdvout ARPACK utility routine that prints vectors. +c dcopy Level 1 BLAS that copies one vector to another. +c dswap Level 1 BLAS that swaps the contents of two vectors. +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/93: Version ' 2.1' +c +c\SCCS Information: @(#) +c FILE: sgets.F SID: 2.4 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\Remarks +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsgets ( ishift, which, kev, np, ritz, bounds, + & shifts ) +c +c %----------------------------------------------------% +c | Include files for debugging and timing information | +c %----------------------------------------------------% +c + include 'debug.h' + include 'stat.h' +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + integer ishift, kev, np +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & bounds(kev+np), ritz(kev+np), shifts(np) +c +c %------------% +c | Parameters | +c %------------% +c + Double precision + & one, zero + parameter (one = 1.0D+0, zero = 0.0D+0) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer kevd2, msglvl +c +c %----------------------% +c | External Subroutines | +c %----------------------% +c + external dswap, dcopy, igraphdsortr, igraphsecond +c +c %---------------------% +c | Intrinsic Functions | +c %---------------------% +c + intrinsic max, min +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c +c %-------------------------------% +c | Initialize timing statistics | +c | & message level for debugging | +c %-------------------------------% +c + call igraphsecond (t0) + msglvl = msgets +c + if (which .eq. 'BE') then +c +c %-----------------------------------------------------% +c | Both ends of the spectrum are requested. | +c | Sort the eigenvalues into algebraically increasing | +c | order first then swap high end of the spectrum next | +c | to low end in appropriate locations. | +c | NOTE: when np < floor(kev/2) be careful not to swap | +c | overlapping locations. | +c %-----------------------------------------------------% +c + call igraphdsortr ('LA', .true., kev+np, ritz, bounds) + kevd2 = kev / 2 + if ( kev .gt. 1 ) then + call dswap ( min(kevd2,np), ritz, 1, + & ritz( max(kevd2,np)+1 ), 1) + call dswap ( min(kevd2,np), bounds, 1, + & bounds( max(kevd2,np)+1 ), 1) + end if +c + else +c +c %----------------------------------------------------% +c | LM, SM, LA, SA case. | +c | Sort the eigenvalues of H into the desired order | +c | and apply the resulting order to BOUNDS. | +c | The eigenvalues are sorted so that the wanted part | +c | are always in the last KEV locations. | +c %----------------------------------------------------% +c + call igraphdsortr (which, .true., kev+np, ritz, bounds) + end if +c + if (ishift .eq. 1 .and. np .gt. 0) then +c +c %-------------------------------------------------------% +c | Sort the unwanted Ritz values used as shifts so that | +c | the ones with largest Ritz estimates are first. | +c | This will tend to minimize the effects of the | +c | forward instability of the iteration when the shifts | +c | are applied in subroutine igraphdsapps. | +c %-------------------------------------------------------% +c + call igraphdsortr ('SM', .true., np, bounds, ritz) + call dcopy (np, ritz, 1, shifts, 1) + end if +c + call igraphsecond (t1) + tsgets = tsgets + (t1 - t0) +c + if (msglvl .gt. 0) then + call igraphivout (logfil, 1, kev, ndigit, '_sgets: KEV is') + call igraphivout (logfil, 1, np, ndigit, '_sgets: NP is') + call igraphdvout (logfil, kev+np, ritz, ndigit, + & '_sgets: Eigenvalues of current H matrix') + call igraphdvout (logfil, kev+np, bounds, ndigit, + & '_sgets: Associated Ritz estimates') + end if +c + return +c +c %---------------% +c | End of igraphdsgets | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsortc.f b/src/rigraph/vendor/arpack/dsortc.f new file mode 100644 index 0000000..a356adc --- /dev/null +++ b/src/rigraph/vendor/arpack/dsortc.f @@ -0,0 +1,344 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsortc +c +c\Description: +c Sorts the complex array in XREAL and XIMAG into the order +c specified by WHICH and optionally applies the permutation to the +c real array Y. It is assumed that if an element of XIMAG is +c nonzero, then its negative is also an element. In other words, +c both members of a complex conjugate pair are to be sorted and the +c pairs are kept adjacent to each other. +c +c\Usage: +c call igraphdsortc +c ( WHICH, APPLY, N, XREAL, XIMAG, Y ) +c +c\Arguments +c WHICH Character*2. (Input) +c 'LM' -> sort XREAL,XIMAG into increasing order of magnitude. +c 'SM' -> sort XREAL,XIMAG into decreasing order of magnitude. +c 'LR' -> sort XREAL into increasing order of algebraic. +c 'SR' -> sort XREAL into decreasing order of algebraic. +c 'LI' -> sort XIMAG into increasing order of magnitude. +c 'SI' -> sort XIMAG into decreasing order of magnitude. +c NOTE: If an element of XIMAG is non-zero, then its negative +c is also an element. +c +c APPLY Logical. (Input) +c APPLY = .TRUE. -> apply the sorted order to array Y. +c APPLY = .FALSE. -> do not apply the sorted order to array Y. +c +c N Integer. (INPUT) +c Size of the arrays. +c +c XREAL, Double precision array of length N. (INPUT/OUTPUT) +c XIMAG Real and imaginary part of the array to be sorted. +c +c Y Double precision array of length N. (INPUT/OUTPUT) +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c xx/xx/92: Version ' 2.1' +c Adapted from the sort routine in LANSO. +c +c\SCCS Information: @(#) +c FILE: sortc.F SID: 2.3 DATE OF SID: 4/20/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsortc (which, apply, n, xreal, ximag, y) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + logical apply + integer n +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & xreal(0:n-1), ximag(0:n-1), y(0:n-1) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, igap, j + Double precision + & temp, temp1, temp2 +c +c %--------------------% +c | External Functions | +c %--------------------% +c + Double precision + & dlapy2 + external dlapy2 +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + igap = n / 2 +c + if (which .eq. 'LM') then +c +c %------------------------------------------------------% +c | Sort XREAL,XIMAG into increasing order of magnitude. | +c %------------------------------------------------------% +c + 10 continue + if (igap .eq. 0) go to 9000 +c + do 30 i = igap, n-1 + j = i-igap + 20 continue +c + if (j.lt.0) go to 30 +c + temp1 = dlapy2(xreal(j),ximag(j)) + temp2 = dlapy2(xreal(j+igap),ximag(j+igap)) +c + if (temp1.gt.temp2) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 30 + end if + j = j-igap + go to 20 + 30 continue + igap = igap / 2 + go to 10 +c + else if (which .eq. 'SM') then +c +c %------------------------------------------------------% +c | Sort XREAL,XIMAG into decreasing order of magnitude. | +c %------------------------------------------------------% +c + 40 continue + if (igap .eq. 0) go to 9000 +c + do 60 i = igap, n-1 + j = i-igap + 50 continue +c + if (j .lt. 0) go to 60 +c + temp1 = dlapy2(xreal(j),ximag(j)) + temp2 = dlapy2(xreal(j+igap),ximag(j+igap)) +c + if (temp1.lt.temp2) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 60 + endif + j = j-igap + go to 50 + 60 continue + igap = igap / 2 + go to 40 +c + else if (which .eq. 'LR') then +c +c %------------------------------------------------% +c | Sort XREAL into increasing order of algebraic. | +c %------------------------------------------------% +c + 70 continue + if (igap .eq. 0) go to 9000 +c + do 90 i = igap, n-1 + j = i-igap + 80 continue +c + if (j.lt.0) go to 90 +c + if (xreal(j).gt.xreal(j+igap)) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 90 + endif + j = j-igap + go to 80 + 90 continue + igap = igap / 2 + go to 70 +c + else if (which .eq. 'SR') then +c +c %------------------------------------------------% +c | Sort XREAL into decreasing order of algebraic. | +c %------------------------------------------------% +c + 100 continue + if (igap .eq. 0) go to 9000 + do 120 i = igap, n-1 + j = i-igap + 110 continue +c + if (j.lt.0) go to 120 +c + if (xreal(j).lt.xreal(j+igap)) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 120 + endif + j = j-igap + go to 110 + 120 continue + igap = igap / 2 + go to 100 +c + else if (which .eq. 'LI') then +c +c %------------------------------------------------% +c | Sort XIMAG into increasing order of magnitude. | +c %------------------------------------------------% +c + 130 continue + if (igap .eq. 0) go to 9000 + do 150 i = igap, n-1 + j = i-igap + 140 continue +c + if (j.lt.0) go to 150 +c + if (abs(ximag(j)).gt.abs(ximag(j+igap))) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 150 + endif + j = j-igap + go to 140 + 150 continue + igap = igap / 2 + go to 130 +c + else if (which .eq. 'SI') then +c +c %------------------------------------------------% +c | Sort XIMAG into decreasing order of magnitude. | +c %------------------------------------------------% +c + 160 continue + if (igap .eq. 0) go to 9000 + do 180 i = igap, n-1 + j = i-igap + 170 continue +c + if (j.lt.0) go to 180 +c + if (abs(ximag(j)).lt.abs(ximag(j+igap))) then + temp = xreal(j) + xreal(j) = xreal(j+igap) + xreal(j+igap) = temp +c + temp = ximag(j) + ximag(j) = ximag(j+igap) + ximag(j+igap) = temp +c + if (apply) then + temp = y(j) + y(j) = y(j+igap) + y(j+igap) = temp + end if + else + go to 180 + endif + j = j-igap + go to 170 + 180 continue + igap = igap / 2 + go to 160 + end if +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsortc | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dsortr.f b/src/rigraph/vendor/arpack/dsortr.f new file mode 100644 index 0000000..d75bd61 --- /dev/null +++ b/src/rigraph/vendor/arpack/dsortr.f @@ -0,0 +1,218 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdsortr +c +c\Description: +c Sort the array X1 in the order specified by WHICH and optionally +c applies the permutation to the array X2. +c +c\Usage: +c call igraphdsortr +c ( WHICH, APPLY, N, X1, X2 ) +c +c\Arguments +c WHICH Character*2. (Input) +c 'LM' -> X1 is sorted into increasing order of magnitude. +c 'SM' -> X1 is sorted into decreasing order of magnitude. +c 'LA' -> X1 is sorted into increasing order of algebraic. +c 'SA' -> X1 is sorted into decreasing order of algebraic. +c +c APPLY Logical. (Input) +c APPLY = .TRUE. -> apply the sorted order to X2. +c APPLY = .FALSE. -> do not apply the sorted order to X2. +c +c N Integer. (INPUT) +c Size of the arrays. +c +c X1 Double precision array of length N. (INPUT/OUTPUT) +c The array to be sorted. +c +c X2 Double precision array of length N. (INPUT/OUTPUT) +c Only referenced if APPLY = .TRUE. +c +c\EndDoc +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\Revision history: +c 12/16/93: Version ' 2.1'. +c Adapted from the sort routine in LANSO. +c +c\SCCS Information: @(#) +c FILE: sortr.F SID: 2.3 DATE OF SID: 4/19/96 RELEASE: 2 +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdsortr (which, apply, n, x1, x2) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + character*2 which + logical apply + integer n +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & x1(0:n-1), x2(0:n-1) +c +c %---------------% +c | Local Scalars | +c %---------------% +c + integer i, igap, j + Double precision + & temp +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + igap = n / 2 +c + if (which .eq. 'SA') then +c +c X1 is sorted into decreasing order of algebraic. +c + 10 continue + if (igap .eq. 0) go to 9000 + do 30 i = igap, n-1 + j = i-igap + 20 continue +c + if (j.lt.0) go to 30 +c + if (x1(j).lt.x1(j+igap)) then + temp = x1(j) + x1(j) = x1(j+igap) + x1(j+igap) = temp + if (apply) then + temp = x2(j) + x2(j) = x2(j+igap) + x2(j+igap) = temp + end if + else + go to 30 + endif + j = j-igap + go to 20 + 30 continue + igap = igap / 2 + go to 10 +c + else if (which .eq. 'SM') then +c +c X1 is sorted into decreasing order of magnitude. +c + 40 continue + if (igap .eq. 0) go to 9000 + do 60 i = igap, n-1 + j = i-igap + 50 continue +c + if (j.lt.0) go to 60 +c + if (abs(x1(j)).lt.abs(x1(j+igap))) then + temp = x1(j) + x1(j) = x1(j+igap) + x1(j+igap) = temp + if (apply) then + temp = x2(j) + x2(j) = x2(j+igap) + x2(j+igap) = temp + end if + else + go to 60 + endif + j = j-igap + go to 50 + 60 continue + igap = igap / 2 + go to 40 +c + else if (which .eq. 'LA') then +c +c X1 is sorted into increasing order of algebraic. +c + 70 continue + if (igap .eq. 0) go to 9000 + do 90 i = igap, n-1 + j = i-igap + 80 continue +c + if (j.lt.0) go to 90 +c + if (x1(j).gt.x1(j+igap)) then + temp = x1(j) + x1(j) = x1(j+igap) + x1(j+igap) = temp + if (apply) then + temp = x2(j) + x2(j) = x2(j+igap) + x2(j+igap) = temp + end if + else + go to 90 + endif + j = j-igap + go to 80 + 90 continue + igap = igap / 2 + go to 70 +c + else if (which .eq. 'LM') then +c +c X1 is sorted into increasing order of magnitude. +c + 100 continue + if (igap .eq. 0) go to 9000 + do 120 i = igap, n-1 + j = i-igap + 110 continue +c + if (j.lt.0) go to 120 +c + if (abs(x1(j)).gt.abs(x1(j+igap))) then + temp = x1(j) + x1(j) = x1(j+igap) + x1(j+igap) = temp + if (apply) then + temp = x2(j) + x2(j) = x2(j+igap) + x2(j+igap) = temp + end if + else + go to 120 + endif + j = j-igap + go to 110 + 120 continue + igap = igap / 2 + go to 100 + end if +c + 9000 continue + return +c +c %---------------% +c | End of igraphdsortr | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dstatn.f b/src/rigraph/vendor/arpack/dstatn.f new file mode 100644 index 0000000..afd0a57 --- /dev/null +++ b/src/rigraph/vendor/arpack/dstatn.f @@ -0,0 +1,61 @@ +c +c %---------------------------------------------% +c | Initialize statistic and timing information | +c | for nonsymmetric Arnoldi code. | +c %---------------------------------------------% +c +c\Author +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: statn.F SID: 2.4 DATE OF SID: 4/20/96 RELEASE: 2 +c + subroutine igraphdstatn +c +c %--------------------------------% +c | See stat.doc for documentation | +c %--------------------------------% +c + include 'stat.h' +c +c %-----------------------% +c | Executable Statements | +c %-----------------------% +c + nopx = 0 + nbx = 0 + nrorth = 0 + nitref = 0 + nrstrt = 0 +c + tnaupd = 0.0D+0 + tnaup2 = 0.0D+0 + tnaitr = 0.0D+0 + tneigh = 0.0D+0 + tngets = 0.0D+0 + tnapps = 0.0D+0 + tnconv = 0.0D+0 + titref = 0.0D+0 + tgetv0 = 0.0D+0 + trvec = 0.0D+0 +c +c %----------------------------------------------------% +c | User time including reverse communication overhead | +c %----------------------------------------------------% +c + tmvopx = 0.0D+0 + tmvbx = 0.0D+0 +c + return +c +c +c %---------------% +c | End of igraphdstatn | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dstats.f b/src/rigraph/vendor/arpack/dstats.f new file mode 100644 index 0000000..545ed19 --- /dev/null +++ b/src/rigraph/vendor/arpack/dstats.f @@ -0,0 +1,47 @@ +c +c\SCCS Information: @(#) +c FILE: stats.F SID: 2.1 DATE OF SID: 4/19/96 RELEASE: 2 +c %---------------------------------------------% +c | Initialize statistic and timing information | +c | for symmetric Arnoldi code. | +c %---------------------------------------------% + + subroutine igraphdstats + +c %--------------------------------% +c | See stat.doc for documentation | +c %--------------------------------% + include 'stat.h' + +c %-----------------------% +c | Executable Statements | +c %-----------------------% + + nopx = 0 + nbx = 0 + nrorth = 0 + nitref = 0 + nrstrt = 0 + + tsaupd = 0.0D+0 + tsaup2 = 0.0D+0 + tsaitr = 0.0D+0 + tseigt = 0.0D+0 + tsgets = 0.0D+0 + tsapps = 0.0D+0 + tsconv = 0.0D+0 + titref = 0.0D+0 + tgetv0 = 0.0D+0 + trvec = 0.0D+0 + +c %----------------------------------------------------% +c | User time including reverse communication overhead | +c %----------------------------------------------------% + tmvopx = 0.0D+0 + tmvbx = 0.0D+0 + + return +c +c End of igraphdstats +c + end diff --git a/src/rigraph/vendor/arpack/dstqrb.f b/src/rigraph/vendor/arpack/dstqrb.f new file mode 100644 index 0000000..eff1369 --- /dev/null +++ b/src/rigraph/vendor/arpack/dstqrb.f @@ -0,0 +1,594 @@ +c----------------------------------------------------------------------- +c\BeginDoc +c +c\Name: igraphdstqrb +c +c\Description: +c Computes all eigenvalues and the last component of the eigenvectors +c of a symmetric tridiagonal matrix using the implicit QL or QR method. +c +c This is mostly a modification of the LAPACK routine dsteqr. +c See Remarks. +c +c\Usage: +c call igraphdstqrb +c ( N, D, E, Z, WORK, INFO ) +c +c\Arguments +c N Integer. (INPUT) +c The number of rows and columns in the matrix. N >= 0. +c +c D Double precision array, dimension (N). (INPUT/OUTPUT) +c On entry, D contains the diagonal elements of the +c tridiagonal matrix. +c On exit, D contains the eigenvalues, in ascending order. +c If an error exit is made, the eigenvalues are correct +c for indices 1,2,...,INFO-1, but they are unordered and +c may not be the smallest eigenvalues of the matrix. +c +c E Double precision array, dimension (N-1). (INPUT/OUTPUT) +c On entry, E contains the subdiagonal elements of the +c tridiagonal matrix in positions 1 through N-1. +c On exit, E has been destroyed. +c +c Z Double precision array, dimension (N). (OUTPUT) +c On exit, Z contains the last row of the orthonormal +c eigenvector matrix of the symmetric tridiagonal matrix. +c If an error exit is made, Z contains the last row of the +c eigenvector matrix associated with the stored eigenvalues. +c +c WORK Double precision array, dimension (max(1,2*N-2)). (WORKSPACE) +c Workspace used in accumulating the transformation for +c computing the last components of the eigenvectors. +c +c INFO Integer. (OUTPUT) +c = 0: normal return. +c < 0: if INFO = -i, the i-th argument had an illegal value. +c > 0: if INFO = +i, the i-th eigenvalue has not converged +c after a total of 30*N iterations. +c +c\Remarks +c 1. None. +c +c----------------------------------------------------------------------- +c +c\BeginLib +c +c\Local variables: +c xxxxxx real +c +c\Routines called: +c daxpy Level 1 BLAS that computes a vector triad. +c dcopy Level 1 BLAS that copies one vector to another. +c dswap Level 1 BLAS that swaps the contents of two vectors. +c lsame LAPACK character comparison routine. +c dlae2 LAPACK routine that computes the eigenvalues of a 2-by-2 +c symmetric matrix. +c dlaev2 LAPACK routine that eigendecomposition of a 2-by-2 symmetric +c matrix. +c dlamch LAPACK routine that determines machine constants. +c dlanst LAPACK routine that computes the norm of a matrix. +c dlapy2 LAPACK routine to compute sqrt(x**2+y**2) carefully. +c dlartg LAPACK Givens rotation construction routine. +c dlascl LAPACK routine for careful scaling of a matrix. +c dlaset LAPACK matrix initialization routine. +c dlasr LAPACK routine that applies an orthogonal transformation to +c a matrix. +c dlasrt LAPACK sorting routine. +c dsteqr LAPACK routine that computes eigenvalues and eigenvectors +c of a symmetric tridiagonal matrix. +c xerbla LAPACK error handler routine. +c +c\Authors +c Danny Sorensen Phuong Vu +c Richard Lehoucq CRPC / Rice University +c Dept. of Computational & Houston, Texas +c Applied Mathematics +c Rice University +c Houston, Texas +c +c\SCCS Information: @(#) +c FILE: stqrb.F SID: 2.5 DATE OF SID: 8/27/96 RELEASE: 2 +c +c\Remarks +c 1. Starting with version 2.5, this routine is a modified version +c of LAPACK version 2.0 subroutine SSTEQR. No lines are deleted, +c only commeted out and new lines inserted. +c All lines commented out have "c$$$" at the beginning. +c Note that the LAPACK version 1.0 subroutine SSTEQR contained +c bugs. +c +c\EndLib +c +c----------------------------------------------------------------------- +c + subroutine igraphdstqrb ( n, d, e, z, work, info ) +c +c %------------------% +c | Scalar Arguments | +c %------------------% +c + integer info, n +c +c %-----------------% +c | Array Arguments | +c %-----------------% +c + Double precision + & d( n ), e( n-1 ), z( n ), work( 2*n-2 ) +c +c .. parameters .. + Double precision + & zero, one, two, three + parameter ( zero = 0.0D+0, one = 1.0D+0, + & two = 2.0D+0, three = 3.0D+0 ) + integer maxit + parameter ( maxit = 30 ) +c .. +c .. local scalars .. + integer i, icompz, ii, iscale, j, jtot, k, l, l1, lend, + & lendm1, lendp1, lendsv, lm1, lsv, m, mm, mm1, + & nm1, nmaxit + Double precision + & anorm, b, c, eps, eps2, f, g, p, r, rt1, rt2, + & s, safmax, safmin, ssfmax, ssfmin, tst +c .. +c .. external functions .. + logical lsame + Double precision + & dlamch, dlanst, dlapy2 + external lsame, dlamch, dlanst, dlapy2 +c .. +c .. external subroutines .. + external dlae2, dlaev2, dlartg, dlascl, dlaset, dlasr, + & dlasrt, dswap, xerbla +c .. +c .. intrinsic functions .. + intrinsic abs, max, sign, sqrt +c .. +c .. executable statements .. +c +c test the input parameters. +c + info = 0 +c +c$$$ IF( LSAME( COMPZ, 'N' ) ) THEN +c$$$ ICOMPZ = 0 +c$$$ ELSE IF( LSAME( COMPZ, 'V' ) ) THEN +c$$$ ICOMPZ = 1 +c$$$ ELSE IF( LSAME( COMPZ, 'I' ) ) THEN +c$$$ ICOMPZ = 2 +c$$$ ELSE +c$$$ ICOMPZ = -1 +c$$$ END IF +c$$$ IF( ICOMPZ.LT.0 ) THEN +c$$$ INFO = -1 +c$$$ ELSE IF( N.LT.0 ) THEN +c$$$ INFO = -2 +c$$$ ELSE IF( ( LDZ.LT.1 ) .OR. ( ICOMPZ.GT.0 .AND. LDZ.LT.MAX( 1, +c$$$ $ N ) ) ) THEN +c$$$ INFO = -6 +c$$$ END IF +c$$$ IF( INFO.NE.0 ) THEN +c$$$ CALL XERBLA( 'SSTEQR', -INFO ) +c$$$ RETURN +c$$$ END IF +c +c *** New starting with version 2.5 *** +c + icompz = 2 +c ************************************* +c +c quick return if possible +c + if( n.eq.0 ) + $ return +c + if( n.eq.1 ) then + if( icompz.eq.2 ) z( 1 ) = one + return + end if +c +c determine the unit roundoff and over/underflow thresholds. +c + eps = dlamch( 'e' ) + eps2 = eps**2 + safmin = dlamch( 's' ) + safmax = one / safmin + ssfmax = sqrt( safmax ) / three + ssfmin = sqrt( safmin ) / eps2 +c +c compute the eigenvalues and eigenvectors of the tridiagonal +c matrix. +c +c$$ if( icompz.eq.2 ) +c$$$ $ call dlaset( 'full', n, n, zero, one, z, ldz ) +c +c *** New starting with version 2.5 *** +c + if ( icompz .eq. 2 ) then + do 5 j = 1, n-1 + z(j) = zero + 5 continue + z( n ) = one + end if +c ************************************* +c + nmaxit = n*maxit + jtot = 0 +c +c determine where the matrix splits and choose ql or qr iteration +c for each block, according to whether top or bottom diagonal +c element is smaller. +c + l1 = 1 + nm1 = n - 1 +c + 10 continue + if( l1.gt.n ) + $ go to 160 + if( l1.gt.1 ) + $ e( l1-1 ) = zero + if( l1.le.nm1 ) then + do 20 m = l1, nm1 + tst = abs( e( m ) ) + if( tst.eq.zero ) + $ go to 30 + if( tst.le.( sqrt( abs( d( m ) ) )*sqrt( abs( d( m+ + $ 1 ) ) ) )*eps ) then + e( m ) = zero + go to 30 + end if + 20 continue + end if + m = n +c + 30 continue + l = l1 + lsv = l + lend = m + lendsv = lend + l1 = m + 1 + if( lend.eq.l ) + $ go to 10 +c +c scale submatrix in rows and columns l to lend +c + anorm = dlanst( 'i', lend-l+1, d( l ), e( l ) ) + iscale = 0 + if( anorm.eq.zero ) + $ go to 10 + if( anorm.gt.ssfmax ) then + iscale = 1 + call dlascl( 'g', 0, 0, anorm, ssfmax, lend-l+1, 1, d( l ), n, + $ info ) + call dlascl( 'g', 0, 0, anorm, ssfmax, lend-l, 1, e( l ), n, + $ info ) + else if( anorm.lt.ssfmin ) then + iscale = 2 + call dlascl( 'g', 0, 0, anorm, ssfmin, lend-l+1, 1, d( l ), n, + $ info ) + call dlascl( 'g', 0, 0, anorm, ssfmin, lend-l, 1, e( l ), n, + $ info ) + end if +c +c choose between ql and qr iteration +c + if( abs( d( lend ) ).lt.abs( d( l ) ) ) then + lend = lsv + l = lendsv + end if +c + if( lend.gt.l ) then +c +c ql iteration +c +c look for small subdiagonal element. +c + 40 continue + if( l.ne.lend ) then + lendm1 = lend - 1 + do 50 m = l, lendm1 + tst = abs( e( m ) )**2 + if( tst.le.( eps2*abs( d( m ) ) )*abs( d( m+1 ) )+ + $ safmin )go to 60 + 50 continue + end if +c + m = lend +c + 60 continue + if( m.lt.lend ) + $ e( m ) = zero + p = d( l ) + if( m.eq.l ) + $ go to 80 +c +c if remaining matrix is 2-by-2, use dlae2 or dlaev2 +c to compute its eigensystem. +c + if( m.eq.l+1 ) then + if( icompz.gt.0 ) then + call dlaev2( d( l ), e( l ), d( l+1 ), rt1, rt2, c, s ) + work( l ) = c + work( n-1+l ) = s +c$$$ call dlasr( 'r', 'v', 'b', n, 2, work( l ), +c$$$ $ work( n-1+l ), z( 1, l ), ldz ) +c +c *** New starting with version 2.5 *** +c + tst = z(l+1) + z(l+1) = c*tst - s*z(l) + z(l) = s*tst + c*z(l) +c ************************************* + else + call dlae2( d( l ), e( l ), d( l+1 ), rt1, rt2 ) + end if + d( l ) = rt1 + d( l+1 ) = rt2 + e( l ) = zero + l = l + 2 + if( l.le.lend ) + $ go to 40 + go to 140 + end if +c + if( jtot.eq.nmaxit ) + $ go to 140 + jtot = jtot + 1 +c +c form shift. +c + g = ( d( l+1 )-p ) / ( two*e( l ) ) + r = dlapy2( g, one ) + g = d( m ) - p + ( e( l ) / ( g+sign( r, g ) ) ) +c + s = one + c = one + p = zero +c +c inner loop +c + mm1 = m - 1 + do 70 i = mm1, l, -1 + f = s*e( i ) + b = c*e( i ) + call dlartg( g, f, c, s, r ) + if( i.ne.m-1 ) + $ e( i+1 ) = r + g = d( i+1 ) - p + r = ( d( i )-g )*s + two*c*b + p = s*r + d( i+1 ) = g + p + g = c*r - b +c +c if eigenvectors are desired, then save rotations. +c + if( icompz.gt.0 ) then + work( i ) = c + work( n-1+i ) = -s + end if +c + 70 continue +c +c if eigenvectors are desired, then apply saved rotations. +c + if( icompz.gt.0 ) then + mm = m - l + 1 +c$$$ call dlasr( 'r', 'v', 'b', n, mm, work( l ), work( n-1+l ), +c$$$ $ z( 1, l ), ldz ) +c +c *** New starting with version 2.5 *** +c + call dlasr( 'r', 'v', 'b', 1, mm, work( l ), + & work( n-1+l ), z( l ), 1 ) +c ************************************* + end if +c + d( l ) = d( l ) - p + e( l ) = g + go to 40 +c +c eigenvalue found. +c + 80 continue + d( l ) = p +c + l = l + 1 + if( l.le.lend ) + $ go to 40 + go to 140 +c + else +c +c qr iteration +c +c look for small superdiagonal element. +c + 90 continue + if( l.ne.lend ) then + lendp1 = lend + 1 + do 100 m = l, lendp1, -1 + tst = abs( e( m-1 ) )**2 + if( tst.le.( eps2*abs( d( m ) ) )*abs( d( m-1 ) )+ + $ safmin )go to 110 + 100 continue + end if +c + m = lend +c + 110 continue + if( m.gt.lend ) + $ e( m-1 ) = zero + p = d( l ) + if( m.eq.l ) + $ go to 130 +c +c if remaining matrix is 2-by-2, use dlae2 or dlaev2 +c to compute its eigensystem. +c + if( m.eq.l-1 ) then + if( icompz.gt.0 ) then + call dlaev2( d( l-1 ), e( l-1 ), d( l ), rt1, rt2, c, s ) +c$$$ work( m ) = c +c$$$ work( n-1+m ) = s +c$$$ call dlasr( 'r', 'v', 'f', n, 2, work( m ), +c$$$ $ work( n-1+m ), z( 1, l-1 ), ldz ) +c +c *** New starting with version 2.5 *** +c + tst = z(l) + z(l) = c*tst - s*z(l-1) + z(l-1) = s*tst + c*z(l-1) +c ************************************* + else + call dlae2( d( l-1 ), e( l-1 ), d( l ), rt1, rt2 ) + end if + d( l-1 ) = rt1 + d( l ) = rt2 + e( l-1 ) = zero + l = l - 2 + if( l.ge.lend ) + $ go to 90 + go to 140 + end if +c + if( jtot.eq.nmaxit ) + $ go to 140 + jtot = jtot + 1 +c +c form shift. +c + g = ( d( l-1 )-p ) / ( two*e( l-1 ) ) + r = dlapy2( g, one ) + g = d( m ) - p + ( e( l-1 ) / ( g+sign( r, g ) ) ) +c + s = one + c = one + p = zero +c +c inner loop +c + lm1 = l - 1 + do 120 i = m, lm1 + f = s*e( i ) + b = c*e( i ) + call dlartg( g, f, c, s, r ) + if( i.ne.m ) + $ e( i-1 ) = r + g = d( i ) - p + r = ( d( i+1 )-g )*s + two*c*b + p = s*r + d( i ) = g + p + g = c*r - b +c +c if eigenvectors are desired, then save rotations. +c + if( icompz.gt.0 ) then + work( i ) = c + work( n-1+i ) = s + end if +c + 120 continue +c +c if eigenvectors are desired, then apply saved rotations. +c + if( icompz.gt.0 ) then + mm = l - m + 1 +c$$$ call dlasr( 'r', 'v', 'f', n, mm, work( m ), work( n-1+m ), +c$$$ $ z( 1, m ), ldz ) +c +c *** New starting with version 2.5 *** +c + call dlasr( 'r', 'v', 'f', 1, mm, work( m ), work( n-1+m ), + & z( m ), 1 ) +c ************************************* + end if +c + d( l ) = d( l ) - p + e( lm1 ) = g + go to 90 +c +c eigenvalue found. +c + 130 continue + d( l ) = p +c + l = l - 1 + if( l.ge.lend ) + $ go to 90 + go to 140 +c + end if +c +c undo scaling if necessary +c + 140 continue + if( iscale.eq.1 ) then + call dlascl( 'g', 0, 0, ssfmax, anorm, lendsv-lsv+1, 1, + $ d( lsv ), n, info ) + call dlascl( 'g', 0, 0, ssfmax, anorm, lendsv-lsv, 1, e( lsv ), + $ n, info ) + else if( iscale.eq.2 ) then + call dlascl( 'g', 0, 0, ssfmin, anorm, lendsv-lsv+1, 1, + $ d( lsv ), n, info ) + call dlascl( 'g', 0, 0, ssfmin, anorm, lendsv-lsv, 1, e( lsv ), + $ n, info ) + end if +c +c check for no convergence to an eigenvalue after a total +c of n*maxit iterations. +c + if( jtot.lt.nmaxit ) + $ go to 10 + do 150 i = 1, n - 1 + if( e( i ).ne.zero ) + $ info = info + 1 + 150 continue + go to 190 +c +c order eigenvalues and eigenvectors. +c + 160 continue + if( icompz.eq.0 ) then +c +c use quick sort +c + call dlasrt( 'i', n, d, info ) +c + else +c +c use selection sort to minimize swaps of eigenvectors +c + do 180 ii = 2, n + i = ii - 1 + k = i + p = d( i ) + do 170 j = ii, n + if( d( j ).lt.p ) then + k = j + p = d( j ) + end if + 170 continue + if( k.ne.i ) then + d( k ) = d( i ) + d( i ) = p +c$$$ call dswap( n, z( 1, i ), 1, z( 1, k ), 1 ) +c *** New starting with version 2.5 *** +c + p = z(k) + z(k) = z(i) + z(i) = p +c ************************************* + end if + 180 continue + end if +c + 190 continue + return +c +c %---------------% +c | End of igraphdstqrb | +c %---------------% +c + end diff --git a/src/rigraph/vendor/arpack/dvout.f b/src/rigraph/vendor/arpack/dvout.f new file mode 100644 index 0000000..8bd7b1b --- /dev/null +++ b/src/rigraph/vendor/arpack/dvout.f @@ -0,0 +1,122 @@ +*----------------------------------------------------------------------- +* Routine: DVOUT +* +* Purpose: Real vector output routine. +* +* Usage: CALL DVOUT (LOUT, N, SX, IDIGIT, IFMT) +* +* Arguments +* N - Length of array SX. (Input) +* SX - Real array to be printed. (Input) +* IFMT - Format to be used in printing array SX. (Input) +* IDIGIT - Print up to IABS(IDIGIT) decimal digits per number. (In) +* If IDIGIT .LT. 0, printing is done with 72 columns. +* If IDIGIT .GT. 0, printing is done with 132 columns. +* +*----------------------------------------------------------------------- +* + SUBROUTINE IGRAPHDVOUT( LOUT, N, SX, IDIGIT, IFMT ) +* ... +* ... SPECIFICATIONS FOR ARGUMENTS +* ... +* ... SPECIFICATIONS FOR LOCAL VARIABLES +* .. Scalar Arguments .. + CHARACTER*( * ) IFMT + INTEGER IDIGIT, LOUT, N +* .. +* .. Array Arguments .. + DOUBLE PRECISION SX( * ) +* .. +* .. Local Scalars .. + CHARACTER*80 LINE + INTEGER I, K1, K2, LLL, NDIGIT +* .. +* .. Intrinsic Functions .. + INTRINSIC LEN, MIN, MIN0 +* .. +* .. Executable Statements .. +* ... +* ... FIRST EXECUTABLE STATEMENT +* +* +c$$$ LLL = MIN( LEN( IFMT ), 80 ) +c$$$ DO 10 I = 1, LLL +c$$$ LINE( I: I ) = '-' +c$$$ 10 CONTINUE +c$$$* +c$$$ DO 20 I = LLL + 1, 80 +c$$$ LINE( I: I ) = ' ' +c$$$ 20 CONTINUE +c$$$* +c$$$ WRITE( LOUT, FMT = 9999 )IFMT, LINE( 1: LLL ) +c$$$ 9999 FORMAT( / 1X, A, / 1X, A ) +c$$$* +c$$$ IF( N.LE.0 ) +c$$$ $ RETURN +c$$$ NDIGIT = IDIGIT +c$$$ IF( IDIGIT.EQ.0 ) +c$$$ $ NDIGIT = 4 +c$$$* +c$$$*======================================================================= +c$$$* CODE FOR OUTPUT USING 72 COLUMNS FORMAT +c$$$*======================================================================= +c$$$* +c$$$ IF( IDIGIT.LT.0 ) THEN +c$$$ NDIGIT = -IDIGIT +c$$$ IF( NDIGIT.LE.4 ) THEN +c$$$ DO 30 K1 = 1, N, 5 +c$$$ K2 = MIN0( N, K1+4 ) +c$$$ WRITE( LOUT, FMT = 9998 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 30 CONTINUE +c$$$ ELSE IF( NDIGIT.LE.6 ) THEN +c$$$ DO 40 K1 = 1, N, 4 +c$$$ K2 = MIN0( N, K1+3 ) +c$$$ WRITE( LOUT, FMT = 9997 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 40 CONTINUE +c$$$ ELSE IF( NDIGIT.LE.10 ) THEN +c$$$ DO 50 K1 = 1, N, 3 +c$$$ K2 = MIN0( N, K1+2 ) +c$$$ WRITE( LOUT, FMT = 9996 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 50 CONTINUE +c$$$ ELSE +c$$$ DO 60 K1 = 1, N, 2 +c$$$ K2 = MIN0( N, K1+1 ) +c$$$ WRITE( LOUT, FMT = 9995 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 60 CONTINUE +c$$$ END IF +c$$$* +c$$$*======================================================================= +c$$$* CODE FOR OUTPUT USING 132 COLUMNS FORMAT +c$$$*======================================================================= +c$$$* +c$$$ ELSE +c$$$ IF( NDIGIT.LE.4 ) THEN +c$$$ DO 70 K1 = 1, N, 10 +c$$$ K2 = MIN0( N, K1+9 ) +c$$$ WRITE( LOUT, FMT = 9998 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 70 CONTINUE +c$$$ ELSE IF( NDIGIT.LE.6 ) THEN +c$$$ DO 80 K1 = 1, N, 8 +c$$$ K2 = MIN0( N, K1+7 ) +c$$$ WRITE( LOUT, FMT = 9997 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 80 CONTINUE +c$$$ ELSE IF( NDIGIT.LE.10 ) THEN +c$$$ DO 90 K1 = 1, N, 6 +c$$$ K2 = MIN0( N, K1+5 ) +c$$$ WRITE( LOUT, FMT = 9996 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 90 CONTINUE +c$$$ ELSE +c$$$ DO 100 K1 = 1, N, 5 +c$$$ K2 = MIN0( N, K1+4 ) +c$$$ WRITE( LOUT, FMT = 9995 )K1, K2, ( SX( I ), I = K1, K2 ) +c$$$ 100 CONTINUE +c$$$ END IF +c$$$ END IF +c$$$ WRITE( LOUT, FMT = 9994 ) +c$$$ RETURN +c$$$ 9998 FORMAT( 1X, I4, ' - ', I4, ':', 1P, 10D12.3 ) +c$$$ 9997 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 8D14.5 ) +c$$$ 9996 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 6D18.9 ) +c$$$ 9995 FORMAT( 1X, I4, ' - ', I4, ':', 1X, 1P, 5D24.13 ) +c$$$ 9994 FORMAT( 1X, ' ' ) + END diff --git a/src/rigraph/vendor/arpack/ivout.f b/src/rigraph/vendor/arpack/ivout.f new file mode 100644 index 0000000..3f6089c --- /dev/null +++ b/src/rigraph/vendor/arpack/ivout.f @@ -0,0 +1,120 @@ +C----------------------------------------------------------------------- +C Routine: IVOUT +C +C Purpose: Integer vector output routine. +C +C Usage: CALL IVOUT (LOUT, N, IX, IDIGIT, IFMT) +C +C Arguments +C N - Length of array IX. (Input) +C IX - Integer array to be printed. (Input) +C IFMT - Format to be used in printing array IX. (Input) +C IDIGIT - Print up to ABS(IDIGIT) decimal digits / number. (Input) +C If IDIGIT .LT. 0, printing is done with 72 columns. +C If IDIGIT .GT. 0, printing is done with 132 columns. +C +C----------------------------------------------------------------------- +C + SUBROUTINE IGRAPHIVOUT (LOUT, N, IX, IDIGIT, IFMT) +C ... +C ... SPECIFICATIONS FOR ARGUMENTS + INTEGER IX(*), N, IDIGIT, LOUT + CHARACTER IFMT*(*) +C ... +C ... SPECIFICATIONS FOR LOCAL VARIABLES + INTEGER I, NDIGIT, K1, K2, LLL + CHARACTER*80 LINE +* ... +* ... SPECIFICATIONS INTRINSICS + INTRINSIC MIN +* +C +c$$$ LLL = MIN ( LEN ( IFMT ), 80 ) +c$$$ DO 1 I = 1, LLL +c$$$ LINE(I:I) = '-' +c$$$ 1 CONTINUE +c$$$C +c$$$ DO 2 I = LLL+1, 80 +c$$$ LINE(I:I) = ' ' +c$$$ 2 CONTINUE +c$$$C +c$$$ WRITE ( LOUT, 2000 ) IFMT, LINE(1:LLL) +c$$$ 2000 FORMAT ( /1X, A /1X, A ) +c$$$C +c$$$ IF (N .LE. 0) RETURN +c$$$ NDIGIT = IDIGIT +c$$$ IF (IDIGIT .EQ. 0) NDIGIT = 4 +c$$$C +c$$$C======================================================================= +c$$$C CODE FOR OUTPUT USING 72 COLUMNS FORMAT +c$$$C======================================================================= +c$$$C +c$$$ IF (IDIGIT .LT. 0) THEN +c$$$C +c$$$ NDIGIT = -IDIGIT +c$$$ IF (NDIGIT .LE. 4) THEN +c$$$ DO 10 K1 = 1, N, 10 +c$$$ K2 = MIN0(N,K1+9) +c$$$ WRITE(LOUT,1000) K1,K2,(IX(I),I=K1,K2) +c$$$ 10 CONTINUE +c$$$C +c$$$ ELSE IF (NDIGIT .LE. 6) THEN +c$$$ DO 30 K1 = 1, N, 7 +c$$$ K2 = MIN0(N,K1+6) +c$$$ WRITE(LOUT,1001) K1,K2,(IX(I),I=K1,K2) +c$$$ 30 CONTINUE +c$$$C +c$$$ ELSE IF (NDIGIT .LE. 10) THEN +c$$$ DO 50 K1 = 1, N, 5 +c$$$ K2 = MIN0(N,K1+4) +c$$$ WRITE(LOUT,1002) K1,K2,(IX(I),I=K1,K2) +c$$$ 50 CONTINUE +c$$$C +c$$$ ELSE +c$$$ DO 70 K1 = 1, N, 3 +c$$$ K2 = MIN0(N,K1+2) +c$$$ WRITE(LOUT,1003) K1,K2,(IX(I),I=K1,K2) +c$$$ 70 CONTINUE +c$$$ END IF +c$$$C +c$$$C======================================================================= +c$$$C CODE FOR OUTPUT USING 132 COLUMNS FORMAT +c$$$C======================================================================= +c$$$C +c$$$ ELSE +c$$$C +c$$$ IF (NDIGIT .LE. 4) THEN +c$$$ DO 90 K1 = 1, N, 20 +c$$$ K2 = MIN0(N,K1+19) +c$$$ WRITE(LOUT,1000) K1,K2,(IX(I),I=K1,K2) +c$$$ 90 CONTINUE +c$$$C +c$$$ ELSE IF (NDIGIT .LE. 6) THEN +c$$$ DO 110 K1 = 1, N, 15 +c$$$ K2 = MIN0(N,K1+14) +c$$$ WRITE(LOUT,1001) K1,K2,(IX(I),I=K1,K2) +c$$$ 110 CONTINUE +c$$$C +c$$$ ELSE IF (NDIGIT .LE. 10) THEN +c$$$ DO 130 K1 = 1, N, 10 +c$$$ K2 = MIN0(N,K1+9) +c$$$ WRITE(LOUT,1002) K1,K2,(IX(I),I=K1,K2) +c$$$ 130 CONTINUE +c$$$C +c$$$ ELSE +c$$$ DO 150 K1 = 1, N, 7 +c$$$ K2 = MIN0(N,K1+6) +c$$$ WRITE(LOUT,1003) K1,K2,(IX(I),I=K1,K2) +c$$$ 150 CONTINUE +c$$$ END IF +c$$$ END IF +c$$$ WRITE (LOUT,1004) +c$$$C +c$$$ 1000 FORMAT(1X,I4,' - ',I4,':',20(1X,I5)) +c$$$ 1001 FORMAT(1X,I4,' - ',I4,':',15(1X,I7)) +c$$$ 1002 FORMAT(1X,I4,' - ',I4,':',10(1X,I11)) +c$$$ 1003 FORMAT(1X,I4,' - ',I4,':',7(1X,I15)) +c$$$ 1004 FORMAT(1X,' ') +c$$$C + RETURN + END diff --git a/src/rigraph/vendor/arpack/second.f b/src/rigraph/vendor/arpack/second.f new file mode 100644 index 0000000..37023c3 --- /dev/null +++ b/src/rigraph/vendor/arpack/second.f @@ -0,0 +1,35 @@ + SUBROUTINE IGRAPHSECOND( T ) +* + REAL T +* +* -- LAPACK auxiliary routine (preliminary version) -- +* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., +* Courant Institute, Argonne National Lab, and Rice University +* July 26, 1991 +* +* Purpose +* ======= +* +* SECOND returns the user time for a process in igraphseconds. +* This version gets the time from the system function ETIME. +* +* .. Local Scalars .. + REAL T1 +* .. +* .. Local Arrays .. + REAL TARRAY( 2 ) +* .. +* .. External Functions .. + REAL ETIME +* .. +* .. Executable Statements .. +* + TARRAY( 1 ) = 0.0 + T1 = ETIME( TARRAY ) + T = TARRAY( 1 ) + + RETURN +* +* End of SECOND +* + END diff --git a/src/rigraph/vendor/arpack/stat.h b/src/rigraph/vendor/arpack/stat.h new file mode 100644 index 0000000..ae407cb --- /dev/null +++ b/src/rigraph/vendor/arpack/stat.h @@ -0,0 +1,21 @@ +c %--------------------------------% +c | See stat.doc for documentation | +c %--------------------------------% +c +c\SCCS Information: @(#) +c FILE: stat.h SID: 2.2 DATE OF SID: 11/16/95 RELEASE: 2 +c + real t0, t1, t2, t3, t4, t5 +c save t0, t1, t2, t3, t4, t5 +c + integer nopx, nbx, nrorth, nitref, nrstrt + real tsaupd, tsaup2, tsaitr, tseigt, tsgets, tsapps, tsconv, + & tnaupd, tnaup2, tnaitr, tneigh, tngets, tnapps, tnconv, + & tcaupd, tcaup2, tcaitr, tceigh, tcgets, tcapps, tcconv, + & tmvopx, tmvbx, tgetv0, titref, trvec + common /timing/ + & nopx, nbx, nrorth, nitref, nrstrt, + & tsaupd, tsaup2, tsaitr, tseigt, tsgets, tsapps, tsconv, + & tnaupd, tnaup2, tnaitr, tneigh, tngets, tnapps, tnconv, + & tcaupd, tcaup2, tcaitr, tceigh, tcgets, tcapps, tcconv, + & tmvopx, tmvbx, tgetv0, titref, trvec diff --git a/src/rigraph/vendor/arpack/wrap.f b/src/rigraph/vendor/arpack/wrap.f new file mode 100644 index 0000000..39c4720 --- /dev/null +++ b/src/rigraph/vendor/arpack/wrap.f @@ -0,0 +1,151 @@ +c----------------------------------------------------------------------- +c Wrapper functions, so we don't need to pass logicals from +c C to Fortran, because that generates LTO warnings, as the compiler +c apparently cannot match a Fortran logical to a C type. +c----------------------------------------------------------------------- +c + subroutine igraphxdsortr (which, apply, n, x1, x2) +c + character*2 which + integer apply + integer n + Double precision + & x1(0:n-1), x2(0:n-1) +c + logical applyx +c + if (apply .eq. 1) then + applyx = .true. + else + applyx = .false. + end if +c + call igraphdsortr(which, applyx, n, x1, x2) +c + return +c + end +c +c----------------------------------------------------------------------- +c + subroutine igraphxdsortc (which, apply, n, xreal, ximag, y) +c + character*2 which + integer apply + integer n +c + Double precision + & xreal(0:n-1), ximag(0:n-1), y(0:n-1) +c + logical applyx +c + if (apply .eq. 1) then + applyx = .true. + else + applyx = .false. + end if +c + call igraphdsortc(which, applyx, n, xreal, ximag, y) +c + return +c + end +c +c----------------------------------------------------------------------- +c + subroutine igraphxdneupd (rvec, howmny, select, dr, di, z, ldz, + & sigmar, sigmai, workev, bmat, n, which, nev, tol, + & resid, ncv, v, ldv, iparam, ipntr, workd, + & workl, lworkl, info) +c + character bmat, howmny, which*2 + integer rvec + integer info, ldz, ldv, lworkl, n, ncv, nev + Double precision + & sigmar, sigmai, tol +c + integer iparam(11), ipntr(14) + integer select(ncv) + Double precision + & dr(nev+1), di(nev+1), resid(n), v(ldv,ncv), z(ldz,*), + & workd(3*n), workl(lworkl), workev(3*ncv) +c + logical rvecx + integer i + logical selectx(ncv) +c + if (rvec .eq. 1) then + rvecx = .true. + else + rvecx = .false. + end if +c + i = 1 + 100 if (i .gt. ncv) then + go to 110 + end if + if (select(i) .eq. 1) then + selectx(i) = .true. + else + selectx(i) = .false. + end if + i = i + 1 + go to 100 + 110 continue +c + call igraphdneupd(rvecx, howmny, selectx, dr, di, z, ldz, + & sigmar, sigmai, workev, bmat, n, which, nev, tol, + & resid, ncv, v, ldv, iparam, ipntr, workd, + & workl, lworkl, info) +c + return +c + end +c +c----------------------------------------------------------------------- +c + subroutine igraphxdseupd (rvec, howmny, select, d, z, ldz, + & sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c + character bmat, howmny, which*2 + integer rvec, select(ncv) + integer info, ldz, ldv, lworkl, n, ncv, nev + Double precision + & sigma, tol +c + integer iparam(7), ipntr(11) + Double precision + & d(nev), resid(n), v(ldv,ncv), z(ldz, nev), + & workd(2*n), workl(lworkl) +c + logical rvecx + integer i + logical selectx(ncv) +c + if (rvec .eq. 1) then + rvecx = .true. + else + rvecx = .false. + end if +c + i = 1 + 100 if (i .gt. ncv) then + go to 110 + end if + if (select(i) .eq. 1) then + selectx(i) = .true. + else + selectx(i) = .false. + end if + i = i + 1 + go to 100 + 110 continue +c + call igraphdseupd(rvecx, howmny, selectx, d, z, ldz, + & sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, + & ipntr, workd, workl, lworkl, info ) +c + return +c + end diff --git a/src/rigraph/vendor/cs/SuiteSparse_config.h b/src/rigraph/vendor/cs/SuiteSparse_config.h new file mode 100644 index 0000000..bd0cced --- /dev/null +++ b/src/rigraph/vendor/cs/SuiteSparse_config.h @@ -0,0 +1,221 @@ +/* ========================================================================== */ +/* === SuiteSparse_config =================================================== */ +/* ========================================================================== */ + +/* Configuration file for SuiteSparse: a Suite of Sparse matrix packages + * (AMD, COLAMD, CCOLAMD, CAMD, CHOLMOD, UMFPACK, CXSparse, and others). + * + * SuiteSparse_config.h provides the definition of the long integer. On most + * systems, a C program can be compiled in LP64 mode, in which long's and + * pointers are both 64-bits, and int's are 32-bits. Windows 64, however, uses + * the LLP64 model, in which int's and long's are 32-bits, and long long's and + * pointers are 64-bits. + * + * SuiteSparse packages that include long integer versions are + * intended for the LP64 mode. However, as a workaround for Windows 64 + * (and perhaps other systems), the long integer can be redefined. + * + * If _WIN64 is defined, then the __int64 type is used instead of long. + * + * The long integer can also be defined at compile time. For example, this + * could be added to SuiteSparse_config.mk: + * + * CFLAGS = -O -D'SuiteSparse_long=long long' \ + * -D'SuiteSparse_long_max=9223372036854775801' -D'SuiteSparse_long_idd="lld"' + * + * This file defines SuiteSparse_long as either long (on all but _WIN64) or + * __int64 on Windows 64. The intent is that a SuiteSparse_long is always a + * 64-bit integer in a 64-bit code. ptrdiff_t might be a better choice than + * long; it is always the same size as a pointer. + * + * This file also defines the SUITESPARSE_VERSION and related definitions. + * + * Copyright (c) 2012, Timothy A. Davis. No licensing restrictions apply + * to this file or to the SuiteSparse_config directory. + * Author: Timothy A. Davis. + */ + +#ifndef SUITESPARSE_CONFIG_H +#define SUITESPARSE_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* ========================================================================== */ +/* === SuiteSparse_long ===================================================== */ +/* ========================================================================== */ + +#ifndef SuiteSparse_long + +#ifdef _WIN64 + +#define SuiteSparse_long __int64 +#define SuiteSparse_long_max _I64_MAX +#define SuiteSparse_long_idd "I64d" + +#else + +#define SuiteSparse_long long +#define SuiteSparse_long_max LONG_MAX +#define SuiteSparse_long_idd "ld" + +#endif +#define SuiteSparse_long_id "%" SuiteSparse_long_idd +#endif + +/* Disable unneeded parts for igraph */ +#if 0 /* start comment */ + +/* ========================================================================== */ +/* === SuiteSparse_config parameters and functions ========================== */ +/* ========================================================================== */ + +/* SuiteSparse-wide parameters are placed in this struct. It is meant to be + an extern, globally-accessible struct. It is not meant to be updated + frequently by multiple threads. Rather, if an application needs to modify + SuiteSparse_config, it should do it once at the beginning of the application, + before multiple threads are launched. + + The intent of these function pointers is that they not be used in your + application directly, except to assign them to the desired user-provided + functions. Rather, you should use the + */ + +struct SuiteSparse_config_struct +{ + void *(*malloc_func) (size_t) ; /* pointer to malloc */ + void *(*calloc_func) (size_t, size_t) ; /* pointer to calloc */ + void *(*realloc_func) (void *, size_t) ; /* pointer to realloc */ + void (*free_func) (void *) ; /* pointer to free */ + int (*printf_func) (const char *, ...) ; /* pointer to printf */ + double (*hypot_func) (double, double) ; /* pointer to hypot */ + int (*divcomplex_func) (double, double, double, double, double *, double *); +} ; + +extern struct SuiteSparse_config_struct SuiteSparse_config ; + +void SuiteSparse_start ( void ) ; /* called to start SuiteSparse */ + +void SuiteSparse_finish ( void ) ; /* called to finish SuiteSparse */ + +void *SuiteSparse_malloc /* pointer to allocated block of memory */ +( + size_t nitems, /* number of items to malloc (>=1 is enforced) */ + size_t size_of_item /* sizeof each item */ +) ; + +void *SuiteSparse_calloc /* pointer to allocated block of memory */ +( + size_t nitems, /* number of items to calloc (>=1 is enforced) */ + size_t size_of_item /* sizeof each item */ +) ; + +void *SuiteSparse_realloc /* pointer to reallocated block of memory, or + to original block if the realloc failed. */ +( + size_t nitems_new, /* new number of items in the object */ + size_t nitems_old, /* old number of items in the object */ + size_t size_of_item, /* sizeof each item */ + void *p, /* old object to reallocate */ + int *ok /* 1 if successful, 0 otherwise */ +) ; + +void *SuiteSparse_free /* always returns NULL */ +( + void *p /* block to free */ +) ; + +void SuiteSparse_tic /* start the timer */ +( + double tic [2] /* output, contents undefined on input */ +) ; + +double SuiteSparse_toc /* return time in seconds since last tic */ +( + double tic [2] /* input: from last call to SuiteSparse_tic */ +) ; + +double SuiteSparse_time /* returns current wall clock time in seconds */ +( + void +) ; + +/* returns sqrt (x^2 + y^2), computed reliably */ +double SuiteSparse_hypot (double x, double y) ; + +/* complex division of c = a/b */ +int SuiteSparse_divcomplex +( + double ar, double ai, /* real and imaginary parts of a */ + double br, double bi, /* real and imaginary parts of b */ + double *cr, double *ci /* real and imaginary parts of c */ +) ; + +/* determine which timer to use, if any */ +#ifndef NTIMER +#ifdef _POSIX_C_SOURCE +#if _POSIX_C_SOURCE >= 199309L +#define SUITESPARSE_TIMER_ENABLED +#endif +#endif +#endif + +/* SuiteSparse printf macro */ +#define SUITESPARSE_PRINTF(params) \ +{ \ + if (SuiteSparse_config.printf_func != NULL) \ + { \ + (void) (SuiteSparse_config.printf_func) params ; \ + } \ +} + +/* ========================================================================== */ +/* === SuiteSparse version ================================================== */ +/* ========================================================================== */ + +/* SuiteSparse is not a package itself, but a collection of packages, some of + * which must be used together (UMFPACK requires AMD, CHOLMOD requires AMD, + * COLAMD, CAMD, and CCOLAMD, etc). A version number is provided here for the + * collection itself, which is also the version number of SuiteSparse_config. + */ + +int SuiteSparse_version /* returns SUITESPARSE_VERSION */ +( + /* output, not defined on input. Not used if NULL. Returns + the three version codes in version [0..2]: + version [0] is SUITESPARSE_MAIN_VERSION + version [1] is SUITESPARSE_SUB_VERSION + version [2] is SUITESPARSE_SUBSUB_VERSION + */ + int version [3] +) ; + +/* Versions prior to 4.2.0 do not have the above function. The following + code fragment will work with any version of SuiteSparse: + + #ifdef SUITESPARSE_HAS_VERSION_FUNCTION + v = SuiteSparse_version (NULL) ; + #else + v = SUITESPARSE_VERSION ; + #endif +*/ +#define SUITESPARSE_HAS_VERSION_FUNCTION + +#endif /* end comment */ + +#define SUITESPARSE_DATE "Mar 3, 2021" +#define SUITESPARSE_VER_CODE(main,sub) ((main) * 1000 + (sub)) +#define SUITESPARSE_MAIN_VERSION 5 +#define SUITESPARSE_SUB_VERSION 9 +#define SUITESPARSE_SUBSUB_VERSION 0 +#define SUITESPARSE_VERSION \ + SUITESPARSE_VER_CODE(SUITESPARSE_MAIN_VERSION,SUITESPARSE_SUB_VERSION) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/rigraph/vendor/cs/cs.h b/src/rigraph/vendor/cs/cs.h new file mode 100644 index 0000000..0e58521 --- /dev/null +++ b/src/rigraph/vendor/cs/cs.h @@ -0,0 +1,758 @@ +/* ========================================================================== */ +/* CXSparse/Include/cs.h file */ +/* ========================================================================== */ + +/* This is the CXSparse/Include/cs.h file. It has the same name (cs.h) as + the CSparse/Include/cs.h file. The 'make install' for SuiteSparse installs + CXSparse, and this file, instead of CSparse. The two packages have the same + cs.h include filename, because CXSparse is a superset of CSparse. Any user + program that uses CSparse can rely on CXSparse instead, with no change to the + user code. The #include "cs.h" line will work for both versions, in user + code, and the function names and user-visible typedefs from CSparse all + appear in CXSparse. For experimenting and changing the package itself, I + recommend using CSparse since it's simpler and easier to modify. For + using the package in production codes, I recommend CXSparse since it has + more features (support for complex matrices, and both int and long + versions). + */ + +/* ========================================================================== */ + +#ifndef _CXS_H +#define _CXS_H +#include +#include +#include +#include +#ifdef MATLAB_MEX_FILE +#include "mex.h" +#endif + + +#ifdef __cplusplus +#ifndef NCOMPLEX +#include +typedef std::complex cs_complex_t ; +#endif +extern "C" { +#else +#ifndef NCOMPLEX +#include +#define cs_complex_t double _Complex +#endif +#endif + +#define CS_VER 3 /* CXSparse Version */ +#define CS_SUBVER 2 +#define CS_SUBSUB 0 +#define CS_DATE "Sept 12, 2017" /* CSparse release date */ +#define CS_COPYRIGHT "Copyright (c) Timothy A. Davis, 2006-2016" +#define CXSPARSE + +#include "SuiteSparse_config.h" +#define cs_long_t SuiteSparse_long +#define cs_long_t_id SuiteSparse_long_id +#define cs_long_t_max SuiteSparse_long_max + +/* -------------------------------------------------------------------------- */ +/* double/int version of CXSparse */ +/* -------------------------------------------------------------------------- */ + +/* --- primary CSparse routines and data structures ------------------------- */ + +typedef struct cs_di_sparse /* matrix in compressed-column or triplet form */ +{ + int nzmax ; /* maximum number of entries */ + int m ; /* number of rows */ + int n ; /* number of columns */ + int *p ; /* column pointers (size n+1) or col indices (size nzmax) */ + int *i ; /* row indices, size nzmax */ + double *x ; /* numerical values, size nzmax */ + int nz ; /* # of entries in triplet matrix, -1 for compressed-col */ +} cs_di ; + +cs_di *cs_di_add (const cs_di *A, const cs_di *B, double alpha, double beta) ; +int cs_di_cholsol (int order, const cs_di *A, double *b) ; +int cs_di_dupl (cs_di *A) ; +int cs_di_entry (cs_di *T, int i, int j, double x) ; +int cs_di_lusol (int order, const cs_di *A, double *b, double tol) ; +int cs_di_gaxpy (const cs_di *A, const double *x, double *y) ; +cs_di *cs_di_multiply (const cs_di *A, const cs_di *B) ; +int cs_di_qrsol (int order, const cs_di *A, double *b) ; +cs_di *cs_di_transpose (const cs_di *A, int values) ; +cs_di *cs_di_compress (const cs_di *T) ; +double cs_di_norm (const cs_di *A) ; +/*int cs_di_print (const cs_di *A, int brief) ;*/ +cs_di *cs_di_load (FILE *f) ; + +/* utilities */ +void *cs_di_calloc (int n, size_t size) ; +void *cs_di_free (void *p) ; +void *cs_di_realloc (void *p, int n, size_t size, int *ok) ; +cs_di *cs_di_spalloc (int m, int n, int nzmax, int values, int t) ; +cs_di *cs_di_spfree (cs_di *A) ; +int cs_di_sprealloc (cs_di *A, int nzmax) ; +void *cs_di_malloc (int n, size_t size) ; + +/* --- secondary CSparse routines and data structures ----------------------- */ + +typedef struct cs_di_symbolic /* symbolic Cholesky, LU, or QR analysis */ +{ + int *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ + int *q ; /* fill-reducing column permutation for LU and QR */ + int *parent ; /* elimination tree for Cholesky and QR */ + int *cp ; /* column pointers for Cholesky, row counts for QR */ + int *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ + int m2 ; /* # of rows for QR, after adding fictitious rows */ + double lnz ; /* # entries in L for LU or Cholesky; in V for QR */ + double unz ; /* # entries in U for LU; in R for QR */ +} cs_dis ; + +typedef struct cs_di_numeric /* numeric Cholesky, LU, or QR factorization */ +{ + cs_di *L ; /* L for LU and Cholesky, V for QR */ + cs_di *U ; /* U for LU, r for QR, not used for Cholesky */ + int *pinv ; /* partial pivoting for LU */ + double *B ; /* beta [0..n-1] for QR */ +} cs_din ; + +typedef struct cs_di_dmperm_results /* cs_di_dmperm or cs_di_scc output */ +{ + int *p ; /* size m, row permutation */ + int *q ; /* size n, column permutation */ + int *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ + int *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ + int nb ; /* # of blocks in fine dmperm decomposition */ + int rr [5] ; /* coarse row decomposition */ + int cc [5] ; /* coarse column decomposition */ +} cs_did ; + +int *cs_di_amd (int order, const cs_di *A) ; +cs_din *cs_di_chol (const cs_di *A, const cs_dis *S) ; +cs_did *cs_di_dmperm (const cs_di *A, int seed) ; +int cs_di_droptol (cs_di *A, double tol) ; +int cs_di_dropzeros (cs_di *A) ; +int cs_di_happly (const cs_di *V, int i, double beta, double *x) ; +int cs_di_ipvec (const int *p, const double *b, double *x, int n) ; +int cs_di_lsolve (const cs_di *L, double *x) ; +int cs_di_ltsolve (const cs_di *L, double *x) ; +cs_din *cs_di_lu (const cs_di *A, const cs_dis *S, double tol) ; +cs_di *cs_di_permute (const cs_di *A, const int *pinv, const int *q, + int values) ; +int *cs_di_pinv (const int *p, int n) ; +int cs_di_pvec (const int *p, const double *b, double *x, int n) ; +cs_din *cs_di_qr (const cs_di *A, const cs_dis *S) ; +cs_dis *cs_di_schol (int order, const cs_di *A) ; +cs_dis *cs_di_sqr (int order, const cs_di *A, int qr) ; +cs_di *cs_di_symperm (const cs_di *A, const int *pinv, int values) ; +int cs_di_usolve (const cs_di *U, double *x) ; +int cs_di_utsolve (const cs_di *U, double *x) ; +int cs_di_updown (cs_di *L, int sigma, const cs_di *C, const int *parent) ; + +/* utilities */ +cs_dis *cs_di_sfree (cs_dis *S) ; +cs_din *cs_di_nfree (cs_din *N) ; +cs_did *cs_di_dfree (cs_did *D) ; + +/* --- tertiary CSparse routines -------------------------------------------- */ + +int *cs_di_counts (const cs_di *A, const int *parent, const int *post, + int ata) ; +double cs_di_cumsum (int *p, int *c, int n) ; +int cs_di_dfs (int j, cs_di *G, int top, int *xi, int *pstack, + const int *pinv) ; +int *cs_di_etree (const cs_di *A, int ata) ; +int cs_di_fkeep (cs_di *A, int (*fkeep) (int, int, double, void *), + void *other) ; +double cs_di_house (double *x, double *beta, int n) ; +int *cs_di_maxtrans (const cs_di *A, int seed) ; +int *cs_di_post (const int *parent, int n) ; +cs_did *cs_di_scc (cs_di *A) ; +int cs_di_scatter (const cs_di *A, int j, double beta, int *w, double *x, + int mark, cs_di *C, int nz) ; +int cs_di_tdfs (int j, int k, int *head, const int *next, int *post, + int *stack) ; +int cs_di_leaf (int i, int j, const int *first, int *maxfirst, int *prevleaf, + int *ancestor, int *jleaf) ; +int cs_di_reach (cs_di *G, const cs_di *B, int k, int *xi, const int *pinv) ; +int cs_di_spsolve (cs_di *L, const cs_di *B, int k, int *xi, double *x, + const int *pinv, int lo) ; +int cs_di_ereach (const cs_di *A, int k, const int *parent, int *s, int *w) ; +int *cs_di_randperm (int n, int seed) ; + +/* utilities */ +cs_did *cs_di_dalloc (int m, int n) ; +cs_di *cs_di_done (cs_di *C, void *w, void *x, int ok) ; +int *cs_di_idone (int *p, cs_di *C, void *w, int ok) ; +cs_din *cs_di_ndone (cs_din *N, cs_di *C, void *w, void *x, int ok) ; +cs_did *cs_di_ddone (cs_did *D, cs_di *C, void *w, int ok) ; + + +/* -------------------------------------------------------------------------- */ +/* double/cs_long_t version of CXSparse */ +/* -------------------------------------------------------------------------- */ + +/* --- primary CSparse routines and data structures ------------------------- */ + +typedef struct cs_dl_sparse /* matrix in compressed-column or triplet form */ +{ + cs_long_t nzmax ; /* maximum number of entries */ + cs_long_t m ; /* number of rows */ + cs_long_t n ; /* number of columns */ + cs_long_t *p ; /* column pointers (size n+1) or col indlces (size nzmax) */ + cs_long_t *i ; /* row indices, size nzmax */ + double *x ; /* numerical values, size nzmax */ + cs_long_t nz ; /* # of entries in triplet matrix, -1 for compressed-col */ +} cs_dl ; + +cs_dl *cs_dl_add (const cs_dl *A, const cs_dl *B, double alpha, double beta) ; +cs_long_t cs_dl_cholsol (cs_long_t order, const cs_dl *A, double *b) ; +cs_long_t cs_dl_dupl (cs_dl *A) ; +cs_long_t cs_dl_entry (cs_dl *T, cs_long_t i, cs_long_t j, double x) ; +cs_long_t cs_dl_lusol (cs_long_t order, const cs_dl *A, double *b, double tol) ; +cs_long_t cs_dl_gaxpy (const cs_dl *A, const double *x, double *y) ; +cs_dl *cs_dl_multiply (const cs_dl *A, const cs_dl *B) ; +cs_long_t cs_dl_qrsol (cs_long_t order, const cs_dl *A, double *b) ; +cs_dl *cs_dl_transpose (const cs_dl *A, cs_long_t values) ; +cs_dl *cs_dl_compress (const cs_dl *T) ; +double cs_dl_norm (const cs_dl *A) ; +/*cs_long_t cs_dl_print (const cs_dl *A, cs_long_t brief) ;*/ +cs_dl *cs_dl_load (FILE *f) ; + +/* utilities */ +void *cs_dl_calloc (cs_long_t n, size_t size) ; +void *cs_dl_free (void *p) ; +void *cs_dl_realloc (void *p, cs_long_t n, size_t size, cs_long_t *ok) ; +cs_dl *cs_dl_spalloc (cs_long_t m, cs_long_t n, cs_long_t nzmax, cs_long_t values, + cs_long_t t) ; +cs_dl *cs_dl_spfree (cs_dl *A) ; +cs_long_t cs_dl_sprealloc (cs_dl *A, cs_long_t nzmax) ; +void *cs_dl_malloc (cs_long_t n, size_t size) ; + +/* --- secondary CSparse routines and data structures ----------------------- */ + +typedef struct cs_dl_symbolic /* symbolic Cholesky, LU, or QR analysis */ +{ + cs_long_t *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ + cs_long_t *q ; /* fill-reducing column permutation for LU and QR */ + cs_long_t *parent ; /* elimination tree for Cholesky and QR */ + cs_long_t *cp ; /* column pointers for Cholesky, row counts for QR */ + cs_long_t *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ + cs_long_t m2 ; /* # of rows for QR, after adding fictitious rows */ + double lnz ; /* # entries in L for LU or Cholesky; in V for QR */ + double unz ; /* # entries in U for LU; in R for QR */ +} cs_dls ; + +typedef struct cs_dl_numeric /* numeric Cholesky, LU, or QR factorization */ +{ + cs_dl *L ; /* L for LU and Cholesky, V for QR */ + cs_dl *U ; /* U for LU, r for QR, not used for Cholesky */ + cs_long_t *pinv ; /* partial pivoting for LU */ + double *B ; /* beta [0..n-1] for QR */ +} cs_dln ; + +typedef struct cs_dl_dmperm_results /* cs_dl_dmperm or cs_dl_scc output */ +{ + cs_long_t *p ; /* size m, row permutation */ + cs_long_t *q ; /* size n, column permutation */ + cs_long_t *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ + cs_long_t *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ + cs_long_t nb ; /* # of blocks in fine dmperm decomposition */ + cs_long_t rr [5] ; /* coarse row decomposition */ + cs_long_t cc [5] ; /* coarse column decomposition */ +} cs_dld ; + +cs_long_t *cs_dl_amd (cs_long_t order, const cs_dl *A) ; +cs_dln *cs_dl_chol (const cs_dl *A, const cs_dls *S) ; +cs_dld *cs_dl_dmperm (const cs_dl *A, cs_long_t seed) ; +cs_long_t cs_dl_droptol (cs_dl *A, double tol) ; +cs_long_t cs_dl_dropzeros (cs_dl *A) ; +cs_long_t cs_dl_happly (const cs_dl *V, cs_long_t i, double beta, double *x) ; +cs_long_t cs_dl_ipvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; +cs_long_t cs_dl_lsolve (const cs_dl *L, double *x) ; +cs_long_t cs_dl_ltsolve (const cs_dl *L, double *x) ; +cs_dln *cs_dl_lu (const cs_dl *A, const cs_dls *S, double tol) ; +cs_dl *cs_dl_permute (const cs_dl *A, const cs_long_t *pinv, const cs_long_t *q, + cs_long_t values) ; +cs_long_t *cs_dl_pinv (const cs_long_t *p, cs_long_t n) ; +cs_long_t cs_dl_pvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; +cs_dln *cs_dl_qr (const cs_dl *A, const cs_dls *S) ; +cs_dls *cs_dl_schol (cs_long_t order, const cs_dl *A) ; +cs_dls *cs_dl_sqr (cs_long_t order, const cs_dl *A, cs_long_t qr) ; +cs_dl *cs_dl_symperm (const cs_dl *A, const cs_long_t *pinv, cs_long_t values) ; +cs_long_t cs_dl_usolve (const cs_dl *U, double *x) ; +cs_long_t cs_dl_utsolve (const cs_dl *U, double *x) ; +cs_long_t cs_dl_updown (cs_dl *L, cs_long_t sigma, const cs_dl *C, + const cs_long_t *parent) ; + +/* utilities */ +cs_dls *cs_dl_sfree (cs_dls *S) ; +cs_dln *cs_dl_nfree (cs_dln *N) ; +cs_dld *cs_dl_dfree (cs_dld *D) ; + +/* --- tertiary CSparse routines -------------------------------------------- */ + +cs_long_t *cs_dl_counts (const cs_dl *A, const cs_long_t *parent, + const cs_long_t *post, cs_long_t ata) ; +double cs_dl_cumsum (cs_long_t *p, cs_long_t *c, cs_long_t n) ; +cs_long_t cs_dl_dfs (cs_long_t j, cs_dl *G, cs_long_t top, cs_long_t *xi, + cs_long_t *pstack, const cs_long_t *pinv) ; +cs_long_t *cs_dl_etree (const cs_dl *A, cs_long_t ata) ; +cs_long_t cs_dl_fkeep (cs_dl *A, + cs_long_t (*fkeep) (cs_long_t, cs_long_t, double, void *), void *other) ; +double cs_dl_house (double *x, double *beta, cs_long_t n) ; +cs_long_t *cs_dl_maxtrans (const cs_dl *A, cs_long_t seed) ; +cs_long_t *cs_dl_post (const cs_long_t *parent, cs_long_t n) ; +cs_dld *cs_dl_scc (cs_dl *A) ; +cs_long_t cs_dl_scatter (const cs_dl *A, cs_long_t j, double beta, cs_long_t *w, + double *x, cs_long_t mark,cs_dl *C, cs_long_t nz) ; +cs_long_t cs_dl_tdfs (cs_long_t j, cs_long_t k, cs_long_t *head, const cs_long_t *next, + cs_long_t *post, cs_long_t *stack) ; +cs_long_t cs_dl_leaf (cs_long_t i, cs_long_t j, const cs_long_t *first, + cs_long_t *maxfirst, cs_long_t *prevleaf, cs_long_t *ancestor, cs_long_t *jleaf) ; +cs_long_t cs_dl_reach (cs_dl *G, const cs_dl *B, cs_long_t k, cs_long_t *xi, + const cs_long_t *pinv) ; +cs_long_t cs_dl_spsolve (cs_dl *L, const cs_dl *B, cs_long_t k, cs_long_t *xi, + double *x, const cs_long_t *pinv, cs_long_t lo) ; +cs_long_t cs_dl_ereach (const cs_dl *A, cs_long_t k, const cs_long_t *parent, + cs_long_t *s, cs_long_t *w) ; +cs_long_t *cs_dl_randperm (cs_long_t n, cs_long_t seed) ; + +/* utilities */ +cs_dld *cs_dl_dalloc (cs_long_t m, cs_long_t n) ; +cs_dl *cs_dl_done (cs_dl *C, void *w, void *x, cs_long_t ok) ; +cs_long_t *cs_dl_idone (cs_long_t *p, cs_dl *C, void *w, cs_long_t ok) ; +cs_dln *cs_dl_ndone (cs_dln *N, cs_dl *C, void *w, void *x, cs_long_t ok) ; +cs_dld *cs_dl_ddone (cs_dld *D, cs_dl *C, void *w, cs_long_t ok) ; + + +/* -------------------------------------------------------------------------- */ +/* complex/int version of CXSparse */ +/* -------------------------------------------------------------------------- */ + +#ifndef NCOMPLEX + +/* --- primary CSparse routines and data structures ------------------------- */ + +typedef struct cs_ci_sparse /* matrix in compressed-column or triplet form */ +{ + int nzmax ; /* maximum number of entries */ + int m ; /* number of rows */ + int n ; /* number of columns */ + int *p ; /* column pointers (size n+1) or col indices (size nzmax) */ + int *i ; /* row indices, size nzmax */ + cs_complex_t *x ; /* numerical values, size nzmax */ + int nz ; /* # of entries in triplet matrix, -1 for compressed-col */ +} cs_ci ; + +cs_ci *cs_ci_add (const cs_ci *A, const cs_ci *B, cs_complex_t alpha, + cs_complex_t beta) ; +int cs_ci_cholsol (int order, const cs_ci *A, cs_complex_t *b) ; +int cs_ci_dupl (cs_ci *A) ; +int cs_ci_entry (cs_ci *T, int i, int j, cs_complex_t x) ; +int cs_ci_lusol (int order, const cs_ci *A, cs_complex_t *b, double tol) ; +int cs_ci_gaxpy (const cs_ci *A, const cs_complex_t *x, cs_complex_t *y) ; +cs_ci *cs_ci_multiply (const cs_ci *A, const cs_ci *B) ; +int cs_ci_qrsol (int order, const cs_ci *A, cs_complex_t *b) ; +cs_ci *cs_ci_transpose (const cs_ci *A, int values) ; +cs_ci *cs_ci_compress (const cs_ci *T) ; +double cs_ci_norm (const cs_ci *A) ; +/*int cs_ci_print (const cs_ci *A, int brief) ;*/ +cs_ci *cs_ci_load (FILE *f) ; + +/* utilities */ +void *cs_ci_calloc (int n, size_t size) ; +void *cs_ci_free (void *p) ; +void *cs_ci_realloc (void *p, int n, size_t size, int *ok) ; +cs_ci *cs_ci_spalloc (int m, int n, int nzmax, int values, int t) ; +cs_ci *cs_ci_spfree (cs_ci *A) ; +int cs_ci_sprealloc (cs_ci *A, int nzmax) ; +void *cs_ci_malloc (int n, size_t size) ; + +/* --- secondary CSparse routines and data structures ----------------------- */ + +typedef struct cs_ci_symbolic /* symbolic Cholesky, LU, or QR analysis */ +{ + int *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ + int *q ; /* fill-reducing column permutation for LU and QR */ + int *parent ; /* elimination tree for Cholesky and QR */ + int *cp ; /* column pointers for Cholesky, row counts for QR */ + int *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ + int m2 ; /* # of rows for QR, after adding fictitious rows */ + double lnz ; /* # entries in L for LU or Cholesky; in V for QR */ + double unz ; /* # entries in U for LU; in R for QR */ +} cs_cis ; + +typedef struct cs_ci_numeric /* numeric Cholesky, LU, or QR factorization */ +{ + cs_ci *L ; /* L for LU and Cholesky, V for QR */ + cs_ci *U ; /* U for LU, r for QR, not used for Cholesky */ + int *pinv ; /* partial pivoting for LU */ + double *B ; /* beta [0..n-1] for QR */ +} cs_cin ; + +typedef struct cs_ci_dmperm_results /* cs_ci_dmperm or cs_ci_scc output */ +{ + int *p ; /* size m, row permutation */ + int *q ; /* size n, column permutation */ + int *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ + int *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ + int nb ; /* # of blocks in fine dmperm decomposition */ + int rr [5] ; /* coarse row decomposition */ + int cc [5] ; /* coarse column decomposition */ +} cs_cid ; + +int *cs_ci_amd (int order, const cs_ci *A) ; +cs_cin *cs_ci_chol (const cs_ci *A, const cs_cis *S) ; +cs_cid *cs_ci_dmperm (const cs_ci *A, int seed) ; +int cs_ci_droptol (cs_ci *A, double tol) ; +int cs_ci_dropzeros (cs_ci *A) ; +int cs_ci_happly (const cs_ci *V, int i, double beta, cs_complex_t *x) ; +int cs_ci_ipvec (const int *p, const cs_complex_t *b, cs_complex_t *x, int n) ; +int cs_ci_lsolve (const cs_ci *L, cs_complex_t *x) ; +int cs_ci_ltsolve (const cs_ci *L, cs_complex_t *x) ; +cs_cin *cs_ci_lu (const cs_ci *A, const cs_cis *S, double tol) ; +cs_ci *cs_ci_permute (const cs_ci *A, const int *pinv, const int *q, + int values) ; +int *cs_ci_pinv (const int *p, int n) ; +int cs_ci_pvec (const int *p, const cs_complex_t *b, cs_complex_t *x, int n) ; +cs_cin *cs_ci_qr (const cs_ci *A, const cs_cis *S) ; +cs_cis *cs_ci_schol (int order, const cs_ci *A) ; +cs_cis *cs_ci_sqr (int order, const cs_ci *A, int qr) ; +cs_ci *cs_ci_symperm (const cs_ci *A, const int *pinv, int values) ; +int cs_ci_usolve (const cs_ci *U, cs_complex_t *x) ; +int cs_ci_utsolve (const cs_ci *U, cs_complex_t *x) ; +int cs_ci_updown (cs_ci *L, int sigma, const cs_ci *C, const int *parent) ; + +/* utilities */ +cs_cis *cs_ci_sfree (cs_cis *S) ; +cs_cin *cs_ci_nfree (cs_cin *N) ; +cs_cid *cs_ci_dfree (cs_cid *D) ; + +/* --- tertiary CSparse routines -------------------------------------------- */ + +int *cs_ci_counts (const cs_ci *A, const int *parent, const int *post, + int ata) ; +double cs_ci_cumsum (int *p, int *c, int n) ; +int cs_ci_dfs (int j, cs_ci *G, int top, int *xi, int *pstack, + const int *pinv) ; +int *cs_ci_etree (const cs_ci *A, int ata) ; +int cs_ci_fkeep (cs_ci *A, int (*fkeep) (int, int, cs_complex_t, void *), + void *other) ; +cs_complex_t cs_ci_house (cs_complex_t *x, double *beta, int n) ; +int *cs_ci_maxtrans (const cs_ci *A, int seed) ; +int *cs_ci_post (const int *parent, int n) ; +cs_cid *cs_ci_scc (cs_ci *A) ; +int cs_ci_scatter (const cs_ci *A, int j, cs_complex_t beta, int *w, + cs_complex_t *x, int mark,cs_ci *C, int nz) ; +int cs_ci_tdfs (int j, int k, int *head, const int *next, int *post, + int *stack) ; +int cs_ci_leaf (int i, int j, const int *first, int *maxfirst, int *prevleaf, + int *ancestor, int *jleaf) ; +int cs_ci_reach (cs_ci *G, const cs_ci *B, int k, int *xi, const int *pinv) ; +int cs_ci_spsolve (cs_ci *L, const cs_ci *B, int k, int *xi, + cs_complex_t *x, const int *pinv, int lo) ; +int cs_ci_ereach (const cs_ci *A, int k, const int *parent, int *s, int *w) ; +int *cs_ci_randperm (int n, int seed) ; + +/* utilities */ +cs_cid *cs_ci_dalloc (int m, int n) ; +cs_ci *cs_ci_done (cs_ci *C, void *w, void *x, int ok) ; +int *cs_ci_idone (int *p, cs_ci *C, void *w, int ok) ; +cs_cin *cs_ci_ndone (cs_cin *N, cs_ci *C, void *w, void *x, int ok) ; +cs_cid *cs_ci_ddone (cs_cid *D, cs_ci *C, void *w, int ok) ; + + +/* -------------------------------------------------------------------------- */ +/* complex/cs_long_t version of CXSparse */ +/* -------------------------------------------------------------------------- */ + +/* --- primary CSparse routines and data structures ------------------------- */ + +typedef struct cs_cl_sparse /* matrix in compressed-column or triplet form */ +{ + cs_long_t nzmax ; /* maximum number of entries */ + cs_long_t m ; /* number of rows */ + cs_long_t n ; /* number of columns */ + cs_long_t *p ; /* column pointers (size n+1) or col indlces (size nzmax) */ + cs_long_t *i ; /* row indices, size nzmax */ + cs_complex_t *x ; /* numerical values, size nzmax */ + cs_long_t nz ; /* # of entries in triplet matrix, -1 for compressed-col */ +} cs_cl ; + +cs_cl *cs_cl_add (const cs_cl *A, const cs_cl *B, cs_complex_t alpha, + cs_complex_t beta) ; +cs_long_t cs_cl_cholsol (cs_long_t order, const cs_cl *A, cs_complex_t *b) ; +cs_long_t cs_cl_dupl (cs_cl *A) ; +cs_long_t cs_cl_entry (cs_cl *T, cs_long_t i, cs_long_t j, cs_complex_t x) ; +cs_long_t cs_cl_lusol (cs_long_t order, const cs_cl *A, cs_complex_t *b, + double tol) ; +cs_long_t cs_cl_gaxpy (const cs_cl *A, const cs_complex_t *x, cs_complex_t *y) ; +cs_cl *cs_cl_multiply (const cs_cl *A, const cs_cl *B) ; +cs_long_t cs_cl_qrsol (cs_long_t order, const cs_cl *A, cs_complex_t *b) ; +cs_cl *cs_cl_transpose (const cs_cl *A, cs_long_t values) ; +cs_cl *cs_cl_compress (const cs_cl *T) ; +double cs_cl_norm (const cs_cl *A) ; +/*cs_long_t cs_cl_print (const cs_cl *A, cs_long_t brief) ;*/ +cs_cl *cs_cl_load (FILE *f) ; + +/* utilities */ +void *cs_cl_calloc (cs_long_t n, size_t size) ; +void *cs_cl_free (void *p) ; +void *cs_cl_realloc (void *p, cs_long_t n, size_t size, cs_long_t *ok) ; +cs_cl *cs_cl_spalloc (cs_long_t m, cs_long_t n, cs_long_t nzmax, cs_long_t values, + cs_long_t t) ; +cs_cl *cs_cl_spfree (cs_cl *A) ; +cs_long_t cs_cl_sprealloc (cs_cl *A, cs_long_t nzmax) ; +void *cs_cl_malloc (cs_long_t n, size_t size) ; + +/* --- secondary CSparse routines and data structures ----------------------- */ + +typedef struct cs_cl_symbolic /* symbolic Cholesky, LU, or QR analysis */ +{ + cs_long_t *pinv ; /* inverse row perm. for QR, fill red. perm for Chol */ + cs_long_t *q ; /* fill-reducing column permutation for LU and QR */ + cs_long_t *parent ; /* elimination tree for Cholesky and QR */ + cs_long_t *cp ; /* column pointers for Cholesky, row counts for QR */ + cs_long_t *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */ + cs_long_t m2 ; /* # of rows for QR, after adding fictitious rows */ + double lnz ; /* # entries in L for LU or Cholesky; in V for QR */ + double unz ; /* # entries in U for LU; in R for QR */ +} cs_cls ; + +typedef struct cs_cl_numeric /* numeric Cholesky, LU, or QR factorization */ +{ + cs_cl *L ; /* L for LU and Cholesky, V for QR */ + cs_cl *U ; /* U for LU, r for QR, not used for Cholesky */ + cs_long_t *pinv ; /* partial pivoting for LU */ + double *B ; /* beta [0..n-1] for QR */ +} cs_cln ; + +typedef struct cs_cl_dmperm_results /* cs_cl_dmperm or cs_cl_scc output */ +{ + cs_long_t *p ; /* size m, row permutation */ + cs_long_t *q ; /* size n, column permutation */ + cs_long_t *r ; /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */ + cs_long_t *s ; /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */ + cs_long_t nb ; /* # of blocks in fine dmperm decomposition */ + cs_long_t rr [5] ; /* coarse row decomposition */ + cs_long_t cc [5] ; /* coarse column decomposition */ +} cs_cld ; + +cs_long_t *cs_cl_amd (cs_long_t order, const cs_cl *A) ; +cs_cln *cs_cl_chol (const cs_cl *A, const cs_cls *S) ; +cs_cld *cs_cl_dmperm (const cs_cl *A, cs_long_t seed) ; +cs_long_t cs_cl_droptol (cs_cl *A, double tol) ; +cs_long_t cs_cl_dropzeros (cs_cl *A) ; +cs_long_t cs_cl_happly (const cs_cl *V, cs_long_t i, double beta, cs_complex_t *x) ; +cs_long_t cs_cl_ipvec (const cs_long_t *p, const cs_complex_t *b, + cs_complex_t *x, cs_long_t n) ; +cs_long_t cs_cl_lsolve (const cs_cl *L, cs_complex_t *x) ; +cs_long_t cs_cl_ltsolve (const cs_cl *L, cs_complex_t *x) ; +cs_cln *cs_cl_lu (const cs_cl *A, const cs_cls *S, double tol) ; +cs_cl *cs_cl_permute (const cs_cl *A, const cs_long_t *pinv, const cs_long_t *q, + cs_long_t values) ; +cs_long_t *cs_cl_pinv (const cs_long_t *p, cs_long_t n) ; +cs_long_t cs_cl_pvec (const cs_long_t *p, const cs_complex_t *b, + cs_complex_t *x, cs_long_t n) ; +cs_cln *cs_cl_qr (const cs_cl *A, const cs_cls *S) ; +cs_cls *cs_cl_schol (cs_long_t order, const cs_cl *A) ; +cs_cls *cs_cl_sqr (cs_long_t order, const cs_cl *A, cs_long_t qr) ; +cs_cl *cs_cl_symperm (const cs_cl *A, const cs_long_t *pinv, cs_long_t values) ; +cs_long_t cs_cl_usolve (const cs_cl *U, cs_complex_t *x) ; +cs_long_t cs_cl_utsolve (const cs_cl *U, cs_complex_t *x) ; +cs_long_t cs_cl_updown (cs_cl *L, cs_long_t sigma, const cs_cl *C, + const cs_long_t *parent) ; + +/* utilities */ +cs_cls *cs_cl_sfree (cs_cls *S) ; +cs_cln *cs_cl_nfree (cs_cln *N) ; +cs_cld *cs_cl_dfree (cs_cld *D) ; + +/* --- tertiary CSparse routines -------------------------------------------- */ + +cs_long_t *cs_cl_counts (const cs_cl *A, const cs_long_t *parent, + const cs_long_t *post, cs_long_t ata) ; +double cs_cl_cumsum (cs_long_t *p, cs_long_t *c, cs_long_t n) ; +cs_long_t cs_cl_dfs (cs_long_t j, cs_cl *G, cs_long_t top, cs_long_t *xi, + cs_long_t *pstack, const cs_long_t *pinv) ; +cs_long_t *cs_cl_etree (const cs_cl *A, cs_long_t ata) ; +cs_long_t cs_cl_fkeep (cs_cl *A, + cs_long_t (*fkeep) (cs_long_t, cs_long_t, cs_complex_t, void *), void *other) ; +cs_complex_t cs_cl_house (cs_complex_t *x, double *beta, cs_long_t n) ; +cs_long_t *cs_cl_maxtrans (const cs_cl *A, cs_long_t seed) ; +cs_long_t *cs_cl_post (const cs_long_t *parent, cs_long_t n) ; +cs_cld *cs_cl_scc (cs_cl *A) ; +cs_long_t cs_cl_scatter (const cs_cl *A, cs_long_t j, cs_complex_t beta, + cs_long_t *w, cs_complex_t *x, cs_long_t mark,cs_cl *C, cs_long_t nz) ; +cs_long_t cs_cl_tdfs (cs_long_t j, cs_long_t k, cs_long_t *head, const cs_long_t *next, + cs_long_t *post, cs_long_t *stack) ; +cs_long_t cs_cl_leaf (cs_long_t i, cs_long_t j, const cs_long_t *first, + cs_long_t *maxfirst, cs_long_t *prevleaf, cs_long_t *ancestor, cs_long_t *jleaf) ; +cs_long_t cs_cl_reach (cs_cl *G, const cs_cl *B, cs_long_t k, cs_long_t *xi, + const cs_long_t *pinv) ; +cs_long_t cs_cl_spsolve (cs_cl *L, const cs_cl *B, cs_long_t k, cs_long_t *xi, + cs_complex_t *x, const cs_long_t *pinv, cs_long_t lo) ; +cs_long_t cs_cl_ereach (const cs_cl *A, cs_long_t k, const cs_long_t *parent, + cs_long_t *s, cs_long_t *w) ; +cs_long_t *cs_cl_randperm (cs_long_t n, cs_long_t seed) ; + +/* utilities */ +cs_cld *cs_cl_dalloc (cs_long_t m, cs_long_t n) ; +cs_cl *cs_cl_done (cs_cl *C, void *w, void *x, cs_long_t ok) ; +cs_long_t *cs_cl_idone (cs_long_t *p, cs_cl *C, void *w, cs_long_t ok) ; +cs_cln *cs_cl_ndone (cs_cln *N, cs_cl *C, void *w, void *x, cs_long_t ok) ; +cs_cld *cs_cl_ddone (cs_cld *D, cs_cl *C, void *w, cs_long_t ok) ; + +#endif + +/* -------------------------------------------------------------------------- */ +/* Macros for constructing each version of CSparse */ +/* -------------------------------------------------------------------------- */ + +#ifdef CS_LONG +#define CS_INT cs_long_t +#define CS_INT_MAX cs_long_t_max +#define CS_ID cs_long_t_id +#ifdef CS_COMPLEX +#define CS_ENTRY cs_complex_t +#define CS_NAME(nm) cs_cl ## nm +#define cs cs_cl +#else +#define CS_ENTRY double +#define CS_NAME(nm) cs_dl ## nm +#define cs cs_dl +#endif +#else +#define CS_INT int +#define CS_INT_MAX INT_MAX +#define CS_ID "%d" +#ifdef CS_COMPLEX +#define CS_ENTRY cs_complex_t +#define CS_NAME(nm) cs_ci ## nm +#define cs cs_ci +#else +#define CS_ENTRY double +#define CS_NAME(nm) cs_di ## nm +#define cs cs_di +#endif +#endif + +#ifdef CS_COMPLEX +#define CS_REAL(x) creal(x) +#define CS_IMAG(x) cimag(x) +#define CS_CONJ(x) conj(x) +#define CS_ABS(x) cabs(x) +#else +#define CS_REAL(x) (x) +#define CS_IMAG(x) (0.) +#define CS_CONJ(x) (x) +#define CS_ABS(x) fabs(x) +#endif + +#define CS_MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define CS_MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define CS_FLIP(i) (-(i)-2) +#define CS_UNFLIP(i) (((i) < 0) ? CS_FLIP(i) : (i)) +#define CS_MARKED(w,j) (w [j] < 0) +#define CS_MARK(w,j) { w [j] = CS_FLIP (w [j]) ; } +#define CS_CSC(A) (A && (A->nz == -1)) +#define CS_TRIPLET(A) (A && (A->nz >= 0)) + +/* --- primary CSparse routines and data structures ------------------------- */ + +#define cs_add CS_NAME (_add) +#define cs_cholsol CS_NAME (_cholsol) +#define cs_dupl CS_NAME (_dupl) +#define cs_entry CS_NAME (_entry) +#define cs_lusol CS_NAME (_lusol) +#define cs_gaxpy CS_NAME (_gaxpy) +#define cs_multiply CS_NAME (_multiply) +#define cs_qrsol CS_NAME (_qrsol) +#define cs_transpose CS_NAME (_transpose) +#define cs_compress CS_NAME (_compress) +#define cs_norm CS_NAME (_norm) +/*#define cs_print CS_NAME (_print)*/ +#define cs_load CS_NAME (_load) + +/* utilities */ +#define cs_calloc CS_NAME (_calloc) +#define cs_free CS_NAME (_free) +#define cs_realloc CS_NAME (_realloc) +#define cs_spalloc CS_NAME (_spalloc) +#define cs_spfree CS_NAME (_spfree) +#define cs_sprealloc CS_NAME (_sprealloc) +#define cs_malloc CS_NAME (_malloc) + +/* --- secondary CSparse routines and data structures ----------------------- */ +#define css CS_NAME (s) +#define csn CS_NAME (n) +#define csd CS_NAME (d) + +#define cs_amd CS_NAME (_amd) +#define cs_chol CS_NAME (_chol) +#define cs_dmperm CS_NAME (_dmperm) +#define cs_droptol CS_NAME (_droptol) +#define cs_dropzeros CS_NAME (_dropzeros) +#define cs_happly CS_NAME (_happly) +#define cs_ipvec CS_NAME (_ipvec) +#define cs_lsolve CS_NAME (_lsolve) +#define cs_ltsolve CS_NAME (_ltsolve) +#define cs_lu CS_NAME (_lu) +#define cs_permute CS_NAME (_permute) +#define cs_pinv CS_NAME (_pinv) +#define cs_pvec CS_NAME (_pvec) +#define cs_qr CS_NAME (_qr) +#define cs_schol CS_NAME (_schol) +#define cs_sqr CS_NAME (_sqr) +#define cs_symperm CS_NAME (_symperm) +#define cs_usolve CS_NAME (_usolve) +#define cs_utsolve CS_NAME (_utsolve) +#define cs_updown CS_NAME (_updown) + +/* utilities */ +#define cs_sfree CS_NAME (_sfree) +#define cs_nfree CS_NAME (_nfree) +#define cs_dfree CS_NAME (_dfree) + +/* --- tertiary CSparse routines -------------------------------------------- */ +#define cs_counts CS_NAME (_counts) +#define cs_cumsum CS_NAME (_cumsum) +#define cs_dfs CS_NAME (_dfs) +#define cs_etree CS_NAME (_etree) +#define cs_fkeep CS_NAME (_fkeep) +#define cs_house CS_NAME (_house) +#define cs_invmatch CS_NAME (_invmatch) +#define cs_maxtrans CS_NAME (_maxtrans) +#define cs_post CS_NAME (_post) +#define cs_scc CS_NAME (_scc) +#define cs_scatter CS_NAME (_scatter) +#define cs_tdfs CS_NAME (_tdfs) +#define cs_reach CS_NAME (_reach) +#define cs_spsolve CS_NAME (_spsolve) +#define cs_ereach CS_NAME (_ereach) +#define cs_randperm CS_NAME (_randperm) +#define cs_leaf CS_NAME (_leaf) + +/* utilities */ +#define cs_dalloc CS_NAME (_dalloc) +#define cs_done CS_NAME (_done) +#define cs_idone CS_NAME (_idone) +#define cs_ndone CS_NAME (_ndone) +#define cs_ddone CS_NAME (_ddone) + +/* -------------------------------------------------------------------------- */ +/* Conversion routines */ +/* -------------------------------------------------------------------------- */ + +#ifndef NCOMPLEX +cs_di *cs_i_real (cs_ci *A, int real) ; +cs_ci *cs_i_complex (cs_di *A, int real) ; +cs_dl *cs_l_real (cs_cl *A, cs_long_t real) ; +cs_cl *cs_l_complex (cs_dl *A, cs_long_t real) ; +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/rigraph/vendor/cs/cs_add.c b/src/rigraph/vendor/cs/cs_add.c new file mode 100644 index 0000000..44b0d3f --- /dev/null +++ b/src/rigraph/vendor/cs/cs_add.c @@ -0,0 +1,28 @@ +#include "cs.h" +/* C = alpha*A + beta*B */ +cs *cs_add (const cs *A, const cs *B, CS_ENTRY alpha, CS_ENTRY beta) +{ + CS_INT p, j, nz = 0, anz, *Cp, *Ci, *Bp, m, n, bnz, *w, values ; + CS_ENTRY *x, *Bx, *Cx ; + cs *C ; + if (!CS_CSC (A) || !CS_CSC (B)) return (NULL) ; /* check inputs */ + if (A->m != B->m || A->n != B->n) return (NULL) ; + m = A->m ; anz = A->p [A->n] ; + n = B->n ; Bp = B->p ; Bx = B->x ; bnz = Bp [n] ; + w = cs_calloc (m, sizeof (CS_INT)) ; /* get workspace */ + values = (A->x != NULL) && (Bx != NULL) ; + x = values ? cs_malloc (m, sizeof (CS_ENTRY)) : NULL ; /* get workspace */ + C = cs_spalloc (m, n, anz + bnz, values, 0) ; /* allocate result*/ + if (!C || !w || (values && !x)) return (cs_done (C, w, x, 0)) ; + Cp = C->p ; Ci = C->i ; Cx = C->x ; + for (j = 0 ; j < n ; j++) + { + Cp [j] = nz ; /* column j of C starts here */ + nz = cs_scatter (A, j, alpha, w, x, j+1, C, nz) ; /* alpha*A(:,j)*/ + nz = cs_scatter (B, j, beta, w, x, j+1, C, nz) ; /* beta*B(:,j) */ + if (values) for (p = Cp [j] ; p < nz ; p++) Cx [p] = x [Ci [p]] ; + } + Cp [n] = nz ; /* finalize the last column of C */ + cs_sprealloc (C, 0) ; /* remove extra space from C */ + return (cs_done (C, w, x, 1)) ; /* success; free workspace, return C */ +} diff --git a/src/rigraph/vendor/cs/cs_amd.c b/src/rigraph/vendor/cs/cs_amd.c new file mode 100644 index 0000000..3f5c702 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_amd.c @@ -0,0 +1,364 @@ +#include "cs.h" +/* clear w */ +static CS_INT cs_wclear (CS_INT mark, CS_INT lemax, CS_INT *w, CS_INT n) +{ + CS_INT k ; + if (mark < 2 || (mark + lemax < 0)) + { + for (k = 0 ; k < n ; k++) if (w [k] != 0) w [k] = 1 ; + mark = 2 ; + } + return (mark) ; /* at this point, w [0..n-1] < mark holds */ +} + +/* keep off-diagonal entries; drop diagonal entries */ +static CS_INT cs_diag (CS_INT i, CS_INT j, CS_ENTRY aij, void *other) { return (i != j) ; } + +/* p = amd(A+A') if symmetric is true, or amd(A'A) otherwise */ +CS_INT *cs_amd (CS_INT order, const cs *A) /* order 0:natural, 1:Chol, 2:LU, 3:QR */ +{ + cs *C, *A2, *AT ; + CS_INT *Cp, *Ci, *last, *W, *len, *nv, *next, *P, *head, *elen, *degree, *w, + *hhead, *ATp, *ATi, d, dk, dext, lemax = 0, e, elenk, eln, i, j, k, k1, + k2, k3, jlast, ln, dense, nzmax, mindeg = 0, nvi, nvj, nvk, mark, wnvi, + ok, cnz, nel = 0, p, p1, p2, p3, p4, pj, pk, pk1, pk2, pn, q, n, m, t ; + CS_INT h ; + /* --- Construct matrix C ----------------------------------------------- */ + if (!CS_CSC (A) || order <= 0 || order > 3) return (NULL) ; /* check */ + AT = cs_transpose (A, 0) ; /* compute A' */ + if (!AT) return (NULL) ; + m = A->m ; n = A->n ; + dense = CS_MAX (16, 10 * sqrt ((double) n)) ; /* find dense threshold */ + dense = CS_MIN (n-2, dense) ; + if (order == 1 && n == m) + { + C = cs_add (A, AT, 0, 0) ; /* C = A+A' */ + } + else if (order == 2) + { + ATp = AT->p ; /* drop dense columns from AT */ + ATi = AT->i ; + for (p2 = 0, j = 0 ; j < m ; j++) + { + p = ATp [j] ; /* column j of AT starts here */ + ATp [j] = p2 ; /* new column j starts here */ + if (ATp [j+1] - p > dense) continue ; /* skip dense col j */ + for ( ; p < ATp [j+1] ; p++) ATi [p2++] = ATi [p] ; + } + ATp [m] = p2 ; /* finalize AT */ + A2 = cs_transpose (AT, 0) ; /* A2 = AT' */ + C = A2 ? cs_multiply (AT, A2) : NULL ; /* C=A'*A with no dense rows */ + cs_spfree (A2) ; + } + else + { + C = cs_multiply (AT, A) ; /* C=A'*A */ + } + cs_spfree (AT) ; + if (!C) return (NULL) ; + cs_fkeep (C, &cs_diag, NULL) ; /* drop diagonal entries */ + Cp = C->p ; + cnz = Cp [n] ; + P = cs_malloc (n+1, sizeof (CS_INT)) ; /* allocate result */ + W = cs_malloc (8*(n+1), sizeof (CS_INT)) ; /* get workspace */ + t = cnz + cnz/5 + 2*n ; /* add elbow room to C */ + if (!P || !W || !cs_sprealloc (C, t)) return (cs_idone (P, C, W, 0)) ; + len = W ; nv = W + (n+1) ; next = W + 2*(n+1) ; + head = W + 3*(n+1) ; elen = W + 4*(n+1) ; degree = W + 5*(n+1) ; + w = W + 6*(n+1) ; hhead = W + 7*(n+1) ; + last = P ; /* use P as workspace for last */ + /* --- Initialize quotient graph ---------------------------------------- */ + for (k = 0 ; k < n ; k++) len [k] = Cp [k+1] - Cp [k] ; + len [n] = 0 ; + nzmax = C->nzmax ; + Ci = C->i ; + for (i = 0 ; i <= n ; i++) + { + head [i] = -1 ; /* degree list i is empty */ + last [i] = -1 ; + next [i] = -1 ; + hhead [i] = -1 ; /* hash list i is empty */ + nv [i] = 1 ; /* node i is just one node */ + w [i] = 1 ; /* node i is alive */ + elen [i] = 0 ; /* Ek of node i is empty */ + degree [i] = len [i] ; /* degree of node i */ + } + mark = cs_wclear (0, 0, w, n) ; /* clear w */ + elen [n] = -2 ; /* n is a dead element */ + Cp [n] = -1 ; /* n is a root of assembly tree */ + w [n] = 0 ; /* n is a dead element */ + /* --- Initialize degree lists ------------------------------------------ */ + for (i = 0 ; i < n ; i++) + { + d = degree [i] ; + if (d == 0) /* node i is empty */ + { + elen [i] = -2 ; /* element i is dead */ + nel++ ; + Cp [i] = -1 ; /* i is a root of assembly tree */ + w [i] = 0 ; + } + else if (d > dense) /* node i is dense */ + { + nv [i] = 0 ; /* absorb i into element n */ + elen [i] = -1 ; /* node i is dead */ + nel++ ; + Cp [i] = CS_FLIP (n) ; + nv [n]++ ; + } + else + { + if (head [d] != -1) last [head [d]] = i ; + next [i] = head [d] ; /* put node i in degree list d */ + head [d] = i ; + } + } + while (nel < n) /* while (selecting pivots) do */ + { + /* --- Select node of minimum approximate degree -------------------- */ + for (k = -1 ; mindeg < n && (k = head [mindeg]) == -1 ; mindeg++) ; + if (next [k] != -1) last [next [k]] = -1 ; + head [mindeg] = next [k] ; /* remove k from degree list */ + elenk = elen [k] ; /* elenk = |Ek| */ + nvk = nv [k] ; /* # of nodes k represents */ + nel += nvk ; /* nv[k] nodes of A eliminated */ + /* --- Garbage collection ------------------------------------------- */ + if (elenk > 0 && cnz + mindeg >= nzmax) + { + for (j = 0 ; j < n ; j++) + { + if ((p = Cp [j]) >= 0) /* j is a live node or element */ + { + Cp [j] = Ci [p] ; /* save first entry of object */ + Ci [p] = CS_FLIP (j) ; /* first entry is now CS_FLIP(j) */ + } + } + for (q = 0, p = 0 ; p < cnz ; ) /* scan all of memory */ + { + if ((j = CS_FLIP (Ci [p++])) >= 0) /* found object j */ + { + Ci [q] = Cp [j] ; /* restore first entry of object */ + Cp [j] = q++ ; /* new pointer to object j */ + for (k3 = 0 ; k3 < len [j]-1 ; k3++) Ci [q++] = Ci [p++] ; + } + } + cnz = q ; /* Ci [cnz...nzmax-1] now free */ + } + /* --- Construct new element ---------------------------------------- */ + dk = 0 ; + nv [k] = -nvk ; /* flag k as in Lk */ + p = Cp [k] ; + pk1 = (elenk == 0) ? p : cnz ; /* do in place if elen[k] == 0 */ + pk2 = pk1 ; + for (k1 = 1 ; k1 <= elenk + 1 ; k1++) + { + if (k1 > elenk) + { + e = k ; /* search the nodes in k */ + pj = p ; /* list of nodes starts at Ci[pj]*/ + ln = len [k] - elenk ; /* length of list of nodes in k */ + } + else + { + e = Ci [p++] ; /* search the nodes in e */ + pj = Cp [e] ; + ln = len [e] ; /* length of list of nodes in e */ + } + for (k2 = 1 ; k2 <= ln ; k2++) + { + i = Ci [pj++] ; + if ((nvi = nv [i]) <= 0) continue ; /* node i dead, or seen */ + dk += nvi ; /* degree[Lk] += size of node i */ + nv [i] = -nvi ; /* negate nv[i] to denote i in Lk*/ + Ci [pk2++] = i ; /* place i in Lk */ + if (next [i] != -1) last [next [i]] = last [i] ; + if (last [i] != -1) /* remove i from degree list */ + { + next [last [i]] = next [i] ; + } + else + { + head [degree [i]] = next [i] ; + } + } + if (e != k) + { + Cp [e] = CS_FLIP (k) ; /* absorb e into k */ + w [e] = 0 ; /* e is now a dead element */ + } + } + if (elenk != 0) cnz = pk2 ; /* Ci [cnz...nzmax] is free */ + degree [k] = dk ; /* external degree of k - |Lk\i| */ + Cp [k] = pk1 ; /* element k is in Ci[pk1..pk2-1] */ + len [k] = pk2 - pk1 ; + elen [k] = -2 ; /* k is now an element */ + /* --- Find set differences ----------------------------------------- */ + mark = cs_wclear (mark, lemax, w, n) ; /* clear w if necessary */ + for (pk = pk1 ; pk < pk2 ; pk++) /* scan 1: find |Le\Lk| */ + { + i = Ci [pk] ; + if ((eln = elen [i]) <= 0) continue ;/* skip if elen[i] empty */ + nvi = -nv [i] ; /* nv [i] was negated */ + wnvi = mark - nvi ; + for (p = Cp [i] ; p <= Cp [i] + eln - 1 ; p++) /* scan Ei */ + { + e = Ci [p] ; + if (w [e] >= mark) + { + w [e] -= nvi ; /* decrement |Le\Lk| */ + } + else if (w [e] != 0) /* ensure e is a live element */ + { + w [e] = degree [e] + wnvi ; /* 1st time e seen in scan 1 */ + } + } + } + /* --- Degree update ------------------------------------------------ */ + for (pk = pk1 ; pk < pk2 ; pk++) /* scan2: degree update */ + { + i = Ci [pk] ; /* consider node i in Lk */ + p1 = Cp [i] ; + p2 = p1 + elen [i] - 1 ; + pn = p1 ; + for (h = 0, d = 0, p = p1 ; p <= p2 ; p++) /* scan Ei */ + { + e = Ci [p] ; + if (w [e] != 0) /* e is an unabsorbed element */ + { + dext = w [e] - mark ; /* dext = |Le\Lk| */ + if (dext > 0) + { + d += dext ; /* sum up the set differences */ + Ci [pn++] = e ; /* keep e in Ei */ + h += e ; /* compute the hash of node i */ + } + else + { + Cp [e] = CS_FLIP (k) ; /* aggressive absorb. e->k */ + w [e] = 0 ; /* e is a dead element */ + } + } + } + elen [i] = pn - p1 + 1 ; /* elen[i] = |Ei| */ + p3 = pn ; + p4 = p1 + len [i] ; + for (p = p2 + 1 ; p < p4 ; p++) /* prune edges in Ai */ + { + j = Ci [p] ; + if ((nvj = nv [j]) <= 0) continue ; /* node j dead or in Lk */ + d += nvj ; /* degree(i) += |j| */ + Ci [pn++] = j ; /* place j in node list of i */ + h += j ; /* compute hash for node i */ + } + if (d == 0) /* check for mass elimination */ + { + Cp [i] = CS_FLIP (k) ; /* absorb i into k */ + nvi = -nv [i] ; + dk -= nvi ; /* |Lk| -= |i| */ + nvk += nvi ; /* |k| += nv[i] */ + nel += nvi ; + nv [i] = 0 ; + elen [i] = -1 ; /* node i is dead */ + } + else + { + degree [i] = CS_MIN (degree [i], d) ; /* update degree(i) */ + Ci [pn] = Ci [p3] ; /* move first node to end */ + Ci [p3] = Ci [p1] ; /* move 1st el. to end of Ei */ + Ci [p1] = k ; /* add k as 1st element in of Ei */ + len [i] = pn - p1 + 1 ; /* new len of adj. list of node i */ + h = ((h<0) ? (-h):h) % n ; /* finalize hash of i */ + next [i] = hhead [h] ; /* place i in hash bucket */ + hhead [h] = i ; + last [i] = h ; /* save hash of i in last[i] */ + } + } /* scan2 is done */ + degree [k] = dk ; /* finalize |Lk| */ + lemax = CS_MAX (lemax, dk) ; + mark = cs_wclear (mark+lemax, lemax, w, n) ; /* clear w */ + /* --- Supernode detection ------------------------------------------ */ + for (pk = pk1 ; pk < pk2 ; pk++) + { + i = Ci [pk] ; + if (nv [i] >= 0) continue ; /* skip if i is dead */ + h = last [i] ; /* scan hash bucket of node i */ + i = hhead [h] ; + hhead [h] = -1 ; /* hash bucket will be empty */ + for ( ; i != -1 && next [i] != -1 ; i = next [i], mark++) + { + ln = len [i] ; + eln = elen [i] ; + for (p = Cp [i]+1 ; p <= Cp [i] + ln-1 ; p++) w [Ci [p]] = mark; + jlast = i ; + for (j = next [i] ; j != -1 ; ) /* compare i with all j */ + { + ok = (len [j] == ln) && (elen [j] == eln) ; + for (p = Cp [j] + 1 ; ok && p <= Cp [j] + ln - 1 ; p++) + { + if (w [Ci [p]] != mark) ok = 0 ; /* compare i and j*/ + } + if (ok) /* i and j are identical */ + { + Cp [j] = CS_FLIP (i) ; /* absorb j into i */ + nv [i] += nv [j] ; + nv [j] = 0 ; + elen [j] = -1 ; /* node j is dead */ + j = next [j] ; /* delete j from hash bucket */ + next [jlast] = j ; + } + else + { + jlast = j ; /* j and i are different */ + j = next [j] ; + } + } + } + } + /* --- Finalize new element------------------------------------------ */ + for (p = pk1, pk = pk1 ; pk < pk2 ; pk++) /* finalize Lk */ + { + i = Ci [pk] ; + if ((nvi = -nv [i]) <= 0) continue ;/* skip if i is dead */ + nv [i] = nvi ; /* restore nv[i] */ + d = degree [i] + dk - nvi ; /* compute external degree(i) */ + d = CS_MIN (d, n - nel - nvi) ; + if (head [d] != -1) last [head [d]] = i ; + next [i] = head [d] ; /* put i back in degree list */ + last [i] = -1 ; + head [d] = i ; + mindeg = CS_MIN (mindeg, d) ; /* find new minimum degree */ + degree [i] = d ; + Ci [p++] = i ; /* place i in Lk */ + } + nv [k] = nvk ; /* # nodes absorbed into k */ + if ((len [k] = p-pk1) == 0) /* length of adj list of element k*/ + { + Cp [k] = -1 ; /* k is a root of the tree */ + w [k] = 0 ; /* k is now a dead element */ + } + if (elenk != 0) cnz = p ; /* free unused space in Lk */ + } + /* --- Postordering ----------------------------------------------------- */ + for (i = 0 ; i < n ; i++) Cp [i] = CS_FLIP (Cp [i]) ;/* fix assembly tree */ + for (j = 0 ; j <= n ; j++) head [j] = -1 ; + for (j = n ; j >= 0 ; j--) /* place unordered nodes in lists */ + { + if (nv [j] > 0) continue ; /* skip if j is an element */ + next [j] = head [Cp [j]] ; /* place j in list of its parent */ + head [Cp [j]] = j ; + } + for (e = n ; e >= 0 ; e--) /* place elements in lists */ + { + if (nv [e] <= 0) continue ; /* skip unless e is an element */ + if (Cp [e] != -1) + { + next [e] = head [Cp [e]] ; /* place e in list of its parent */ + head [Cp [e]] = e ; + } + } + for (k = 0, i = 0 ; i <= n ; i++) /* postorder the assembly tree */ + { + if (Cp [i] == -1) k = cs_tdfs (i, k, head, next, P, w) ; + } + return (cs_idone (P, C, W, 1)) ; +} diff --git a/src/rigraph/vendor/cs/cs_chol.c b/src/rigraph/vendor/cs/cs_chol.c new file mode 100644 index 0000000..535809a --- /dev/null +++ b/src/rigraph/vendor/cs/cs_chol.c @@ -0,0 +1,59 @@ +#include "cs.h" +/* L = chol (A, [pinv parent cp]), pinv is optional */ +csn *cs_chol (const cs *A, const css *S) +{ + CS_ENTRY d, lki, *Lx, *x, *Cx ; + CS_INT top, i, p, k, n, *Li, *Lp, *cp, *pinv, *s, *c, *parent, *Cp, *Ci ; + cs *L, *C, *E ; + csn *N ; + if (!CS_CSC (A) || !S || !S->cp || !S->parent) return (NULL) ; + n = A->n ; + N = cs_calloc (1, sizeof (csn)) ; /* allocate result */ + c = cs_malloc (2*n, sizeof (CS_INT)) ; /* get CS_INT workspace */ + x = cs_malloc (n, sizeof (CS_ENTRY)) ; /* get CS_ENTRY workspace */ + cp = S->cp ; pinv = S->pinv ; parent = S->parent ; + C = pinv ? cs_symperm (A, pinv, 1) : ((cs *) A) ; + E = pinv ? C : NULL ; /* E is alias for A, or a copy E=A(p,p) */ + if (!N || !c || !x || !C) return (cs_ndone (N, E, c, x, 0)) ; + s = c + n ; + Cp = C->p ; Ci = C->i ; Cx = C->x ; + N->L = L = cs_spalloc (n, n, cp [n], 1, 0) ; /* allocate result */ + if (!L) return (cs_ndone (N, E, c, x, 0)) ; + Lp = L->p ; Li = L->i ; Lx = L->x ; + for (k = 0 ; k < n ; k++) Lp [k] = c [k] = cp [k] ; + for (k = 0 ; k < n ; k++) /* compute L(k,:) for L*L' = C */ + { + /* --- Nonzero pattern of L(k,:) ------------------------------------ */ + top = cs_ereach (C, k, parent, s, c) ; /* find pattern of L(k,:) */ + x [k] = 0 ; /* x (0:k) is now zero */ + for (p = Cp [k] ; p < Cp [k+1] ; p++) /* x = full(triu(C(:,k))) */ + { + if (Ci [p] <= k) x [Ci [p]] = Cx [p] ; + } + d = x [k] ; /* d = C(k,k) */ + x [k] = 0 ; /* clear x for k+1st iteration */ + /* --- Triangular solve --------------------------------------------- */ + for ( ; top < n ; top++) /* solve L(0:k-1,0:k-1) * x = C(:,k) */ + { + i = s [top] ; /* s [top..n-1] is pattern of L(k,:) */ + lki = x [i] / Lx [Lp [i]] ; /* L(k,i) = x (i) / L(i,i) */ + x [i] = 0 ; /* clear x for k+1st iteration */ + for (p = Lp [i] + 1 ; p < c [i] ; p++) + { + x [Li [p]] -= Lx [p] * lki ; + } + d -= lki * CS_CONJ (lki) ; /* d = d - L(k,i)*L(k,i) */ + p = c [i]++ ; + Li [p] = k ; /* store L(k,i) in column i */ + Lx [p] = CS_CONJ (lki) ; + } + /* --- Compute L(k,k) ----------------------------------------------- */ + if (CS_REAL (d) <= 0 || CS_IMAG (d) != 0) + return (cs_ndone (N, E, c, x, 0)) ; /* not pos def */ + p = c [k]++ ; + Li [p] = k ; /* store L(k,k) = sqrt (d) in column k */ + Lx [p] = sqrt (d) ; + } + Lp [n] = cp [n] ; /* finalize L */ + return (cs_ndone (N, E, c, x, 1)) ; /* success: free E,s,x; return N */ +} diff --git a/src/rigraph/vendor/cs/cs_cholsol.c b/src/rigraph/vendor/cs/cs_cholsol.c new file mode 100644 index 0000000..6e7dc4f --- /dev/null +++ b/src/rigraph/vendor/cs/cs_cholsol.c @@ -0,0 +1,26 @@ +#include "cs.h" +/* x=A\b where A is symmetric positive definite; b overwritten with solution */ +CS_INT cs_cholsol (CS_INT order, const cs *A, CS_ENTRY *b) +{ + CS_ENTRY *x ; + css *S ; + csn *N ; + CS_INT n, ok ; + if (!CS_CSC (A) || !b) return (0) ; /* check inputs */ + n = A->n ; + S = cs_schol (order, A) ; /* ordering and symbolic analysis */ + N = cs_chol (A, S) ; /* numeric Cholesky factorization */ + x = cs_malloc (n, sizeof (CS_ENTRY)) ; /* get workspace */ + ok = (S && N && x) ; + if (ok) + { + cs_ipvec (S->pinv, b, x, n) ; /* x = P*b */ + cs_lsolve (N->L, x) ; /* x = L\x */ + cs_ltsolve (N->L, x) ; /* x = L'\x */ + cs_pvec (S->pinv, x, b, n) ; /* b = P'*x */ + } + cs_free (x) ; + cs_sfree (S) ; + cs_nfree (N) ; + return (ok) ; +} diff --git a/src/rigraph/vendor/cs/cs_compress.c b/src/rigraph/vendor/cs/cs_compress.c new file mode 100644 index 0000000..dc62eba --- /dev/null +++ b/src/rigraph/vendor/cs/cs_compress.c @@ -0,0 +1,22 @@ +#include "cs.h" +/* C = compressed-column form of a triplet matrix T */ +cs *cs_compress (const cs *T) +{ + CS_INT m, n, nz, p, k, *Cp, *Ci, *w, *Ti, *Tj ; + CS_ENTRY *Cx, *Tx ; + cs *C ; + if (!CS_TRIPLET (T)) return (NULL) ; /* check inputs */ + m = T->m ; n = T->n ; Ti = T->i ; Tj = T->p ; Tx = T->x ; nz = T->nz ; + C = cs_spalloc (m, n, nz, Tx != NULL, 0) ; /* allocate result */ + w = cs_calloc (n, sizeof (CS_INT)) ; /* get workspace */ + if (!C || !w) return (cs_done (C, w, NULL, 0)) ; /* out of memory */ + Cp = C->p ; Ci = C->i ; Cx = C->x ; + for (k = 0 ; k < nz ; k++) w [Tj [k]]++ ; /* column counts */ + cs_cumsum (Cp, w, n) ; /* column pointers */ + for (k = 0 ; k < nz ; k++) + { + Ci [p = w [Tj [k]]++] = Ti [k] ; /* A(i,j) is the pth entry in C */ + if (Cx) Cx [p] = Tx [k] ; + } + return (cs_done (C, w, NULL, 1)) ; /* success; free w and return C */ +} diff --git a/src/rigraph/vendor/cs/cs_counts.c b/src/rigraph/vendor/cs/cs_counts.c new file mode 100644 index 0000000..a1b3de0 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_counts.c @@ -0,0 +1,61 @@ +#include "cs.h" +/* column counts of LL'=A or LL'=A'A, given parent & post ordering */ +#define HEAD(k,j) (ata ? head [k] : j) +#define NEXT(J) (ata ? next [J] : -1) +static void init_ata (cs *AT, const CS_INT *post, CS_INT *w, CS_INT **head, CS_INT **next) +{ + CS_INT i, k, p, m = AT->n, n = AT->m, *ATp = AT->p, *ATi = AT->i ; + *head = w+4*n, *next = w+5*n+1 ; + for (k = 0 ; k < n ; k++) w [post [k]] = k ; /* invert post */ + for (i = 0 ; i < m ; i++) + { + for (k = n, p = ATp[i] ; p < ATp[i+1] ; p++) k = CS_MIN (k, w [ATi[p]]); + (*next) [i] = (*head) [k] ; /* place row i in linked list k */ + (*head) [k] = i ; + } +} +CS_INT *cs_counts (const cs *A, const CS_INT *parent, const CS_INT *post, CS_INT ata) +{ + CS_INT i, j, k, n, m, J, s, p, q, jleaf, *ATp, *ATi, *maxfirst, *prevleaf, + *ancestor, *head = NULL, *next = NULL, *colcount, *w, *first, *delta ; + cs *AT ; + if (!CS_CSC (A) || !parent || !post) return (NULL) ; /* check inputs */ + m = A->m ; n = A->n ; + s = 4*n + (ata ? (n+m+1) : 0) ; + delta = colcount = cs_malloc (n, sizeof (CS_INT)) ; /* allocate result */ + w = cs_malloc (s, sizeof (CS_INT)) ; /* get workspace */ + AT = cs_transpose (A, 0) ; /* AT = A' */ + if (!AT || !colcount || !w) return (cs_idone (colcount, AT, w, 0)) ; + ancestor = w ; maxfirst = w+n ; prevleaf = w+2*n ; first = w+3*n ; + for (k = 0 ; k < s ; k++) w [k] = -1 ; /* clear workspace w [0..s-1] */ + for (k = 0 ; k < n ; k++) /* find first [j] */ + { + j = post [k] ; + delta [j] = (first [j] == -1) ? 1 : 0 ; /* delta[j]=1 if j is a leaf */ + for ( ; j != -1 && first [j] == -1 ; j = parent [j]) first [j] = k ; + } + ATp = AT->p ; ATi = AT->i ; + if (ata) init_ata (AT, post, w, &head, &next) ; + for (i = 0 ; i < n ; i++) ancestor [i] = i ; /* each node in its own set */ + for (k = 0 ; k < n ; k++) + { + j = post [k] ; /* j is the kth node in postordered etree */ + if (parent [j] != -1) delta [parent [j]]-- ; /* j is not a root */ + for (J = HEAD (k,j) ; J != -1 ; J = NEXT (J)) /* J=j for LL'=A case */ + { + for (p = ATp [J] ; p < ATp [J+1] ; p++) + { + i = ATi [p] ; + q = cs_leaf (i, j, first, maxfirst, prevleaf, ancestor, &jleaf); + if (jleaf >= 1) delta [j]++ ; /* A(i,j) is in skeleton */ + if (jleaf == 2) delta [q]-- ; /* account for overlap in q */ + } + } + if (parent [j] != -1) ancestor [j] = parent [j] ; + } + for (j = 0 ; j < n ; j++) /* sum up delta's of each child */ + { + if (parent [j] != -1) colcount [parent [j]] += colcount [j] ; + } + return (cs_idone (colcount, AT, w, 1)) ; /* success: free workspace */ +} diff --git a/src/rigraph/vendor/cs/cs_cumsum.c b/src/rigraph/vendor/cs/cs_cumsum.c new file mode 100644 index 0000000..e839497 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_cumsum.c @@ -0,0 +1,17 @@ +#include "cs.h" +/* p [0..n] = cumulative sum of c [0..n-1], and then copy p [0..n-1] into c */ +double cs_cumsum (CS_INT *p, CS_INT *c, CS_INT n) +{ + CS_INT i, nz = 0 ; + double nz2 = 0 ; + if (!p || !c) return (-1) ; /* check inputs */ + for (i = 0 ; i < n ; i++) + { + p [i] = nz ; + nz += c [i] ; + nz2 += c [i] ; /* also in double to avoid CS_INT overflow */ + c [i] = p [i] ; /* also copy p[0..n-1] back into c[0..n-1]*/ + } + p [n] = nz ; + return (nz2) ; /* return sum (c [0..n-1]) */ +} diff --git a/src/rigraph/vendor/cs/cs_dfs.c b/src/rigraph/vendor/cs/cs_dfs.c new file mode 100644 index 0000000..6c7115d --- /dev/null +++ b/src/rigraph/vendor/cs/cs_dfs.c @@ -0,0 +1,36 @@ +#include "cs.h" +/* depth-first-search of the graph of a matrix, starting at node j */ +CS_INT cs_dfs (CS_INT j, cs *G, CS_INT top, CS_INT *xi, CS_INT *pstack, const CS_INT *pinv) +{ + CS_INT i, p, p2, done, jnew, head = 0, *Gp, *Gi ; + if (!CS_CSC (G) || !xi || !pstack) return (-1) ; /* check inputs */ + Gp = G->p ; Gi = G->i ; + xi [0] = j ; /* initialize the recursion stack */ + while (head >= 0) + { + j = xi [head] ; /* get j from the top of the recursion stack */ + jnew = pinv ? (pinv [j]) : j ; + if (!CS_MARKED (Gp, j)) + { + CS_MARK (Gp, j) ; /* mark node j as visited */ + pstack [head] = (jnew < 0) ? 0 : CS_UNFLIP (Gp [jnew]) ; + } + done = 1 ; /* node j done if no unvisited neighbors */ + p2 = (jnew < 0) ? 0 : CS_UNFLIP (Gp [jnew+1]) ; + for (p = pstack [head] ; p < p2 ; p++) /* examine all neighbors of j */ + { + i = Gi [p] ; /* consider neighbor node i */ + if (CS_MARKED (Gp, i)) continue ; /* skip visited node i */ + pstack [head] = p ; /* pause depth-first search of node j */ + xi [++head] = i ; /* start dfs at node i */ + done = 0 ; /* node j is not done */ + break ; /* break, to start dfs (i) */ + } + if (done) /* depth-first search at node j is done */ + { + head-- ; /* remove j from the recursion stack */ + xi [--top] = j ; /* and place in the output stack */ + } + } + return (top) ; +} diff --git a/src/rigraph/vendor/cs/cs_dmperm.c b/src/rigraph/vendor/cs/cs_dmperm.c new file mode 100644 index 0000000..e213845 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_dmperm.c @@ -0,0 +1,144 @@ +#include "cs.h" +/* breadth-first search for coarse decomposition (C0,C1,R1 or R0,R3,C3) */ +static CS_INT cs_bfs (const cs *A, CS_INT n, CS_INT *wi, CS_INT *wj, CS_INT *queue, + const CS_INT *imatch, const CS_INT *jmatch, CS_INT mark) +{ + CS_INT *Ap, *Ai, head = 0, tail = 0, j, i, p, j2 ; + cs *C ; + for (j = 0 ; j < n ; j++) /* place all unmatched nodes in queue */ + { + if (imatch [j] >= 0) continue ; /* skip j if matched */ + wj [j] = 0 ; /* j in set C0 (R0 if transpose) */ + queue [tail++] = j ; /* place unmatched col j in queue */ + } + if (tail == 0) return (1) ; /* quick return if no unmatched nodes */ + C = (mark == 1) ? ((cs *) A) : cs_transpose (A, 0) ; + if (!C) return (0) ; /* bfs of C=A' to find R3,C3 from R0 */ + Ap = C->p ; Ai = C->i ; + while (head < tail) /* while queue is not empty */ + { + j = queue [head++] ; /* get the head of the queue */ + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; + if (wi [i] >= 0) continue ; /* skip if i is marked */ + wi [i] = mark ; /* i in set R1 (C3 if transpose) */ + j2 = jmatch [i] ; /* traverse alternating path to j2 */ + if (wj [j2] >= 0) continue ;/* skip j2 if it is marked */ + wj [j2] = mark ; /* j2 in set C1 (R3 if transpose) */ + queue [tail++] = j2 ; /* add j2 to queue */ + } + } + if (mark != 1) cs_spfree (C) ; /* free A' if it was created */ + return (1) ; +} + +/* collect matched rows and columns into p and q */ +static void cs_matched (CS_INT n, const CS_INT *wj, const CS_INT *imatch, CS_INT *p, CS_INT *q, + CS_INT *cc, CS_INT *rr, CS_INT set, CS_INT mark) +{ + CS_INT kc = cc [set], j ; + CS_INT kr = rr [set-1] ; + for (j = 0 ; j < n ; j++) + { + if (wj [j] != mark) continue ; /* skip if j is not in C set */ + p [kr++] = imatch [j] ; + q [kc++] = j ; + } + cc [set+1] = kc ; + rr [set] = kr ; +} + +/* collect unmatched rows into the permutation vector p */ +static void cs_unmatched (CS_INT m, const CS_INT *wi, CS_INT *p, CS_INT *rr, CS_INT set) +{ + CS_INT i, kr = rr [set] ; + for (i = 0 ; i < m ; i++) if (wi [i] == 0) p [kr++] = i ; + rr [set+1] = kr ; +} + +/* return 1 if row i is in R2 */ +static CS_INT cs_rprune (CS_INT i, CS_INT j, CS_ENTRY aij, void *other) +{ + CS_INT *rr = (CS_INT *) other ; + return (i >= rr [1] && i < rr [2]) ; +} + +/* Given A, compute coarse and then fine dmperm */ +csd *cs_dmperm (const cs *A, CS_INT seed) +{ + CS_INT m, n, i, j, k, cnz, nc, *jmatch, *imatch, *wi, *wj, *pinv, *Cp, *Ci, + *ps, *rs, nb1, nb2, *p, *q, *cc, *rr, *r, *s, ok ; + cs *C ; + csd *D, *scc ; + /* --- Maximum matching ------------------------------------------------- */ + if (!CS_CSC (A)) return (NULL) ; /* check inputs */ + m = A->m ; n = A->n ; + D = cs_dalloc (m, n) ; /* allocate result */ + if (!D) return (NULL) ; + p = D->p ; q = D->q ; r = D->r ; s = D->s ; cc = D->cc ; rr = D->rr ; + jmatch = cs_maxtrans (A, seed) ; /* max transversal */ + imatch = jmatch + m ; /* imatch = inverse of jmatch */ + if (!jmatch) return (cs_ddone (D, NULL, jmatch, 0)) ; + /* --- Coarse decomposition --------------------------------------------- */ + wi = r ; wj = s ; /* use r and s as workspace */ + for (j = 0 ; j < n ; j++) wj [j] = -1 ; /* unmark all cols for bfs */ + for (i = 0 ; i < m ; i++) wi [i] = -1 ; /* unmark all rows for bfs */ + cs_bfs (A, n, wi, wj, q, imatch, jmatch, 1) ; /* find C1, R1 from C0*/ + ok = cs_bfs (A, m, wj, wi, p, jmatch, imatch, 3) ; /* find R3, C3 from R0*/ + if (!ok) return (cs_ddone (D, NULL, jmatch, 0)) ; + cs_unmatched (n, wj, q, cc, 0) ; /* unmatched set C0 */ + cs_matched (n, wj, imatch, p, q, cc, rr, 1, 1) ; /* set R1 and C1 */ + cs_matched (n, wj, imatch, p, q, cc, rr, 2, -1) ; /* set R2 and C2 */ + cs_matched (n, wj, imatch, p, q, cc, rr, 3, 3) ; /* set R3 and C3 */ + cs_unmatched (m, wi, p, rr, 3) ; /* unmatched set R0 */ + cs_free (jmatch) ; + /* --- Fine decomposition ----------------------------------------------- */ + pinv = cs_pinv (p, m) ; /* pinv=p' */ + if (!pinv) return (cs_ddone (D, NULL, NULL, 0)) ; + C = cs_permute (A, pinv, q, 0) ;/* C=A(p,q) (it will hold A(R2,C2)) */ + cs_free (pinv) ; + if (!C) return (cs_ddone (D, NULL, NULL, 0)) ; + Cp = C->p ; + nc = cc [3] - cc [2] ; /* delete cols C0, C1, and C3 from C */ + if (cc [2] > 0) for (j = cc [2] ; j <= cc [3] ; j++) Cp [j-cc[2]] = Cp [j] ; + C->n = nc ; + if (rr [2] - rr [1] < m) /* delete rows R0, R1, and R3 from C */ + { + cs_fkeep (C, cs_rprune, rr) ; + cnz = Cp [nc] ; + Ci = C->i ; + if (rr [1] > 0) for (k = 0 ; k < cnz ; k++) Ci [k] -= rr [1] ; + } + C->m = nc ; + scc = cs_scc (C) ; /* find strongly connected components of C*/ + if (!scc) return (cs_ddone (D, C, NULL, 0)) ; + /* --- Combine coarse and fine decompositions --------------------------- */ + ps = scc->p ; /* C(ps,ps) is the permuted matrix */ + rs = scc->r ; /* kth block is rs[k]..rs[k+1]-1 */ + nb1 = scc->nb ; /* # of blocks of A(R2,C2) */ + for (k = 0 ; k < nc ; k++) wj [k] = q [ps [k] + cc [2]] ; + for (k = 0 ; k < nc ; k++) q [k + cc [2]] = wj [k] ; + for (k = 0 ; k < nc ; k++) wi [k] = p [ps [k] + rr [1]] ; + for (k = 0 ; k < nc ; k++) p [k + rr [1]] = wi [k] ; + nb2 = 0 ; /* create the fine block partitions */ + r [0] = s [0] = 0 ; + if (cc [2] > 0) nb2++ ; /* leading coarse block A (R1, [C0 C1]) */ + for (k = 0 ; k < nb1 ; k++) /* coarse block A (R2,C2) */ + { + r [nb2] = rs [k] + rr [1] ; /* A (R2,C2) splits into nb1 fine blocks */ + s [nb2] = rs [k] + cc [2] ; + nb2++ ; + } + if (rr [2] < m) + { + r [nb2] = rr [2] ; /* trailing coarse block A ([R3 R0], C3) */ + s [nb2] = cc [3] ; + nb2++ ; + } + r [nb2] = m ; + s [nb2] = n ; + D->nb = nb2 ; + cs_dfree (scc) ; + return (cs_ddone (D, C, NULL, 1)) ; +} diff --git a/src/rigraph/vendor/cs/cs_droptol.c b/src/rigraph/vendor/cs/cs_droptol.c new file mode 100644 index 0000000..59b8df2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_droptol.c @@ -0,0 +1,9 @@ +#include "cs.h" +static CS_INT cs_tol (CS_INT i, CS_INT j, CS_ENTRY aij, void *tol) +{ + return (CS_ABS (aij) > *((double *) tol)) ; +} +CS_INT cs_droptol (cs *A, double tol) +{ + return (cs_fkeep (A, &cs_tol, &tol)) ; /* keep all large entries */ +} diff --git a/src/rigraph/vendor/cs/cs_dropzeros.c b/src/rigraph/vendor/cs/cs_dropzeros.c new file mode 100644 index 0000000..d93f605 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_dropzeros.c @@ -0,0 +1,9 @@ +#include "cs.h" +static CS_INT cs_nonzero (CS_INT i, CS_INT j, CS_ENTRY aij, void *other) +{ + return (aij != 0) ; +} +CS_INT cs_dropzeros (cs *A) +{ + return (cs_fkeep (A, &cs_nonzero, NULL)) ; /* keep all nonzero entries */ +} diff --git a/src/rigraph/vendor/cs/cs_dupl.c b/src/rigraph/vendor/cs/cs_dupl.c new file mode 100644 index 0000000..fdf2e1e --- /dev/null +++ b/src/rigraph/vendor/cs/cs_dupl.c @@ -0,0 +1,34 @@ +#include "cs.h" +/* remove duplicate entries from A */ +CS_INT cs_dupl (cs *A) +{ + CS_INT i, j, p, q, nz = 0, n, m, *Ap, *Ai, *w ; + CS_ENTRY *Ax ; + if (!CS_CSC (A)) return (0) ; /* check inputs */ + m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; + w = cs_malloc (m, sizeof (CS_INT)) ; /* get workspace */ + if (!w) return (0) ; /* out of memory */ + for (i = 0 ; i < m ; i++) w [i] = -1 ; /* row i not yet seen */ + for (j = 0 ; j < n ; j++) + { + q = nz ; /* column j will start at q */ + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; /* A(i,j) is nonzero */ + if (w [i] >= q) + { + Ax [w [i]] += Ax [p] ; /* A(i,j) is a duplicate */ + } + else + { + w [i] = nz ; /* record where row i occurs */ + Ai [nz] = i ; /* keep A(i,j) */ + Ax [nz++] = Ax [p] ; + } + } + Ap [j] = q ; /* record start of column j */ + } + Ap [n] = nz ; /* finalize A */ + cs_free (w) ; /* free workspace */ + return (cs_sprealloc (A, 0)) ; /* remove extra space from A */ +} diff --git a/src/rigraph/vendor/cs/cs_entry.c b/src/rigraph/vendor/cs/cs_entry.c new file mode 100644 index 0000000..f712ba7 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_entry.c @@ -0,0 +1,13 @@ +#include "cs.h" +/* add an entry to a triplet matrix; return 1 if ok, 0 otherwise */ +CS_INT cs_entry (cs *T, CS_INT i, CS_INT j, CS_ENTRY x) +{ + if (!CS_TRIPLET (T) || i < 0 || j < 0) return (0) ; /* check inputs */ + if (T->nz >= T->nzmax && !cs_sprealloc (T,2*(T->nzmax))) return (0) ; + if (T->x) T->x [T->nz] = x ; + T->i [T->nz] = i ; + T->p [T->nz++] = j ; + T->m = CS_MAX (T->m, i+1) ; + T->n = CS_MAX (T->n, j+1) ; + return (1) ; +} diff --git a/src/rigraph/vendor/cs/cs_ereach.c b/src/rigraph/vendor/cs/cs_ereach.c new file mode 100644 index 0000000..9edad52 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_ereach.c @@ -0,0 +1,23 @@ +#include "cs.h" +/* find nonzero pattern of Cholesky L(k,1:k-1) using etree and triu(A(:,k)) */ +CS_INT cs_ereach (const cs *A, CS_INT k, const CS_INT *parent, CS_INT *s, CS_INT *w) +{ + CS_INT i, p, n, len, top, *Ap, *Ai ; + if (!CS_CSC (A) || !parent || !s || !w) return (-1) ; /* check inputs */ + top = n = A->n ; Ap = A->p ; Ai = A->i ; + CS_MARK (w, k) ; /* mark node k as visited */ + for (p = Ap [k] ; p < Ap [k+1] ; p++) + { + i = Ai [p] ; /* A(i,k) is nonzero */ + if (i > k) continue ; /* only use upper triangular part of A */ + for (len = 0 ; !CS_MARKED (w,i) ; i = parent [i]) /* traverse up etree*/ + { + s [len++] = i ; /* L(k,i) is nonzero */ + CS_MARK (w, i) ; /* mark i as visited */ + } + while (len > 0) s [--top] = s [--len] ; /* push path onto stack */ + } + for (p = top ; p < n ; p++) CS_MARK (w, s [p]) ; /* unmark all nodes */ + CS_MARK (w, k) ; /* unmark node k */ + return (top) ; /* s [top..n-1] contains pattern of L(k,:)*/ +} diff --git a/src/rigraph/vendor/cs/cs_etree.c b/src/rigraph/vendor/cs/cs_etree.c new file mode 100644 index 0000000..5620928 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_etree.c @@ -0,0 +1,30 @@ +#include "cs.h" +/* compute the etree of A (using triu(A), or A'A without forming A'A */ +CS_INT *cs_etree (const cs *A, CS_INT ata) +{ + CS_INT i, k, p, m, n, inext, *Ap, *Ai, *w, *parent, *ancestor, *prev ; + if (!CS_CSC (A)) return (NULL) ; /* check inputs */ + m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; + parent = cs_malloc (n, sizeof (CS_INT)) ; /* allocate result */ + w = cs_malloc (n + (ata ? m : 0), sizeof (CS_INT)) ; /* get workspace */ + if (!w || !parent) return (cs_idone (parent, NULL, w, 0)) ; + ancestor = w ; prev = w + n ; + if (ata) for (i = 0 ; i < m ; i++) prev [i] = -1 ; + for (k = 0 ; k < n ; k++) + { + parent [k] = -1 ; /* node k has no parent yet */ + ancestor [k] = -1 ; /* nor does k have an ancestor */ + for (p = Ap [k] ; p < Ap [k+1] ; p++) + { + i = ata ? (prev [Ai [p]]) : (Ai [p]) ; + for ( ; i != -1 && i < k ; i = inext) /* traverse from i to k */ + { + inext = ancestor [i] ; /* inext = ancestor of i */ + ancestor [i] = k ; /* path compression */ + if (inext == -1) parent [i] = k ; /* no anc., parent is k */ + } + if (ata) prev [Ai [p]] = k ; + } + } + return (cs_idone (parent, NULL, w, 1)) ; +} diff --git a/src/rigraph/vendor/cs/cs_fkeep.c b/src/rigraph/vendor/cs/cs_fkeep.c new file mode 100644 index 0000000..09219e8 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_fkeep.c @@ -0,0 +1,25 @@ +#include "cs.h" +/* drop entries for which fkeep(A(i,j)) is false; return nz if OK, else -1 */ +CS_INT cs_fkeep (cs *A, CS_INT (*fkeep) (CS_INT, CS_INT, CS_ENTRY, void *), void *other) +{ + CS_INT j, p, nz = 0, n, *Ap, *Ai ; + CS_ENTRY *Ax ; + if (!CS_CSC (A) || !fkeep) return (-1) ; /* check inputs */ + n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; + for (j = 0 ; j < n ; j++) + { + p = Ap [j] ; /* get current location of col j */ + Ap [j] = nz ; /* record new location of col j */ + for ( ; p < Ap [j+1] ; p++) + { + if (fkeep (Ai [p], j, Ax ? Ax [p] : 1, other)) + { + if (Ax) Ax [nz] = Ax [p] ; /* keep A(i,j) */ + Ai [nz++] = Ai [p] ; + } + } + } + Ap [n] = nz ; /* finalize A */ + cs_sprealloc (A, 0) ; /* remove extra space from A */ + return (nz) ; +} diff --git a/src/rigraph/vendor/cs/cs_gaxpy.c b/src/rigraph/vendor/cs/cs_gaxpy.c new file mode 100644 index 0000000..db93cbc --- /dev/null +++ b/src/rigraph/vendor/cs/cs_gaxpy.c @@ -0,0 +1,17 @@ +#include "cs.h" +/* y = A*x+y */ +CS_INT cs_gaxpy (const cs *A, const CS_ENTRY *x, CS_ENTRY *y) +{ + CS_INT p, j, n, *Ap, *Ai ; + CS_ENTRY *Ax ; + if (!CS_CSC (A) || !x || !y) return (0) ; /* check inputs */ + n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; + for (j = 0 ; j < n ; j++) + { + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + y [Ai [p]] += Ax [p] * x [j] ; + } + } + return (1) ; +} diff --git a/src/rigraph/vendor/cs/cs_happly.c b/src/rigraph/vendor/cs/cs_happly.c new file mode 100644 index 0000000..98a306c --- /dev/null +++ b/src/rigraph/vendor/cs/cs_happly.c @@ -0,0 +1,19 @@ +#include "cs.h" +/* apply the ith Householder vector to x */ +CS_INT cs_happly (const cs *V, CS_INT i, double beta, CS_ENTRY *x) +{ + CS_INT p, *Vp, *Vi ; + CS_ENTRY *Vx, tau = 0 ; + if (!CS_CSC (V) || !x) return (0) ; /* check inputs */ + Vp = V->p ; Vi = V->i ; Vx = V->x ; + for (p = Vp [i] ; p < Vp [i+1] ; p++) /* tau = v'*x */ + { + tau += CS_CONJ (Vx [p]) * x [Vi [p]] ; + } + tau *= beta ; /* tau = beta*(v'*x) */ + for (p = Vp [i] ; p < Vp [i+1] ; p++) /* x = x - v*tau */ + { + x [Vi [p]] -= Vx [p] * tau ; + } + return (1) ; +} diff --git a/src/rigraph/vendor/cs/cs_house.c b/src/rigraph/vendor/cs/cs_house.c new file mode 100644 index 0000000..e825c89 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_house.c @@ -0,0 +1,30 @@ +#include "cs.h" +/* create a Householder reflection [v,beta,s]=house(x), overwrite x with v, + * where (I-beta*v*v')*x = s*e1 and e1 = [1 0 ... 0]'. + * Note that this CXSparse version is different than CSparse. See Higham, + * Accuracy & Stability of Num Algorithms, 2nd ed, 2002, page 357. */ +CS_ENTRY cs_house (CS_ENTRY *x, double *beta, CS_INT n) +{ + CS_ENTRY s = 0 ; + CS_INT i ; + if (!x || !beta) return (-1) ; /* check inputs */ + /* s = norm(x) */ + for (i = 0 ; i < n ; i++) s += x [i] * CS_CONJ (x [i]) ; + s = sqrt (s) ; + if (s == 0) + { + (*beta) = 0 ; + x [0] = 1 ; + } + else + { + /* s = sign(x[0]) * norm (x) ; */ + if (x [0] != 0) + { + s *= x [0] / CS_ABS (x [0]) ; + } + x [0] += s ; + (*beta) = 1. / CS_REAL (CS_CONJ (s) * x [0]) ; + } + return (-s) ; +} diff --git a/src/rigraph/vendor/cs/cs_ipvec.c b/src/rigraph/vendor/cs/cs_ipvec.c new file mode 100644 index 0000000..4935ace --- /dev/null +++ b/src/rigraph/vendor/cs/cs_ipvec.c @@ -0,0 +1,9 @@ +#include "cs.h" +/* x(p) = b, for dense vectors x and b; p=NULL denotes identity */ +CS_INT cs_ipvec (const CS_INT *p, const CS_ENTRY *b, CS_ENTRY *x, CS_INT n) +{ + CS_INT k ; + if (!x || !b) return (0) ; /* check inputs */ + for (k = 0 ; k < n ; k++) x [p ? p [k] : k] = b [k] ; + return (1) ; +} diff --git a/src/rigraph/vendor/cs/cs_leaf.c b/src/rigraph/vendor/cs/cs_leaf.c new file mode 100644 index 0000000..bd93bda --- /dev/null +++ b/src/rigraph/vendor/cs/cs_leaf.c @@ -0,0 +1,22 @@ +#include "cs.h" +/* consider A(i,j), node j in ith row subtree and return lca(jprev,j) */ +CS_INT cs_leaf (CS_INT i, CS_INT j, const CS_INT *first, CS_INT *maxfirst, CS_INT *prevleaf, + CS_INT *ancestor, CS_INT *jleaf) +{ + CS_INT q, s, sparent, jprev ; + if (!first || !maxfirst || !prevleaf || !ancestor || !jleaf) return (-1) ; + *jleaf = 0 ; + if (i <= j || first [j] <= maxfirst [i]) return (-1) ; /* j not a leaf */ + maxfirst [i] = first [j] ; /* update max first[j] seen so far */ + jprev = prevleaf [i] ; /* jprev = previous leaf of ith subtree */ + prevleaf [i] = j ; + *jleaf = (jprev == -1) ? 1: 2 ; /* j is first or subsequent leaf */ + if (*jleaf == 1) return (i) ; /* if 1st leaf, q = root of ith subtree */ + for (q = jprev ; q != ancestor [q] ; q = ancestor [q]) ; + for (s = jprev ; s != q ; s = sparent) + { + sparent = ancestor [s] ; /* path compression */ + ancestor [s] = q ; + } + return (q) ; /* q = least common ancester (jprev,j) */ +} diff --git a/src/rigraph/vendor/cs/cs_load.c b/src/rigraph/vendor/cs/cs_load.c new file mode 100644 index 0000000..91e1f37 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_load.c @@ -0,0 +1,26 @@ +#include "cs.h" +/* load a triplet matrix from a file */ +cs *cs_load (FILE *f) +{ + double i, j ; /* use double for integers to avoid csi conflicts */ + double x ; +#ifdef CS_COMPLEX + double xi ; +#endif + cs *T ; + if (!f) return (NULL) ; /* check inputs */ + T = cs_spalloc (0, 0, 1, 1, 1) ; /* allocate result */ +#ifdef CS_COMPLEX + while (fscanf (f, "%lg %lg %lg %lg\n", &i, &j, &x, &xi) == 4) +#else + while (fscanf (f, "%lg %lg %lg\n", &i, &j, &x) == 3) +#endif + { +#ifdef CS_COMPLEX + if (!cs_entry (T, (CS_INT) i, (CS_INT) j, x + xi*I)) return (cs_spfree (T)) ; +#else + if (!cs_entry (T, (CS_INT) i, (CS_INT) j, x)) return (cs_spfree (T)) ; +#endif + } + return (T) ; +} diff --git a/src/rigraph/vendor/cs/cs_lsolve.c b/src/rigraph/vendor/cs/cs_lsolve.c new file mode 100644 index 0000000..099b0c5 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_lsolve.c @@ -0,0 +1,18 @@ +#include "cs.h" +/* solve Lx=b where x and b are dense. x=b on input, solution on output. */ +CS_INT cs_lsolve (const cs *L, CS_ENTRY *x) +{ + CS_INT p, j, n, *Lp, *Li ; + CS_ENTRY *Lx ; + if (!CS_CSC (L) || !x) return (0) ; /* check inputs */ + n = L->n ; Lp = L->p ; Li = L->i ; Lx = L->x ; + for (j = 0 ; j < n ; j++) + { + x [j] /= Lx [Lp [j]] ; + for (p = Lp [j]+1 ; p < Lp [j+1] ; p++) + { + x [Li [p]] -= Lx [p] * x [j] ; + } + } + return (1) ; +} diff --git a/src/rigraph/vendor/cs/cs_ltsolve.c b/src/rigraph/vendor/cs/cs_ltsolve.c new file mode 100644 index 0000000..29b1ca2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_ltsolve.c @@ -0,0 +1,18 @@ +#include "cs.h" +/* solve L'x=b where x and b are dense. x=b on input, solution on output. */ +CS_INT cs_ltsolve (const cs *L, CS_ENTRY *x) +{ + CS_INT p, j, n, *Lp, *Li ; + CS_ENTRY *Lx ; + if (!CS_CSC (L) || !x) return (0) ; /* check inputs */ + n = L->n ; Lp = L->p ; Li = L->i ; Lx = L->x ; + for (j = n-1 ; j >= 0 ; j--) + { + for (p = Lp [j]+1 ; p < Lp [j+1] ; p++) + { + x [j] -= CS_CONJ (Lx [p]) * x [Li [p]] ; + } + x [j] /= CS_CONJ (Lx [Lp [j]]) ; + } + return (1) ; +} diff --git a/src/rigraph/vendor/cs/cs_lu.c b/src/rigraph/vendor/cs/cs_lu.c new file mode 100644 index 0000000..270172c --- /dev/null +++ b/src/rigraph/vendor/cs/cs_lu.c @@ -0,0 +1,88 @@ +#include "cs.h" +/* [L,U,pinv]=lu(A, [q lnz unz]). lnz and unz can be guess */ +csn *cs_lu (const cs *A, const css *S, double tol) +{ + cs *L, *U ; + csn *N ; + CS_ENTRY pivot, *Lx, *Ux, *x ; + double a, t ; + CS_INT *Lp, *Li, *Up, *Ui, *pinv, *xi, *q, n, ipiv, k, top, p, i, col, lnz,unz; + if (!CS_CSC (A) || !S) return (NULL) ; /* check inputs */ + n = A->n ; + q = S->q ; lnz = S->lnz ; unz = S->unz ; + x = cs_malloc (n, sizeof (CS_ENTRY)) ; /* get CS_ENTRY workspace */ + xi = cs_malloc (2*n, sizeof (CS_INT)) ; /* get CS_INT workspace */ + N = cs_calloc (1, sizeof (csn)) ; /* allocate result */ + if (!x || !xi || !N) return (cs_ndone (N, NULL, xi, x, 0)) ; + N->L = L = cs_spalloc (n, n, lnz, 1, 0) ; /* allocate result L */ + N->U = U = cs_spalloc (n, n, unz, 1, 0) ; /* allocate result U */ + N->pinv = pinv = cs_malloc (n, sizeof (CS_INT)) ; /* allocate result pinv */ + if (!L || !U || !pinv) return (cs_ndone (N, NULL, xi, x, 0)) ; + Lp = L->p ; Up = U->p ; + for (i = 0 ; i < n ; i++) x [i] = 0 ; /* clear workspace */ + for (i = 0 ; i < n ; i++) pinv [i] = -1 ; /* no rows pivotal yet */ + for (k = 0 ; k <= n ; k++) Lp [k] = 0 ; /* no cols of L yet */ + lnz = unz = 0 ; + for (k = 0 ; k < n ; k++) /* compute L(:,k) and U(:,k) */ + { + /* --- Triangular solve --------------------------------------------- */ + Lp [k] = lnz ; /* L(:,k) starts here */ + Up [k] = unz ; /* U(:,k) starts here */ + if ((lnz + n > L->nzmax && !cs_sprealloc (L, 2*L->nzmax + n)) || + (unz + n > U->nzmax && !cs_sprealloc (U, 2*U->nzmax + n))) + { + return (cs_ndone (N, NULL, xi, x, 0)) ; + } + Li = L->i ; Lx = L->x ; Ui = U->i ; Ux = U->x ; + col = q ? (q [k]) : k ; + top = cs_spsolve (L, A, col, xi, x, pinv, 1) ; /* x = L\A(:,col) */ + /* --- Find pivot --------------------------------------------------- */ + ipiv = -1 ; + a = -1 ; + for (p = top ; p < n ; p++) + { + i = xi [p] ; /* x(i) is nonzero */ + if (pinv [i] < 0) /* row i is not yet pivotal */ + { + if ((t = CS_ABS (x [i])) > a) + { + a = t ; /* largest pivot candidate so far */ + ipiv = i ; + } + } + else /* x(i) is the entry U(pinv[i],k) */ + { + Ui [unz] = pinv [i] ; + Ux [unz++] = x [i] ; + } + } + if (ipiv == -1 || a <= 0) return (cs_ndone (N, NULL, xi, x, 0)) ; + /* tol=1 for partial pivoting; tol<1 gives preference to diagonal */ + if (pinv [col] < 0 && CS_ABS (x [col]) >= a*tol) ipiv = col ; + /* --- Divide by pivot ---------------------------------------------- */ + pivot = x [ipiv] ; /* the chosen pivot */ + Ui [unz] = k ; /* last entry in U(:,k) is U(k,k) */ + Ux [unz++] = pivot ; + pinv [ipiv] = k ; /* ipiv is the kth pivot row */ + Li [lnz] = ipiv ; /* first entry in L(:,k) is L(k,k) = 1 */ + Lx [lnz++] = 1 ; + for (p = top ; p < n ; p++) /* L(k+1:n,k) = x / pivot */ + { + i = xi [p] ; + if (pinv [i] < 0) /* x(i) is an entry in L(:,k) */ + { + Li [lnz] = i ; /* save unpermuted row in L */ + Lx [lnz++] = x [i] / pivot ; /* scale pivot column */ + } + x [i] = 0 ; /* x [0..n-1] = 0 for next k */ + } + } + /* --- Finalize L and U ------------------------------------------------- */ + Lp [n] = lnz ; + Up [n] = unz ; + Li = L->i ; /* fix row indices of L for final pinv */ + for (p = 0 ; p < lnz ; p++) Li [p] = pinv [Li [p]] ; + cs_sprealloc (L, 0) ; /* remove extra space from L and U */ + cs_sprealloc (U, 0) ; + return (cs_ndone (N, NULL, xi, x, 1)) ; /* success */ +} diff --git a/src/rigraph/vendor/cs/cs_lusol.c b/src/rigraph/vendor/cs/cs_lusol.c new file mode 100644 index 0000000..e0727e2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_lusol.c @@ -0,0 +1,26 @@ +#include "cs.h" +/* x=A\b where A is unsymmetric; b overwritten with solution */ +CS_INT cs_lusol (CS_INT order, const cs *A, CS_ENTRY *b, double tol) +{ + CS_ENTRY *x ; + css *S ; + csn *N ; + CS_INT n, ok ; + if (!CS_CSC (A) || !b) return (0) ; /* check inputs */ + n = A->n ; + S = cs_sqr (order, A, 0) ; /* ordering and symbolic analysis */ + N = cs_lu (A, S, tol) ; /* numeric LU factorization */ + x = cs_malloc (n, sizeof (CS_ENTRY)) ; /* get workspace */ + ok = (S && N && x) ; + if (ok) + { + cs_ipvec (N->pinv, b, x, n) ; /* x = b(p) */ + cs_lsolve (N->L, x) ; /* x = L\x */ + cs_usolve (N->U, x) ; /* x = U\x */ + cs_ipvec (S->q, x, b, n) ; /* b(q) = x */ + } + cs_free (x) ; + cs_sfree (S) ; + cs_nfree (N) ; + return (ok) ; +} diff --git a/src/rigraph/vendor/cs/cs_malloc.c b/src/rigraph/vendor/cs/cs_malloc.c new file mode 100644 index 0000000..2a3f6da --- /dev/null +++ b/src/rigraph/vendor/cs/cs_malloc.c @@ -0,0 +1,35 @@ +#include "cs.h" +#ifdef MATLAB_MEX_FILE +#define malloc mxMalloc +#define free mxFree +#define realloc mxRealloc +#define calloc mxCalloc +#endif + +/* wrapper for malloc */ +void *cs_malloc (CS_INT n, size_t size) +{ + return (malloc (CS_MAX (n,1) * size)) ; +} + +/* wrapper for calloc */ +void *cs_calloc (CS_INT n, size_t size) +{ + return (calloc (CS_MAX (n,1), size)) ; +} + +/* wrapper for free */ +void *cs_free (void *p) +{ + if (p) free (p) ; /* free p if it is not already NULL */ + return (NULL) ; /* return NULL to simplify the use of cs_free */ +} + +/* wrapper for realloc */ +void *cs_realloc (void *p, CS_INT n, size_t size, CS_INT *ok) +{ + void *pnew ; + pnew = realloc (p, CS_MAX (n,1) * size) ; /* realloc the block */ + *ok = (pnew != NULL) ; /* realloc fails if pnew is NULL */ + return ((*ok) ? pnew : p) ; /* return original p if failure */ +} diff --git a/src/rigraph/vendor/cs/cs_maxtrans.c b/src/rigraph/vendor/cs/cs_maxtrans.c new file mode 100644 index 0000000..4947cee --- /dev/null +++ b/src/rigraph/vendor/cs/cs_maxtrans.c @@ -0,0 +1,92 @@ +#include "cs.h" +/* find an augmenting path starting at column k and extend the match if found */ +static void cs_augment (CS_INT k, const cs *A, CS_INT *jmatch, CS_INT *cheap, CS_INT *w, + CS_INT *js, CS_INT *is, CS_INT *ps) +{ + CS_INT found = 0, p, i = -1, *Ap = A->p, *Ai = A->i, head = 0, j ; + js [0] = k ; /* start with just node k in jstack */ + while (head >= 0) + { + /* --- Start (or continue) depth-first-search at node j ------------- */ + j = js [head] ; /* get j from top of jstack */ + if (w [j] != k) /* 1st time j visited for kth path */ + { + w [j] = k ; /* mark j as visited for kth path */ + for (p = cheap [j] ; p < Ap [j+1] && !found ; p++) + { + i = Ai [p] ; /* try a cheap assignment (i,j) */ + found = (jmatch [i] == -1) ; + } + cheap [j] = p ; /* start here next time j is traversed*/ + if (found) + { + is [head] = i ; /* column j matched with row i */ + break ; /* end of augmenting path */ + } + ps [head] = Ap [j] ; /* no cheap match: start dfs for j */ + } + /* --- Depth-first-search of neighbors of j ------------------------- */ + for (p = ps [head] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; /* consider row i */ + if (w [jmatch [i]] == k) continue ; /* skip jmatch [i] if marked */ + ps [head] = p + 1 ; /* pause dfs of node j */ + is [head] = i ; /* i will be matched with j if found */ + js [++head] = jmatch [i] ; /* start dfs at column jmatch [i] */ + break ; + } + if (p == Ap [j+1]) head-- ; /* node j is done; pop from stack */ + } /* augment the match if path found: */ + if (found) for (p = head ; p >= 0 ; p--) jmatch [is [p]] = js [p] ; +} + +/* find a maximum transveral */ +CS_INT *cs_maxtrans (const cs *A, CS_INT seed) /*[jmatch [0..m-1]; imatch [0..n-1]]*/ +{ + CS_INT i, j, k, n, m, p, n2 = 0, m2 = 0, *Ap, *jimatch, *w, *cheap, *js, *is, + *ps, *Ai, *Cp, *jmatch, *imatch, *q ; + cs *C ; + if (!CS_CSC (A)) return (NULL) ; /* check inputs */ + n = A->n ; m = A->m ; Ap = A->p ; Ai = A->i ; + w = jimatch = cs_calloc (m+n, sizeof (CS_INT)) ; /* allocate result */ + if (!jimatch) return (NULL) ; + for (k = 0, j = 0 ; j < n ; j++) /* count nonempty rows and columns */ + { + n2 += (Ap [j] < Ap [j+1]) ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + w [Ai [p]] = 1 ; + k += (j == Ai [p]) ; /* count entries already on diagonal */ + } + } + if (k == CS_MIN (m,n)) /* quick return if diagonal zero-free */ + { + jmatch = jimatch ; imatch = jimatch + m ; + for (i = 0 ; i < k ; i++) jmatch [i] = i ; + for ( ; i < m ; i++) jmatch [i] = -1 ; + for (j = 0 ; j < k ; j++) imatch [j] = j ; + for ( ; j < n ; j++) imatch [j] = -1 ; + return (cs_idone (jimatch, NULL, NULL, 1)) ; + } + for (i = 0 ; i < m ; i++) m2 += w [i] ; + C = (m2 < n2) ? cs_transpose (A,0) : ((cs *) A) ; /* transpose if needed */ + if (!C) return (cs_idone (jimatch, (m2 < n2) ? C : NULL, NULL, 0)) ; + n = C->n ; m = C->m ; Cp = C->p ; + jmatch = (m2 < n2) ? jimatch + n : jimatch ; + imatch = (m2 < n2) ? jimatch : jimatch + m ; + w = cs_malloc (5*n, sizeof (CS_INT)) ; /* get workspace */ + if (!w) return (cs_idone (jimatch, (m2 < n2) ? C : NULL, w, 0)) ; + cheap = w + n ; js = w + 2*n ; is = w + 3*n ; ps = w + 4*n ; + for (j = 0 ; j < n ; j++) cheap [j] = Cp [j] ; /* for cheap assignment */ + for (j = 0 ; j < n ; j++) w [j] = -1 ; /* all columns unflagged */ + for (i = 0 ; i < m ; i++) jmatch [i] = -1 ; /* nothing matched yet */ + q = cs_randperm (n, seed) ; /* q = random permutation */ + for (k = 0 ; k < n ; k++) /* augment, starting at column q[k] */ + { + cs_augment (q ? q [k]: k, C, jmatch, cheap, w, js, is, ps) ; + } + cs_free (q) ; + for (j = 0 ; j < n ; j++) imatch [j] = -1 ; /* find row match */ + for (i = 0 ; i < m ; i++) if (jmatch [i] >= 0) imatch [jmatch [i]] = i ; + return (cs_idone (jimatch, (m2 < n2) ? C : NULL, w, 1)) ; +} diff --git a/src/rigraph/vendor/cs/cs_multiply.c b/src/rigraph/vendor/cs/cs_multiply.c new file mode 100644 index 0000000..34e3a36 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_multiply.c @@ -0,0 +1,35 @@ +#include "cs.h" +/* C = A*B */ +cs *cs_multiply (const cs *A, const cs *B) +{ + CS_INT p, j, nz = 0, anz, *Cp, *Ci, *Bp, m, n, bnz, *w, values, *Bi ; + CS_ENTRY *x, *Bx, *Cx ; + cs *C ; + if (!CS_CSC (A) || !CS_CSC (B)) return (NULL) ; /* check inputs */ + if (A->n != B->m) return (NULL) ; + m = A->m ; anz = A->p [A->n] ; + n = B->n ; Bp = B->p ; Bi = B->i ; Bx = B->x ; bnz = Bp [n] ; + w = cs_calloc (m, sizeof (CS_INT)) ; /* get workspace */ + values = (A->x != NULL) && (Bx != NULL) ; + x = values ? cs_malloc (m, sizeof (CS_ENTRY)) : NULL ; /* get workspace */ + C = cs_spalloc (m, n, anz + bnz, values, 0) ; /* allocate result */ + if (!C || !w || (values && !x)) return (cs_done (C, w, x, 0)) ; + Cp = C->p ; + for (j = 0 ; j < n ; j++) + { + if (nz + m > C->nzmax && !cs_sprealloc (C, 2*(C->nzmax)+m)) + { + return (cs_done (C, w, x, 0)) ; /* out of memory */ + } + Ci = C->i ; Cx = C->x ; /* C->i and C->x may be reallocated */ + Cp [j] = nz ; /* column j of C starts here */ + for (p = Bp [j] ; p < Bp [j+1] ; p++) + { + nz = cs_scatter (A, Bi [p], Bx ? Bx [p] : 1, w, x, j+1, C, nz) ; + } + if (values) for (p = Cp [j] ; p < nz ; p++) Cx [p] = x [Ci [p]] ; + } + Cp [n] = nz ; /* finalize the last column of C */ + cs_sprealloc (C, 0) ; /* remove extra space from C */ + return (cs_done (C, w, x, 1)) ; /* success; free workspace, return C */ +} diff --git a/src/rigraph/vendor/cs/cs_norm.c b/src/rigraph/vendor/cs/cs_norm.c new file mode 100644 index 0000000..0e7b3c6 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_norm.c @@ -0,0 +1,16 @@ +#include "cs.h" +/* 1-norm of a sparse matrix = max (sum (abs (A))), largest column sum */ +double cs_norm (const cs *A) +{ + CS_INT p, j, n, *Ap ; + CS_ENTRY *Ax ; + double norm = 0, s ; + if (!CS_CSC (A) || !A->x) return (-1) ; /* check inputs */ + n = A->n ; Ap = A->p ; Ax = A->x ; + for (j = 0 ; j < n ; j++) + { + for (s = 0, p = Ap [j] ; p < Ap [j+1] ; p++) s += CS_ABS (Ax [p]) ; + norm = CS_MAX (norm, s) ; + } + return (norm) ; +} diff --git a/src/rigraph/vendor/cs/cs_permute.c b/src/rigraph/vendor/cs/cs_permute.c new file mode 100644 index 0000000..9adae45 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_permute.c @@ -0,0 +1,25 @@ +#include "cs.h" +/* C = A(p,q) where p and q are permutations of 0..m-1 and 0..n-1. */ +cs *cs_permute (const cs *A, const CS_INT *pinv, const CS_INT *q, CS_INT values) +{ + CS_INT t, j, k, nz = 0, m, n, *Ap, *Ai, *Cp, *Ci ; + CS_ENTRY *Cx, *Ax ; + cs *C ; + if (!CS_CSC (A)) return (NULL) ; /* check inputs */ + m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; + C = cs_spalloc (m, n, Ap [n], values && Ax != NULL, 0) ; /* alloc result */ + if (!C) return (cs_done (C, NULL, NULL, 0)) ; /* out of memory */ + Cp = C->p ; Ci = C->i ; Cx = C->x ; + for (k = 0 ; k < n ; k++) + { + Cp [k] = nz ; /* column k of C is column q[k] of A */ + j = q ? (q [k]) : k ; + for (t = Ap [j] ; t < Ap [j+1] ; t++) + { + if (Cx) Cx [nz] = Ax [t] ; /* row i of A is row pinv[i] of C */ + Ci [nz++] = pinv ? (pinv [Ai [t]]) : Ai [t] ; + } + } + Cp [n] = nz ; /* finalize the last column of C */ + return (cs_done (C, NULL, NULL, 1)) ; +} diff --git a/src/rigraph/vendor/cs/cs_pinv.c b/src/rigraph/vendor/cs/cs_pinv.c new file mode 100644 index 0000000..de0660e --- /dev/null +++ b/src/rigraph/vendor/cs/cs_pinv.c @@ -0,0 +1,11 @@ +#include "cs.h" +/* pinv = p', or p = pinv' */ +CS_INT *cs_pinv (CS_INT const *p, CS_INT n) +{ + CS_INT k, *pinv ; + if (!p) return (NULL) ; /* p = NULL denotes identity */ + pinv = cs_malloc (n, sizeof (CS_INT)) ; /* allocate result */ + if (!pinv) return (NULL) ; /* out of memory */ + for (k = 0 ; k < n ; k++) pinv [p [k]] = k ;/* invert the permutation */ + return (pinv) ; /* return result */ +} diff --git a/src/rigraph/vendor/cs/cs_post.c b/src/rigraph/vendor/cs/cs_post.c new file mode 100644 index 0000000..0f61203 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_post.c @@ -0,0 +1,24 @@ +#include "cs.h" +/* post order a forest */ +CS_INT *cs_post (const CS_INT *parent, CS_INT n) +{ + CS_INT j, k = 0, *post, *w, *head, *next, *stack ; + if (!parent) return (NULL) ; /* check inputs */ + post = cs_malloc (n, sizeof (CS_INT)) ; /* allocate result */ + w = cs_malloc (3*n, sizeof (CS_INT)) ; /* get workspace */ + if (!w || !post) return (cs_idone (post, NULL, w, 0)) ; + head = w ; next = w + n ; stack = w + 2*n ; + for (j = 0 ; j < n ; j++) head [j] = -1 ; /* empty linked lists */ + for (j = n-1 ; j >= 0 ; j--) /* traverse nodes in reverse order*/ + { + if (parent [j] == -1) continue ; /* j is a root */ + next [j] = head [parent [j]] ; /* add j to list of its parent */ + head [parent [j]] = j ; + } + for (j = 0 ; j < n ; j++) + { + if (parent [j] != -1) continue ; /* skip j if it is not a root */ + k = cs_tdfs (j, k, head, next, post, stack) ; + } + return (cs_idone (post, NULL, w, 1)) ; /* success; free w, return post */ +} diff --git a/src/rigraph/vendor/cs/cs_print.c b/src/rigraph/vendor/cs/cs_print.c new file mode 100644 index 0000000..7e7b16d --- /dev/null +++ b/src/rigraph/vendor/cs/cs_print.c @@ -0,0 +1,55 @@ +#include "cs.h" +/* print a sparse matrix; use %g for integers to avoid differences with CS_INT */ + +/* Disabled for igraph as it prints to stdio */ +#if 0 +CS_INT cs_print (const cs *A, CS_INT brief) +{ + CS_INT p, j, m, n, nzmax, nz, *Ap, *Ai ; + CS_ENTRY *Ax ; + if (!A) { printf ("(null)\n") ; return (0) ; } + m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; + nzmax = A->nzmax ; nz = A->nz ; + printf ("CXSparse Version %d.%d.%d, %s. %s\n", CS_VER, CS_SUBVER, + CS_SUBSUB, CS_DATE, CS_COPYRIGHT) ; + if (nz < 0) + { + printf ("%g-by-%g, nzmax: %g nnz: %g, 1-norm: %g\n", (double) m, + (double) n, (double) nzmax, (double) (Ap [n]), cs_norm (A)) ; + for (j = 0 ; j < n ; j++) + { + printf (" col %g : locations %g to %g\n", (double) j, + (double) (Ap [j]), (double) (Ap [j+1]-1)) ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + printf (" %g : ", (double) (Ai [p])) ; +#ifdef CS_COMPLEX + printf ("(%g, %g)\n", + Ax ? CS_REAL (Ax [p]) : 1, Ax ? CS_IMAG (Ax [p]) : 0) ; +#else + printf ("%g\n", Ax ? Ax [p] : 1) ; +#endif + if (brief && p > 20) { printf (" ...\n") ; return (1) ; } + } + } + } + else + { + printf ("triplet: %g-by-%g, nzmax: %g nnz: %g\n", (double) m, + (double) n, (double) nzmax, (double) nz) ; + for (p = 0 ; p < nz ; p++) + { + + printf (" %g %g : ", (double) (Ai [p]), (double) (Ap [p])) ; +#ifdef CS_COMPLEX + printf ("(%g, %g)\n", + Ax ? CS_REAL (Ax [p]) : 1, Ax ? CS_IMAG (Ax [p]) : 0) ; +#else + printf ("%g\n", Ax ? Ax [p] : 1) ; +#endif + if (brief && p > 20) { printf (" ...\n") ; return (1) ; } + } + } + return (1) ; +} +#endif diff --git a/src/rigraph/vendor/cs/cs_pvec.c b/src/rigraph/vendor/cs/cs_pvec.c new file mode 100644 index 0000000..1254c2a --- /dev/null +++ b/src/rigraph/vendor/cs/cs_pvec.c @@ -0,0 +1,9 @@ +#include "cs.h" +/* x = b(p), for dense vectors x and b; p=NULL denotes identity */ +CS_INT cs_pvec (const CS_INT *p, const CS_ENTRY *b, CS_ENTRY *x, CS_INT n) +{ + CS_INT k ; + if (!x || !b) return (0) ; /* check inputs */ + for (k = 0 ; k < n ; k++) x [k] = b [p ? p [k] : k] ; + return (1) ; +} diff --git a/src/rigraph/vendor/cs/cs_qr.c b/src/rigraph/vendor/cs/cs_qr.c new file mode 100644 index 0000000..8bce32e --- /dev/null +++ b/src/rigraph/vendor/cs/cs_qr.c @@ -0,0 +1,74 @@ +#include "cs.h" +/* sparse QR factorization [V,beta,pinv,R] = qr (A) */ +csn *cs_qr (const cs *A, const css *S) +{ + CS_ENTRY *Rx, *Vx, *Ax, *x ; + double *Beta ; + CS_INT i, k, p, n, vnz, p1, top, m2, len, col, rnz, *s, *leftmost, *Ap, *Ai, + *parent, *Rp, *Ri, *Vp, *Vi, *w, *pinv, *q ; + cs *R, *V ; + csn *N ; + if (!CS_CSC (A) || !S) return (NULL) ; + n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; + q = S->q ; parent = S->parent ; pinv = S->pinv ; m2 = S->m2 ; + vnz = S->lnz ; rnz = S->unz ; leftmost = S->leftmost ; + w = cs_malloc (m2+n, sizeof (CS_INT)) ; /* get CS_INT workspace */ + x = cs_malloc (m2, sizeof (CS_ENTRY)) ; /* get CS_ENTRY workspace */ + N = cs_calloc (1, sizeof (csn)) ; /* allocate result */ + if (!w || !x || !N) return (cs_ndone (N, NULL, w, x, 0)) ; + s = w + m2 ; /* s is size n */ + for (k = 0 ; k < m2 ; k++) x [k] = 0 ; /* clear workspace x */ + N->L = V = cs_spalloc (m2, n, vnz, 1, 0) ; /* allocate result V */ + N->U = R = cs_spalloc (m2, n, rnz, 1, 0) ; /* allocate result R */ + N->B = Beta = cs_malloc (n, sizeof (double)) ; /* allocate result Beta */ + if (!R || !V || !Beta) return (cs_ndone (N, NULL, w, x, 0)) ; + Rp = R->p ; Ri = R->i ; Rx = R->x ; + Vp = V->p ; Vi = V->i ; Vx = V->x ; + for (i = 0 ; i < m2 ; i++) w [i] = -1 ; /* clear w, to mark nodes */ + rnz = 0 ; vnz = 0 ; + for (k = 0 ; k < n ; k++) /* compute V and R */ + { + Rp [k] = rnz ; /* R(:,k) starts here */ + Vp [k] = p1 = vnz ; /* V(:,k) starts here */ + w [k] = k ; /* add V(k,k) to pattern of V */ + Vi [vnz++] = k ; + top = n ; + col = q ? q [k] : k ; + for (p = Ap [col] ; p < Ap [col+1] ; p++) /* find R(:,k) pattern */ + { + i = leftmost [Ai [p]] ; /* i = min(find(A(i,q))) */ + for (len = 0 ; w [i] != k ; i = parent [i]) /* traverse up to k */ + { + s [len++] = i ; + w [i] = k ; + } + while (len > 0) s [--top] = s [--len] ; /* push path on stack */ + i = pinv [Ai [p]] ; /* i = permuted row of A(:,col) */ + x [i] = Ax [p] ; /* x (i) = A(:,col) */ + if (i > k && w [i] < k) /* pattern of V(:,k) = x (k+1:m) */ + { + Vi [vnz++] = i ; /* add i to pattern of V(:,k) */ + w [i] = k ; + } + } + for (p = top ; p < n ; p++) /* for each i in pattern of R(:,k) */ + { + i = s [p] ; /* R(i,k) is nonzero */ + cs_happly (V, i, Beta [i], x) ; /* apply (V(i),Beta(i)) to x */ + Ri [rnz] = i ; /* R(i,k) = x(i) */ + Rx [rnz++] = x [i] ; + x [i] = 0 ; + if (parent [i] == k) vnz = cs_scatter (V, i, 0, w, NULL, k, V, vnz); + } + for (p = p1 ; p < vnz ; p++) /* gather V(:,k) = x */ + { + Vx [p] = x [Vi [p]] ; + x [Vi [p]] = 0 ; + } + Ri [rnz] = k ; /* R(k,k) = norm (x) */ + Rx [rnz++] = cs_house (Vx+p1, Beta+k, vnz-p1) ; /* [v,beta]=house(x) */ + } + Rp [n] = rnz ; /* finalize R */ + Vp [n] = vnz ; /* finalize V */ + return (cs_ndone (N, NULL, w, x, 1)) ; /* success */ +} diff --git a/src/rigraph/vendor/cs/cs_qrsol.c b/src/rigraph/vendor/cs/cs_qrsol.c new file mode 100644 index 0000000..d817ef2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_qrsol.c @@ -0,0 +1,53 @@ +#include "cs.h" +/* x=A\b where A can be rectangular; b overwritten with solution */ +CS_INT cs_qrsol (CS_INT order, const cs *A, CS_ENTRY *b) +{ + CS_ENTRY *x ; + css *S ; + csn *N ; + cs *AT = NULL ; + CS_INT k, m, n, ok ; + if (!CS_CSC (A) || !b) return (0) ; /* check inputs */ + n = A->n ; + m = A->m ; + if (m >= n) + { + S = cs_sqr (order, A, 1) ; /* ordering and symbolic analysis */ + N = cs_qr (A, S) ; /* numeric QR factorization */ + x = cs_calloc (S ? S->m2 : 1, sizeof (CS_ENTRY)) ; /* get workspace */ + ok = (S && N && x) ; + if (ok) + { + cs_ipvec (S->pinv, b, x, m) ; /* x(0:m-1) = b(p(0:m-1) */ + for (k = 0 ; k < n ; k++) /* apply Householder refl. to x */ + { + cs_happly (N->L, k, N->B [k], x) ; + } + cs_usolve (N->U, x) ; /* x = R\x */ + cs_ipvec (S->q, x, b, n) ; /* b(q(0:n-1)) = x(0:n-1) */ + } + } + else + { + AT = cs_transpose (A, 1) ; /* Ax=b is underdetermined */ + S = cs_sqr (order, AT, 1) ; /* ordering and symbolic analysis */ + N = cs_qr (AT, S) ; /* numeric QR factorization of A' */ + x = cs_calloc (S ? S->m2 : 1, sizeof (CS_ENTRY)) ; /* get workspace */ + ok = (AT && S && N && x) ; + if (ok) + { + cs_pvec (S->q, b, x, m) ; /* x(q(0:m-1)) = b(0:m-1) */ + cs_utsolve (N->U, x) ; /* x = R'\x */ + for (k = m-1 ; k >= 0 ; k--) /* apply Householder refl. to x */ + { + cs_happly (N->L, k, N->B [k], x) ; + } + cs_pvec (S->pinv, x, b, n) ; /* b(0:n-1) = x(p(0:n-1)) */ + } + } + cs_free (x) ; + cs_sfree (S) ; + cs_nfree (N) ; + cs_spfree (AT) ; + return (ok) ; +} diff --git a/src/rigraph/vendor/cs/cs_randperm.c b/src/rigraph/vendor/cs/cs_randperm.c new file mode 100644 index 0000000..4570da0 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_randperm.c @@ -0,0 +1,28 @@ +#include "cs.h" + +#include "igraph_random.h" + +/* return a random permutation vector, the identity perm, or p = n-1:-1:0. + * seed = -1 means p = n-1:-1:0. seed = 0 means p = identity. otherwise + * p = random permutation. */ +CS_INT *cs_randperm (CS_INT n, CS_INT seed) +{ + CS_INT *p, k, j, t ; + if (seed == 0) return (NULL) ; /* return p = NULL (identity) */ + p = cs_malloc (n, sizeof (CS_INT)) ; /* allocate result */ + if (!p) return (NULL) ; /* out of memory */ + for (k = 0 ; k < n ; k++) p [k] = n-k-1 ; + if (seed == -1) return (p) ; /* return reverse permutation */ + /* srand (seed) ; /\* get new random number seed *\/ */ + RNG_BEGIN(); + for (k = 0 ; k < n ; k++) + { + /* j = k + (rand ( ) % (n-k)) ; /\* j = rand CS_INT in range k to n-1 *\/ */ + j = RNG_INTEGER(k, n-1) ; + t = p [j] ; /* swap p[k] and p[j] */ + p [j] = p [k] ; + p [k] = t ; + } + RNG_END(); + return (p) ; +} diff --git a/src/rigraph/vendor/cs/cs_reach.c b/src/rigraph/vendor/cs/cs_reach.c new file mode 100644 index 0000000..0efb342 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_reach.c @@ -0,0 +1,19 @@ +#include "cs.h" +/* xi [top...n-1] = nodes reachable from graph of G*P' via nodes in B(:,k). + * xi [n...2n-1] used as workspace */ +CS_INT cs_reach (cs *G, const cs *B, CS_INT k, CS_INT *xi, const CS_INT *pinv) +{ + CS_INT p, n, top, *Bp, *Bi, *Gp ; + if (!CS_CSC (G) || !CS_CSC (B) || !xi) return (-1) ; /* check inputs */ + n = G->n ; Bp = B->p ; Bi = B->i ; Gp = G->p ; + top = n ; + for (p = Bp [k] ; p < Bp [k+1] ; p++) + { + if (!CS_MARKED (Gp, Bi [p])) /* start a dfs at unmarked node i */ + { + top = cs_dfs (Bi [p], G, top, xi, xi+n, pinv) ; + } + } + for (p = top ; p < n ; p++) CS_MARK (Gp, xi [p]) ; /* restore G */ + return (top) ; +} diff --git a/src/rigraph/vendor/cs/cs_scatter.c b/src/rigraph/vendor/cs/cs_scatter.c new file mode 100644 index 0000000..734fdb2 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_scatter.c @@ -0,0 +1,22 @@ +#include "cs.h" +/* x = x + beta * A(:,j), where x is a dense vector and A(:,j) is sparse */ +CS_INT cs_scatter (const cs *A, CS_INT j, CS_ENTRY beta, CS_INT *w, CS_ENTRY *x, CS_INT mark, + cs *C, CS_INT nz) +{ + CS_INT i, p, *Ap, *Ai, *Ci ; + CS_ENTRY *Ax ; + if (!CS_CSC (A) || !w || !CS_CSC (C)) return (-1) ; /* check inputs */ + Ap = A->p ; Ai = A->i ; Ax = A->x ; Ci = C->i ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; /* A(i,j) is nonzero */ + if (w [i] < mark) + { + w [i] = mark ; /* i is new entry in column j */ + Ci [nz++] = i ; /* add i to pattern of C(:,j) */ + if (x) x [i] = beta * Ax [p] ; /* x(i) = beta*A(i,j) */ + } + else if (x) x [i] += beta * Ax [p] ; /* i exists in C(:,j) already */ + } + return (nz) ; +} diff --git a/src/rigraph/vendor/cs/cs_scc.c b/src/rigraph/vendor/cs/cs_scc.c new file mode 100644 index 0000000..cc6d805 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_scc.c @@ -0,0 +1,41 @@ +#include "cs.h" +/* find the strongly connected components of a square matrix */ +csd *cs_scc (cs *A) /* matrix A temporarily modified, then restored */ +{ + CS_INT n, i, k, b, nb = 0, top, *xi, *pstack, *p, *r, *Ap, *ATp, *rcopy, *Blk ; + cs *AT ; + csd *D ; + if (!CS_CSC (A)) return (NULL) ; /* check inputs */ + n = A->n ; Ap = A->p ; + D = cs_dalloc (n, 0) ; /* allocate result */ + AT = cs_transpose (A, 0) ; /* AT = A' */ + xi = cs_malloc (2*n+1, sizeof (CS_INT)) ; /* get workspace */ + if (!D || !AT || !xi) return (cs_ddone (D, AT, xi, 0)) ; + Blk = xi ; rcopy = pstack = xi + n ; + p = D->p ; r = D->r ; ATp = AT->p ; + top = n ; + for (i = 0 ; i < n ; i++) /* first dfs(A) to find finish times (xi) */ + { + if (!CS_MARKED (Ap, i)) top = cs_dfs (i, A, top, xi, pstack, NULL) ; + } + for (i = 0 ; i < n ; i++) CS_MARK (Ap, i) ; /* restore A; unmark all nodes*/ + top = n ; + nb = n ; + for (k = 0 ; k < n ; k++) /* dfs(A') to find strongly connnected comp */ + { + i = xi [k] ; /* get i in reverse order of finish times */ + if (CS_MARKED (ATp, i)) continue ; /* skip node i if already ordered */ + r [nb--] = top ; /* node i is the start of a component in p */ + top = cs_dfs (i, AT, top, p, pstack, NULL) ; + } + r [nb] = 0 ; /* first block starts at zero; shift r up */ + for (k = nb ; k <= n ; k++) r [k-nb] = r [k] ; + D->nb = nb = n-nb ; /* nb = # of strongly connected components */ + for (b = 0 ; b < nb ; b++) /* sort each block in natural order */ + { + for (k = r [b] ; k < r [b+1] ; k++) Blk [p [k]] = b ; + } + for (b = 0 ; b <= nb ; b++) rcopy [b] = r [b] ; + for (i = 0 ; i < n ; i++) p [rcopy [Blk [i]]++] = i ; + return (cs_ddone (D, AT, xi, 1)) ; +} diff --git a/src/rigraph/vendor/cs/cs_schol.c b/src/rigraph/vendor/cs/cs_schol.c new file mode 100644 index 0000000..7da2a57 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_schol.c @@ -0,0 +1,26 @@ +#include "cs.h" +/* ordering and symbolic analysis for a Cholesky factorization */ +css *cs_schol (CS_INT order, const cs *A) +{ + CS_INT n, *c, *post, *P ; + cs *C ; + css *S ; + if (!CS_CSC (A)) return (NULL) ; /* check inputs */ + n = A->n ; + S = cs_calloc (1, sizeof (css)) ; /* allocate result S */ + if (!S) return (NULL) ; /* out of memory */ + P = cs_amd (order, A) ; /* P = amd(A+A'), or natural */ + S->pinv = cs_pinv (P, n) ; /* find inverse permutation */ + cs_free (P) ; + if (order && !S->pinv) return (cs_sfree (S)) ; + C = cs_symperm (A, S->pinv, 0) ; /* C = spones(triu(A(P,P))) */ + S->parent = cs_etree (C, 0) ; /* find etree of C */ + post = cs_post (S->parent, n) ; /* postorder the etree */ + c = cs_counts (C, S->parent, post, 0) ; /* find column counts of chol(C) */ + cs_free (post) ; + cs_spfree (C) ; + S->cp = cs_malloc (n+1, sizeof (CS_INT)) ; /* allocate result S->cp */ + S->unz = S->lnz = cs_cumsum (S->cp, c, n) ; /* find column pointers for L */ + cs_free (c) ; + return ((S->lnz >= 0) ? S : cs_sfree (S)) ; +} diff --git a/src/rigraph/vendor/cs/cs_spsolve.c b/src/rigraph/vendor/cs/cs_spsolve.c new file mode 100644 index 0000000..8c6ecce --- /dev/null +++ b/src/rigraph/vendor/cs/cs_spsolve.c @@ -0,0 +1,28 @@ +#include "cs.h" +/* solve Gx=b(:,k), where G is either upper (lo=0) or lower (lo=1) triangular */ +CS_INT cs_spsolve (cs *G, const cs *B, CS_INT k, CS_INT *xi, CS_ENTRY *x, const CS_INT *pinv, + CS_INT lo) +{ + CS_INT j, J, p, q, px, top, n, *Gp, *Gi, *Bp, *Bi ; + CS_ENTRY *Gx, *Bx ; + if (!CS_CSC (G) || !CS_CSC (B) || !xi || !x) return (-1) ; + Gp = G->p ; Gi = G->i ; Gx = G->x ; n = G->n ; + Bp = B->p ; Bi = B->i ; Bx = B->x ; + top = cs_reach (G, B, k, xi, pinv) ; /* xi[top..n-1]=Reach(B(:,k)) */ + for (p = top ; p < n ; p++) x [xi [p]] = 0 ; /* clear x */ + for (p = Bp [k] ; p < Bp [k+1] ; p++) x [Bi [p]] = Bx [p] ; /* scatter B */ + for (px = top ; px < n ; px++) + { + j = xi [px] ; /* x(j) is nonzero */ + J = pinv ? (pinv [j]) : j ; /* j maps to col J of G */ + if (J < 0) continue ; /* column J is empty */ + x [j] /= Gx [lo ? (Gp [J]) : (Gp [J+1]-1)] ;/* x(j) /= G(j,j) */ + p = lo ? (Gp [J]+1) : (Gp [J]) ; /* lo: L(j,j) 1st entry */ + q = lo ? (Gp [J+1]) : (Gp [J+1]-1) ; /* up: U(j,j) last entry */ + for ( ; p < q ; p++) + { + x [Gi [p]] -= Gx [p] * x [j] ; /* x(i) -= G(i,j) * x(j) */ + } + } + return (top) ; /* return top of stack */ +} diff --git a/src/rigraph/vendor/cs/cs_sqr.c b/src/rigraph/vendor/cs/cs_sqr.c new file mode 100644 index 0000000..1b14ca4 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_sqr.c @@ -0,0 +1,87 @@ +#include "cs.h" +/* compute nnz(V) = S->lnz, S->pinv, S->leftmost, S->m2 from A and S->parent */ +static CS_INT cs_vcount (const cs *A, css *S) +{ + CS_INT i, k, p, pa, n = A->n, m = A->m, *Ap = A->p, *Ai = A->i, *next, *head, + *tail, *nque, *pinv, *leftmost, *w, *parent = S->parent ; + S->pinv = pinv = cs_malloc (m+n, sizeof (CS_INT)) ; /* allocate pinv, */ + S->leftmost = leftmost = cs_malloc (m, sizeof (CS_INT)) ; /* and leftmost */ + w = cs_malloc (m+3*n, sizeof (CS_INT)) ; /* get workspace */ + if (!pinv || !w || !leftmost) + { + cs_free (w) ; /* pinv and leftmost freed later */ + return (0) ; /* out of memory */ + } + next = w ; head = w + m ; tail = w + m + n ; nque = w + m + 2*n ; + for (k = 0 ; k < n ; k++) head [k] = -1 ; /* queue k is empty */ + for (k = 0 ; k < n ; k++) tail [k] = -1 ; + for (k = 0 ; k < n ; k++) nque [k] = 0 ; + for (i = 0 ; i < m ; i++) leftmost [i] = -1 ; + for (k = n-1 ; k >= 0 ; k--) + { + for (p = Ap [k] ; p < Ap [k+1] ; p++) + { + leftmost [Ai [p]] = k ; /* leftmost[i] = min(find(A(i,:)))*/ + } + } + for (i = m-1 ; i >= 0 ; i--) /* scan rows in reverse order */ + { + pinv [i] = -1 ; /* row i is not yet ordered */ + k = leftmost [i] ; + if (k == -1) continue ; /* row i is empty */ + if (nque [k]++ == 0) tail [k] = i ; /* first row in queue k */ + next [i] = head [k] ; /* put i at head of queue k */ + head [k] = i ; + } + S->lnz = 0 ; + S->m2 = m ; + for (k = 0 ; k < n ; k++) /* find row permutation and nnz(V)*/ + { + i = head [k] ; /* remove row i from queue k */ + S->lnz++ ; /* count V(k,k) as nonzero */ + if (i < 0) i = S->m2++ ; /* add a fictitious row */ + pinv [i] = k ; /* associate row i with V(:,k) */ + if (--nque [k] <= 0) continue ; /* skip if V(k+1:m,k) is empty */ + S->lnz += nque [k] ; /* nque [k] is nnz (V(k+1:m,k)) */ + if ((pa = parent [k]) != -1) /* move all rows to parent of k */ + { + if (nque [pa] == 0) tail [pa] = tail [k] ; + next [tail [k]] = head [pa] ; + head [pa] = next [i] ; + nque [pa] += nque [k] ; + } + } + for (i = 0 ; i < m ; i++) if (pinv [i] < 0) pinv [i] = k++ ; + cs_free (w) ; + return (1) ; +} + +/* symbolic ordering and analysis for QR or LU */ +css *cs_sqr (CS_INT order, const cs *A, CS_INT qr) +{ + CS_INT n, k, ok = 1, *post ; + css *S ; + if (!CS_CSC (A)) return (NULL) ; /* check inputs */ + n = A->n ; + S = cs_calloc (1, sizeof (css)) ; /* allocate result S */ + if (!S) return (NULL) ; /* out of memory */ + S->q = cs_amd (order, A) ; /* fill-reducing ordering */ + if (order && !S->q) return (cs_sfree (S)) ; + if (qr) /* QR symbolic analysis */ + { + cs *C = order ? cs_permute (A, NULL, S->q, 0) : ((cs *) A) ; + S->parent = cs_etree (C, 1) ; /* etree of C'*C, where C=A(:,q) */ + post = cs_post (S->parent, n) ; + S->cp = cs_counts (C, S->parent, post, 1) ; /* col counts chol(C'*C) */ + cs_free (post) ; + ok = C && S->parent && S->cp && cs_vcount (C, S) ; + if (ok) for (S->unz = 0, k = 0 ; k < n ; k++) S->unz += S->cp [k] ; + if (order) cs_spfree (C) ; + } + else + { + S->unz = 4*(A->p [n]) + n ; /* for LU factorization only, */ + S->lnz = S->unz ; /* guess nnz(L) and nnz(U) */ + } + return (ok ? S : cs_sfree (S)) ; /* return result S */ +} diff --git a/src/rigraph/vendor/cs/cs_symperm.c b/src/rigraph/vendor/cs/cs_symperm.c new file mode 100644 index 0000000..7bbd6fe --- /dev/null +++ b/src/rigraph/vendor/cs/cs_symperm.c @@ -0,0 +1,39 @@ +#include "cs.h" +/* C = A(p,p) where A and C are symmetric the upper part stored; pinv not p */ +cs *cs_symperm (const cs *A, const CS_INT *pinv, CS_INT values) +{ + CS_INT i, j, p, q, i2, j2, n, *Ap, *Ai, *Cp, *Ci, *w ; + CS_ENTRY *Cx, *Ax ; + cs *C ; + if (!CS_CSC (A)) return (NULL) ; /* check inputs */ + n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; + C = cs_spalloc (n, n, Ap [n], values && (Ax != NULL), 0) ; /* alloc result*/ + w = cs_calloc (n, sizeof (CS_INT)) ; /* get workspace */ + if (!C || !w) return (cs_done (C, w, NULL, 0)) ; /* out of memory */ + Cp = C->p ; Ci = C->i ; Cx = C->x ; + for (j = 0 ; j < n ; j++) /* count entries in each column of C */ + { + j2 = pinv ? pinv [j] : j ; /* column j of A is column j2 of C */ + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; + if (i > j) continue ; /* skip lower triangular part of A */ + i2 = pinv ? pinv [i] : i ; /* row i of A is row i2 of C */ + w [CS_MAX (i2, j2)]++ ; /* column count of C */ + } + } + cs_cumsum (Cp, w, n) ; /* compute column pointers of C */ + for (j = 0 ; j < n ; j++) + { + j2 = pinv ? pinv [j] : j ; /* column j of A is column j2 of C */ + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; + if (i > j) continue ; /* skip lower triangular part of A*/ + i2 = pinv ? pinv [i] : i ; /* row i of A is row i2 of C */ + Ci [q = w [CS_MAX (i2, j2)]++] = CS_MIN (i2, j2) ; + if (Cx) Cx [q] = (i2 <= j2) ? Ax [p] : CS_CONJ (Ax [p]) ; + } + } + return (cs_done (C, w, NULL, 1)) ; /* success; free workspace, return C */ +} diff --git a/src/rigraph/vendor/cs/cs_tdfs.c b/src/rigraph/vendor/cs/cs_tdfs.c new file mode 100644 index 0000000..f32077b --- /dev/null +++ b/src/rigraph/vendor/cs/cs_tdfs.c @@ -0,0 +1,24 @@ +#include "cs.h" +/* depth-first search and postorder of a tree rooted at node j */ +CS_INT cs_tdfs (CS_INT j, CS_INT k, CS_INT *head, const CS_INT *next, CS_INT *post, CS_INT *stack) +{ + CS_INT i, p, top = 0 ; + if (!head || !next || !post || !stack) return (-1) ; /* check inputs */ + stack [0] = j ; /* place j on the stack */ + while (top >= 0) /* while (stack is not empty) */ + { + p = stack [top] ; /* p = top of stack */ + i = head [p] ; /* i = youngest child of p */ + if (i == -1) + { + top-- ; /* p has no unordered children left */ + post [k++] = p ; /* node p is the kth postordered node */ + } + else + { + head [p] = next [i] ; /* remove i from children of p */ + stack [++top] = i ; /* start dfs on child node i */ + } + } + return (k) ; +} diff --git a/src/rigraph/vendor/cs/cs_transpose.c b/src/rigraph/vendor/cs/cs_transpose.c new file mode 100644 index 0000000..15a6c00 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_transpose.c @@ -0,0 +1,25 @@ +#include "cs.h" +/* C = A' */ +cs *cs_transpose (const cs *A, CS_INT values) +{ + CS_INT p, q, j, *Cp, *Ci, n, m, *Ap, *Ai, *w ; + CS_ENTRY *Cx, *Ax ; + cs *C ; + if (!CS_CSC (A)) return (NULL) ; /* check inputs */ + m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; + C = cs_spalloc (n, m, Ap [n], values && Ax, 0) ; /* allocate result */ + w = cs_calloc (m, sizeof (CS_INT)) ; /* get workspace */ + if (!C || !w) return (cs_done (C, w, NULL, 0)) ; /* out of memory */ + Cp = C->p ; Ci = C->i ; Cx = C->x ; + for (p = 0 ; p < Ap [n] ; p++) w [Ai [p]]++ ; /* row counts */ + cs_cumsum (Cp, w, m) ; /* row pointers */ + for (j = 0 ; j < n ; j++) + { + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + Ci [q = w [Ai [p]]++] = j ; /* place A(i,j) as entry C(j,i) */ + if (Cx) Cx [q] = (values > 0) ? CS_CONJ (Ax [p]) : Ax [p] ; + } + } + return (cs_done (C, w, NULL, 1)) ; /* success; free w and return C */ +} diff --git a/src/rigraph/vendor/cs/cs_updown.c b/src/rigraph/vendor/cs/cs_updown.c new file mode 100644 index 0000000..e49af83 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_updown.c @@ -0,0 +1,48 @@ +#include "cs.h" +/* sparse Cholesky update/downdate, L*L' + sigma*w*w' (sigma = +1 or -1) */ +CS_INT cs_updown (cs *L, CS_INT sigma, const cs *C, const CS_INT *parent) +{ + CS_INT n, p, f, j, *Lp, *Li, *Cp, *Ci ; + CS_ENTRY *Lx, *Cx, alpha, gamma, w1, w2, *w ; + double beta = 1, beta2 = 1, delta ; +#ifdef CS_COMPLEX + cs_complex_t phase ; +#endif + if (!CS_CSC (L) || !CS_CSC (C) || !parent) return (0) ; /* check inputs */ + Lp = L->p ; Li = L->i ; Lx = L->x ; n = L->n ; + Cp = C->p ; Ci = C->i ; Cx = C->x ; + if ((p = Cp [0]) >= Cp [1]) return (1) ; /* return if C empty */ + w = cs_malloc (n, sizeof (CS_ENTRY)) ; /* get workspace */ + if (!w) return (0) ; /* out of memory */ + f = Ci [p] ; + for ( ; p < Cp [1] ; p++) f = CS_MIN (f, Ci [p]) ; /* f = min (find (C)) */ + for (j = f ; j != -1 ; j = parent [j]) w [j] = 0 ; /* clear workspace w */ + for (p = Cp [0] ; p < Cp [1] ; p++) w [Ci [p]] = Cx [p] ; /* w = C */ + for (j = f ; j != -1 ; j = parent [j]) /* walk path f up to root */ + { + p = Lp [j] ; + alpha = w [j] / Lx [p] ; /* alpha = w(j) / L(j,j) */ + beta2 = beta*beta + sigma*alpha*CS_CONJ(alpha) ; + if (beta2 <= 0) break ; /* not positive definite */ + beta2 = sqrt (beta2) ; + delta = (sigma > 0) ? (beta / beta2) : (beta2 / beta) ; + gamma = sigma * CS_CONJ(alpha) / (beta2 * beta) ; + Lx [p] = delta * Lx [p] + ((sigma > 0) ? (gamma * w [j]) : 0) ; + beta = beta2 ; +#ifdef CS_COMPLEX + phase = CS_ABS (Lx [p]) / Lx [p] ; /* phase = abs(L(j,j))/L(j,j)*/ + Lx [p] *= phase ; /* L(j,j) = L(j,j) * phase */ +#endif + for (p++ ; p < Lp [j+1] ; p++) + { + w1 = w [Li [p]] ; + w [Li [p]] = w2 = w1 - alpha * Lx [p] ; + Lx [p] = delta * Lx [p] + gamma * ((sigma > 0) ? w1 : w2) ; +#ifdef CS_COMPLEX + Lx [p] *= phase ; /* L(i,j) = L(i,j) * phase */ +#endif + } + } + cs_free (w) ; + return (beta2 > 0) ; +} diff --git a/src/rigraph/vendor/cs/cs_usolve.c b/src/rigraph/vendor/cs/cs_usolve.c new file mode 100644 index 0000000..6e89c83 --- /dev/null +++ b/src/rigraph/vendor/cs/cs_usolve.c @@ -0,0 +1,18 @@ +#include "cs.h" +/* solve Ux=b where x and b are dense. x=b on input, solution on output. */ +CS_INT cs_usolve (const cs *U, CS_ENTRY *x) +{ + CS_INT p, j, n, *Up, *Ui ; + CS_ENTRY *Ux ; + if (!CS_CSC (U) || !x) return (0) ; /* check inputs */ + n = U->n ; Up = U->p ; Ui = U->i ; Ux = U->x ; + for (j = n-1 ; j >= 0 ; j--) + { + x [j] /= Ux [Up [j+1]-1] ; + for (p = Up [j] ; p < Up [j+1]-1 ; p++) + { + x [Ui [p]] -= Ux [p] * x [j] ; + } + } + return (1) ; +} diff --git a/src/rigraph/vendor/cs/cs_util.c b/src/rigraph/vendor/cs/cs_util.c new file mode 100644 index 0000000..bb887ea --- /dev/null +++ b/src/rigraph/vendor/cs/cs_util.c @@ -0,0 +1,120 @@ +#include "cs.h" +/* allocate a sparse matrix (triplet form or compressed-column form) */ +cs *cs_spalloc (CS_INT m, CS_INT n, CS_INT nzmax, CS_INT values, CS_INT triplet) +{ + cs *A = cs_calloc (1, sizeof (cs)) ; /* allocate the cs struct */ + if (!A) return (NULL) ; /* out of memory */ + A->m = m ; /* define dimensions and nzmax */ + A->n = n ; + A->nzmax = nzmax = CS_MAX (nzmax, 1) ; + A->nz = triplet ? 0 : -1 ; /* allocate triplet or comp.col */ + A->p = cs_malloc (triplet ? nzmax : n+1, sizeof (CS_INT)) ; + A->i = cs_malloc (nzmax, sizeof (CS_INT)) ; + A->x = values ? cs_malloc (nzmax, sizeof (CS_ENTRY)) : NULL ; + return ((!A->p || !A->i || (values && !A->x)) ? cs_spfree (A) : A) ; +} + +/* change the max # of entries sparse matrix */ +CS_INT cs_sprealloc (cs *A, CS_INT nzmax) +{ + CS_INT ok, oki, okj = 1, okx = 1 ; + if (!A) return (0) ; + if (nzmax <= 0) nzmax = (CS_CSC (A)) ? (A->p [A->n]) : A->nz ; + nzmax = CS_MAX (nzmax, 1) ; + A->i = cs_realloc (A->i, nzmax, sizeof (CS_INT), &oki) ; + if (CS_TRIPLET (A)) A->p = cs_realloc (A->p, nzmax, sizeof (CS_INT), &okj) ; + if (A->x) A->x = cs_realloc (A->x, nzmax, sizeof (CS_ENTRY), &okx) ; + ok = (oki && okj && okx) ; + if (ok) A->nzmax = nzmax ; + return (ok) ; +} + +/* free a sparse matrix */ +cs *cs_spfree (cs *A) +{ + if (!A) return (NULL) ; /* do nothing if A already NULL */ + cs_free (A->p) ; + cs_free (A->i) ; + cs_free (A->x) ; + return ((cs *) cs_free (A)) ; /* free the cs struct and return NULL */ +} + +/* free a numeric factorization */ +csn *cs_nfree (csn *N) +{ + if (!N) return (NULL) ; /* do nothing if N already NULL */ + cs_spfree (N->L) ; + cs_spfree (N->U) ; + cs_free (N->pinv) ; + cs_free (N->B) ; + return ((csn *) cs_free (N)) ; /* free the csn struct and return NULL */ +} + +/* free a symbolic factorization */ +css *cs_sfree (css *S) +{ + if (!S) return (NULL) ; /* do nothing if S already NULL */ + cs_free (S->pinv) ; + cs_free (S->q) ; + cs_free (S->parent) ; + cs_free (S->cp) ; + cs_free (S->leftmost) ; + return ((css *) cs_free (S)) ; /* free the css struct and return NULL */ +} + +/* allocate a cs_dmperm or cs_scc result */ +csd *cs_dalloc (CS_INT m, CS_INT n) +{ + csd *D ; + D = cs_calloc (1, sizeof (csd)) ; + if (!D) return (NULL) ; + D->p = cs_malloc (m, sizeof (CS_INT)) ; + D->r = cs_malloc (m+6, sizeof (CS_INT)) ; + D->q = cs_malloc (n, sizeof (CS_INT)) ; + D->s = cs_malloc (n+6, sizeof (CS_INT)) ; + return ((!D->p || !D->r || !D->q || !D->s) ? cs_dfree (D) : D) ; +} + +/* free a cs_dmperm or cs_scc result */ +csd *cs_dfree (csd *D) +{ + if (!D) return (NULL) ; /* do nothing if D already NULL */ + cs_free (D->p) ; + cs_free (D->q) ; + cs_free (D->r) ; + cs_free (D->s) ; + return ((csd *) cs_free (D)) ; /* free the csd struct and return NULL */ +} + +/* free workspace and return a sparse matrix result */ +cs *cs_done (cs *C, void *w, void *x, CS_INT ok) +{ + cs_free (w) ; /* free workspace */ + cs_free (x) ; + return (ok ? C : cs_spfree (C)) ; /* return result if OK, else free it */ +} + +/* free workspace and return CS_INT array result */ +CS_INT *cs_idone (CS_INT *p, cs *C, void *w, CS_INT ok) +{ + cs_spfree (C) ; /* free temporary matrix */ + cs_free (w) ; /* free workspace */ + return (ok ? p : (CS_INT *) cs_free (p)) ; /* return result, or free it */ +} + +/* free workspace and return a numeric factorization (Cholesky, LU, or QR) */ +csn *cs_ndone (csn *N, cs *C, void *w, void *x, CS_INT ok) +{ + cs_spfree (C) ; /* free temporary matrix */ + cs_free (w) ; /* free workspace */ + cs_free (x) ; + return (ok ? N : cs_nfree (N)) ; /* return result if OK, else free it */ +} + +/* free workspace and return a csd result */ +csd *cs_ddone (csd *D, cs *C, void *w, CS_INT ok) +{ + cs_spfree (C) ; /* free temporary matrix */ + cs_free (w) ; /* free workspace */ + return (ok ? D : cs_dfree (D)) ; /* return result if OK, else free it */ +} diff --git a/src/rigraph/vendor/cs/cs_utsolve.c b/src/rigraph/vendor/cs/cs_utsolve.c new file mode 100644 index 0000000..d879fae --- /dev/null +++ b/src/rigraph/vendor/cs/cs_utsolve.c @@ -0,0 +1,18 @@ +#include "cs.h" +/* solve U'x=b where x and b are dense. x=b on input, solution on output. */ +CS_INT cs_utsolve (const cs *U, CS_ENTRY *x) +{ + CS_INT p, j, n, *Up, *Ui ; + CS_ENTRY *Ux ; + if (!CS_CSC (U) || !x) return (0) ; /* check inputs */ + n = U->n ; Up = U->p ; Ui = U->i ; Ux = U->x ; + for (j = 0 ; j < n ; j++) + { + for (p = Up [j] ; p < Up [j+1]-1 ; p++) + { + x [j] -= CS_CONJ (Ux [p]) * x [Ui [p]] ; + } + x [j] /= CS_CONJ (Ux [Up [j+1]-1]) ; + } + return (1) ; +} diff --git a/src/rigraph/vendor/mini-gmp/mini-gmp.c b/src/rigraph/vendor/mini-gmp/mini-gmp.c new file mode 100644 index 0000000..76a691b --- /dev/null +++ b/src/rigraph/vendor/mini-gmp/mini-gmp.c @@ -0,0 +1,4578 @@ +/* mini-gmp, a minimalistic implementation of a GNU GMP subset. + + Contributed to the GNU project by Niels Möller + +Copyright 1991-1997, 1999-2019 Free Software Foundation, Inc. + +This file is part of the GNU MP Library. + +The GNU MP Library is free software; you can redistribute it and/or modify +it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + +or + + * the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any + later version. + +or both in parallel, as here. + +The GNU MP Library 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 copies of the GNU General Public License and the +GNU Lesser General Public License along with the GNU MP Library. If not, +see https://www.gnu.org/licenses/. */ + +/* NOTE: All functions in this file which are not declared in + mini-gmp.h are internal, and are not intended to be compatible + with GMP or with future versions of mini-gmp. */ + +/* Much of the material copied from GMP files, including: gmp-impl.h, + longlong.h, mpn/generic/add_n.c, mpn/generic/addmul_1.c, + mpn/generic/lshift.c, mpn/generic/mul_1.c, + mpn/generic/mul_basecase.c, mpn/generic/rshift.c, + mpn/generic/sbpi1_div_qr.c, mpn/generic/sub_n.c, + mpn/generic/submul_1.c. */ + +#include +#include +#include +#include +#include +#include + +#include "mini-gmp.h" + +#if !defined(MINI_GMP_DONT_USE_FLOAT_H) +#include +#endif + +#include "igraph_error.h" + + +/* Macros */ +#define GMP_LIMB_BITS (sizeof(mp_limb_t) * CHAR_BIT) + +#define GMP_LIMB_MAX ((mp_limb_t) ~ (mp_limb_t) 0) +#define GMP_LIMB_HIGHBIT ((mp_limb_t) 1 << (GMP_LIMB_BITS - 1)) + +#define GMP_HLIMB_BIT ((mp_limb_t) 1 << (GMP_LIMB_BITS / 2)) +#define GMP_LLIMB_MASK (GMP_HLIMB_BIT - 1) + +#define GMP_ULONG_BITS (sizeof(unsigned long) * CHAR_BIT) +#define GMP_ULONG_HIGHBIT ((unsigned long) 1 << (GMP_ULONG_BITS - 1)) + +#define GMP_ABS(x) ((x) >= 0 ? (x) : -(x)) +#define GMP_NEG_CAST(T,x) (-((T)((x) + 1) - 1)) + +#define GMP_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define GMP_MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define GMP_CMP(a,b) (((a) > (b)) - ((a) < (b))) + +#if defined(DBL_MANT_DIG) && FLT_RADIX == 2 +#define GMP_DBL_MANT_BITS DBL_MANT_DIG +#else +#define GMP_DBL_MANT_BITS (53) +#endif + +/* Return non-zero if xp,xsize and yp,ysize overlap. + If xp+xsize<=yp there's no overlap, or if yp+ysize<=xp there's no + overlap. If both these are false, there's an overlap. */ +#define GMP_MPN_OVERLAP_P(xp, xsize, yp, ysize) \ + ((xp) + (xsize) > (yp) && (yp) + (ysize) > (xp)) + +#define gmp_assert_nocarry(x) do { \ + mp_limb_t __cy = (x); \ + assert (__cy == 0); \ + } while (0) + +#define gmp_clz(count, x) do { \ + mp_limb_t __clz_x = (x); \ + unsigned __clz_c = 0; \ + int LOCAL_SHIFT_BITS = 8; \ + if (GMP_LIMB_BITS > LOCAL_SHIFT_BITS) \ + for (; \ + (__clz_x & ((mp_limb_t) 0xff << (GMP_LIMB_BITS - 8))) == 0; \ + __clz_c += 8) \ + { __clz_x <<= LOCAL_SHIFT_BITS; } \ + for (; (__clz_x & GMP_LIMB_HIGHBIT) == 0; __clz_c++) \ + __clz_x <<= 1; \ + (count) = __clz_c; \ + } while (0) + +#define gmp_ctz(count, x) do { \ + mp_limb_t __ctz_x = (x); \ + unsigned __ctz_c = 0; \ + gmp_clz (__ctz_c, __ctz_x & - __ctz_x); \ + (count) = GMP_LIMB_BITS - 1 - __ctz_c; \ + } while (0) + +#define gmp_add_ssaaaa(sh, sl, ah, al, bh, bl) \ + do { \ + mp_limb_t __x; \ + __x = (al) + (bl); \ + (sh) = (ah) + (bh) + (__x < (al)); \ + (sl) = __x; \ + } while (0) + +#define gmp_sub_ddmmss(sh, sl, ah, al, bh, bl) \ + do { \ + mp_limb_t __x; \ + __x = (al) - (bl); \ + (sh) = (ah) - (bh) - ((al) < (bl)); \ + (sl) = __x; \ + } while (0) + +#define gmp_umul_ppmm(w1, w0, u, v) \ + do { \ + int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; \ + if (sizeof(unsigned int) * CHAR_BIT >= 2 * GMP_LIMB_BITS) \ + { \ + unsigned int __ww = (unsigned int) (u) * (v); \ + w0 = (mp_limb_t) __ww; \ + w1 = (mp_limb_t) (__ww >> LOCAL_GMP_LIMB_BITS); \ + } \ + else if (GMP_ULONG_BITS >= 2 * GMP_LIMB_BITS) \ + { \ + unsigned long int __ww = (unsigned long int) (u) * (v); \ + w0 = (mp_limb_t) __ww; \ + w1 = (mp_limb_t) (__ww >> LOCAL_GMP_LIMB_BITS); \ + } \ + else { \ + mp_limb_t __x0, __x1, __x2, __x3; \ + unsigned __ul, __vl, __uh, __vh; \ + mp_limb_t __u = (u), __v = (v); \ + \ + __ul = __u & GMP_LLIMB_MASK; \ + __uh = __u >> (GMP_LIMB_BITS / 2); \ + __vl = __v & GMP_LLIMB_MASK; \ + __vh = __v >> (GMP_LIMB_BITS / 2); \ + \ + __x0 = (mp_limb_t) __ul * __vl; \ + __x1 = (mp_limb_t) __ul * __vh; \ + __x2 = (mp_limb_t) __uh * __vl; \ + __x3 = (mp_limb_t) __uh * __vh; \ + \ + __x1 += __x0 >> (GMP_LIMB_BITS / 2);/* this can't give carry */ \ + __x1 += __x2; /* but this indeed can */ \ + if (__x1 < __x2) /* did we get it? */ \ + __x3 += GMP_HLIMB_BIT; /* yes, add it in the proper pos. */ \ + \ + (w1) = __x3 + (__x1 >> (GMP_LIMB_BITS / 2)); \ + (w0) = (__x1 << (GMP_LIMB_BITS / 2)) + (__x0 & GMP_LLIMB_MASK); \ + } \ + } while (0) + +#define gmp_udiv_qrnnd_preinv(q, r, nh, nl, d, di) \ + do { \ + mp_limb_t _qh, _ql, _r, _mask; \ + gmp_umul_ppmm (_qh, _ql, (nh), (di)); \ + gmp_add_ssaaaa (_qh, _ql, _qh, _ql, (nh) + 1, (nl)); \ + _r = (nl) - _qh * (d); \ + _mask = -(mp_limb_t) (_r > _ql); /* both > and >= are OK */ \ + _qh += _mask; \ + _r += _mask & (d); \ + if (_r >= (d)) \ + { \ + _r -= (d); \ + _qh++; \ + } \ + \ + (r) = _r; \ + (q) = _qh; \ + } while (0) + +#define gmp_udiv_qr_3by2(q, r1, r0, n2, n1, n0, d1, d0, dinv) \ + do { \ + mp_limb_t _q0, _t1, _t0, _mask; \ + gmp_umul_ppmm ((q), _q0, (n2), (dinv)); \ + gmp_add_ssaaaa ((q), _q0, (q), _q0, (n2), (n1)); \ + \ + /* Compute the two most significant limbs of n - q'd */ \ + (r1) = (n1) - (d1) * (q); \ + gmp_sub_ddmmss ((r1), (r0), (r1), (n0), (d1), (d0)); \ + gmp_umul_ppmm (_t1, _t0, (d0), (q)); \ + gmp_sub_ddmmss ((r1), (r0), (r1), (r0), _t1, _t0); \ + (q)++; \ + \ + /* Conditionally adjust q and the remainders */ \ + _mask = - (mp_limb_t) ((r1) >= _q0); \ + (q) += _mask; \ + gmp_add_ssaaaa ((r1), (r0), (r1), (r0), _mask & (d1), _mask & (d0)); \ + if ((r1) >= (d1)) \ + { \ + if ((r1) > (d1) || (r0) >= (d0)) \ + { \ + (q)++; \ + gmp_sub_ddmmss ((r1), (r0), (r1), (r0), (d1), (d0)); \ + } \ + } \ + } while (0) + +/* Swap macros. */ +#define MP_LIMB_T_SWAP(x, y) \ + do { \ + mp_limb_t __mp_limb_t_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_limb_t_swap__tmp; \ + } while (0) +#define MP_SIZE_T_SWAP(x, y) \ + do { \ + mp_size_t __mp_size_t_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_size_t_swap__tmp; \ + } while (0) +#define MP_BITCNT_T_SWAP(x,y) \ + do { \ + mp_bitcnt_t __mp_bitcnt_t_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_bitcnt_t_swap__tmp; \ + } while (0) +#define MP_PTR_SWAP(x, y) \ + do { \ + mp_ptr __mp_ptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_ptr_swap__tmp; \ + } while (0) +#define MP_SRCPTR_SWAP(x, y) \ + do { \ + mp_srcptr __mp_srcptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_srcptr_swap__tmp; \ + } while (0) + +#define MPN_PTR_SWAP(xp,xs, yp,ys) \ + do { \ + MP_PTR_SWAP (xp, yp); \ + MP_SIZE_T_SWAP (xs, ys); \ + } while(0) +#define MPN_SRCPTR_SWAP(xp,xs, yp,ys) \ + do { \ + MP_SRCPTR_SWAP (xp, yp); \ + MP_SIZE_T_SWAP (xs, ys); \ + } while(0) + +#define MPZ_PTR_SWAP(x, y) \ + do { \ + mpz_ptr __mpz_ptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mpz_ptr_swap__tmp; \ + } while (0) +#define MPZ_SRCPTR_SWAP(x, y) \ + do { \ + mpz_srcptr __mpz_srcptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mpz_srcptr_swap__tmp; \ + } while (0) + +const int mp_bits_per_limb = GMP_LIMB_BITS; + + +/* Memory allocation and other helper functions. */ +static void +gmp_die (const char *msg) +{ + /* + fprintf (stderr, "%s\n", msg); + abort(); + */ + IGRAPH_FATAL(msg); +} + +static void * +gmp_default_alloc (size_t size) +{ + void *p; + + assert (size > 0); + + p = malloc (size); + if (!p) + gmp_die("gmp_default_alloc: Virtual memory exhausted."); + + return p; +} + +static void * +gmp_default_realloc (void *old, size_t unused_old_size, size_t new_size) +{ + void * p; + + p = realloc (old, new_size); + + if (!p) + gmp_die("gmp_default_realloc: Virtual memory exhausted."); + + return p; +} + +static void +gmp_default_free (void *p, size_t unused_size) +{ + free (p); +} + +static void * (*gmp_allocate_func) (size_t) = gmp_default_alloc; +static void * (*gmp_reallocate_func) (void *, size_t, size_t) = gmp_default_realloc; +static void (*gmp_free_func) (void *, size_t) = gmp_default_free; + +void +mp_get_memory_functions (void *(**alloc_func) (size_t), + void *(**realloc_func) (void *, size_t, size_t), + void (**free_func) (void *, size_t)) +{ + if (alloc_func) + *alloc_func = gmp_allocate_func; + + if (realloc_func) + *realloc_func = gmp_reallocate_func; + + if (free_func) + *free_func = gmp_free_func; +} + +void +mp_set_memory_functions (void *(*alloc_func) (size_t), + void *(*realloc_func) (void *, size_t, size_t), + void (*free_func) (void *, size_t)) +{ + if (!alloc_func) + alloc_func = gmp_default_alloc; + if (!realloc_func) + realloc_func = gmp_default_realloc; + if (!free_func) + free_func = gmp_default_free; + + gmp_allocate_func = alloc_func; + gmp_reallocate_func = realloc_func; + gmp_free_func = free_func; +} + +#define gmp_xalloc(size) ((*gmp_allocate_func)((size))) +#define gmp_free(p) ((*gmp_free_func) ((p), 0)) + +static mp_ptr +gmp_xalloc_limbs (mp_size_t size) +{ + return (mp_ptr) gmp_xalloc (size * sizeof (mp_limb_t)); +} + +static mp_ptr +gmp_xrealloc_limbs (mp_ptr old, mp_size_t size) +{ + assert (size > 0); + return (mp_ptr) (*gmp_reallocate_func) (old, 0, size * sizeof (mp_limb_t)); +} + + +/* MPN interface */ + +void +mpn_copyi (mp_ptr d, mp_srcptr s, mp_size_t n) +{ + mp_size_t i; + for (i = 0; i < n; i++) + d[i] = s[i]; +} + +void +mpn_copyd (mp_ptr d, mp_srcptr s, mp_size_t n) +{ + while (--n >= 0) + d[n] = s[n]; +} + +int +mpn_cmp (mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + while (--n >= 0) + { + if (ap[n] != bp[n]) + return ap[n] > bp[n] ? 1 : -1; + } + return 0; +} + +static int +mpn_cmp4 (mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) +{ + if (an != bn) + return an < bn ? -1 : 1; + else + return mpn_cmp (ap, bp, an); +} + +static mp_size_t +mpn_normalized_size (mp_srcptr xp, mp_size_t n) +{ + while (n > 0 && xp[n-1] == 0) + --n; + return n; +} + +int +mpn_zero_p(mp_srcptr rp, mp_size_t n) +{ + return mpn_normalized_size (rp, n) == 0; +} + +void +mpn_zero (mp_ptr rp, mp_size_t n) +{ + while (--n >= 0) + rp[n] = 0; +} + +mp_limb_t +mpn_add_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b) +{ + mp_size_t i; + + assert (n > 0); + i = 0; + do + { + mp_limb_t r = ap[i] + b; + /* Carry out */ + b = (r < b); + rp[i] = r; + } + while (++i < n); + + return b; +} + +mp_limb_t +mpn_add_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + mp_size_t i; + mp_limb_t cy; + + for (i = 0, cy = 0; i < n; i++) + { + mp_limb_t a, b, r; + a = ap[i]; b = bp[i]; + r = a + cy; + cy = (r < cy); + r += b; + cy += (r < b); + rp[i] = r; + } + return cy; +} + +mp_limb_t +mpn_add (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) +{ + mp_limb_t cy; + + assert (an >= bn); + + cy = mpn_add_n (rp, ap, bp, bn); + if (an > bn) + cy = mpn_add_1 (rp + bn, ap + bn, an - bn, cy); + return cy; +} + +mp_limb_t +mpn_sub_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b) +{ + mp_size_t i; + + assert (n > 0); + + i = 0; + do + { + mp_limb_t a = ap[i]; + /* Carry out */ + mp_limb_t cy = a < b; + rp[i] = a - b; + b = cy; + } + while (++i < n); + + return b; +} + +mp_limb_t +mpn_sub_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + mp_size_t i; + mp_limb_t cy; + + for (i = 0, cy = 0; i < n; i++) + { + mp_limb_t a, b; + a = ap[i]; b = bp[i]; + b += cy; + cy = (b < cy); + cy += (a < b); + rp[i] = a - b; + } + return cy; +} + +mp_limb_t +mpn_sub (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) +{ + mp_limb_t cy; + + assert (an >= bn); + + cy = mpn_sub_n (rp, ap, bp, bn); + if (an > bn) + cy = mpn_sub_1 (rp + bn, ap + bn, an - bn, cy); + return cy; +} + +mp_limb_t +mpn_mul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) +{ + mp_limb_t ul, cl, hpl, lpl; + + assert (n >= 1); + + cl = 0; + do + { + ul = *up++; + gmp_umul_ppmm (hpl, lpl, ul, vl); + + lpl += cl; + cl = (lpl < cl) + hpl; + + *rp++ = lpl; + } + while (--n != 0); + + return cl; +} + +mp_limb_t +mpn_addmul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) +{ + mp_limb_t ul, cl, hpl, lpl, rl; + + assert (n >= 1); + + cl = 0; + do + { + ul = *up++; + gmp_umul_ppmm (hpl, lpl, ul, vl); + + lpl += cl; + cl = (lpl < cl) + hpl; + + rl = *rp; + lpl = rl + lpl; + cl += lpl < rl; + *rp++ = lpl; + } + while (--n != 0); + + return cl; +} + +mp_limb_t +mpn_submul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) +{ + mp_limb_t ul, cl, hpl, lpl, rl; + + assert (n >= 1); + + cl = 0; + do + { + ul = *up++; + gmp_umul_ppmm (hpl, lpl, ul, vl); + + lpl += cl; + cl = (lpl < cl) + hpl; + + rl = *rp; + lpl = rl - lpl; + cl += lpl > rl; + *rp++ = lpl; + } + while (--n != 0); + + return cl; +} + +mp_limb_t +mpn_mul (mp_ptr rp, mp_srcptr up, mp_size_t un, mp_srcptr vp, mp_size_t vn) +{ + assert (un >= vn); + assert (vn >= 1); + assert (!GMP_MPN_OVERLAP_P(rp, un + vn, up, un)); + assert (!GMP_MPN_OVERLAP_P(rp, un + vn, vp, vn)); + + /* We first multiply by the low order limb. This result can be + stored, not added, to rp. We also avoid a loop for zeroing this + way. */ + + rp[un] = mpn_mul_1 (rp, up, un, vp[0]); + + /* Now accumulate the product of up[] and the next higher limb from + vp[]. */ + + while (--vn >= 1) + { + rp += 1, vp += 1; + rp[un] = mpn_addmul_1 (rp, up, un, vp[0]); + } + return rp[un]; +} + +void +mpn_mul_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + mpn_mul (rp, ap, n, bp, n); +} + +void +mpn_sqr (mp_ptr rp, mp_srcptr ap, mp_size_t n) +{ + mpn_mul (rp, ap, n, ap, n); +} + +mp_limb_t +mpn_lshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt) +{ + mp_limb_t high_limb, low_limb; + unsigned int tnc; + mp_limb_t retval; + + assert (n >= 1); + assert (cnt >= 1); + assert (cnt < GMP_LIMB_BITS); + + up += n; + rp += n; + + tnc = GMP_LIMB_BITS - cnt; + low_limb = *--up; + retval = low_limb >> tnc; + high_limb = (low_limb << cnt); + + while (--n != 0) + { + low_limb = *--up; + *--rp = high_limb | (low_limb >> tnc); + high_limb = (low_limb << cnt); + } + *--rp = high_limb; + + return retval; +} + +mp_limb_t +mpn_rshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt) +{ + mp_limb_t high_limb, low_limb; + unsigned int tnc; + mp_limb_t retval; + + assert (n >= 1); + assert (cnt >= 1); + assert (cnt < GMP_LIMB_BITS); + + tnc = GMP_LIMB_BITS - cnt; + high_limb = *up++; + retval = (high_limb << tnc); + low_limb = high_limb >> cnt; + + while (--n != 0) + { + high_limb = *up++; + *rp++ = low_limb | (high_limb << tnc); + low_limb = high_limb >> cnt; + } + *rp = low_limb; + + return retval; +} + +static mp_bitcnt_t +mpn_common_scan (mp_limb_t limb, mp_size_t i, mp_srcptr up, mp_size_t un, + mp_limb_t ux) +{ + unsigned cnt; + + assert (ux == 0 || ux == GMP_LIMB_MAX); + assert (0 <= i && i <= un ); + + while (limb == 0) + { + i++; + if (i == un) + return (ux == 0 ? ~(mp_bitcnt_t) 0 : un * GMP_LIMB_BITS); + limb = ux ^ up[i]; + } + gmp_ctz (cnt, limb); + return (mp_bitcnt_t) i * GMP_LIMB_BITS + cnt; +} + +mp_bitcnt_t +mpn_scan1 (mp_srcptr ptr, mp_bitcnt_t bit) +{ + mp_size_t i; + i = bit / GMP_LIMB_BITS; + + return mpn_common_scan ( ptr[i] & (GMP_LIMB_MAX << (bit % GMP_LIMB_BITS)), + i, ptr, i, 0); +} + +mp_bitcnt_t +mpn_scan0 (mp_srcptr ptr, mp_bitcnt_t bit) +{ + mp_size_t i; + i = bit / GMP_LIMB_BITS; + + return mpn_common_scan (~ptr[i] & (GMP_LIMB_MAX << (bit % GMP_LIMB_BITS)), + i, ptr, i, GMP_LIMB_MAX); +} + +void +mpn_com (mp_ptr rp, mp_srcptr up, mp_size_t n) +{ + while (--n >= 0) + *rp++ = ~ *up++; +} + +mp_limb_t +mpn_neg (mp_ptr rp, mp_srcptr up, mp_size_t n) +{ + while (*up == 0) + { + *rp = 0; + if (!--n) + return 0; + ++up; ++rp; + } + *rp = - *up; + mpn_com (++rp, ++up, --n); + return 1; +} + + +/* MPN division interface. */ + +/* The 3/2 inverse is defined as + + m = floor( (B^3-1) / (B u1 + u0)) - B +*/ +mp_limb_t +mpn_invert_3by2 (mp_limb_t u1, mp_limb_t u0) +{ + mp_limb_t r, m; + + { + mp_limb_t p, ql; + unsigned ul, uh, qh; + + /* For notation, let b denote the half-limb base, so that B = b^2. + Split u1 = b uh + ul. */ + ul = u1 & GMP_LLIMB_MASK; + uh = u1 >> (GMP_LIMB_BITS / 2); + + /* Approximation of the high half of quotient. Differs from the 2/1 + inverse of the half limb uh, since we have already subtracted + u0. */ + qh = (u1 ^ GMP_LIMB_MAX) / uh; + + /* Adjust to get a half-limb 3/2 inverse, i.e., we want + + qh' = floor( (b^3 - 1) / u) - b = floor ((b^3 - b u - 1) / u + = floor( (b (~u) + b-1) / u), + + and the remainder + + r = b (~u) + b-1 - qh (b uh + ul) + = b (~u - qh uh) + b-1 - qh ul + + Subtraction of qh ul may underflow, which implies adjustments. + But by normalization, 2 u >= B > qh ul, so we need to adjust by + at most 2. + */ + + r = ((~u1 - (mp_limb_t) qh * uh) << (GMP_LIMB_BITS / 2)) | GMP_LLIMB_MASK; + + p = (mp_limb_t) qh * ul; + /* Adjustment steps taken from udiv_qrnnd_c */ + if (r < p) + { + qh--; + r += u1; + if (r >= u1) /* i.e. we didn't get carry when adding to r */ + if (r < p) + { + qh--; + r += u1; + } + } + r -= p; + + /* Low half of the quotient is + + ql = floor ( (b r + b-1) / u1). + + This is a 3/2 division (on half-limbs), for which qh is a + suitable inverse. */ + + p = (r >> (GMP_LIMB_BITS / 2)) * qh + r; + /* Unlike full-limb 3/2, we can add 1 without overflow. For this to + work, it is essential that ql is a full mp_limb_t. */ + ql = (p >> (GMP_LIMB_BITS / 2)) + 1; + + /* By the 3/2 trick, we don't need the high half limb. */ + r = (r << (GMP_LIMB_BITS / 2)) + GMP_LLIMB_MASK - ql * u1; + + if (r >= (GMP_LIMB_MAX & (p << (GMP_LIMB_BITS / 2)))) + { + ql--; + r += u1; + } + m = ((mp_limb_t) qh << (GMP_LIMB_BITS / 2)) + ql; + if (r >= u1) + { + m++; + r -= u1; + } + } + + /* Now m is the 2/1 inverse of u1. If u0 > 0, adjust it to become a + 3/2 inverse. */ + if (u0 > 0) + { + mp_limb_t th, tl; + r = ~r; + r += u0; + if (r < u0) + { + m--; + if (r >= u1) + { + m--; + r -= u1; + } + r -= u1; + } + gmp_umul_ppmm (th, tl, u0, m); + r += th; + if (r < th) + { + m--; + m -= ((r > u1) | ((r == u1) & (tl > u0))); + } + } + + return m; +} + +struct gmp_div_inverse +{ + /* Normalization shift count. */ + unsigned shift; + /* Normalized divisor (d0 unused for mpn_div_qr_1) */ + mp_limb_t d1, d0; + /* Inverse, for 2/1 or 3/2. */ + mp_limb_t di; +}; + +static void +mpn_div_qr_1_invert (struct gmp_div_inverse *inv, mp_limb_t d) +{ + unsigned shift; + + assert (d > 0); + gmp_clz (shift, d); + inv->shift = shift; + inv->d1 = d << shift; + inv->di = mpn_invert_limb (inv->d1); +} + +static void +mpn_div_qr_2_invert (struct gmp_div_inverse *inv, + mp_limb_t d1, mp_limb_t d0) +{ + unsigned shift; + + assert (d1 > 0); + gmp_clz (shift, d1); + inv->shift = shift; + if (shift > 0) + { + d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift)); + d0 <<= shift; + } + inv->d1 = d1; + inv->d0 = d0; + inv->di = mpn_invert_3by2 (d1, d0); +} + +static void +mpn_div_qr_invert (struct gmp_div_inverse *inv, + mp_srcptr dp, mp_size_t dn) +{ + assert (dn > 0); + + if (dn == 1) + mpn_div_qr_1_invert (inv, dp[0]); + else if (dn == 2) + mpn_div_qr_2_invert (inv, dp[1], dp[0]); + else + { + unsigned shift; + mp_limb_t d1, d0; + + d1 = dp[dn-1]; + d0 = dp[dn-2]; + assert (d1 > 0); + gmp_clz (shift, d1); + inv->shift = shift; + if (shift > 0) + { + d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift)); + d0 = (d0 << shift) | (dp[dn-3] >> (GMP_LIMB_BITS - shift)); + } + inv->d1 = d1; + inv->d0 = d0; + inv->di = mpn_invert_3by2 (d1, d0); + } +} + +/* Not matching current public gmp interface, rather corresponding to + the sbpi1_div_* functions. */ +static mp_limb_t +mpn_div_qr_1_preinv (mp_ptr qp, mp_srcptr np, mp_size_t nn, + const struct gmp_div_inverse *inv) +{ + mp_limb_t d, di; + mp_limb_t r; + mp_ptr tp = NULL; + + if (inv->shift > 0) + { + /* Shift, reusing qp area if possible. In-place shift if qp == np. */ + tp = qp ? qp : gmp_xalloc_limbs (nn); + r = mpn_lshift (tp, np, nn, inv->shift); + np = tp; + } + else + r = 0; + + d = inv->d1; + di = inv->di; + while (--nn >= 0) + { + mp_limb_t q; + + gmp_udiv_qrnnd_preinv (q, r, r, np[nn], d, di); + if (qp) + qp[nn] = q; + } + if ((inv->shift > 0) && (tp != qp)) + gmp_free (tp); + + return r >> inv->shift; +} + +static void +mpn_div_qr_2_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn, + const struct gmp_div_inverse *inv) +{ + unsigned shift; + mp_size_t i; + mp_limb_t d1, d0, di, r1, r0; + + assert (nn >= 2); + shift = inv->shift; + d1 = inv->d1; + d0 = inv->d0; + di = inv->di; + + if (shift > 0) + r1 = mpn_lshift (np, np, nn, shift); + else + r1 = 0; + + r0 = np[nn - 1]; + + i = nn - 2; + do + { + mp_limb_t n0, q; + n0 = np[i]; + gmp_udiv_qr_3by2 (q, r1, r0, r1, r0, n0, d1, d0, di); + + if (qp) + qp[i] = q; + } + while (--i >= 0); + + if (shift > 0) + { + assert ((r0 & (GMP_LIMB_MAX >> (GMP_LIMB_BITS - shift))) == 0); + r0 = (r0 >> shift) | (r1 << (GMP_LIMB_BITS - shift)); + r1 >>= shift; + } + + np[1] = r1; + np[0] = r0; +} + +static void +mpn_div_qr_pi1 (mp_ptr qp, + mp_ptr np, mp_size_t nn, mp_limb_t n1, + mp_srcptr dp, mp_size_t dn, + mp_limb_t dinv) +{ + mp_size_t i; + + mp_limb_t d1, d0; + mp_limb_t cy, cy1; + mp_limb_t q; + + assert (dn > 2); + assert (nn >= dn); + + d1 = dp[dn - 1]; + d0 = dp[dn - 2]; + + assert ((d1 & GMP_LIMB_HIGHBIT) != 0); + /* Iteration variable is the index of the q limb. + * + * We divide + * by + */ + + i = nn - dn; + do + { + mp_limb_t n0 = np[dn-1+i]; + + if (n1 == d1 && n0 == d0) + { + q = GMP_LIMB_MAX; + mpn_submul_1 (np+i, dp, dn, q); + n1 = np[dn-1+i]; /* update n1, last loop's value will now be invalid */ + } + else + { + gmp_udiv_qr_3by2 (q, n1, n0, n1, n0, np[dn-2+i], d1, d0, dinv); + + cy = mpn_submul_1 (np + i, dp, dn-2, q); + + cy1 = n0 < cy; + n0 = n0 - cy; + cy = n1 < cy1; + n1 = n1 - cy1; + np[dn-2+i] = n0; + + if (cy != 0) + { + n1 += d1 + mpn_add_n (np + i, np + i, dp, dn - 1); + q--; + } + } + + if (qp) + qp[i] = q; + } + while (--i >= 0); + + np[dn - 1] = n1; +} + +static void +mpn_div_qr_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn, + mp_srcptr dp, mp_size_t dn, + const struct gmp_div_inverse *inv) +{ + assert (dn > 0); + assert (nn >= dn); + + if (dn == 1) + np[0] = mpn_div_qr_1_preinv (qp, np, nn, inv); + else if (dn == 2) + mpn_div_qr_2_preinv (qp, np, nn, inv); + else + { + mp_limb_t nh; + unsigned shift; + + assert (inv->d1 == dp[dn-1]); + assert (inv->d0 == dp[dn-2]); + assert ((inv->d1 & GMP_LIMB_HIGHBIT) != 0); + + shift = inv->shift; + if (shift > 0) + nh = mpn_lshift (np, np, nn, shift); + else + nh = 0; + + mpn_div_qr_pi1 (qp, np, nn, nh, dp, dn, inv->di); + + if (shift > 0) + gmp_assert_nocarry (mpn_rshift (np, np, dn, shift)); + } +} + +static void +mpn_div_qr (mp_ptr qp, mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn) +{ + struct gmp_div_inverse inv; + mp_ptr tp = NULL; + + assert (dn > 0); + assert (nn >= dn); + + mpn_div_qr_invert (&inv, dp, dn); + if (dn > 2 && inv.shift > 0) + { + tp = gmp_xalloc_limbs (dn); + gmp_assert_nocarry (mpn_lshift (tp, dp, dn, inv.shift)); + dp = tp; + } + mpn_div_qr_preinv (qp, np, nn, dp, dn, &inv); + if (tp) + gmp_free (tp); +} + + +/* MPN base conversion. */ +static unsigned +mpn_base_power_of_two_p (unsigned b) +{ + switch (b) + { + case 2: return 1; + case 4: return 2; + case 8: return 3; + case 16: return 4; + case 32: return 5; + case 64: return 6; + case 128: return 7; + case 256: return 8; + default: return 0; + } +} + +struct mpn_base_info +{ + /* bb is the largest power of the base which fits in one limb, and + exp is the corresponding exponent. */ + unsigned exp; + mp_limb_t bb; +}; + +static void +mpn_get_base_info (struct mpn_base_info *info, mp_limb_t b) +{ + mp_limb_t m; + mp_limb_t p; + unsigned exp; + + m = GMP_LIMB_MAX / b; + for (exp = 1, p = b; p <= m; exp++) + p *= b; + + info->exp = exp; + info->bb = p; +} + +static mp_bitcnt_t +mpn_limb_size_in_base_2 (mp_limb_t u) +{ + unsigned shift; + + assert (u > 0); + gmp_clz (shift, u); + return GMP_LIMB_BITS - shift; +} + +static size_t +mpn_get_str_bits (unsigned char *sp, unsigned bits, mp_srcptr up, mp_size_t un) +{ + unsigned char mask; + size_t sn, j; + mp_size_t i; + unsigned shift; + + sn = ((un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1]) + + bits - 1) / bits; + + mask = (1U << bits) - 1; + + for (i = 0, j = sn, shift = 0; j-- > 0;) + { + unsigned char digit = up[i] >> shift; + + shift += bits; + + if (shift >= GMP_LIMB_BITS && ++i < un) + { + shift -= GMP_LIMB_BITS; + digit |= up[i] << (bits - shift); + } + sp[j] = digit & mask; + } + return sn; +} + +/* We generate digits from the least significant end, and reverse at + the end. */ +static size_t +mpn_limb_get_str (unsigned char *sp, mp_limb_t w, + const struct gmp_div_inverse *binv) +{ + mp_size_t i; + for (i = 0; w > 0; i++) + { + mp_limb_t h, l, r; + + h = w >> (GMP_LIMB_BITS - binv->shift); + l = w << binv->shift; + + gmp_udiv_qrnnd_preinv (w, r, h, l, binv->d1, binv->di); + assert ((r & (GMP_LIMB_MAX >> (GMP_LIMB_BITS - binv->shift))) == 0); + r >>= binv->shift; + + sp[i] = r; + } + return i; +} + +static size_t +mpn_get_str_other (unsigned char *sp, + int base, const struct mpn_base_info *info, + mp_ptr up, mp_size_t un) +{ + struct gmp_div_inverse binv; + size_t sn; + size_t i; + + mpn_div_qr_1_invert (&binv, base); + + sn = 0; + + if (un > 1) + { + struct gmp_div_inverse bbinv; + mpn_div_qr_1_invert (&bbinv, info->bb); + + do + { + mp_limb_t w; + size_t done; + w = mpn_div_qr_1_preinv (up, up, un, &bbinv); + un -= (up[un-1] == 0); + done = mpn_limb_get_str (sp + sn, w, &binv); + + for (sn += done; done < info->exp; done++) + sp[sn++] = 0; + } + while (un > 1); + } + sn += mpn_limb_get_str (sp + sn, up[0], &binv); + + /* Reverse order */ + for (i = 0; 2*i + 1 < sn; i++) + { + unsigned char t = sp[i]; + sp[i] = sp[sn - i - 1]; + sp[sn - i - 1] = t; + } + + return sn; +} + +size_t +mpn_get_str (unsigned char *sp, int base, mp_ptr up, mp_size_t un) +{ + unsigned bits; + + assert (un > 0); + assert (up[un-1] > 0); + + bits = mpn_base_power_of_two_p (base); + if (bits) + return mpn_get_str_bits (sp, bits, up, un); + else + { + struct mpn_base_info info; + + mpn_get_base_info (&info, base); + return mpn_get_str_other (sp, base, &info, up, un); + } +} + +static mp_size_t +mpn_set_str_bits (mp_ptr rp, const unsigned char *sp, size_t sn, + unsigned bits) +{ + mp_size_t rn; + size_t j; + unsigned shift; + + for (j = sn, rn = 0, shift = 0; j-- > 0; ) + { + if (shift == 0) + { + rp[rn++] = sp[j]; + shift += bits; + } + else + { + rp[rn-1] |= (mp_limb_t) sp[j] << shift; + shift += bits; + if (shift >= GMP_LIMB_BITS) + { + shift -= GMP_LIMB_BITS; + if (shift > 0) + rp[rn++] = (mp_limb_t) sp[j] >> (bits - shift); + } + } + } + rn = mpn_normalized_size (rp, rn); + return rn; +} + +/* Result is usually normalized, except for all-zero input, in which + case a single zero limb is written at *RP, and 1 is returned. */ +static mp_size_t +mpn_set_str_other (mp_ptr rp, const unsigned char *sp, size_t sn, + mp_limb_t b, const struct mpn_base_info *info) +{ + mp_size_t rn; + mp_limb_t w; + unsigned k; + size_t j; + + assert (sn > 0); + + k = 1 + (sn - 1) % info->exp; + + j = 0; + w = sp[j++]; + while (--k != 0) + w = w * b + sp[j++]; + + rp[0] = w; + + for (rn = 1; j < sn;) + { + mp_limb_t cy; + + w = sp[j++]; + for (k = 1; k < info->exp; k++) + w = w * b + sp[j++]; + + cy = mpn_mul_1 (rp, rp, rn, info->bb); + cy += mpn_add_1 (rp, rp, rn, w); + if (cy > 0) + rp[rn++] = cy; + } + assert (j == sn); + + return rn; +} + +mp_size_t +mpn_set_str (mp_ptr rp, const unsigned char *sp, size_t sn, int base) +{ + unsigned bits; + + if (sn == 0) + return 0; + + bits = mpn_base_power_of_two_p (base); + if (bits) + return mpn_set_str_bits (rp, sp, sn, bits); + else + { + struct mpn_base_info info; + + mpn_get_base_info (&info, base); + return mpn_set_str_other (rp, sp, sn, base, &info); + } +} + + +/* MPZ interface */ +void +mpz_init (mpz_t r) +{ + static const mp_limb_t dummy_limb = GMP_LIMB_MAX & 0xc1a0; + + r->_mp_alloc = 0; + r->_mp_size = 0; + r->_mp_d = (mp_ptr) &dummy_limb; +} + +/* The utility of this function is a bit limited, since many functions + assigns the result variable using mpz_swap. */ +void +mpz_init2 (mpz_t r, mp_bitcnt_t bits) +{ + mp_size_t rn; + + bits -= (bits != 0); /* Round down, except if 0 */ + rn = 1 + bits / GMP_LIMB_BITS; + + r->_mp_alloc = rn; + r->_mp_size = 0; + r->_mp_d = gmp_xalloc_limbs (rn); +} + +void +mpz_clear (mpz_t r) +{ + if (r->_mp_alloc) + gmp_free (r->_mp_d); +} + +static mp_ptr +mpz_realloc (mpz_t r, mp_size_t size) +{ + size = GMP_MAX (size, 1); + + if (r->_mp_alloc) + r->_mp_d = gmp_xrealloc_limbs (r->_mp_d, size); + else + r->_mp_d = gmp_xalloc_limbs (size); + r->_mp_alloc = size; + + if (GMP_ABS (r->_mp_size) > size) + r->_mp_size = 0; + + return r->_mp_d; +} + +/* Realloc for an mpz_t WHAT if it has less than NEEDED limbs. */ +#define MPZ_REALLOC(z,n) ((n) > (z)->_mp_alloc \ + ? mpz_realloc(z,n) \ + : (z)->_mp_d) + +/* MPZ assignment and basic conversions. */ +void +mpz_set_si (mpz_t r, signed long int x) +{ + if (x >= 0) + mpz_set_ui (r, x); + else /* (x < 0) */ + if (GMP_LIMB_BITS < GMP_ULONG_BITS) + { + mpz_set_ui (r, GMP_NEG_CAST (unsigned long int, x)); + mpz_neg (r, r); + } + else + { + r->_mp_size = -1; + MPZ_REALLOC (r, 1)[0] = GMP_NEG_CAST (unsigned long int, x); + } +} + +void +mpz_set_ui (mpz_t r, unsigned long int x) +{ + if (x > 0) + { + r->_mp_size = 1; + MPZ_REALLOC (r, 1)[0] = x; + if (GMP_LIMB_BITS < GMP_ULONG_BITS) + { + int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; + while (x >>= LOCAL_GMP_LIMB_BITS) + { + ++ r->_mp_size; + MPZ_REALLOC (r, r->_mp_size)[r->_mp_size - 1] = x; + } + } + } + else + r->_mp_size = 0; +} + +void +mpz_set (mpz_t r, const mpz_t x) +{ + /* Allow the NOP r == x */ + if (r != x) + { + mp_size_t n; + mp_ptr rp; + + n = GMP_ABS (x->_mp_size); + rp = MPZ_REALLOC (r, n); + + mpn_copyi (rp, x->_mp_d, n); + r->_mp_size = x->_mp_size; + } +} + +void +mpz_init_set_si (mpz_t r, signed long int x) +{ + mpz_init (r); + mpz_set_si (r, x); +} + +void +mpz_init_set_ui (mpz_t r, unsigned long int x) +{ + mpz_init (r); + mpz_set_ui (r, x); +} + +void +mpz_init_set (mpz_t r, const mpz_t x) +{ + mpz_init (r); + mpz_set (r, x); +} + +int +mpz_fits_slong_p (const mpz_t u) +{ + return (LONG_MAX + LONG_MIN == 0 || mpz_cmp_ui (u, LONG_MAX) <= 0) && + mpz_cmpabs_ui (u, GMP_NEG_CAST (unsigned long int, LONG_MIN)) <= 0; +} + +static int +mpn_absfits_ulong_p (mp_srcptr up, mp_size_t un) +{ + int ulongsize = GMP_ULONG_BITS / GMP_LIMB_BITS; + mp_limb_t ulongrem = 0; + + if (GMP_ULONG_BITS % GMP_LIMB_BITS != 0) + ulongrem = (mp_limb_t) (ULONG_MAX >> GMP_LIMB_BITS * ulongsize) + 1; + + return un <= ulongsize || (up[ulongsize] < ulongrem && un == ulongsize + 1); +} + +int +mpz_fits_ulong_p (const mpz_t u) +{ + mp_size_t us = u->_mp_size; + + return us >= 0 && mpn_absfits_ulong_p (u->_mp_d, us); +} + +long int +mpz_get_si (const mpz_t u) +{ + unsigned long r = mpz_get_ui (u); + unsigned long c = -LONG_MAX - LONG_MIN; + + if (u->_mp_size < 0) + /* This expression is necessary to properly handle -LONG_MIN */ + return -(long) c - (long) ((r - c) & LONG_MAX); + else + return (long) (r & LONG_MAX); +} + +unsigned long int +mpz_get_ui (const mpz_t u) +{ + if (GMP_LIMB_BITS < GMP_ULONG_BITS) + { + int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; + unsigned long r = 0; + mp_size_t n = GMP_ABS (u->_mp_size); + n = GMP_MIN (n, 1 + (mp_size_t) (GMP_ULONG_BITS - 1) / GMP_LIMB_BITS); + while (--n >= 0) + r = (r << LOCAL_GMP_LIMB_BITS) + u->_mp_d[n]; + return r; + } + + return u->_mp_size == 0 ? 0 : u->_mp_d[0]; +} + +size_t +mpz_size (const mpz_t u) +{ + return GMP_ABS (u->_mp_size); +} + +mp_limb_t +mpz_getlimbn (const mpz_t u, mp_size_t n) +{ + if (n >= 0 && n < GMP_ABS (u->_mp_size)) + return u->_mp_d[n]; + else + return 0; +} + +void +mpz_realloc2 (mpz_t x, mp_bitcnt_t n) +{ + mpz_realloc (x, 1 + (n - (n != 0)) / GMP_LIMB_BITS); +} + +mp_srcptr +mpz_limbs_read (mpz_srcptr x) +{ + return x->_mp_d; +} + +mp_ptr +mpz_limbs_modify (mpz_t x, mp_size_t n) +{ + assert (n > 0); + return MPZ_REALLOC (x, n); +} + +mp_ptr +mpz_limbs_write (mpz_t x, mp_size_t n) +{ + return mpz_limbs_modify (x, n); +} + +void +mpz_limbs_finish (mpz_t x, mp_size_t xs) +{ + mp_size_t xn; + xn = mpn_normalized_size (x->_mp_d, GMP_ABS (xs)); + x->_mp_size = xs < 0 ? -xn : xn; +} + +static mpz_srcptr +mpz_roinit_normal_n (mpz_t x, mp_srcptr xp, mp_size_t xs) +{ + x->_mp_alloc = 0; + x->_mp_d = (mp_ptr) xp; + x->_mp_size = xs; + return x; +} + +mpz_srcptr +mpz_roinit_n (mpz_t x, mp_srcptr xp, mp_size_t xs) +{ + mpz_roinit_normal_n (x, xp, xs); + mpz_limbs_finish (x, xs); + return x; +} + + +/* Conversions and comparison to double. */ +void +mpz_set_d (mpz_t r, double x) +{ + int sign; + mp_ptr rp; + mp_size_t rn, i; + double B; + double Bi; + mp_limb_t f; + + /* x != x is true when x is a NaN, and x == x * 0.5 is true when x is + zero or infinity. */ + if (x != x || x == x * 0.5) + { + r->_mp_size = 0; + return; + } + + sign = x < 0.0 ; + if (sign) + x = - x; + + if (x < 1.0) + { + r->_mp_size = 0; + return; + } + B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1); + Bi = 1.0 / B; + for (rn = 1; x >= B; rn++) + x *= Bi; + + rp = MPZ_REALLOC (r, rn); + + f = (mp_limb_t) x; + x -= f; + assert (x < 1.0); + i = rn-1; + rp[i] = f; + while (--i >= 0) + { + x = B * x; + f = (mp_limb_t) x; + x -= f; + assert (x < 1.0); + rp[i] = f; + } + + r->_mp_size = sign ? - rn : rn; +} + +void +mpz_init_set_d (mpz_t r, double x) +{ + mpz_init (r); + mpz_set_d (r, x); +} + +double +mpz_get_d (const mpz_t u) +{ + int m; + mp_limb_t l; + mp_size_t un; + double x; + double B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1); + + un = GMP_ABS (u->_mp_size); + + if (un == 0) + return 0.0; + + l = u->_mp_d[--un]; + gmp_clz (m, l); + m = m + GMP_DBL_MANT_BITS - GMP_LIMB_BITS; + if (m < 0) + l &= GMP_LIMB_MAX << -m; + + for (x = l; --un >= 0;) + { + x = B*x; + if (m > 0) { + l = u->_mp_d[un]; + m -= GMP_LIMB_BITS; + if (m < 0) + l &= GMP_LIMB_MAX << -m; + x += l; + } + } + + if (u->_mp_size < 0) + x = -x; + + return x; +} + +int +mpz_cmpabs_d (const mpz_t x, double d) +{ + mp_size_t xn; + double B, Bi; + mp_size_t i; + + xn = x->_mp_size; + d = GMP_ABS (d); + + if (xn != 0) + { + xn = GMP_ABS (xn); + + B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1); + Bi = 1.0 / B; + + /* Scale d so it can be compared with the top limb. */ + for (i = 1; i < xn; i++) + d *= Bi; + + if (d >= B) + return -1; + + /* Compare floor(d) to top limb, subtract and cancel when equal. */ + for (i = xn; i-- > 0;) + { + mp_limb_t f, xl; + + f = (mp_limb_t) d; + xl = x->_mp_d[i]; + if (xl > f) + return 1; + else if (xl < f) + return -1; + d = B * (d - f); + } + } + return - (d > 0.0); +} + +int +mpz_cmp_d (const mpz_t x, double d) +{ + if (x->_mp_size < 0) + { + if (d >= 0.0) + return -1; + else + return -mpz_cmpabs_d (x, d); + } + else + { + if (d < 0.0) + return 1; + else + return mpz_cmpabs_d (x, d); + } +} + + +/* MPZ comparisons and the like. */ +int +mpz_sgn (const mpz_t u) +{ + return GMP_CMP (u->_mp_size, 0); +} + +int +mpz_cmp_si (const mpz_t u, long v) +{ + mp_size_t usize = u->_mp_size; + + if (v >= 0) + return mpz_cmp_ui (u, v); + else if (usize >= 0) + return 1; + else + return - mpz_cmpabs_ui (u, GMP_NEG_CAST (unsigned long int, v)); +} + +int +mpz_cmp_ui (const mpz_t u, unsigned long v) +{ + mp_size_t usize = u->_mp_size; + + if (usize < 0) + return -1; + else + return mpz_cmpabs_ui (u, v); +} + +int +mpz_cmp (const mpz_t a, const mpz_t b) +{ + mp_size_t asize = a->_mp_size; + mp_size_t bsize = b->_mp_size; + + if (asize != bsize) + return (asize < bsize) ? -1 : 1; + else if (asize >= 0) + return mpn_cmp (a->_mp_d, b->_mp_d, asize); + else + return mpn_cmp (b->_mp_d, a->_mp_d, -asize); +} + +int +mpz_cmpabs_ui (const mpz_t u, unsigned long v) +{ + mp_size_t un = GMP_ABS (u->_mp_size); + + if (! mpn_absfits_ulong_p (u->_mp_d, un)) + return 1; + else + { + unsigned long uu = mpz_get_ui (u); + return GMP_CMP(uu, v); + } +} + +int +mpz_cmpabs (const mpz_t u, const mpz_t v) +{ + return mpn_cmp4 (u->_mp_d, GMP_ABS (u->_mp_size), + v->_mp_d, GMP_ABS (v->_mp_size)); +} + +void +mpz_abs (mpz_t r, const mpz_t u) +{ + mpz_set (r, u); + r->_mp_size = GMP_ABS (r->_mp_size); +} + +void +mpz_neg (mpz_t r, const mpz_t u) +{ + mpz_set (r, u); + r->_mp_size = -r->_mp_size; +} + +void +mpz_swap (mpz_t u, mpz_t v) +{ + MP_SIZE_T_SWAP (u->_mp_size, v->_mp_size); + MP_SIZE_T_SWAP (u->_mp_alloc, v->_mp_alloc); + MP_PTR_SWAP (u->_mp_d, v->_mp_d); +} + + +/* MPZ addition and subtraction */ + + +void +mpz_add_ui (mpz_t r, const mpz_t a, unsigned long b) +{ + mpz_t bb; + mpz_init_set_ui (bb, b); + mpz_add (r, a, bb); + mpz_clear (bb); +} + +void +mpz_sub_ui (mpz_t r, const mpz_t a, unsigned long b) +{ + mpz_ui_sub (r, b, a); + mpz_neg (r, r); +} + +void +mpz_ui_sub (mpz_t r, unsigned long a, const mpz_t b) +{ + mpz_neg (r, b); + mpz_add_ui (r, r, a); +} + +static mp_size_t +mpz_abs_add (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t an = GMP_ABS (a->_mp_size); + mp_size_t bn = GMP_ABS (b->_mp_size); + mp_ptr rp; + mp_limb_t cy; + + if (an < bn) + { + MPZ_SRCPTR_SWAP (a, b); + MP_SIZE_T_SWAP (an, bn); + } + + rp = MPZ_REALLOC (r, an + 1); + cy = mpn_add (rp, a->_mp_d, an, b->_mp_d, bn); + + rp[an] = cy; + + return an + cy; +} + +static mp_size_t +mpz_abs_sub (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t an = GMP_ABS (a->_mp_size); + mp_size_t bn = GMP_ABS (b->_mp_size); + int cmp; + mp_ptr rp; + + cmp = mpn_cmp4 (a->_mp_d, an, b->_mp_d, bn); + if (cmp > 0) + { + rp = MPZ_REALLOC (r, an); + gmp_assert_nocarry (mpn_sub (rp, a->_mp_d, an, b->_mp_d, bn)); + return mpn_normalized_size (rp, an); + } + else if (cmp < 0) + { + rp = MPZ_REALLOC (r, bn); + gmp_assert_nocarry (mpn_sub (rp, b->_mp_d, bn, a->_mp_d, an)); + return -mpn_normalized_size (rp, bn); + } + else + return 0; +} + +void +mpz_add (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t rn; + + if ( (a->_mp_size ^ b->_mp_size) >= 0) + rn = mpz_abs_add (r, a, b); + else + rn = mpz_abs_sub (r, a, b); + + r->_mp_size = a->_mp_size >= 0 ? rn : - rn; +} + +void +mpz_sub (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t rn; + + if ( (a->_mp_size ^ b->_mp_size) >= 0) + rn = mpz_abs_sub (r, a, b); + else + rn = mpz_abs_add (r, a, b); + + r->_mp_size = a->_mp_size >= 0 ? rn : - rn; +} + + +/* MPZ multiplication */ +void +mpz_mul_si (mpz_t r, const mpz_t u, long int v) +{ + if (v < 0) + { + mpz_mul_ui (r, u, GMP_NEG_CAST (unsigned long int, v)); + mpz_neg (r, r); + } + else + mpz_mul_ui (r, u, v); +} + +void +mpz_mul_ui (mpz_t r, const mpz_t u, unsigned long int v) +{ + mpz_t vv; + mpz_init_set_ui (vv, v); + mpz_mul (r, u, vv); + mpz_clear (vv); + return; +} + +void +mpz_mul (mpz_t r, const mpz_t u, const mpz_t v) +{ + int sign; + mp_size_t un, vn, rn; + mpz_t t; + mp_ptr tp; + + un = u->_mp_size; + vn = v->_mp_size; + + if (un == 0 || vn == 0) + { + r->_mp_size = 0; + return; + } + + sign = (un ^ vn) < 0; + + un = GMP_ABS (un); + vn = GMP_ABS (vn); + + mpz_init2 (t, (un + vn) * GMP_LIMB_BITS); + + tp = t->_mp_d; + if (un >= vn) + mpn_mul (tp, u->_mp_d, un, v->_mp_d, vn); + else + mpn_mul (tp, v->_mp_d, vn, u->_mp_d, un); + + rn = un + vn; + rn -= tp[rn-1] == 0; + + t->_mp_size = sign ? - rn : rn; + mpz_swap (r, t); + mpz_clear (t); +} + +void +mpz_mul_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bits) +{ + mp_size_t un, rn; + mp_size_t limbs; + unsigned shift; + mp_ptr rp; + + un = GMP_ABS (u->_mp_size); + if (un == 0) + { + r->_mp_size = 0; + return; + } + + limbs = bits / GMP_LIMB_BITS; + shift = bits % GMP_LIMB_BITS; + + rn = un + limbs + (shift > 0); + rp = MPZ_REALLOC (r, rn); + if (shift > 0) + { + mp_limb_t cy = mpn_lshift (rp + limbs, u->_mp_d, un, shift); + rp[rn-1] = cy; + rn -= (cy == 0); + } + else + mpn_copyd (rp + limbs, u->_mp_d, un); + + mpn_zero (rp, limbs); + + r->_mp_size = (u->_mp_size < 0) ? - rn : rn; +} + +void +mpz_addmul_ui (mpz_t r, const mpz_t u, unsigned long int v) +{ + mpz_t t; + mpz_init_set_ui (t, v); + mpz_mul (t, u, t); + mpz_add (r, r, t); + mpz_clear (t); +} + +void +mpz_submul_ui (mpz_t r, const mpz_t u, unsigned long int v) +{ + mpz_t t; + mpz_init_set_ui (t, v); + mpz_mul (t, u, t); + mpz_sub (r, r, t); + mpz_clear (t); +} + +void +mpz_addmul (mpz_t r, const mpz_t u, const mpz_t v) +{ + mpz_t t; + mpz_init (t); + mpz_mul (t, u, v); + mpz_add (r, r, t); + mpz_clear (t); +} + +void +mpz_submul (mpz_t r, const mpz_t u, const mpz_t v) +{ + mpz_t t; + mpz_init (t); + mpz_mul (t, u, v); + mpz_sub (r, r, t); + mpz_clear (t); +} + + +/* MPZ division */ +enum mpz_div_round_mode { GMP_DIV_FLOOR, GMP_DIV_CEIL, GMP_DIV_TRUNC }; + +/* Allows q or r to be zero. Returns 1 iff remainder is non-zero. */ +static int +mpz_div_qr (mpz_t q, mpz_t r, + const mpz_t n, const mpz_t d, enum mpz_div_round_mode mode) +{ + mp_size_t ns, ds, nn, dn, qs; + ns = n->_mp_size; + ds = d->_mp_size; + + if (ds == 0) + gmp_die("mpz_div_qr: Divide by zero."); + + if (ns == 0) + { + if (q) + q->_mp_size = 0; + if (r) + r->_mp_size = 0; + return 0; + } + + nn = GMP_ABS (ns); + dn = GMP_ABS (ds); + + qs = ds ^ ns; + + if (nn < dn) + { + if (mode == GMP_DIV_CEIL && qs >= 0) + { + /* q = 1, r = n - d */ + if (r) + mpz_sub (r, n, d); + if (q) + mpz_set_ui (q, 1); + } + else if (mode == GMP_DIV_FLOOR && qs < 0) + { + /* q = -1, r = n + d */ + if (r) + mpz_add (r, n, d); + if (q) + mpz_set_si (q, -1); + } + else + { + /* q = 0, r = d */ + if (r) + mpz_set (r, n); + if (q) + q->_mp_size = 0; + } + return 1; + } + else + { + mp_ptr np, qp; + mp_size_t qn, rn; + mpz_t tq, tr; + + mpz_init_set (tr, n); + np = tr->_mp_d; + + qn = nn - dn + 1; + + if (q) + { + mpz_init2 (tq, qn * GMP_LIMB_BITS); + qp = tq->_mp_d; + } + else + qp = NULL; + + mpn_div_qr (qp, np, nn, d->_mp_d, dn); + + if (qp) + { + qn -= (qp[qn-1] == 0); + + tq->_mp_size = qs < 0 ? -qn : qn; + } + rn = mpn_normalized_size (np, dn); + tr->_mp_size = ns < 0 ? - rn : rn; + + if (mode == GMP_DIV_FLOOR && qs < 0 && rn != 0) + { + if (q) + mpz_sub_ui (tq, tq, 1); + if (r) + mpz_add (tr, tr, d); + } + else if (mode == GMP_DIV_CEIL && qs >= 0 && rn != 0) + { + if (q) + mpz_add_ui (tq, tq, 1); + if (r) + mpz_sub (tr, tr, d); + } + + if (q) + { + mpz_swap (tq, q); + mpz_clear (tq); + } + if (r) + mpz_swap (tr, r); + + mpz_clear (tr); + + return rn != 0; + } +} + +void +mpz_cdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, r, n, d, GMP_DIV_CEIL); +} + +void +mpz_fdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, r, n, d, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, r, n, d, GMP_DIV_TRUNC); +} + +void +mpz_cdiv_q (mpz_t q, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, NULL, n, d, GMP_DIV_CEIL); +} + +void +mpz_fdiv_q (mpz_t q, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, NULL, n, d, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_q (mpz_t q, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC); +} + +void +mpz_cdiv_r (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, GMP_DIV_CEIL); +} + +void +mpz_fdiv_r (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_r (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, GMP_DIV_TRUNC); +} + +void +mpz_mod (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, d->_mp_size >= 0 ? GMP_DIV_FLOOR : GMP_DIV_CEIL); +} + +static void +mpz_div_q_2exp (mpz_t q, const mpz_t u, mp_bitcnt_t bit_index, + enum mpz_div_round_mode mode) +{ + mp_size_t un, qn; + mp_size_t limb_cnt; + mp_ptr qp; + int adjust; + + un = u->_mp_size; + if (un == 0) + { + q->_mp_size = 0; + return; + } + limb_cnt = bit_index / GMP_LIMB_BITS; + qn = GMP_ABS (un) - limb_cnt; + bit_index %= GMP_LIMB_BITS; + + if (mode == ((un > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* un != 0 here. */ + /* Note: Below, the final indexing at limb_cnt is valid because at + that point we have qn > 0. */ + adjust = (qn <= 0 + || !mpn_zero_p (u->_mp_d, limb_cnt) + || (u->_mp_d[limb_cnt] + & (((mp_limb_t) 1 << bit_index) - 1))); + else + adjust = 0; + + if (qn <= 0) + qn = 0; + else + { + qp = MPZ_REALLOC (q, qn); + + if (bit_index != 0) + { + mpn_rshift (qp, u->_mp_d + limb_cnt, qn, bit_index); + qn -= qp[qn - 1] == 0; + } + else + { + mpn_copyi (qp, u->_mp_d + limb_cnt, qn); + } + } + + q->_mp_size = qn; + + if (adjust) + mpz_add_ui (q, q, 1); + if (un < 0) + mpz_neg (q, q); +} + +static void +mpz_div_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bit_index, + enum mpz_div_round_mode mode) +{ + mp_size_t us, un, rn; + mp_ptr rp; + mp_limb_t mask; + + us = u->_mp_size; + if (us == 0 || bit_index == 0) + { + r->_mp_size = 0; + return; + } + rn = (bit_index + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; + assert (rn > 0); + + rp = MPZ_REALLOC (r, rn); + un = GMP_ABS (us); + + mask = GMP_LIMB_MAX >> (rn * GMP_LIMB_BITS - bit_index); + + if (rn > un) + { + /* Quotient (with truncation) is zero, and remainder is + non-zero */ + if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */ + { + /* Have to negate and sign extend. */ + mp_size_t i; + + gmp_assert_nocarry (! mpn_neg (rp, u->_mp_d, un)); + for (i = un; i < rn - 1; i++) + rp[i] = GMP_LIMB_MAX; + + rp[rn-1] = mask; + us = -us; + } + else + { + /* Just copy */ + if (r != u) + mpn_copyi (rp, u->_mp_d, un); + + rn = un; + } + } + else + { + if (r != u) + mpn_copyi (rp, u->_mp_d, rn - 1); + + rp[rn-1] = u->_mp_d[rn-1] & mask; + + if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */ + { + /* If r != 0, compute 2^{bit_count} - r. */ + mpn_neg (rp, rp, rn); + + rp[rn-1] &= mask; + + /* us is not used for anything else, so we can modify it + here to indicate flipped sign. */ + us = -us; + } + } + rn = mpn_normalized_size (rp, rn); + r->_mp_size = us < 0 ? -rn : rn; +} + +void +mpz_cdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_q_2exp (r, u, cnt, GMP_DIV_CEIL); +} + +void +mpz_fdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_q_2exp (r, u, cnt, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_q_2exp (r, u, cnt, GMP_DIV_TRUNC); +} + +void +mpz_cdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_r_2exp (r, u, cnt, GMP_DIV_CEIL); +} + +void +mpz_fdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_r_2exp (r, u, cnt, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_r_2exp (r, u, cnt, GMP_DIV_TRUNC); +} + +void +mpz_divexact (mpz_t q, const mpz_t n, const mpz_t d) +{ + gmp_assert_nocarry (mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC)); +} + +int +mpz_divisible_p (const mpz_t n, const mpz_t d) +{ + return mpz_div_qr (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0; +} + +int +mpz_congruent_p (const mpz_t a, const mpz_t b, const mpz_t m) +{ + mpz_t t; + int res; + + /* a == b (mod 0) iff a == b */ + if (mpz_sgn (m) == 0) + return (mpz_cmp (a, b) == 0); + + mpz_init (t); + mpz_sub (t, a, b); + res = mpz_divisible_p (t, m); + mpz_clear (t); + + return res; +} + +static unsigned long +mpz_div_qr_ui (mpz_t q, mpz_t r, + const mpz_t n, unsigned long d, enum mpz_div_round_mode mode) +{ + unsigned long ret; + mpz_t rr, dd; + + mpz_init (rr); + mpz_init_set_ui (dd, d); + mpz_div_qr (q, rr, n, dd, mode); + mpz_clear (dd); + ret = mpz_get_ui (rr); + + if (r) + mpz_swap (r, rr); + mpz_clear (rr); + + return ret; +} + +unsigned long +mpz_cdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, r, n, d, GMP_DIV_CEIL); +} + +unsigned long +mpz_fdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, r, n, d, GMP_DIV_FLOOR); +} + +unsigned long +mpz_tdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, r, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_cdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_CEIL); +} + +unsigned long +mpz_fdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_FLOOR); +} + +unsigned long +mpz_tdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_cdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_CEIL); +} +unsigned long +mpz_fdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR); +} +unsigned long +mpz_tdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_cdiv_ui (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_CEIL); +} + +unsigned long +mpz_fdiv_ui (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_FLOOR); +} + +unsigned long +mpz_tdiv_ui (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_mod_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR); +} + +void +mpz_divexact_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + gmp_assert_nocarry (mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC)); +} + +int +mpz_divisible_ui_p (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0; +} + + +/* GCD */ +static mp_limb_t +mpn_gcd_11 (mp_limb_t u, mp_limb_t v) +{ + unsigned shift; + + assert ( (u | v) > 0); + + if (u == 0) + return v; + else if (v == 0) + return u; + + gmp_ctz (shift, u | v); + + u >>= shift; + v >>= shift; + + if ( (u & 1) == 0) + MP_LIMB_T_SWAP (u, v); + + while ( (v & 1) == 0) + v >>= 1; + + while (u != v) + { + if (u > v) + { + u -= v; + do + u >>= 1; + while ( (u & 1) == 0); + } + else + { + v -= u; + do + v >>= 1; + while ( (v & 1) == 0); + } + } + return u << shift; +} + +unsigned long +mpz_gcd_ui (mpz_t g, const mpz_t u, unsigned long v) +{ + mpz_t t; + mpz_init_set_ui(t, v); + mpz_gcd (t, u, t); + if (v > 0) + v = mpz_get_ui (t); + + if (g) + mpz_swap (t, g); + + mpz_clear (t); + + return v; +} + +static mp_bitcnt_t +mpz_make_odd (mpz_t r) +{ + mp_bitcnt_t shift; + + assert (r->_mp_size > 0); + /* Count trailing zeros, equivalent to mpn_scan1, because we know that there is a 1 */ + shift = mpn_common_scan (r->_mp_d[0], 0, r->_mp_d, 0, 0); + mpz_tdiv_q_2exp (r, r, shift); + + return shift; +} + +void +mpz_gcd (mpz_t g, const mpz_t u, const mpz_t v) +{ + mpz_t tu, tv; + mp_bitcnt_t uz, vz, gz; + + if (u->_mp_size == 0) + { + mpz_abs (g, v); + return; + } + if (v->_mp_size == 0) + { + mpz_abs (g, u); + return; + } + + mpz_init (tu); + mpz_init (tv); + + mpz_abs (tu, u); + uz = mpz_make_odd (tu); + mpz_abs (tv, v); + vz = mpz_make_odd (tv); + gz = GMP_MIN (uz, vz); + + if (tu->_mp_size < tv->_mp_size) + mpz_swap (tu, tv); + + mpz_tdiv_r (tu, tu, tv); + if (tu->_mp_size == 0) + { + mpz_swap (g, tv); + } + else + for (;;) + { + int c; + + mpz_make_odd (tu); + c = mpz_cmp (tu, tv); + if (c == 0) + { + mpz_swap (g, tu); + break; + } + if (c < 0) + mpz_swap (tu, tv); + + if (tv->_mp_size == 1) + { + mp_limb_t vl = tv->_mp_d[0]; + mp_limb_t ul = mpz_tdiv_ui (tu, vl); + mpz_set_ui (g, mpn_gcd_11 (ul, vl)); + break; + } + mpz_sub (tu, tu, tv); + } + mpz_clear (tu); + mpz_clear (tv); + mpz_mul_2exp (g, g, gz); +} + +void +mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, const mpz_t u, const mpz_t v) +{ + mpz_t tu, tv, s0, s1, t0, t1; + mp_bitcnt_t uz, vz, gz; + mp_bitcnt_t power; + + if (u->_mp_size == 0) + { + /* g = 0 u + sgn(v) v */ + signed long sign = mpz_sgn (v); + mpz_abs (g, v); + if (s) + s->_mp_size = 0; + if (t) + mpz_set_si (t, sign); + return; + } + + if (v->_mp_size == 0) + { + /* g = sgn(u) u + 0 v */ + signed long sign = mpz_sgn (u); + mpz_abs (g, u); + if (s) + mpz_set_si (s, sign); + if (t) + t->_mp_size = 0; + return; + } + + mpz_init (tu); + mpz_init (tv); + mpz_init (s0); + mpz_init (s1); + mpz_init (t0); + mpz_init (t1); + + mpz_abs (tu, u); + uz = mpz_make_odd (tu); + mpz_abs (tv, v); + vz = mpz_make_odd (tv); + gz = GMP_MIN (uz, vz); + + uz -= gz; + vz -= gz; + + /* Cofactors corresponding to odd gcd. gz handled later. */ + if (tu->_mp_size < tv->_mp_size) + { + mpz_swap (tu, tv); + MPZ_SRCPTR_SWAP (u, v); + MPZ_PTR_SWAP (s, t); + MP_BITCNT_T_SWAP (uz, vz); + } + + /* Maintain + * + * u = t0 tu + t1 tv + * v = s0 tu + s1 tv + * + * where u and v denote the inputs with common factors of two + * eliminated, and det (s0, t0; s1, t1) = 2^p. Then + * + * 2^p tu = s1 u - t1 v + * 2^p tv = -s0 u + t0 v + */ + + /* After initial division, tu = q tv + tu', we have + * + * u = 2^uz (tu' + q tv) + * v = 2^vz tv + * + * or + * + * t0 = 2^uz, t1 = 2^uz q + * s0 = 0, s1 = 2^vz + */ + + mpz_setbit (t0, uz); + mpz_tdiv_qr (t1, tu, tu, tv); + mpz_mul_2exp (t1, t1, uz); + + mpz_setbit (s1, vz); + power = uz + vz; + + if (tu->_mp_size > 0) + { + mp_bitcnt_t shift; + shift = mpz_make_odd (tu); + mpz_mul_2exp (t0, t0, shift); + mpz_mul_2exp (s0, s0, shift); + power += shift; + + for (;;) + { + int c; + c = mpz_cmp (tu, tv); + if (c == 0) + break; + + if (c < 0) + { + /* tv = tv' + tu + * + * u = t0 tu + t1 (tv' + tu) = (t0 + t1) tu + t1 tv' + * v = s0 tu + s1 (tv' + tu) = (s0 + s1) tu + s1 tv' */ + + mpz_sub (tv, tv, tu); + mpz_add (t0, t0, t1); + mpz_add (s0, s0, s1); + + shift = mpz_make_odd (tv); + mpz_mul_2exp (t1, t1, shift); + mpz_mul_2exp (s1, s1, shift); + } + else + { + mpz_sub (tu, tu, tv); + mpz_add (t1, t0, t1); + mpz_add (s1, s0, s1); + + shift = mpz_make_odd (tu); + mpz_mul_2exp (t0, t0, shift); + mpz_mul_2exp (s0, s0, shift); + } + power += shift; + } + } + + /* Now tv = odd part of gcd, and -s0 and t0 are corresponding + cofactors. */ + + mpz_mul_2exp (tv, tv, gz); + mpz_neg (s0, s0); + + /* 2^p g = s0 u + t0 v. Eliminate one factor of two at a time. To + adjust cofactors, we need u / g and v / g */ + + mpz_divexact (s1, v, tv); + mpz_abs (s1, s1); + mpz_divexact (t1, u, tv); + mpz_abs (t1, t1); + + while (power-- > 0) + { + /* s0 u + t0 v = (s0 - v/g) u - (t0 + u/g) v */ + if (mpz_odd_p (s0) || mpz_odd_p (t0)) + { + mpz_sub (s0, s0, s1); + mpz_add (t0, t0, t1); + } + assert (mpz_even_p (t0) && mpz_even_p (s0)); + mpz_tdiv_q_2exp (s0, s0, 1); + mpz_tdiv_q_2exp (t0, t0, 1); + } + + /* Arrange so that |s| < |u| / 2g */ + mpz_add (s1, s0, s1); + if (mpz_cmpabs (s0, s1) > 0) + { + mpz_swap (s0, s1); + mpz_sub (t0, t0, t1); + } + if (u->_mp_size < 0) + mpz_neg (s0, s0); + if (v->_mp_size < 0) + mpz_neg (t0, t0); + + mpz_swap (g, tv); + if (s) + mpz_swap (s, s0); + if (t) + mpz_swap (t, t0); + + mpz_clear (tu); + mpz_clear (tv); + mpz_clear (s0); + mpz_clear (s1); + mpz_clear (t0); + mpz_clear (t1); +} + +void +mpz_lcm (mpz_t r, const mpz_t u, const mpz_t v) +{ + mpz_t g; + + if (u->_mp_size == 0 || v->_mp_size == 0) + { + r->_mp_size = 0; + return; + } + + mpz_init (g); + + mpz_gcd (g, u, v); + mpz_divexact (g, u, g); + mpz_mul (r, g, v); + + mpz_clear (g); + mpz_abs (r, r); +} + +void +mpz_lcm_ui (mpz_t r, const mpz_t u, unsigned long v) +{ + if (v == 0 || u->_mp_size == 0) + { + r->_mp_size = 0; + return; + } + + v /= mpz_gcd_ui (NULL, u, v); + mpz_mul_ui (r, u, v); + + mpz_abs (r, r); +} + +int +mpz_invert (mpz_t r, const mpz_t u, const mpz_t m) +{ + mpz_t g, tr; + int invertible; + + if (u->_mp_size == 0 || mpz_cmpabs_ui (m, 1) <= 0) + return 0; + + mpz_init (g); + mpz_init (tr); + + mpz_gcdext (g, tr, NULL, u, m); + invertible = (mpz_cmp_ui (g, 1) == 0); + + if (invertible) + { + if (tr->_mp_size < 0) + { + if (m->_mp_size >= 0) + mpz_add (tr, tr, m); + else + mpz_sub (tr, tr, m); + } + mpz_swap (r, tr); + } + + mpz_clear (g); + mpz_clear (tr); + return invertible; +} + + +/* Higher level operations (sqrt, pow and root) */ + +void +mpz_pow_ui (mpz_t r, const mpz_t b, unsigned long e) +{ + unsigned long bit; + mpz_t tr; + mpz_init_set_ui (tr, 1); + + bit = GMP_ULONG_HIGHBIT; + do + { + mpz_mul (tr, tr, tr); + if (e & bit) + mpz_mul (tr, tr, b); + bit >>= 1; + } + while (bit > 0); + + mpz_swap (r, tr); + mpz_clear (tr); +} + +void +mpz_ui_pow_ui (mpz_t r, unsigned long blimb, unsigned long e) +{ + mpz_t b; + + mpz_init_set_ui (b, blimb); + mpz_pow_ui (r, b, e); + mpz_clear (b); +} + +void +mpz_powm (mpz_t r, const mpz_t b, const mpz_t e, const mpz_t m) +{ + mpz_t tr; + mpz_t base; + mp_size_t en, mn; + mp_srcptr mp; + struct gmp_div_inverse minv; + unsigned shift; + mp_ptr tp = NULL; + + en = GMP_ABS (e->_mp_size); + mn = GMP_ABS (m->_mp_size); + if (mn == 0) + gmp_die ("mpz_powm: Zero modulo."); + + if (en == 0) + { + mpz_set_ui (r, 1); + return; + } + + mp = m->_mp_d; + mpn_div_qr_invert (&minv, mp, mn); + shift = minv.shift; + + if (shift > 0) + { + /* To avoid shifts, we do all our reductions, except the final + one, using a *normalized* m. */ + minv.shift = 0; + + tp = gmp_xalloc_limbs (mn); + gmp_assert_nocarry (mpn_lshift (tp, mp, mn, shift)); + mp = tp; + } + + mpz_init (base); + + if (e->_mp_size < 0) + { + if (!mpz_invert (base, b, m)) + gmp_die ("mpz_powm: Negative exponent and non-invertible base."); + } + else + { + mp_size_t bn; + mpz_abs (base, b); + + bn = base->_mp_size; + if (bn >= mn) + { + mpn_div_qr_preinv (NULL, base->_mp_d, base->_mp_size, mp, mn, &minv); + bn = mn; + } + + /* We have reduced the absolute value. Now take care of the + sign. Note that we get zero represented non-canonically as + m. */ + if (b->_mp_size < 0) + { + mp_ptr bp = MPZ_REALLOC (base, mn); + gmp_assert_nocarry (mpn_sub (bp, mp, mn, bp, bn)); + bn = mn; + } + base->_mp_size = mpn_normalized_size (base->_mp_d, bn); + } + mpz_init_set_ui (tr, 1); + + while (--en >= 0) + { + mp_limb_t w = e->_mp_d[en]; + mp_limb_t bit; + + bit = GMP_LIMB_HIGHBIT; + do + { + mpz_mul (tr, tr, tr); + if (w & bit) + mpz_mul (tr, tr, base); + if (tr->_mp_size > mn) + { + mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv); + tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn); + } + bit >>= 1; + } + while (bit > 0); + } + + /* Final reduction */ + if (tr->_mp_size >= mn) + { + minv.shift = shift; + mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv); + tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn); + } + if (tp) + gmp_free (tp); + + mpz_swap (r, tr); + mpz_clear (tr); + mpz_clear (base); +} + +void +mpz_powm_ui (mpz_t r, const mpz_t b, unsigned long elimb, const mpz_t m) +{ + mpz_t e; + + mpz_init_set_ui (e, elimb); + mpz_powm (r, b, e, m); + mpz_clear (e); +} + +/* x=trunc(y^(1/z)), r=y-x^z */ +void +mpz_rootrem (mpz_t x, mpz_t r, const mpz_t y, unsigned long z) +{ + int sgn; + mpz_t t, u; + + sgn = y->_mp_size < 0; + if ((~z & sgn) != 0) + gmp_die ("mpz_rootrem: Negative argument, with even root."); + if (z == 0) + gmp_die ("mpz_rootrem: Zeroth root."); + + if (mpz_cmpabs_ui (y, 1) <= 0) { + if (x) + mpz_set (x, y); + if (r) + r->_mp_size = 0; + return; + } + + mpz_init (u); + mpz_init (t); + mpz_setbit (t, mpz_sizeinbase (y, 2) / z + 1); + + if (z == 2) /* simplify sqrt loop: z-1 == 1 */ + do { + mpz_swap (u, t); /* u = x */ + mpz_tdiv_q (t, y, u); /* t = y/x */ + mpz_add (t, t, u); /* t = y/x + x */ + mpz_tdiv_q_2exp (t, t, 1); /* x'= (y/x + x)/2 */ + } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */ + else /* z != 2 */ { + mpz_t v; + + mpz_init (v); + if (sgn) + mpz_neg (t, t); + + do { + mpz_swap (u, t); /* u = x */ + mpz_pow_ui (t, u, z - 1); /* t = x^(z-1) */ + mpz_tdiv_q (t, y, t); /* t = y/x^(z-1) */ + mpz_mul_ui (v, u, z - 1); /* v = x*(z-1) */ + mpz_add (t, t, v); /* t = y/x^(z-1) + x*(z-1) */ + mpz_tdiv_q_ui (t, t, z); /* x'=(y/x^(z-1) + x*(z-1))/z */ + } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */ + + mpz_clear (v); + } + + if (r) { + mpz_pow_ui (t, u, z); + mpz_sub (r, y, t); + } + if (x) + mpz_swap (x, u); + mpz_clear (u); + mpz_clear (t); +} + +int +mpz_root (mpz_t x, const mpz_t y, unsigned long z) +{ + int res; + mpz_t r; + + mpz_init (r); + mpz_rootrem (x, r, y, z); + res = r->_mp_size == 0; + mpz_clear (r); + + return res; +} + +/* Compute s = floor(sqrt(u)) and r = u - s^2. Allows r == NULL */ +void +mpz_sqrtrem (mpz_t s, mpz_t r, const mpz_t u) +{ + mpz_rootrem (s, r, u, 2); +} + +void +mpz_sqrt (mpz_t s, const mpz_t u) +{ + mpz_rootrem (s, NULL, u, 2); +} + +int +mpz_perfect_square_p (const mpz_t u) +{ + if (u->_mp_size <= 0) + return (u->_mp_size == 0); + else + return mpz_root (NULL, u, 2); +} + +int +mpn_perfect_square_p (mp_srcptr p, mp_size_t n) +{ + mpz_t t; + + assert (n > 0); + assert (p [n-1] != 0); + return mpz_root (NULL, mpz_roinit_normal_n (t, p, n), 2); +} + +mp_size_t +mpn_sqrtrem (mp_ptr sp, mp_ptr rp, mp_srcptr p, mp_size_t n) +{ + mpz_t s, r, u; + mp_size_t res; + + assert (n > 0); + assert (p [n-1] != 0); + + mpz_init (r); + mpz_init (s); + mpz_rootrem (s, r, mpz_roinit_normal_n (u, p, n), 2); + + assert (s->_mp_size == (n+1)/2); + mpn_copyd (sp, s->_mp_d, s->_mp_size); + mpz_clear (s); + res = r->_mp_size; + if (rp) + mpn_copyd (rp, r->_mp_d, res); + mpz_clear (r); + return res; +} + +/* Combinatorics */ + +void +mpz_mfac_uiui (mpz_t x, unsigned long n, unsigned long m) +{ + mpz_set_ui (x, n + (n == 0)); + if (m + 1 < 2) return; + while (n > m + 1) + mpz_mul_ui (x, x, n -= m); +} + +void +mpz_2fac_ui (mpz_t x, unsigned long n) +{ + mpz_mfac_uiui (x, n, 2); +} + +void +mpz_fac_ui (mpz_t x, unsigned long n) +{ + mpz_mfac_uiui (x, n, 1); +} + +void +mpz_bin_uiui (mpz_t r, unsigned long n, unsigned long k) +{ + mpz_t t; + + mpz_set_ui (r, k <= n); + + if (k > (n >> 1)) + k = (k <= n) ? n - k : 0; + + mpz_init (t); + mpz_fac_ui (t, k); + + for (; k > 0; --k) + mpz_mul_ui (r, r, n--); + + mpz_divexact (r, r, t); + mpz_clear (t); +} + + +/* Primality testing */ + +/* Computes Kronecker (a/b) with odd b, a!=0 and GCD(a,b) = 1 */ +/* Adapted from JACOBI_BASE_METHOD==4 in mpn/generic/jacbase.c */ +static int +gmp_jacobi_coprime (mp_limb_t a, mp_limb_t b) +{ + int c, bit = 0; + + assert (b & 1); + assert (a != 0); + /* assert (mpn_gcd_11 (a, b) == 1); */ + + /* Below, we represent a and b shifted right so that the least + significant one bit is implicit. */ + b >>= 1; + + gmp_ctz(c, a); + a >>= 1; + + do + { + a >>= c; + /* (2/b) = -1 if b = 3 or 5 mod 8 */ + bit ^= c & (b ^ (b >> 1)); + if (a < b) + { + bit ^= a & b; + a = b - a; + b -= a; + } + else + { + a -= b; + assert (a != 0); + } + + gmp_ctz(c, a); + ++c; + } + while (b > 0); + + return bit & 1 ? -1 : 1; +} + +static void +gmp_lucas_step_k_2k (mpz_t V, mpz_t Qk, const mpz_t n) +{ + mpz_mod (Qk, Qk, n); + /* V_{2k} <- V_k ^ 2 - 2Q^k */ + mpz_mul (V, V, V); + mpz_submul_ui (V, Qk, 2); + mpz_tdiv_r (V, V, n); + /* Q^{2k} = (Q^k)^2 */ + mpz_mul (Qk, Qk, Qk); +} + +/* Computes V_k, Q^k (mod n) for the Lucas' sequence */ +/* with P=1, Q=Q; k = (n>>b0)|1. */ +/* Requires an odd n > 4; b0 > 0; -2*Q must not overflow a long */ +/* Returns (U_k == 0) and sets V=V_k and Qk=Q^k. */ +static int +gmp_lucas_mod (mpz_t V, mpz_t Qk, long Q, + mp_bitcnt_t b0, const mpz_t n) +{ + mp_bitcnt_t bs; + mpz_t U; + int res; + + assert (b0 > 0); + assert (Q <= - (LONG_MIN / 2)); + assert (Q >= - (LONG_MAX / 2)); + assert (mpz_cmp_ui (n, 4) > 0); + assert (mpz_odd_p (n)); + + mpz_init_set_ui (U, 1); /* U1 = 1 */ + mpz_set_ui (V, 1); /* V1 = 1 */ + mpz_set_si (Qk, Q); + + for (bs = mpz_sizeinbase (n, 2) - 1; --bs >= b0;) + { + /* U_{2k} <- U_k * V_k */ + mpz_mul (U, U, V); + /* V_{2k} <- V_k ^ 2 - 2Q^k */ + /* Q^{2k} = (Q^k)^2 */ + gmp_lucas_step_k_2k (V, Qk, n); + + /* A step k->k+1 is performed if the bit in $n$ is 1 */ + /* mpz_tstbit(n,bs) or the bit is 0 in $n$ but */ + /* should be 1 in $n+1$ (bs == b0) */ + if (b0 == bs || mpz_tstbit (n, bs)) + { + /* Q^{k+1} <- Q^k * Q */ + mpz_mul_si (Qk, Qk, Q); + /* U_{k+1} <- (U_k + V_k) / 2 */ + mpz_swap (U, V); /* Keep in V the old value of U_k */ + mpz_add (U, U, V); + /* We have to compute U/2, so we need an even value, */ + /* equivalent (mod n) */ + if (mpz_odd_p (U)) + mpz_add (U, U, n); + mpz_tdiv_q_2exp (U, U, 1); + /* V_{k+1} <-(D*U_k + V_k) / 2 = + U_{k+1} + (D-1)/2*U_k = U_{k+1} - 2Q*U_k */ + mpz_mul_si (V, V, -2*Q); + mpz_add (V, U, V); + mpz_tdiv_r (V, V, n); + } + mpz_tdiv_r (U, U, n); + } + + res = U->_mp_size == 0; + mpz_clear (U); + return res; +} + +/* Performs strong Lucas' test on x, with parameters suggested */ +/* for the BPSW test. Qk is only passed to recycle a variable. */ +/* Requires GCD (x,6) = 1.*/ +static int +gmp_stronglucas (const mpz_t x, mpz_t Qk) +{ + mp_bitcnt_t b0; + mpz_t V, n; + mp_limb_t maxD, D; /* The absolute value is stored. */ + long Q; + mp_limb_t tl; + + /* Test on the absolute value. */ + mpz_roinit_normal_n (n, x->_mp_d, GMP_ABS (x->_mp_size)); + + assert (mpz_odd_p (n)); + /* assert (mpz_gcd_ui (NULL, n, 6) == 1); */ + if (mpz_root (Qk, n, 2)) + return 0; /* A square is composite. */ + + /* Check Ds up to square root (in case, n is prime) + or avoid overflows */ + maxD = (Qk->_mp_size == 1) ? Qk->_mp_d [0] - 1 : GMP_LIMB_MAX; + + D = 3; + /* Search a D such that (D/n) = -1 in the sequence 5,-7,9,-11,.. */ + /* For those Ds we have (D/n) = (n/|D|) */ + do + { + if (D >= maxD) + return 1 + (D != GMP_LIMB_MAX); /* (1 + ! ~ D) */ + D += 2; + tl = mpz_tdiv_ui (n, D); + if (tl == 0) + return 0; + } + while (gmp_jacobi_coprime (tl, D) == 1); + + mpz_init (V); + + /* n-(D/n) = n+1 = d*2^{b0}, with d = (n>>b0) | 1 */ + b0 = mpz_scan0 (n, 0); + + /* D= P^2 - 4Q; P = 1; Q = (1-D)/4 */ + Q = (D & 2) ? (long) (D >> 2) + 1 : -(long) (D >> 2); + + if (! gmp_lucas_mod (V, Qk, Q, b0, n)) /* If Ud != 0 */ + while (V->_mp_size != 0 && --b0 != 0) /* while Vk != 0 */ + /* V <- V ^ 2 - 2Q^k */ + /* Q^{2k} = (Q^k)^2 */ + gmp_lucas_step_k_2k (V, Qk, n); + + mpz_clear (V); + return (b0 != 0); +} + +static int +gmp_millerrabin (const mpz_t n, const mpz_t nm1, mpz_t y, + const mpz_t q, mp_bitcnt_t k) +{ + assert (k > 0); + + /* Caller must initialize y to the base. */ + mpz_powm (y, y, q, n); + + if (mpz_cmp_ui (y, 1) == 0 || mpz_cmp (y, nm1) == 0) + return 1; + + while (--k > 0) + { + mpz_powm_ui (y, y, 2, n); + if (mpz_cmp (y, nm1) == 0) + return 1; + /* y == 1 means that the previous y was a non-trivial square root + of 1 (mod n). y == 0 means that n is a power of the base. + In either case, n is not prime. */ + if (mpz_cmp_ui (y, 1) <= 0) + return 0; + } + return 0; +} + +/* This product is 0xc0cfd797, and fits in 32 bits. */ +#define GMP_PRIME_PRODUCT \ + (3UL*5UL*7UL*11UL*13UL*17UL*19UL*23UL*29UL) + +/* Bit (p+1)/2 is set, for each odd prime <= 61 */ +#define GMP_PRIME_MASK 0xc96996dcUL + +int +mpz_probab_prime_p (const mpz_t n, int reps) +{ + mpz_t nm1; + mpz_t q; + mpz_t y; + mp_bitcnt_t k; + int is_prime; + int j; + + /* Note that we use the absolute value of n only, for compatibility + with the real GMP. */ + if (mpz_even_p (n)) + return (mpz_cmpabs_ui (n, 2) == 0) ? 2 : 0; + + /* Above test excludes n == 0 */ + assert (n->_mp_size != 0); + + if (mpz_cmpabs_ui (n, 64) < 0) + return (GMP_PRIME_MASK >> (n->_mp_d[0] >> 1)) & 2; + + if (mpz_gcd_ui (NULL, n, GMP_PRIME_PRODUCT) != 1) + return 0; + + /* All prime factors are >= 31. */ + if (mpz_cmpabs_ui (n, 31*31) < 0) + return 2; + + mpz_init (nm1); + mpz_init (q); + + /* Find q and k, where q is odd and n = 1 + 2**k * q. */ + mpz_abs (nm1, n); + nm1->_mp_d[0] -= 1; + k = mpz_scan1 (nm1, 0); + mpz_tdiv_q_2exp (q, nm1, k); + + /* BPSW test */ + mpz_init_set_ui (y, 2); + is_prime = gmp_millerrabin (n, nm1, y, q, k) && gmp_stronglucas (n, y); + reps -= 24; /* skip the first 24 repetitions */ + + /* Use Miller-Rabin, with a deterministic sequence of bases, a[j] = + j^2 + j + 41 using Euler's polynomial. We potentially stop early, + if a[j] >= n - 1. Since n >= 31*31, this can happen only if reps > + 30 (a[30] == 971 > 31*31 == 961). */ + + for (j = 0; is_prime & (j < reps); j++) + { + mpz_set_ui (y, (unsigned long) j*j+j+41); + if (mpz_cmp (y, nm1) >= 0) + { + /* Don't try any further bases. This "early" break does not affect + the result for any reasonable reps value (<=5000 was tested) */ + assert (j >= 30); + break; + } + is_prime = gmp_millerrabin (n, nm1, y, q, k); + } + mpz_clear (nm1); + mpz_clear (q); + mpz_clear (y); + + return is_prime; +} + + +/* Logical operations and bit manipulation. */ + +/* Numbers are treated as if represented in two's complement (and + infinitely sign extended). For a negative values we get the two's + complement from -x = ~x + 1, where ~ is bitwise complement. + Negation transforms + + xxxx10...0 + + into + + yyyy10...0 + + where yyyy is the bitwise complement of xxxx. So least significant + bits, up to and including the first one bit, are unchanged, and + the more significant bits are all complemented. + + To change a bit from zero to one in a negative number, subtract the + corresponding power of two from the absolute value. This can never + underflow. To change a bit from one to zero, add the corresponding + power of two, and this might overflow. E.g., if x = -001111, the + two's complement is 110001. Clearing the least significant bit, we + get two's complement 110000, and -010000. */ + +int +mpz_tstbit (const mpz_t d, mp_bitcnt_t bit_index) +{ + mp_size_t limb_index; + unsigned shift; + mp_size_t ds; + mp_size_t dn; + mp_limb_t w; + int bit; + + ds = d->_mp_size; + dn = GMP_ABS (ds); + limb_index = bit_index / GMP_LIMB_BITS; + if (limb_index >= dn) + return ds < 0; + + shift = bit_index % GMP_LIMB_BITS; + w = d->_mp_d[limb_index]; + bit = (w >> shift) & 1; + + if (ds < 0) + { + /* d < 0. Check if any of the bits below is set: If so, our bit + must be complemented. */ + if (shift > 0 && (mp_limb_t) (w << (GMP_LIMB_BITS - shift)) > 0) + return bit ^ 1; + while (--limb_index >= 0) + if (d->_mp_d[limb_index] > 0) + return bit ^ 1; + } + return bit; +} + +static void +mpz_abs_add_bit (mpz_t d, mp_bitcnt_t bit_index) +{ + mp_size_t dn, limb_index; + mp_limb_t bit; + mp_ptr dp; + + dn = GMP_ABS (d->_mp_size); + + limb_index = bit_index / GMP_LIMB_BITS; + bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS); + + if (limb_index >= dn) + { + mp_size_t i; + /* The bit should be set outside of the end of the number. + We have to increase the size of the number. */ + dp = MPZ_REALLOC (d, limb_index + 1); + + dp[limb_index] = bit; + for (i = dn; i < limb_index; i++) + dp[i] = 0; + dn = limb_index + 1; + } + else + { + mp_limb_t cy; + + dp = d->_mp_d; + + cy = mpn_add_1 (dp + limb_index, dp + limb_index, dn - limb_index, bit); + if (cy > 0) + { + dp = MPZ_REALLOC (d, dn + 1); + dp[dn++] = cy; + } + } + + d->_mp_size = (d->_mp_size < 0) ? - dn : dn; +} + +static void +mpz_abs_sub_bit (mpz_t d, mp_bitcnt_t bit_index) +{ + mp_size_t dn, limb_index; + mp_ptr dp; + mp_limb_t bit; + + dn = GMP_ABS (d->_mp_size); + dp = d->_mp_d; + + limb_index = bit_index / GMP_LIMB_BITS; + bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS); + + assert (limb_index < dn); + + gmp_assert_nocarry (mpn_sub_1 (dp + limb_index, dp + limb_index, + dn - limb_index, bit)); + dn = mpn_normalized_size (dp, dn); + d->_mp_size = (d->_mp_size < 0) ? - dn : dn; +} + +void +mpz_setbit (mpz_t d, mp_bitcnt_t bit_index) +{ + if (!mpz_tstbit (d, bit_index)) + { + if (d->_mp_size >= 0) + mpz_abs_add_bit (d, bit_index); + else + mpz_abs_sub_bit (d, bit_index); + } +} + +void +mpz_clrbit (mpz_t d, mp_bitcnt_t bit_index) +{ + if (mpz_tstbit (d, bit_index)) + { + if (d->_mp_size >= 0) + mpz_abs_sub_bit (d, bit_index); + else + mpz_abs_add_bit (d, bit_index); + } +} + +void +mpz_combit (mpz_t d, mp_bitcnt_t bit_index) +{ + if (mpz_tstbit (d, bit_index) ^ (d->_mp_size < 0)) + mpz_abs_sub_bit (d, bit_index); + else + mpz_abs_add_bit (d, bit_index); +} + +void +mpz_com (mpz_t r, const mpz_t u) +{ + mpz_add_ui (r, u, 1); + mpz_neg (r, r); +} + +void +mpz_and (mpz_t r, const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, rn, i; + mp_ptr up, vp, rp; + + mp_limb_t ux, vx, rx; + mp_limb_t uc, vc, rc; + mp_limb_t ul, vl, rl; + + un = GMP_ABS (u->_mp_size); + vn = GMP_ABS (v->_mp_size); + if (un < vn) + { + MPZ_SRCPTR_SWAP (u, v); + MP_SIZE_T_SWAP (un, vn); + } + if (vn == 0) + { + r->_mp_size = 0; + return; + } + + uc = u->_mp_size < 0; + vc = v->_mp_size < 0; + rc = uc & vc; + + ux = -uc; + vx = -vc; + rx = -rc; + + /* If the smaller input is positive, higher limbs don't matter. */ + rn = vx ? un : vn; + + rp = MPZ_REALLOC (r, rn + (mp_size_t) rc); + + up = u->_mp_d; + vp = v->_mp_d; + + i = 0; + do + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + vl = (vp[i] ^ vx) + vc; + vc = vl < vc; + + rl = ( (ul & vl) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + while (++i < vn); + assert (vc == 0); + + for (; i < rn; i++) + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + rl = ( (ul & vx) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + if (rc) + rp[rn++] = rc; + else + rn = mpn_normalized_size (rp, rn); + + r->_mp_size = rx ? -rn : rn; +} + +void +mpz_ior (mpz_t r, const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, rn, i; + mp_ptr up, vp, rp; + + mp_limb_t ux, vx, rx; + mp_limb_t uc, vc, rc; + mp_limb_t ul, vl, rl; + + un = GMP_ABS (u->_mp_size); + vn = GMP_ABS (v->_mp_size); + if (un < vn) + { + MPZ_SRCPTR_SWAP (u, v); + MP_SIZE_T_SWAP (un, vn); + } + if (vn == 0) + { + mpz_set (r, u); + return; + } + + uc = u->_mp_size < 0; + vc = v->_mp_size < 0; + rc = uc | vc; + + ux = -uc; + vx = -vc; + rx = -rc; + + /* If the smaller input is negative, by sign extension higher limbs + don't matter. */ + rn = vx ? vn : un; + + rp = MPZ_REALLOC (r, rn + (mp_size_t) rc); + + up = u->_mp_d; + vp = v->_mp_d; + + i = 0; + do + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + vl = (vp[i] ^ vx) + vc; + vc = vl < vc; + + rl = ( (ul | vl) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + while (++i < vn); + assert (vc == 0); + + for (; i < rn; i++) + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + rl = ( (ul | vx) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + if (rc) + rp[rn++] = rc; + else + rn = mpn_normalized_size (rp, rn); + + r->_mp_size = rx ? -rn : rn; +} + +void +mpz_xor (mpz_t r, const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, i; + mp_ptr up, vp, rp; + + mp_limb_t ux, vx, rx; + mp_limb_t uc, vc, rc; + mp_limb_t ul, vl, rl; + + un = GMP_ABS (u->_mp_size); + vn = GMP_ABS (v->_mp_size); + if (un < vn) + { + MPZ_SRCPTR_SWAP (u, v); + MP_SIZE_T_SWAP (un, vn); + } + if (vn == 0) + { + mpz_set (r, u); + return; + } + + uc = u->_mp_size < 0; + vc = v->_mp_size < 0; + rc = uc ^ vc; + + ux = -uc; + vx = -vc; + rx = -rc; + + rp = MPZ_REALLOC (r, un + (mp_size_t) rc); + + up = u->_mp_d; + vp = v->_mp_d; + + i = 0; + do + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + vl = (vp[i] ^ vx) + vc; + vc = vl < vc; + + rl = (ul ^ vl ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + while (++i < vn); + assert (vc == 0); + + for (; i < un; i++) + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + rl = (ul ^ ux) + rc; + rc = rl < rc; + rp[i] = rl; + } + if (rc) + rp[un++] = rc; + else + un = mpn_normalized_size (rp, un); + + r->_mp_size = rx ? -un : un; +} + +static unsigned +gmp_popcount_limb (mp_limb_t x) +{ + unsigned c; + + /* Do 16 bits at a time, to avoid limb-sized constants. */ + int LOCAL_SHIFT_BITS = 16; + for (c = 0; x > 0;) + { + unsigned w = x - ((x >> 1) & 0x5555); + w = ((w >> 2) & 0x3333) + (w & 0x3333); + w = (w >> 4) + w; + w = ((w >> 8) & 0x000f) + (w & 0x000f); + c += w; + if (GMP_LIMB_BITS > LOCAL_SHIFT_BITS) + x >>= LOCAL_SHIFT_BITS; + else + x = 0; + } + return c; +} + +mp_bitcnt_t +mpn_popcount (mp_srcptr p, mp_size_t n) +{ + mp_size_t i; + mp_bitcnt_t c; + + for (c = 0, i = 0; i < n; i++) + c += gmp_popcount_limb (p[i]); + + return c; +} + +mp_bitcnt_t +mpz_popcount (const mpz_t u) +{ + mp_size_t un; + + un = u->_mp_size; + + if (un < 0) + return ~(mp_bitcnt_t) 0; + + return mpn_popcount (u->_mp_d, un); +} + +mp_bitcnt_t +mpz_hamdist (const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, i; + mp_limb_t uc, vc, ul, vl, comp; + mp_srcptr up, vp; + mp_bitcnt_t c; + + un = u->_mp_size; + vn = v->_mp_size; + + if ( (un ^ vn) < 0) + return ~(mp_bitcnt_t) 0; + + comp = - (uc = vc = (un < 0)); + if (uc) + { + assert (vn < 0); + un = -un; + vn = -vn; + } + + up = u->_mp_d; + vp = v->_mp_d; + + if (un < vn) + MPN_SRCPTR_SWAP (up, un, vp, vn); + + for (i = 0, c = 0; i < vn; i++) + { + ul = (up[i] ^ comp) + uc; + uc = ul < uc; + + vl = (vp[i] ^ comp) + vc; + vc = vl < vc; + + c += gmp_popcount_limb (ul ^ vl); + } + assert (vc == 0); + + for (; i < un; i++) + { + ul = (up[i] ^ comp) + uc; + uc = ul < uc; + + c += gmp_popcount_limb (ul ^ comp); + } + + return c; +} + +mp_bitcnt_t +mpz_scan1 (const mpz_t u, mp_bitcnt_t starting_bit) +{ + mp_ptr up; + mp_size_t us, un, i; + mp_limb_t limb, ux; + + us = u->_mp_size; + un = GMP_ABS (us); + i = starting_bit / GMP_LIMB_BITS; + + /* Past the end there's no 1 bits for u>=0, or an immediate 1 bit + for u<0. Notice this test picks up any u==0 too. */ + if (i >= un) + return (us >= 0 ? ~(mp_bitcnt_t) 0 : starting_bit); + + up = u->_mp_d; + ux = 0; + limb = up[i]; + + if (starting_bit != 0) + { + if (us < 0) + { + ux = mpn_zero_p (up, i); + limb = ~ limb + ux; + ux = - (mp_limb_t) (limb >= ux); + } + + /* Mask to 0 all bits before starting_bit, thus ignoring them. */ + limb &= GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS); + } + + return mpn_common_scan (limb, i, up, un, ux); +} + +mp_bitcnt_t +mpz_scan0 (const mpz_t u, mp_bitcnt_t starting_bit) +{ + mp_ptr up; + mp_size_t us, un, i; + mp_limb_t limb, ux; + + us = u->_mp_size; + ux = - (mp_limb_t) (us >= 0); + un = GMP_ABS (us); + i = starting_bit / GMP_LIMB_BITS; + + /* When past end, there's an immediate 0 bit for u>=0, or no 0 bits for + u<0. Notice this test picks up all cases of u==0 too. */ + if (i >= un) + return (ux ? starting_bit : ~(mp_bitcnt_t) 0); + + up = u->_mp_d; + limb = up[i] ^ ux; + + if (ux == 0) + limb -= mpn_zero_p (up, i); /* limb = ~(~limb + zero_p) */ + + /* Mask all bits before starting_bit, thus ignoring them. */ + limb &= GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS); + + return mpn_common_scan (limb, i, up, un, ux); +} + + +/* MPZ base conversion. */ + +size_t +mpz_sizeinbase (const mpz_t u, int base) +{ + mp_size_t un; + mp_srcptr up; + mp_ptr tp; + mp_bitcnt_t bits; + struct gmp_div_inverse bi; + size_t ndigits; + + assert (base >= 2); + assert (base <= 62); + + un = GMP_ABS (u->_mp_size); + if (un == 0) + return 1; + + up = u->_mp_d; + + bits = (un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1]); + switch (base) + { + case 2: + return bits; + case 4: + return (bits + 1) / 2; + case 8: + return (bits + 2) / 3; + case 16: + return (bits + 3) / 4; + case 32: + return (bits + 4) / 5; + /* FIXME: Do something more clever for the common case of base + 10. */ + } + + tp = gmp_xalloc_limbs (un); + mpn_copyi (tp, up, un); + mpn_div_qr_1_invert (&bi, base); + + ndigits = 0; + do + { + ndigits++; + mpn_div_qr_1_preinv (tp, tp, un, &bi); + un -= (tp[un-1] == 0); + } + while (un > 0); + + gmp_free (tp); + return ndigits; +} + +char * +mpz_get_str (char *sp, int base, const mpz_t u) +{ + unsigned bits; + const char *digits; + mp_size_t un; + size_t i, sn; + + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + if (base > 1) + { + if (base <= 36) + digits = "0123456789abcdefghijklmnopqrstuvwxyz"; + else if (base > 62) + return NULL; + } + else if (base >= -1) + base = 10; + else + { + base = -base; + if (base > 36) + return NULL; + } + + sn = 1 + mpz_sizeinbase (u, base); + if (!sp) + sp = (char *) gmp_xalloc (1 + sn); + + un = GMP_ABS (u->_mp_size); + + if (un == 0) + { + sp[0] = '0'; + sp[1] = '\0'; + return sp; + } + + i = 0; + + if (u->_mp_size < 0) + sp[i++] = '-'; + + bits = mpn_base_power_of_two_p (base); + + if (bits) + /* Not modified in this case. */ + sn = i + mpn_get_str_bits ((unsigned char *) sp + i, bits, u->_mp_d, un); + else + { + struct mpn_base_info info; + mp_ptr tp; + + mpn_get_base_info (&info, base); + tp = gmp_xalloc_limbs (un); + mpn_copyi (tp, u->_mp_d, un); + + sn = i + mpn_get_str_other ((unsigned char *) sp + i, base, &info, tp, un); + gmp_free (tp); + } + + for (; i < sn; i++) + sp[i] = digits[(unsigned char) sp[i]]; + + sp[sn] = '\0'; + return sp; +} + +int +mpz_set_str (mpz_t r, const char *sp, int base) +{ + unsigned bits, value_of_a; + mp_size_t rn, alloc; + mp_ptr rp; + size_t dn; + int sign; + unsigned char *dp; + + assert (base == 0 || (base >= 2 && base <= 62)); + + while (isspace( (unsigned char) *sp)) + sp++; + + sign = (*sp == '-'); + sp += sign; + + if (base == 0) + { + if (sp[0] == '0') + { + if (sp[1] == 'x' || sp[1] == 'X') + { + base = 16; + sp += 2; + } + else if (sp[1] == 'b' || sp[1] == 'B') + { + base = 2; + sp += 2; + } + else + base = 8; + } + else + base = 10; + } + + if (!*sp) + { + r->_mp_size = 0; + return -1; + } + dp = (unsigned char *) gmp_xalloc (strlen (sp)); + + value_of_a = (base > 36) ? 36 : 10; + for (dn = 0; *sp; sp++) + { + unsigned digit; + + if (isspace ((unsigned char) *sp)) + continue; + else if (*sp >= '0' && *sp <= '9') + digit = *sp - '0'; + else if (*sp >= 'a' && *sp <= 'z') + digit = *sp - 'a' + value_of_a; + else if (*sp >= 'A' && *sp <= 'Z') + digit = *sp - 'A' + 10; + else + digit = base; /* fail */ + + if (digit >= (unsigned) base) + { + gmp_free (dp); + r->_mp_size = 0; + return -1; + } + + dp[dn++] = digit; + } + + if (!dn) + { + gmp_free (dp); + r->_mp_size = 0; + return -1; + } + bits = mpn_base_power_of_two_p (base); + + if (bits > 0) + { + alloc = (dn * bits + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; + rp = MPZ_REALLOC (r, alloc); + rn = mpn_set_str_bits (rp, dp, dn, bits); + } + else + { + struct mpn_base_info info; + mpn_get_base_info (&info, base); + alloc = (dn + info.exp - 1) / info.exp; + rp = MPZ_REALLOC (r, alloc); + rn = mpn_set_str_other (rp, dp, dn, base, &info); + /* Normalization, needed for all-zero input. */ + assert (rn > 0); + rn -= rp[rn-1] == 0; + } + assert (rn <= alloc); + gmp_free (dp); + + r->_mp_size = sign ? - rn : rn; + + return 0; +} + +int +mpz_init_set_str (mpz_t r, const char *sp, int base) +{ + mpz_init (r); + return mpz_set_str (r, sp, base); +} + +size_t +mpz_out_str (FILE *stream, int base, const mpz_t x) +{ + char *str; + size_t len; + + str = mpz_get_str (NULL, base, x); + if (!str) + return 0; + len = strlen (str); + len = fwrite (str, 1, len, stream); + gmp_free (str); + return len; +} + + +static int +gmp_detect_endian (void) +{ + static const int i = 2; + const unsigned char *p = (const unsigned char *) &i; + return 1 - *p; +} + +/* Import and export. Does not support nails. */ +void +mpz_import (mpz_t r, size_t count, int order, size_t size, int endian, + size_t nails, const void *src) +{ + const unsigned char *p; + ptrdiff_t word_step; + mp_ptr rp; + mp_size_t rn; + + /* The current (partial) limb. */ + mp_limb_t limb; + /* The number of bytes already copied to this limb (starting from + the low end). */ + size_t bytes; + /* The index where the limb should be stored, when completed. */ + mp_size_t i; + + if (nails != 0) + gmp_die ("mpz_import: Nails not supported."); + + assert (order == 1 || order == -1); + assert (endian >= -1 && endian <= 1); + + if (endian == 0) + endian = gmp_detect_endian (); + + p = (unsigned char *) src; + + word_step = (order != endian) ? 2 * size : 0; + + /* Process bytes from the least significant end, so point p at the + least significant word. */ + if (order == 1) + { + p += size * (count - 1); + word_step = - word_step; + } + + /* And at least significant byte of that word. */ + if (endian == 1) + p += (size - 1); + + rn = (size * count + sizeof(mp_limb_t) - 1) / sizeof(mp_limb_t); + rp = MPZ_REALLOC (r, rn); + + for (limb = 0, bytes = 0, i = 0; count > 0; count--, p += word_step) + { + size_t j; + for (j = 0; j < size; j++, p -= (ptrdiff_t) endian) + { + limb |= (mp_limb_t) *p << (bytes++ * CHAR_BIT); + if (bytes == sizeof(mp_limb_t)) + { + rp[i++] = limb; + bytes = 0; + limb = 0; + } + } + } + assert (i + (bytes > 0) == rn); + if (limb != 0) + rp[i++] = limb; + else + i = mpn_normalized_size (rp, i); + + r->_mp_size = i; +} + +void * +mpz_export (void *r, size_t *countp, int order, size_t size, int endian, + size_t nails, const mpz_t u) +{ + size_t count; + mp_size_t un; + + if (nails != 0) + gmp_die ("mpz_import: Nails not supported."); + + assert (order == 1 || order == -1); + assert (endian >= -1 && endian <= 1); + assert (size > 0 || u->_mp_size == 0); + + un = u->_mp_size; + count = 0; + if (un != 0) + { + size_t k; + unsigned char *p; + ptrdiff_t word_step; + /* The current (partial) limb. */ + mp_limb_t limb; + /* The number of bytes left to do in this limb. */ + size_t bytes; + /* The index where the limb was read. */ + mp_size_t i; + + un = GMP_ABS (un); + + /* Count bytes in top limb. */ + limb = u->_mp_d[un-1]; + assert (limb != 0); + + k = (GMP_LIMB_BITS <= CHAR_BIT); + if (!k) + { + do { + int LOCAL_CHAR_BIT = CHAR_BIT; + k++; limb >>= LOCAL_CHAR_BIT; + } while (limb != 0); + } + /* else limb = 0; */ + + count = (k + (un-1) * sizeof (mp_limb_t) + size - 1) / size; + + if (!r) + r = gmp_xalloc (count * size); + + if (endian == 0) + endian = gmp_detect_endian (); + + p = (unsigned char *) r; + + word_step = (order != endian) ? 2 * size : 0; + + /* Process bytes from the least significant end, so point p at the + least significant word. */ + if (order == 1) + { + p += size * (count - 1); + word_step = - word_step; + } + + /* And at least significant byte of that word. */ + if (endian == 1) + p += (size - 1); + + for (bytes = 0, i = 0, k = 0; k < count; k++, p += word_step) + { + size_t j; + for (j = 0; j < size; ++j, p -= (ptrdiff_t) endian) + { + if (sizeof (mp_limb_t) == 1) + { + if (i < un) + *p = u->_mp_d[i++]; + else + *p = 0; + } + else + { + int LOCAL_CHAR_BIT = CHAR_BIT; + if (bytes == 0) + { + if (i < un) + limb = u->_mp_d[i++]; + bytes = sizeof (mp_limb_t); + } + *p = limb; + limb >>= LOCAL_CHAR_BIT; + bytes--; + } + } + } + assert (i == un); + assert (k == count); + } + + if (countp) + *countp = count; + + return r; +} diff --git a/src/rigraph/vendor/mini-gmp/mini-gmp.h b/src/rigraph/vendor/mini-gmp/mini-gmp.h new file mode 100644 index 0000000..d575f7d --- /dev/null +++ b/src/rigraph/vendor/mini-gmp/mini-gmp.h @@ -0,0 +1,305 @@ +/* mini-gmp, a minimalistic implementation of a GNU GMP subset. + +Copyright 2011-2015, 2017, 2019-2020 Free Software Foundation, Inc. + +This file is part of the GNU MP Library. + +The GNU MP Library is free software; you can redistribute it and/or modify +it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + +or + + * the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any + later version. + +or both in parallel, as here. + +The GNU MP Library 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 copies of the GNU General Public License and the +GNU Lesser General Public License along with the GNU MP Library. If not, +see https://www.gnu.org/licenses/. */ + +/* About mini-gmp: This is a minimal implementation of a subset of the + GMP interface. It is intended for inclusion into applications which + have modest bignums needs, as a fallback when the real GMP library + is not installed. + + This file defines the public interface. */ + +#ifndef __MINI_GMP_H__ +#define __MINI_GMP_H__ + +/* For size_t */ +#include + +#if defined (__cplusplus) +extern "C" { +#endif + +void mp_set_memory_functions (void *(*) (size_t), + void *(*) (void *, size_t, size_t), + void (*) (void *, size_t)); + +void mp_get_memory_functions (void *(**) (size_t), + void *(**) (void *, size_t, size_t), + void (**) (void *, size_t)); + +#ifndef MINI_GMP_LIMB_TYPE +#define MINI_GMP_LIMB_TYPE long +#endif + +typedef unsigned MINI_GMP_LIMB_TYPE mp_limb_t; +typedef long mp_size_t; +typedef unsigned long mp_bitcnt_t; + +typedef mp_limb_t *mp_ptr; +typedef const mp_limb_t *mp_srcptr; + +typedef struct +{ + int _mp_alloc; /* Number of *limbs* allocated and pointed + to by the _mp_d field. */ + int _mp_size; /* abs(_mp_size) is the number of limbs the + last field points to. If _mp_size is + negative this is a negative number. */ + mp_limb_t *_mp_d; /* Pointer to the limbs. */ +} __mpz_struct; + +typedef __mpz_struct mpz_t[1]; + +typedef __mpz_struct *mpz_ptr; +typedef const __mpz_struct *mpz_srcptr; + +extern const int mp_bits_per_limb; + +void mpn_copyi (mp_ptr, mp_srcptr, mp_size_t); +void mpn_copyd (mp_ptr, mp_srcptr, mp_size_t); +void mpn_zero (mp_ptr, mp_size_t); + +int mpn_cmp (mp_srcptr, mp_srcptr, mp_size_t); +int mpn_zero_p (mp_srcptr, mp_size_t); + +mp_limb_t mpn_add_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); +mp_limb_t mpn_add_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t); +mp_limb_t mpn_add (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t); + +mp_limb_t mpn_sub_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); +mp_limb_t mpn_sub_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t); +mp_limb_t mpn_sub (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t); + +mp_limb_t mpn_mul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); +mp_limb_t mpn_addmul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); +mp_limb_t mpn_submul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t); + +mp_limb_t mpn_mul (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t); +void mpn_mul_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t); +void mpn_sqr (mp_ptr, mp_srcptr, mp_size_t); +int mpn_perfect_square_p (mp_srcptr, mp_size_t); +mp_size_t mpn_sqrtrem (mp_ptr, mp_ptr, mp_srcptr, mp_size_t); + +mp_limb_t mpn_lshift (mp_ptr, mp_srcptr, mp_size_t, unsigned int); +mp_limb_t mpn_rshift (mp_ptr, mp_srcptr, mp_size_t, unsigned int); + +mp_bitcnt_t mpn_scan0 (mp_srcptr, mp_bitcnt_t); +mp_bitcnt_t mpn_scan1 (mp_srcptr, mp_bitcnt_t); + +void mpn_com (mp_ptr, mp_srcptr, mp_size_t); +mp_limb_t mpn_neg (mp_ptr, mp_srcptr, mp_size_t); + +mp_bitcnt_t mpn_popcount (mp_srcptr, mp_size_t); + +mp_limb_t mpn_invert_3by2 (mp_limb_t, mp_limb_t); +#define mpn_invert_limb(x) mpn_invert_3by2 ((x), 0) + +size_t mpn_get_str (unsigned char *, int, mp_ptr, mp_size_t); +mp_size_t mpn_set_str (mp_ptr, const unsigned char *, size_t, int); + +void mpz_init (mpz_t); +void mpz_init2 (mpz_t, mp_bitcnt_t); +void mpz_clear (mpz_t); + +#define mpz_odd_p(z) (((z)->_mp_size != 0) & (int) (z)->_mp_d[0]) +#define mpz_even_p(z) (! mpz_odd_p (z)) + +int mpz_sgn (const mpz_t); +int mpz_cmp_si (const mpz_t, long); +int mpz_cmp_ui (const mpz_t, unsigned long); +int mpz_cmp (const mpz_t, const mpz_t); +int mpz_cmpabs_ui (const mpz_t, unsigned long); +int mpz_cmpabs (const mpz_t, const mpz_t); +int mpz_cmp_d (const mpz_t, double); +int mpz_cmpabs_d (const mpz_t, double); + +void mpz_abs (mpz_t, const mpz_t); +void mpz_neg (mpz_t, const mpz_t); +void mpz_swap (mpz_t, mpz_t); + +void mpz_add_ui (mpz_t, const mpz_t, unsigned long); +void mpz_add (mpz_t, const mpz_t, const mpz_t); +void mpz_sub_ui (mpz_t, const mpz_t, unsigned long); +void mpz_ui_sub (mpz_t, unsigned long, const mpz_t); +void mpz_sub (mpz_t, const mpz_t, const mpz_t); + +void mpz_mul_si (mpz_t, const mpz_t, long int); +void mpz_mul_ui (mpz_t, const mpz_t, unsigned long int); +void mpz_mul (mpz_t, const mpz_t, const mpz_t); +void mpz_mul_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_addmul_ui (mpz_t, const mpz_t, unsigned long int); +void mpz_addmul (mpz_t, const mpz_t, const mpz_t); +void mpz_submul_ui (mpz_t, const mpz_t, unsigned long int); +void mpz_submul (mpz_t, const mpz_t, const mpz_t); + +void mpz_cdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t); +void mpz_fdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t); +void mpz_tdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t); +void mpz_cdiv_q (mpz_t, const mpz_t, const mpz_t); +void mpz_fdiv_q (mpz_t, const mpz_t, const mpz_t); +void mpz_tdiv_q (mpz_t, const mpz_t, const mpz_t); +void mpz_cdiv_r (mpz_t, const mpz_t, const mpz_t); +void mpz_fdiv_r (mpz_t, const mpz_t, const mpz_t); +void mpz_tdiv_r (mpz_t, const mpz_t, const mpz_t); + +void mpz_cdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_fdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_tdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_cdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_fdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t); +void mpz_tdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t); + +void mpz_mod (mpz_t, const mpz_t, const mpz_t); + +void mpz_divexact (mpz_t, const mpz_t, const mpz_t); + +int mpz_divisible_p (const mpz_t, const mpz_t); +int mpz_congruent_p (const mpz_t, const mpz_t, const mpz_t); + +unsigned long mpz_cdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long); +unsigned long mpz_fdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long); +unsigned long mpz_tdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long); +unsigned long mpz_cdiv_q_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_fdiv_q_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_tdiv_q_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_cdiv_r_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_fdiv_r_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_tdiv_r_ui (mpz_t, const mpz_t, unsigned long); +unsigned long mpz_cdiv_ui (const mpz_t, unsigned long); +unsigned long mpz_fdiv_ui (const mpz_t, unsigned long); +unsigned long mpz_tdiv_ui (const mpz_t, unsigned long); + +unsigned long mpz_mod_ui (mpz_t, const mpz_t, unsigned long); + +void mpz_divexact_ui (mpz_t, const mpz_t, unsigned long); + +int mpz_divisible_ui_p (const mpz_t, unsigned long); + +unsigned long mpz_gcd_ui (mpz_t, const mpz_t, unsigned long); +void mpz_gcd (mpz_t, const mpz_t, const mpz_t); +void mpz_gcdext (mpz_t, mpz_t, mpz_t, const mpz_t, const mpz_t); +void mpz_lcm_ui (mpz_t, const mpz_t, unsigned long); +void mpz_lcm (mpz_t, const mpz_t, const mpz_t); +int mpz_invert (mpz_t, const mpz_t, const mpz_t); + +void mpz_sqrtrem (mpz_t, mpz_t, const mpz_t); +void mpz_sqrt (mpz_t, const mpz_t); +int mpz_perfect_square_p (const mpz_t); + +void mpz_pow_ui (mpz_t, const mpz_t, unsigned long); +void mpz_ui_pow_ui (mpz_t, unsigned long, unsigned long); +void mpz_powm (mpz_t, const mpz_t, const mpz_t, const mpz_t); +void mpz_powm_ui (mpz_t, const mpz_t, unsigned long, const mpz_t); + +void mpz_rootrem (mpz_t, mpz_t, const mpz_t, unsigned long); +int mpz_root (mpz_t, const mpz_t, unsigned long); + +void mpz_fac_ui (mpz_t, unsigned long); +void mpz_2fac_ui (mpz_t, unsigned long); +void mpz_mfac_uiui (mpz_t, unsigned long, unsigned long); +void mpz_bin_uiui (mpz_t, unsigned long, unsigned long); + +int mpz_probab_prime_p (const mpz_t, int); + +int mpz_tstbit (const mpz_t, mp_bitcnt_t); +void mpz_setbit (mpz_t, mp_bitcnt_t); +void mpz_clrbit (mpz_t, mp_bitcnt_t); +void mpz_combit (mpz_t, mp_bitcnt_t); + +void mpz_com (mpz_t, const mpz_t); +void mpz_and (mpz_t, const mpz_t, const mpz_t); +void mpz_ior (mpz_t, const mpz_t, const mpz_t); +void mpz_xor (mpz_t, const mpz_t, const mpz_t); + +mp_bitcnt_t mpz_popcount (const mpz_t); +mp_bitcnt_t mpz_hamdist (const mpz_t, const mpz_t); +mp_bitcnt_t mpz_scan0 (const mpz_t, mp_bitcnt_t); +mp_bitcnt_t mpz_scan1 (const mpz_t, mp_bitcnt_t); + +int mpz_fits_slong_p (const mpz_t); +int mpz_fits_ulong_p (const mpz_t); +long int mpz_get_si (const mpz_t); +unsigned long int mpz_get_ui (const mpz_t); +double mpz_get_d (const mpz_t); +size_t mpz_size (const mpz_t); +mp_limb_t mpz_getlimbn (const mpz_t, mp_size_t); + +void mpz_realloc2 (mpz_t, mp_bitcnt_t); +mp_srcptr mpz_limbs_read (mpz_srcptr); +mp_ptr mpz_limbs_modify (mpz_t, mp_size_t); +mp_ptr mpz_limbs_write (mpz_t, mp_size_t); +void mpz_limbs_finish (mpz_t, mp_size_t); +mpz_srcptr mpz_roinit_n (mpz_t, mp_srcptr, mp_size_t); + +#define MPZ_ROINIT_N(xp, xs) {{0, (xs),(xp) }} + +void mpz_set_si (mpz_t, signed long int); +void mpz_set_ui (mpz_t, unsigned long int); +void mpz_set (mpz_t, const mpz_t); +void mpz_set_d (mpz_t, double); + +void mpz_init_set_si (mpz_t, signed long int); +void mpz_init_set_ui (mpz_t, unsigned long int); +void mpz_init_set (mpz_t, const mpz_t); +void mpz_init_set_d (mpz_t, double); + +size_t mpz_sizeinbase (const mpz_t, int); +char *mpz_get_str (char *, int, const mpz_t); +int mpz_set_str (mpz_t, const char *, int); +int mpz_init_set_str (mpz_t, const char *, int); + +/* This long list taken from gmp.h. */ +/* For reference, "defined(EOF)" cannot be used here. In g++ 2.95.4, + defines EOF but not FILE. */ +#if defined (FILE) \ + || defined (H_STDIO) \ + || defined (_H_STDIO) /* AIX */ \ + || defined (_STDIO_H) /* glibc, Sun, SCO */ \ + || defined (_STDIO_H_) /* BSD, OSF */ \ + || defined (__STDIO_H) /* Borland */ \ + || defined (__STDIO_H__) /* IRIX */ \ + || defined (_STDIO_INCLUDED) /* HPUX */ \ + || defined (__dj_include_stdio_h_) /* DJGPP */ \ + || defined (_FILE_DEFINED) /* Microsoft */ \ + || defined (__STDIO__) /* Apple MPW MrC */ \ + || defined (_MSL_STDIO_H) /* Metrowerks */ \ + || defined (_STDIO_H_INCLUDED) /* QNX4 */ \ + || defined (_ISO_STDIO_ISO_H) /* Sun C++ */ \ + || defined (__STDIO_LOADED) /* VMS */ \ + || defined (__DEFINED_FILE) /* musl */ +size_t mpz_out_str (FILE *, int, const mpz_t); +#endif + +void mpz_import (mpz_t, size_t, int, size_t, int, size_t, const void *); +void *mpz_export (void *, size_t *, int, size_t, int, size_t, const mpz_t); + +#if defined (__cplusplus) +} +#endif +#endif /* __MINI_GMP_H__ */ diff --git a/src/rigraph/vendor/plfit/arithmetic_ansi.h b/src/rigraph/vendor/plfit/arithmetic_ansi.h new file mode 100644 index 0000000..c58c98a --- /dev/null +++ b/src/rigraph/vendor/plfit/arithmetic_ansi.h @@ -0,0 +1,133 @@ +/* + * ANSI C implementation of vector operations. + * + * Copyright (c) 2007-2010 Naoaki Okazaki + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* $Id: arithmetic_ansi.h 65 2010-01-29 12:19:16Z naoaki $ */ + +#include +#include + +#if LBFGS_FLOAT == 32 && LBFGS_IEEE_FLOAT +#define fsigndiff(x, y) (((*(uint32_t*)(x)) ^ (*(uint32_t*)(y))) & 0x80000000U) +#else +#define fsigndiff(x, y) (*(x) * (*(y) / fabs(*(y))) < 0.) +#endif/*LBFGS_IEEE_FLOAT*/ + +inline static void* vecalloc(size_t size) +{ + void *memblock = malloc(size); + if (memblock) { + memset(memblock, 0, size); + } + return memblock; +} + +inline static void vecfree(void *memblock) +{ + free(memblock); +} + +inline static void vecset(lbfgsfloatval_t *x, const lbfgsfloatval_t c, const int n) +{ + int i; + + for (i = 0;i < n;++i) { + x[i] = c; + } +} + +inline static void veccpy(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const int n) +{ + int i; + + for (i = 0;i < n;++i) { + y[i] = x[i]; + } +} + +inline static void vecncpy(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const int n) +{ + int i; + + for (i = 0;i < n;++i) { + y[i] = -x[i]; + } +} + +inline static void vecadd(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const lbfgsfloatval_t c, const int n) +{ + int i; + + for (i = 0;i < n;++i) { + y[i] += c * x[i]; + } +} + +inline static void vecdiff(lbfgsfloatval_t *z, const lbfgsfloatval_t *x, const lbfgsfloatval_t *y, const int n) +{ + int i; + + for (i = 0;i < n;++i) { + z[i] = x[i] - y[i]; + } +} + +inline static void vecscale(lbfgsfloatval_t *y, const lbfgsfloatval_t c, const int n) +{ + int i; + + for (i = 0;i < n;++i) { + y[i] *= c; + } +} + +inline static void vecmul(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const int n) +{ + int i; + + for (i = 0;i < n;++i) { + y[i] *= x[i]; + } +} + +inline static void vecdot(lbfgsfloatval_t* s, const lbfgsfloatval_t *x, const lbfgsfloatval_t *y, const int n) +{ + int i; + *s = 0.; + for (i = 0;i < n;++i) { + *s += x[i] * y[i]; + } +} + +inline static void vec2norm(lbfgsfloatval_t* s, const lbfgsfloatval_t *x, const int n) +{ + vecdot(s, x, x, n); + *s = (lbfgsfloatval_t)sqrt(*s); +} + +inline static void vec2norminv(lbfgsfloatval_t* s, const lbfgsfloatval_t *x, const int n) +{ + vec2norm(s, x, n); + *s = (lbfgsfloatval_t)(1.0 / *s); +} diff --git a/src/rigraph/vendor/plfit/arithmetic_sse_double.h b/src/rigraph/vendor/plfit/arithmetic_sse_double.h new file mode 100644 index 0000000..a94d89d --- /dev/null +++ b/src/rigraph/vendor/plfit/arithmetic_sse_double.h @@ -0,0 +1,294 @@ +/* + * SSE2 implementation of vector oprations (64bit double). + * + * Copyright (c) 2007-2010 Naoaki Okazaki + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* $Id: arithmetic_sse_double.h 65 2010-01-29 12:19:16Z naoaki $ */ + +#include + +#if !defined(__APPLE__) +#include +#endif + +#include + +#if 1400 <= _MSC_VER +#include +#endif/*1400 <= _MSC_VER*/ + +#if HAVE_EMMINTRIN_H +#include +#endif/*HAVE_EMMINTRIN_H*/ + +inline static void* vecalloc(size_t size) +{ +#ifdef _MSC_VER + void *memblock = _aligned_malloc(size, 16); +#elif defined(__APPLE__) + /* Memory on Mac OS X is already aligned to 16 bytes */ + void *memblock = malloc(size); +#else + void *memblock = memalign(16, size); +#endif + if (memblock != NULL) { + memset(memblock, 0, size); + } + return memblock; +} + +inline static void vecfree(void *memblock) +{ +#ifdef _MSC_VER + _aligned_free(memblock); +#else + free(memblock); +#endif +} + +#define fsigndiff(x, y) \ + ((_mm_movemask_pd(_mm_set_pd(*(x), *(y))) + 1) & 0x002) + +#define vecset(x, c, n) \ +{ \ + int i; \ + __m128d XMM0 = _mm_set1_pd(c); \ + for (i = 0;i < (n);i += 8) { \ + _mm_store_pd((x)+i , XMM0); \ + _mm_store_pd((x)+i+2, XMM0); \ + _mm_store_pd((x)+i+4, XMM0); \ + _mm_store_pd((x)+i+6, XMM0); \ + } \ +} + +#define veccpy(y, x, n) \ +{ \ + int i; \ + for (i = 0;i < (n);i += 8) { \ + __m128d XMM0 = _mm_load_pd((x)+i ); \ + __m128d XMM1 = _mm_load_pd((x)+i+2); \ + __m128d XMM2 = _mm_load_pd((x)+i+4); \ + __m128d XMM3 = _mm_load_pd((x)+i+6); \ + _mm_store_pd((y)+i , XMM0); \ + _mm_store_pd((y)+i+2, XMM1); \ + _mm_store_pd((y)+i+4, XMM2); \ + _mm_store_pd((y)+i+6, XMM3); \ + } \ +} + +#define vecncpy(y, x, n) \ +{ \ + int i; \ + for (i = 0;i < (n);i += 8) { \ + __m128d XMM0 = _mm_setzero_pd(); \ + __m128d XMM1 = _mm_setzero_pd(); \ + __m128d XMM2 = _mm_setzero_pd(); \ + __m128d XMM3 = _mm_setzero_pd(); \ + __m128d XMM4 = _mm_load_pd((x)+i ); \ + __m128d XMM5 = _mm_load_pd((x)+i+2); \ + __m128d XMM6 = _mm_load_pd((x)+i+4); \ + __m128d XMM7 = _mm_load_pd((x)+i+6); \ + XMM0 = _mm_sub_pd(XMM0, XMM4); \ + XMM1 = _mm_sub_pd(XMM1, XMM5); \ + XMM2 = _mm_sub_pd(XMM2, XMM6); \ + XMM3 = _mm_sub_pd(XMM3, XMM7); \ + _mm_store_pd((y)+i , XMM0); \ + _mm_store_pd((y)+i+2, XMM1); \ + _mm_store_pd((y)+i+4, XMM2); \ + _mm_store_pd((y)+i+6, XMM3); \ + } \ +} + +#define vecadd(y, x, c, n) \ +{ \ + int i; \ + __m128d XMM7 = _mm_set1_pd(c); \ + for (i = 0;i < (n);i += 4) { \ + __m128d XMM0 = _mm_load_pd((x)+i ); \ + __m128d XMM1 = _mm_load_pd((x)+i+2); \ + __m128d XMM2 = _mm_load_pd((y)+i ); \ + __m128d XMM3 = _mm_load_pd((y)+i+2); \ + XMM0 = _mm_mul_pd(XMM0, XMM7); \ + XMM1 = _mm_mul_pd(XMM1, XMM7); \ + XMM2 = _mm_add_pd(XMM2, XMM0); \ + XMM3 = _mm_add_pd(XMM3, XMM1); \ + _mm_store_pd((y)+i , XMM2); \ + _mm_store_pd((y)+i+2, XMM3); \ + } \ +} + +#define vecdiff(z, x, y, n) \ +{ \ + int i; \ + for (i = 0;i < (n);i += 8) { \ + __m128d XMM0 = _mm_load_pd((x)+i ); \ + __m128d XMM1 = _mm_load_pd((x)+i+2); \ + __m128d XMM2 = _mm_load_pd((x)+i+4); \ + __m128d XMM3 = _mm_load_pd((x)+i+6); \ + __m128d XMM4 = _mm_load_pd((y)+i ); \ + __m128d XMM5 = _mm_load_pd((y)+i+2); \ + __m128d XMM6 = _mm_load_pd((y)+i+4); \ + __m128d XMM7 = _mm_load_pd((y)+i+6); \ + XMM0 = _mm_sub_pd(XMM0, XMM4); \ + XMM1 = _mm_sub_pd(XMM1, XMM5); \ + XMM2 = _mm_sub_pd(XMM2, XMM6); \ + XMM3 = _mm_sub_pd(XMM3, XMM7); \ + _mm_store_pd((z)+i , XMM0); \ + _mm_store_pd((z)+i+2, XMM1); \ + _mm_store_pd((z)+i+4, XMM2); \ + _mm_store_pd((z)+i+6, XMM3); \ + } \ +} + +#define vecscale(y, c, n) \ +{ \ + int i; \ + __m128d XMM7 = _mm_set1_pd(c); \ + for (i = 0;i < (n);i += 4) { \ + __m128d XMM0 = _mm_load_pd((y)+i ); \ + __m128d XMM1 = _mm_load_pd((y)+i+2); \ + XMM0 = _mm_mul_pd(XMM0, XMM7); \ + XMM1 = _mm_mul_pd(XMM1, XMM7); \ + _mm_store_pd((y)+i , XMM0); \ + _mm_store_pd((y)+i+2, XMM1); \ + } \ +} + +#define vecmul(y, x, n) \ +{ \ + int i; \ + for (i = 0;i < (n);i += 8) { \ + __m128d XMM0 = _mm_load_pd((x)+i ); \ + __m128d XMM1 = _mm_load_pd((x)+i+2); \ + __m128d XMM2 = _mm_load_pd((x)+i+4); \ + __m128d XMM3 = _mm_load_pd((x)+i+6); \ + __m128d XMM4 = _mm_load_pd((y)+i ); \ + __m128d XMM5 = _mm_load_pd((y)+i+2); \ + __m128d XMM6 = _mm_load_pd((y)+i+4); \ + __m128d XMM7 = _mm_load_pd((y)+i+6); \ + XMM4 = _mm_mul_pd(XMM4, XMM0); \ + XMM5 = _mm_mul_pd(XMM5, XMM1); \ + XMM6 = _mm_mul_pd(XMM6, XMM2); \ + XMM7 = _mm_mul_pd(XMM7, XMM3); \ + _mm_store_pd((y)+i , XMM4); \ + _mm_store_pd((y)+i+2, XMM5); \ + _mm_store_pd((y)+i+4, XMM6); \ + _mm_store_pd((y)+i+6, XMM7); \ + } \ +} + + + +#if 3 <= __SSE__ +/* + Horizontal add with haddps SSE3 instruction. The work register (rw) + is unused. + */ +#define __horizontal_sum(r, rw) \ + r = _mm_hadd_ps(r, r); \ + r = _mm_hadd_ps(r, r); + +#else +/* + Horizontal add with SSE instruction. The work register (rw) is used. + */ +#define __horizontal_sum(r, rw) \ + rw = r; \ + r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(1, 0, 3, 2)); \ + r = _mm_add_ps(r, rw); \ + rw = r; \ + r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(2, 3, 0, 1)); \ + r = _mm_add_ps(r, rw); + +#endif + +#define vecdot(s, x, y, n) \ +{ \ + int i; \ + __m128d XMM0 = _mm_setzero_pd(); \ + __m128d XMM1 = _mm_setzero_pd(); \ + __m128d XMM2, XMM3, XMM4, XMM5; \ + for (i = 0;i < (n);i += 4) { \ + XMM2 = _mm_load_pd((x)+i ); \ + XMM3 = _mm_load_pd((x)+i+2); \ + XMM4 = _mm_load_pd((y)+i ); \ + XMM5 = _mm_load_pd((y)+i+2); \ + XMM2 = _mm_mul_pd(XMM2, XMM4); \ + XMM3 = _mm_mul_pd(XMM3, XMM5); \ + XMM0 = _mm_add_pd(XMM0, XMM2); \ + XMM1 = _mm_add_pd(XMM1, XMM3); \ + } \ + XMM0 = _mm_add_pd(XMM0, XMM1); \ + XMM1 = _mm_shuffle_pd(XMM0, XMM0, _MM_SHUFFLE2(1, 1)); \ + XMM0 = _mm_add_pd(XMM0, XMM1); \ + _mm_store_sd((s), XMM0); \ +} + +#define vec2norm(s, x, n) \ +{ \ + int i; \ + __m128d XMM0 = _mm_setzero_pd(); \ + __m128d XMM1 = _mm_setzero_pd(); \ + __m128d XMM2, XMM3, XMM4, XMM5; \ + for (i = 0;i < (n);i += 4) { \ + XMM2 = _mm_load_pd((x)+i ); \ + XMM3 = _mm_load_pd((x)+i+2); \ + XMM4 = XMM2; \ + XMM5 = XMM3; \ + XMM2 = _mm_mul_pd(XMM2, XMM4); \ + XMM3 = _mm_mul_pd(XMM3, XMM5); \ + XMM0 = _mm_add_pd(XMM0, XMM2); \ + XMM1 = _mm_add_pd(XMM1, XMM3); \ + } \ + XMM0 = _mm_add_pd(XMM0, XMM1); \ + XMM1 = _mm_shuffle_pd(XMM0, XMM0, _MM_SHUFFLE2(1, 1)); \ + XMM0 = _mm_add_pd(XMM0, XMM1); \ + XMM0 = _mm_sqrt_pd(XMM0); \ + _mm_store_sd((s), XMM0); \ +} + + +#define vec2norminv(s, x, n) \ +{ \ + int i; \ + __m128d XMM0 = _mm_setzero_pd(); \ + __m128d XMM1 = _mm_setzero_pd(); \ + __m128d XMM2, XMM3, XMM4, XMM5; \ + for (i = 0;i < (n);i += 4) { \ + XMM2 = _mm_load_pd((x)+i ); \ + XMM3 = _mm_load_pd((x)+i+2); \ + XMM4 = XMM2; \ + XMM5 = XMM3; \ + XMM2 = _mm_mul_pd(XMM2, XMM4); \ + XMM3 = _mm_mul_pd(XMM3, XMM5); \ + XMM0 = _mm_add_pd(XMM0, XMM2); \ + XMM1 = _mm_add_pd(XMM1, XMM3); \ + } \ + XMM2 = _mm_set1_pd(1.0); \ + XMM0 = _mm_add_pd(XMM0, XMM1); \ + XMM1 = _mm_shuffle_pd(XMM0, XMM0, _MM_SHUFFLE2(1, 1)); \ + XMM0 = _mm_add_pd(XMM0, XMM1); \ + XMM0 = _mm_sqrt_pd(XMM0); \ + XMM2 = _mm_div_pd(XMM2, XMM0); \ + _mm_store_sd((s), XMM2); \ +} diff --git a/src/rigraph/vendor/plfit/arithmetic_sse_float.h b/src/rigraph/vendor/plfit/arithmetic_sse_float.h new file mode 100644 index 0000000..b88f0e2 --- /dev/null +++ b/src/rigraph/vendor/plfit/arithmetic_sse_float.h @@ -0,0 +1,291 @@ +/* + * SSE/SSE3 implementation of vector oprations (32bit float). + * + * Copyright (c) 2007-2010 Naoaki Okazaki + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* $Id: arithmetic_sse_float.h 65 2010-01-29 12:19:16Z naoaki $ */ + +#include + +#if !defined(__APPLE__) +#include +#endif + +#include + +#if 1400 <= _MSC_VER +#include +#endif/*_MSC_VER*/ + +#if HAVE_XMMINTRIN_H +#include +#endif/*HAVE_XMMINTRIN_H*/ + +#if LBFGS_FLOAT == 32 && LBFGS_IEEE_FLOAT +#define fsigndiff(x, y) (((*(uint32_t*)(x)) ^ (*(uint32_t*)(y))) & 0x80000000U) +#else +#define fsigndiff(x, y) (*(x) * (*(y) / fabs(*(y))) < 0.) +#endif/*LBFGS_IEEE_FLOAT*/ + +inline static void* vecalloc(size_t size) +{ + void *memblock = _aligned_malloc(size, 16); + if (memblock != NULL) { + memset(memblock, 0, size); + } + return memblock; +} + +inline static void vecfree(void *memblock) +{ + _aligned_free(memblock); +} + +#define vecset(x, c, n) \ +{ \ + int i; \ + __m128 XMM0 = _mm_set_ps1(c); \ + for (i = 0;i < (n);i += 16) { \ + _mm_store_ps((x)+i , XMM0); \ + _mm_store_ps((x)+i+ 4, XMM0); \ + _mm_store_ps((x)+i+ 8, XMM0); \ + _mm_store_ps((x)+i+12, XMM0); \ + } \ +} + +#define veccpy(y, x, n) \ +{ \ + int i; \ + for (i = 0;i < (n);i += 16) { \ + __m128 XMM0 = _mm_load_ps((x)+i ); \ + __m128 XMM1 = _mm_load_ps((x)+i+ 4); \ + __m128 XMM2 = _mm_load_ps((x)+i+ 8); \ + __m128 XMM3 = _mm_load_ps((x)+i+12); \ + _mm_store_ps((y)+i , XMM0); \ + _mm_store_ps((y)+i+ 4, XMM1); \ + _mm_store_ps((y)+i+ 8, XMM2); \ + _mm_store_ps((y)+i+12, XMM3); \ + } \ +} + +#define vecncpy(y, x, n) \ +{ \ + int i; \ + const uint32_t mask = 0x80000000; \ + __m128 XMM4 = _mm_load_ps1((float*)&mask); \ + for (i = 0;i < (n);i += 16) { \ + __m128 XMM0 = _mm_load_ps((x)+i ); \ + __m128 XMM1 = _mm_load_ps((x)+i+ 4); \ + __m128 XMM2 = _mm_load_ps((x)+i+ 8); \ + __m128 XMM3 = _mm_load_ps((x)+i+12); \ + XMM0 = _mm_xor_ps(XMM0, XMM4); \ + XMM1 = _mm_xor_ps(XMM1, XMM4); \ + XMM2 = _mm_xor_ps(XMM2, XMM4); \ + XMM3 = _mm_xor_ps(XMM3, XMM4); \ + _mm_store_ps((y)+i , XMM0); \ + _mm_store_ps((y)+i+ 4, XMM1); \ + _mm_store_ps((y)+i+ 8, XMM2); \ + _mm_store_ps((y)+i+12, XMM3); \ + } \ +} + +#define vecadd(y, x, c, n) \ +{ \ + int i; \ + __m128 XMM7 = _mm_set_ps1(c); \ + for (i = 0;i < (n);i += 8) { \ + __m128 XMM0 = _mm_load_ps((x)+i ); \ + __m128 XMM1 = _mm_load_ps((x)+i+4); \ + __m128 XMM2 = _mm_load_ps((y)+i ); \ + __m128 XMM3 = _mm_load_ps((y)+i+4); \ + XMM0 = _mm_mul_ps(XMM0, XMM7); \ + XMM1 = _mm_mul_ps(XMM1, XMM7); \ + XMM2 = _mm_add_ps(XMM2, XMM0); \ + XMM3 = _mm_add_ps(XMM3, XMM1); \ + _mm_store_ps((y)+i , XMM2); \ + _mm_store_ps((y)+i+4, XMM3); \ + } \ +} + +#define vecdiff(z, x, y, n) \ +{ \ + int i; \ + for (i = 0;i < (n);i += 16) { \ + __m128 XMM0 = _mm_load_ps((x)+i ); \ + __m128 XMM1 = _mm_load_ps((x)+i+ 4); \ + __m128 XMM2 = _mm_load_ps((x)+i+ 8); \ + __m128 XMM3 = _mm_load_ps((x)+i+12); \ + __m128 XMM4 = _mm_load_ps((y)+i ); \ + __m128 XMM5 = _mm_load_ps((y)+i+ 4); \ + __m128 XMM6 = _mm_load_ps((y)+i+ 8); \ + __m128 XMM7 = _mm_load_ps((y)+i+12); \ + XMM0 = _mm_sub_ps(XMM0, XMM4); \ + XMM1 = _mm_sub_ps(XMM1, XMM5); \ + XMM2 = _mm_sub_ps(XMM2, XMM6); \ + XMM3 = _mm_sub_ps(XMM3, XMM7); \ + _mm_store_ps((z)+i , XMM0); \ + _mm_store_ps((z)+i+ 4, XMM1); \ + _mm_store_ps((z)+i+ 8, XMM2); \ + _mm_store_ps((z)+i+12, XMM3); \ + } \ +} + +#define vecscale(y, c, n) \ +{ \ + int i; \ + __m128 XMM7 = _mm_set_ps1(c); \ + for (i = 0;i < (n);i += 8) { \ + __m128 XMM0 = _mm_load_ps((y)+i ); \ + __m128 XMM1 = _mm_load_ps((y)+i+4); \ + XMM0 = _mm_mul_ps(XMM0, XMM7); \ + XMM1 = _mm_mul_ps(XMM1, XMM7); \ + _mm_store_ps((y)+i , XMM0); \ + _mm_store_ps((y)+i+4, XMM1); \ + } \ +} + +#define vecmul(y, x, n) \ +{ \ + int i; \ + for (i = 0;i < (n);i += 16) { \ + __m128 XMM0 = _mm_load_ps((x)+i ); \ + __m128 XMM1 = _mm_load_ps((x)+i+ 4); \ + __m128 XMM2 = _mm_load_ps((x)+i+ 8); \ + __m128 XMM3 = _mm_load_ps((x)+i+12); \ + __m128 XMM4 = _mm_load_ps((y)+i ); \ + __m128 XMM5 = _mm_load_ps((y)+i+ 4); \ + __m128 XMM6 = _mm_load_ps((y)+i+ 8); \ + __m128 XMM7 = _mm_load_ps((y)+i+12); \ + XMM4 = _mm_mul_ps(XMM4, XMM0); \ + XMM5 = _mm_mul_ps(XMM5, XMM1); \ + XMM6 = _mm_mul_ps(XMM6, XMM2); \ + XMM7 = _mm_mul_ps(XMM7, XMM3); \ + _mm_store_ps((y)+i , XMM4); \ + _mm_store_ps((y)+i+ 4, XMM5); \ + _mm_store_ps((y)+i+ 8, XMM6); \ + _mm_store_ps((y)+i+12, XMM7); \ + } \ +} + + + +#if 3 <= __SSE__ +/* + Horizontal add with haddps SSE3 instruction. The work register (rw) + is unused. + */ +#define __horizontal_sum(r, rw) \ + r = _mm_hadd_ps(r, r); \ + r = _mm_hadd_ps(r, r); + +#else +/* + Horizontal add with SSE instruction. The work register (rw) is used. + */ +#define __horizontal_sum(r, rw) \ + rw = r; \ + r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(1, 0, 3, 2)); \ + r = _mm_add_ps(r, rw); \ + rw = r; \ + r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(2, 3, 0, 1)); \ + r = _mm_add_ps(r, rw); + +#endif + +#define vecdot(s, x, y, n) \ +{ \ + int i; \ + __m128 XMM0 = _mm_setzero_ps(); \ + __m128 XMM1 = _mm_setzero_ps(); \ + __m128 XMM2, XMM3, XMM4, XMM5; \ + for (i = 0;i < (n);i += 8) { \ + XMM2 = _mm_load_ps((x)+i ); \ + XMM3 = _mm_load_ps((x)+i+4); \ + XMM4 = _mm_load_ps((y)+i ); \ + XMM5 = _mm_load_ps((y)+i+4); \ + XMM2 = _mm_mul_ps(XMM2, XMM4); \ + XMM3 = _mm_mul_ps(XMM3, XMM5); \ + XMM0 = _mm_add_ps(XMM0, XMM2); \ + XMM1 = _mm_add_ps(XMM1, XMM3); \ + } \ + XMM0 = _mm_add_ps(XMM0, XMM1); \ + __horizontal_sum(XMM0, XMM1); \ + _mm_store_ss((s), XMM0); \ +} + +#define vec2norm(s, x, n) \ +{ \ + int i; \ + __m128 XMM0 = _mm_setzero_ps(); \ + __m128 XMM1 = _mm_setzero_ps(); \ + __m128 XMM2, XMM3; \ + for (i = 0;i < (n);i += 8) { \ + XMM2 = _mm_load_ps((x)+i ); \ + XMM3 = _mm_load_ps((x)+i+4); \ + XMM2 = _mm_mul_ps(XMM2, XMM2); \ + XMM3 = _mm_mul_ps(XMM3, XMM3); \ + XMM0 = _mm_add_ps(XMM0, XMM2); \ + XMM1 = _mm_add_ps(XMM1, XMM3); \ + } \ + XMM0 = _mm_add_ps(XMM0, XMM1); \ + __horizontal_sum(XMM0, XMM1); \ + XMM2 = XMM0; \ + XMM1 = _mm_rsqrt_ss(XMM0); \ + XMM3 = XMM1; \ + XMM1 = _mm_mul_ss(XMM1, XMM1); \ + XMM1 = _mm_mul_ss(XMM1, XMM3); \ + XMM1 = _mm_mul_ss(XMM1, XMM0); \ + XMM1 = _mm_mul_ss(XMM1, _mm_set_ss(-0.5f)); \ + XMM3 = _mm_mul_ss(XMM3, _mm_set_ss(1.5f)); \ + XMM3 = _mm_add_ss(XMM3, XMM1); \ + XMM3 = _mm_mul_ss(XMM3, XMM2); \ + _mm_store_ss((s), XMM3); \ +} + +#define vec2norminv(s, x, n) \ +{ \ + int i; \ + __m128 XMM0 = _mm_setzero_ps(); \ + __m128 XMM1 = _mm_setzero_ps(); \ + __m128 XMM2, XMM3; \ + for (i = 0;i < (n);i += 16) { \ + XMM2 = _mm_load_ps((x)+i ); \ + XMM3 = _mm_load_ps((x)+i+4); \ + XMM2 = _mm_mul_ps(XMM2, XMM2); \ + XMM3 = _mm_mul_ps(XMM3, XMM3); \ + XMM0 = _mm_add_ps(XMM0, XMM2); \ + XMM1 = _mm_add_ps(XMM1, XMM3); \ + } \ + XMM0 = _mm_add_ps(XMM0, XMM1); \ + __horizontal_sum(XMM0, XMM1); \ + XMM2 = XMM0; \ + XMM1 = _mm_rsqrt_ss(XMM0); \ + XMM3 = XMM1; \ + XMM1 = _mm_mul_ss(XMM1, XMM1); \ + XMM1 = _mm_mul_ss(XMM1, XMM3); \ + XMM1 = _mm_mul_ss(XMM1, XMM0); \ + XMM1 = _mm_mul_ss(XMM1, _mm_set_ss(-0.5f)); \ + XMM3 = _mm_mul_ss(XMM3, _mm_set_ss(1.5f)); \ + XMM3 = _mm_add_ss(XMM3, XMM1); \ + _mm_store_ss((s), XMM3); \ +} diff --git a/src/rigraph/vendor/plfit/gss.c b/src/rigraph/vendor/plfit/gss.c new file mode 100644 index 0000000..f503484 --- /dev/null +++ b/src/rigraph/vendor/plfit/gss.c @@ -0,0 +1,153 @@ +/* gss.c + * + * Copyright (C) 2012 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include "plfit_error.h" +#include "gss.h" +#include "platform.h" + +/** + * \def PHI + * + * The golden ratio, i.e. 1+sqrt(5)/2 + */ +#define PHI 1.618033988749895 + +/** + * \def RESPHI + * + * Constant defined as 2 - \c PHI + */ +#define RESPHI 0.3819660112501051 + +/** + * \const _defparam + * + * Default parameters for the GSS algorithm. + */ +static const gss_parameter_t _defparam = { + /* .epsilon = */ DBL_MIN, + /* .on_error = */ GSS_ERROR_STOP +}; + +/** + * Stores whether the last optimization run triggered a warning or not. + */ +static unsigned short int gss_i_warning_flag = 0; + +void gss_parameter_init(gss_parameter_t *param) { + memcpy(param, &_defparam, sizeof(*param)); +} + +unsigned short int gss_get_warning_flag() { + return gss_i_warning_flag; +} + +#define TERMINATE { \ + if (_min) { \ + *(_min) = min; \ + } \ + if (_fmin) { \ + *(_fmin) = fmin; \ + } \ +} + +#define EVALUATE(x, fx) { \ + fx = proc_evaluate(instance, x); \ + if (fmin > fx) { \ + min = x; \ + fmin = fx; \ + } \ + if (proc_progress) { \ + retval = proc_progress(instance, x, fx, min, fmin, \ + (a < b) ? a : b, (a < b) ? b : a, k); \ + if (retval) { \ + TERMINATE; \ + return PLFIT_SUCCESS; \ + } \ + } \ +} + +int gss(double a, double b, double *_min, double *_fmin, + gss_evaluate_t proc_evaluate, gss_progress_t proc_progress, + void* instance, const gss_parameter_t *_param) { + double c, d, min; + double fa, fb, fc, fd, fmin; + int k = 0; + int retval; + unsigned short int successful = 1; + + gss_parameter_t param = _param ? (*_param) : _defparam; + + gss_i_warning_flag = 0; + + if (a > b) { + c = a; a = b; b = c; + } + + min = a; + fmin = proc_evaluate(instance, a); + + c = a + RESPHI*(b-a); + + EVALUATE(a, fa); + EVALUATE(b, fb); + EVALUATE(c, fc); + + if (fc >= fa || fc >= fb) { + if (param.on_error == GSS_ERROR_STOP) { + return PLFIT_FAILURE; + } else { + gss_i_warning_flag = 1; + } + } + + while (fabs(a-b) > param.epsilon) { + k++; + + d = c + RESPHI*(b-c); + EVALUATE(d, fd); + + if (fd >= fa || fd >= fb) { + if (param.on_error == GSS_ERROR_STOP) { + successful = 0; + break; + } else { + gss_i_warning_flag = 1; + } + } + + if (fc <= fd) { + b = a; a = d; + } else { + a = c; c = d; fc = fd; + } + } + + if (successful) { + c = (a+b) / 2.0; + k++; + EVALUATE(c, fc); + TERMINATE; + } + + return successful ? PLFIT_SUCCESS : PLFIT_FAILURE; +} diff --git a/src/rigraph/vendor/plfit/gss.h b/src/rigraph/vendor/plfit/gss.h new file mode 100644 index 0000000..b96b213 --- /dev/null +++ b/src/rigraph/vendor/plfit/gss.h @@ -0,0 +1,146 @@ +/* gss.h + * + * Copyright (C) 2012 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __GSS_H__ +#define __GSS_H__ + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS /* empty */ +# define __END_DECLS /* empty */ +#endif + +__BEGIN_DECLS + +/** + * Enum specifying what the search should do when the function is not U-shaped. + */ +typedef enum { + GSS_ERROR_STOP, /**< Stop and return an error code */ + GSS_ERROR_WARN /**< Continue and set the warning flag */ +} gss_error_handling_t; + +/** + * Parameter settings for a golden section search. + */ +typedef struct { + double epsilon; + gss_error_handling_t on_error; +} gss_parameter_t; + +/** + * Callback interface to provide objective function evaluations for the golden + * section search. + * + * The gss() function calls this function to obtain the values of the objective + * function when needed. A client program must implement this function to evaluate + * the value of the objective function, given the location. + * + * @param instance The user data sent for the gss() function by the client. + * @param x The current value of the variable. + * @retval double The value of the objective function for the current + * variable. + */ +typedef double (*gss_evaluate_t)(void *instance, double x); + +/** + * Callback interface to receive the progress of the optimization process for + * the golden section search. + * + * The gss() function calls this function for each iteration. Implementing + * this function, a client program can store or display the current progress + * of the optimization process. + * + * @param instance The user data sent for the gss() function by the client. + * @param x The current value of the variable. + * @param fx The value of the objective function at x. + * @param min The location of the minimum value of the objective + * function found so far. + * @param fmin The minimum value of the objective function found so far. + * @param left The left side of the current bracket. + * @param right The right side of the current bracket. + * @param k The index of the current iteration. + * @retval int Zero to continue the optimization process. Returning a + * non-zero value will cancel the optimization process. + */ +typedef int (*gss_progress_t)(void *instance, double x, double fx, double min, + double fmin, double left, double right, int k); + +/** + * Start a golden section search optimization. + * + * @param a The left side of the bracket to start from + * @param b The right side of the bracket to start from + * @param min The pointer to the variable that receives the location of the + * final value of the objective function. This argument can be set to + * \c NULL if the location of the final value of the objective + * function is unnecessary. + * @param fmin The pointer to the variable that receives the final value of + * the objective function. This argument can be st to \c NULL if the + * final value of the objective function is unnecessary. + * @param proc_evaluate The callback function to evaluate the objective + * function at a given location. + * @param proc_progress The callback function to receive the progress (the + * last evaluated location, the value of the objective + * function at that location, the width of the current + * bracket, the minimum found so far and the step + * count). This argument can be set to \c NULL if + * a progress report is unnecessary. + * @param instance A user data for the client program. The callback + * functions will receive the value of this argument. + * @param param The pointer to a structure representing parameters for + * GSS algorithm. A client program can set this parameter + * to \c NULL to use the default parameters. + * Call the \ref gss_parameter_init() function to fill a + * structure with the default values. + * @retval int The status code. This function returns zero if the + * minimization process terminates without an error. A + * non-zero value indicates an error; in particular, + * \c PLFIT_FAILURE means that the function is not + * U-shaped. + */ +int gss(double a, double b, double *min, double *fmin, + gss_evaluate_t proc_evaluate, gss_progress_t proc_progress, + void* instance, const gss_parameter_t *_param); + +/** + * Return the state of the warning flag. + * + * The warning flag is 1 if the last optimization was run on a function that + * was not U-shaped. + */ +unsigned short int gss_get_warning_flag(); + +/** + * Initialize GSS parameters to the default values. + * + * Call this function to fill a parameter structure with the default values + * and overwrite parameter values if necessary. + * + * @param param The pointer to the parameter structure. + */ +void gss_parameter_init(gss_parameter_t *param); + +__END_DECLS + +#endif /* __GSS_H__ */ diff --git a/src/rigraph/vendor/plfit/hzeta.c b/src/rigraph/vendor/plfit/hzeta.c new file mode 100644 index 0000000..eefd70c --- /dev/null +++ b/src/rigraph/vendor/plfit/hzeta.c @@ -0,0 +1,651 @@ +/* vim:set ts=4 sw=2 sts=2 et: */ + +/* This file was imported from a private scientific library + * based on GSL coined Home Scientific Libray (HSL) by its author + * Jerome Benoit; this very material is itself inspired from the + * material written by G. Jungan and distributed by GSL. + * Ultimately, some modifications were done in order to render the + * imported material independent from the rest of GSL. + */ + +/* `hsl/specfunc/hzeta.c' C source file +// HSL - Home Scientific Library +// Copyright (C) 2017-2018 Jerome Benoit +// +// HSL 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 2 +// of the License, or (at your option) 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +/* +// The material in this file is mainly inspired by the material written by +// G. Jungan and distributed under GPLv2 by the GNU Scientific Library (GSL) +// ( https://www.gnu.org/software/gsl/ [specfunc/zeta.c]), itself inspired by +// the material written by Moshier and distributed in the Cephes Mathematical +// Library ( http://www.moshier.net/ [zeta.c]). +// +// More specifically, hsl_sf_hzeta_e is a slightly modifed clone of +// gsl_sf_hzeta_e as found in GSL 2.4; the remaining is `inspired by'. +// [Sooner or later a _Working_Note_ may be deposited at ResearchGate +// ( https://www.researchgate.net/profile/Jerome_Benoit )] +*/ + +/* Author: Jerome G. Benoit < jgmbenoit _at_ rezozer _dot_ net > */ + +#ifdef _MSC_VER +#define _USE_MATH_DEFINES +#endif + +#include +#include +#include "hzeta.h" +#include "plfit_error.h" +#include "platform.h" /* because of NAN */ + +/* imported from gsl_machine.h */ + +#define GSL_LOG_DBL_MIN (-7.0839641853226408e+02) +#define GSL_LOG_DBL_MAX 7.0978271289338397e+02 +#define GSL_DBL_EPSILON 2.2204460492503131e-16 + +/* imported from gsl_math.h */ + +#ifndef M_LOG2E +#define M_LOG2E 1.44269504088896340735992468100 /* log_2 (e) */ +#endif + +/* imported from gsl_sf_result.h */ + +struct gsl_sf_result_struct { + double val; + double err; +}; +typedef struct gsl_sf_result_struct gsl_sf_result; + +/* imported and adapted from hsl/specfunc/specfunc_def.h */ + +#define HSL_SF_EVAL_RESULT(FnE) \ + gsl_sf_result result; \ + FnE ; \ + return (result.val); + +#define HSL_SF_EVAL_TUPLE_RESULT(FnET) \ + gsl_sf_result result0; \ + gsl_sf_result result1; \ + FnET ; \ + *tuple1=result1.val; \ + *tuple0=result0.val; \ + return (result0.val); + +/* */ + + +#define HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT 10 +#define HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER 32 + +#define HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX 256 + +// B_{2j}/(2j) +static +double hsl_sf_hzeta_eulermaclaurin_series_coeffs[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={ + +1.0, + +1.0/12.0, + -1.0/720.0, + +1.0/30240.0, + -1.0/1209600.0, + +1.0/47900160.0, + -691.0/1307674368000.0, + +1.0/74724249600.0, + -3.38968029632258286683019539125e-13, + +8.58606205627784456413590545043e-15, + -2.17486869855806187304151642387e-16, + +5.50900282836022951520265260890e-18, + -1.39544646858125233407076862641e-19, + +3.53470703962946747169322997780e-21, + -8.95351742703754685040261131811e-23, + +2.26795245233768306031095073887e-24, + -5.74479066887220244526388198761e-26, + +1.45517247561486490186626486727e-27, + -3.68599494066531017818178247991e-29, + +9.33673425709504467203255515279e-31, + -2.36502241570062993455963519637e-32, + +5.99067176248213430465991239682e-34, + -1.51745488446829026171081313586e-35, + +3.84375812545418823222944529099e-37, + -9.73635307264669103526762127925e-39, + +2.46624704420068095710640028029e-40, + -6.24707674182074369314875679472e-42, + +1.58240302446449142975108170683e-43, + -4.00827368594893596853001219052e-45, + +1.01530758555695563116307139454e-46, + -2.57180415824187174992481940976e-48, + +6.51445603523381493155843485864e-50, + -1.65013099068965245550609878048e-51 + }; // hsl_sf_hzeta_eulermaclaurin_series_coeffs + +// 4\zeta(2j)/(2\pi)^(2j) +static +double hsl_sf_hzeta_eulermaclaurin_series_majorantratios[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={ + -2.0, + +1.0/6.0, + +1.0/360.0, + +1.0/15120.0, + +1.0/604800.0, + +1.0/23950080.0, + +691.0/653837184000.0, + +1.0/37362124800.0, + +3617.0/5335311421440000.0, + +1.71721241125556891282718109009e-14, + +4.34973739711612374608303284773e-16, + +1.10180056567204590304053052178e-17, + +2.79089293716250466814153725281e-19, + +7.06941407925893494338645995561e-21, + +1.79070348540750937008052226362e-22, + +4.53590490467536612062190147774e-24, + +1.14895813377444048905277639752e-25, + +2.91034495122972980373252973454e-27, + +7.37198988133062035636356495982e-29, + +1.86734685141900893440651103056e-30, + +4.73004483140125986911927039274e-32, + +1.19813435249642686093198247936e-33, + +3.03490976893658052342162627173e-35, + +7.68751625090837646445889058198e-37, + +1.94727061452933820705352425585e-38, + +4.93249408840136191421280056051e-40, + +1.24941534836414873862975135893e-41, + +3.16480604892898285950216341362e-43, + +8.01654737189787193706002438098e-45, + +2.03061517111391126232614278906e-46, + +5.14360831648374349984963881946e-48, + +1.30289120704676298631168697172e-49, + +3.30026198137930491101219756091e-51 + }; // hsl_sf_hzeta_eulermaclaurin_series_majorantratios + + +extern +int hsl_sf_hzeta_e(const double s, const double q, gsl_sf_result * result) { + + /* CHECK_POINTER(result) */ + + if ((s <= 1.0) || (q <= 0.0)) { + PLFIT_ERROR("s must be larger than 1.0 and q must be larger than zero", PLFIT_EINVAL); + } + else { + const double max_bits=54.0; // max_bits=\lceil{s}\rceil with \zeta(s,2)=\zeta(s)-1=GSL_DBL_EPSILON + const double ln_term0=-s*log(q); + if (ln_term0 < GSL_LOG_DBL_MIN+1.0) { + PLFIT_ERROR("underflow", PLFIT_UNDRFLOW); + } + else if (GSL_LOG_DBL_MAX-1.0 < ln_term0) { + PLFIT_ERROR("overflow", PLFIT_OVERFLOW); + } +#if 1 + else if (((max_bits < s) && (q < 1.0)) || ((0.5*max_bits < s) && (q < 0.25))) { + result->val=pow(q,-s); + result->err=2.0*GSL_DBL_EPSILON*fabs(result->val); + return (PLFIT_SUCCESS); + } + else if ((0.5*max_bits < s) && (q < 1.0)) { + const double a0=pow(q,-s); + const double p1=pow(q/(1.0+q),s); + const double p2=pow(q/(2.0+q),s); + const double ans=a0*(1.0+p1+p2); + result->val=ans; + result->err=GSL_DBL_EPSILON*(2.0+0.5*s)*fabs(result->val); + return (PLFIT_SUCCESS); + } +#endif + else { // Euler-Maclaurin summation formula + const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q; + const double inv_qshift=1.0/qshift; + const double sqr_inv_qshift=inv_qshift*inv_qshift; + const double inv_sm1=1.0/(s-1.0); + const double pmax=pow(qshift,-s); + double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN}; + double delta=NAN; + double tscp=s; + double scp=tscp; + double pcp=pmax*inv_qshift; + double ratio=scp*pcp; + size_t n=0; + size_t j=0; + double ans=0.0; + double mjr=NAN; + + for(j=0;jval=+ans; + result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr); + return (PLFIT_SUCCESS); + } + } + +} + +extern +double hsl_sf_hzeta(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_hzeta_e(s,q,&result)); } + +extern +int hsl_sf_hzeta_deriv_e(const double s, const double q, gsl_sf_result * result) { + + /* CHECK_POINTER(result) */ + + if ((s <= 1.0) || (q <= 0.0)) { + PLFIT_ERROR("s must be larger than 1.0 and q must be larger than zero", PLFIT_EINVAL); + } + else { + const double ln_hz_term0=-s*log(q); + if (ln_hz_term0 < GSL_LOG_DBL_MIN+1.0) { + PLFIT_ERROR("underflow", PLFIT_UNDRFLOW); + } + else if (GSL_LOG_DBL_MAX-1.0 < ln_hz_term0) { + PLFIT_ERROR("overflow", PLFIT_OVERFLOW); + } + else { // Euler-Maclaurin summation formula + const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q; + const double inv_qshift=1.0/qshift; + const double sqr_inv_qshift=inv_qshift*inv_qshift; + const double inv_sm1=1.0/(s-1.0); + const double pmax=pow(qshift,-s); + const double lmax=log(qshift); + double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN}; + double delta=NAN; + double tscp=s; + double scp=tscp; + double pcp=pmax*inv_qshift; + double lcp=lmax-1.0/s; + double ratio=scp*pcp*lcp; + double qs=NAN; + size_t n=0; + size_t j=0; + double ans=0.0; + double mjr=NAN; + + for(j=0,qs=q;jval=-ans; + result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr); + return (PLFIT_SUCCESS); + } + } + +} + +extern +double hsl_sf_hzeta_deriv(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_hzeta_deriv_e(s,q,&result)); } + +extern +int hsl_sf_hzeta_deriv2_e(const double s, const double q, gsl_sf_result * result) { + + /* CHECK_POINTER(result) */ + + if ((s <= 1.0) || (q <= 0.0)) { + PLFIT_ERROR("s must be larger than 1.0 and q must be larger than zero", PLFIT_EINVAL); + } + else { + const double ln_hz_term0=-s*log(q); + if (ln_hz_term0 < GSL_LOG_DBL_MIN+1.0) { + PLFIT_ERROR("underflow", PLFIT_UNDRFLOW); + } + else if (GSL_LOG_DBL_MAX-1.0 < ln_hz_term0) { + PLFIT_ERROR("overflow", PLFIT_OVERFLOW); + } + else { // Euler-Maclaurin summation formula + const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q; + const double inv_qshift=1.0/qshift; + const double sqr_inv_qshift=inv_qshift*inv_qshift; + const double inv_sm1=1.0/(s-1.0); + const double pmax=pow(qshift,-s); + const double lmax=log(qshift); + const double lmax_p_inv_sm1=lmax+inv_sm1; + const double sqr_inv_sm1=inv_sm1*inv_sm1; + const double sqr_lmax=lmax*lmax; + const double sqr_lmax_p_inv_sm1=lmax_p_inv_sm1*lmax_p_inv_sm1; + double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN}; + double delta=NAN; + double tscp=s; + double slcp=NAN; + double plcp=NAN; + double scp=tscp; + double pcp=pmax*inv_qshift; + double lcp=1.0/s-lmax; + double sqr_lcp=lmax*(lmax-2.0/s); + double ratio=scp*pcp*sqr_lcp; + double qs=NAN; + double lqs=NAN; + size_t n=0; + size_t j=0; + double ans=0.0; + double mjr=NAN; + + for(j=0,qs=q;jval=+ans; + result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr); + return (PLFIT_SUCCESS); + } + } + +} + +extern +double hsl_sf_hzeta_deriv2(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_hzeta_deriv2_e(s,q,&result)); } + +static inline +double hsl_sf_hZeta0_zed(const double s, const double q) { +#if 1 + const long double ld_q=(long double)(q); + const long double ld_s=(long double)(s); + const long double ld_log1prq=log1pl(1.0L/ld_q); + const long double ld_epsilon=expm1l(-ld_s*ld_log1prq); + const long double ld_z=ld_s+(ld_q+0.5L*ld_s+0.5L)*ld_epsilon; + const double z=(double)(ld_z); +#else + double z=s+(q+0.5*s+0.5)*expm1(-s*log1p(1.0/q)); +#endif + return (z); } + +// Z_{0}(s,a) = a^s \left(\frac{1}{2}+\frac{a}{s-1}\right)^{-1} \zeta(s,a) - 1 +// Z_{0}(s,a) = O\left(\frac{(s-1)s}{6a^{2}}\right) +static +int hsl_sf_hZeta0(const double s, const double q, double * value, double * abserror) { + const double criterion=ceil(10.0*s-q); + const size_t shift=(criterion<0.0)?0: + (criterionval=log1p(ln_hZeta0_value); + result->err=(2.0*GSL_DBL_EPSILON*ln_hz_coeff+hZeta0_abserror)/(1.0+ln_hZeta0_value); + } + + if (result_deriv) { + const double ld_hz_coeff2=1.0+inv_sm1*M_LOG2E; + const double ld_hz_coeff1=1.0+inv_qsm1*ld_hz_coeff2; + double hZeta1_value=NAN; + double hZeta1_abserror=NAN; + hsl_sf_hZeta1(s,2.0,M_LN2,&hZeta1_value,&hZeta1_abserror,NULL); + hZeta0_value*=hz_coeff1; + hZeta0_value+=hz_coeff0; + hZeta1_value+=1.0; + hZeta1_value*=-M_LN2*ld_hz_coeff1; + result_deriv->val=hZeta1_value/hZeta0_value; + result_deriv->err=2.0*GSL_DBL_EPSILON*fabs(result_deriv->val)+(hZeta0_abserror+hZeta1_abserror); + } + } + else { + const double ln_q=log(q); + double hZeta0_value=NAN; + double hZeta0_abserror=NAN; + hsl_sf_hZeta0(s,q,&hZeta0_value,&hZeta0_abserror); + if (result) { + const double ln_hz_term0=-s*ln_q; + const double ln_hz_term1=log(0.5+q/(s-1.0)); + result->val=ln_hz_term0+ln_hz_term1+log1p(hZeta0_value); + result->err=2.0*GSL_DBL_EPSILON*(fabs(ln_hz_term0)+fabs(ln_hz_term1))+hZeta0_abserror/(1.0+hZeta0_value); + } + if (result_deriv) { + double hZeta1_value=NAN; + double hZeta1_abserror=NAN; + double ld_hz_coeff1=NAN; + hsl_sf_hZeta1(s,q,ln_q,&hZeta1_value,&hZeta1_abserror,&ld_hz_coeff1); + result_deriv->val=-ln_q*ld_hz_coeff1*(1.0+hZeta1_value)/(1.0+hZeta0_value); + result_deriv->err=2.0*GSL_DBL_EPSILON*fabs(result_deriv->val)+(hZeta0_abserror+hZeta1_abserror); + } + } + + return (PLFIT_SUCCESS); } + +extern +double hsl_sf_lnhzeta_deriv_tuple(const double s, const double q, double * tuple0, double * tuple1) { + HSL_SF_EVAL_TUPLE_RESULT(hsl_sf_lnhzeta_deriv_tuple_e(s,q,&result0,&result1)); } + +extern +int hsl_sf_lnhzeta_e(const double s, const double q, gsl_sf_result * result) { + return (hsl_sf_lnhzeta_deriv_tuple_e(s,q,result,NULL)); } + +extern +double hsl_sf_lnhzeta(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_lnhzeta_e(s,q,&result)); } + +extern +int hsl_sf_lnhzeta_deriv_e(const double s, const double q, gsl_sf_result * result) { + return (hsl_sf_lnhzeta_deriv_tuple_e(s,q,NULL,result)); } + +extern +double hsl_sf_lnhzeta_deriv(const double s, const double q) { + HSL_SF_EVAL_RESULT(hsl_sf_lnhzeta_deriv_e(s,q,&result)); } + +// +// End of file `hsl/specfunc/hzeta.c'. diff --git a/src/rigraph/vendor/plfit/hzeta.h b/src/rigraph/vendor/plfit/hzeta.h new file mode 100644 index 0000000..31d646e --- /dev/null +++ b/src/rigraph/vendor/plfit/hzeta.h @@ -0,0 +1,96 @@ +/* This file was imported from a private scientific library + * based on GSL coined Home Scientific Libray (HSL) by its author + * Jerome Benoit; this very material is itself inspired from the + * material written by G. Jungan and distributed by GSL. + * Ultimately, some modifications were done in order to render the + * imported material independent from the rest of GSL. + */ + +/* `hsl/hsl_sf_zeta.h' C header file +// HSL - Home Scientific Library +// Copyright (C) 2005-2018 Jerome Benoit +// +// HSL 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 2 +// of the License, or (at your option) 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +/* For futher details, see its source conterpart src/hzeta.c */ + +/* Author: Jerome G. Benoit < jgmbenoit _at_ rezozer _dot_ net > */ + +#ifndef __HZETA_H__ +#define __HZETA_H__ + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS /* empty */ +# define __END_DECLS /* empty */ +#endif + +__BEGIN_DECLS + + +/* Hurwitz Zeta Function + * zeta(s,q) = Sum[ (k+q)^(-s), {k,0,Infinity} ] + * + * s > 1.0, q > 0.0 + */ +double hsl_sf_hzeta(const double s, const double q); + +/* First Derivative of Hurwitz Zeta Function + * zeta'(s,q) = - Sum[ Ln(k+q)/(k+q)^(s), {k,0,Infinity} ] + * + * s > 1.0, q > 0.0 + */ +double hsl_sf_hzeta_deriv(const double s, const double q); + +/* Second Derivative of Hurwitz Zeta Function + * zeta''(s,q) = + Sum[ Ln(k+q)^2/(k+q)^(s), {k,0,Infinity} ] + * + * s > 1.0, q > 0.0 + */ +double hsl_sf_hzeta_deriv2(const double s, const double q); + +/* Logarithm of Hurwitz Zeta Function + * lnzeta(s,q) = ln(zeta(s,q)) + * + * s > 1.0, q > 0.0 (and q >> 1) + */ +double hsl_sf_lnhzeta(const double s, const double q); + +/* Logarithmic Derivative of Hurwitz Zeta Function + * lnzeta'(s,q) = zeta'(s,q)/zeta(s,q) + * + * s > 1.0, q > 0.0 (and q >> 1) + */ +double hsl_sf_lnhzeta_deriv(const double s, const double q); + +/* Logarithm and Logarithmic Derivative of Hurwitz Zeta Function: + * nonredundant computation version: + * - lnzeta(s,q) and lnzeta'(s,q) are stored in *deriv0 and *deriv1, respectively; + * - the return value and the value stored in *deriv0 are the same; + * - deriv0 and deriv1 must be effective pointers, that is, not the NULL pointer. + * + * s > 1.0, q > 0.0 (and q >> 1) + */ +double hsl_sf_lnhzeta_deriv_tuple(const double s, const double q, double * deriv0, double * deriv1); + + +__END_DECLS + +#endif // __HZETA_H__ diff --git a/src/rigraph/vendor/plfit/kolmogorov.c b/src/rigraph/vendor/plfit/kolmogorov.c new file mode 100644 index 0000000..432878f --- /dev/null +++ b/src/rigraph/vendor/plfit/kolmogorov.c @@ -0,0 +1,66 @@ +/* kolmogorov.c + * + * Copyright (C) 2010-2011 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include "kolmogorov.h" + +double plfit_kolmogorov(double z) { + const double fj[4] = { -2, -8, -18, -32 }; + const double w = 2.50662827; + const double c1 = -1.2337005501361697; /* -pi^2 / 8 */ + const double c2 = -11.103304951225528; /* 9*c1 */ + const double c3 = -30.842513753404244; /* 25*c1 */ + + double u = fabs(z); + double v; + + if (u < 0.2) + return 1; + + if (u < 0.755) { + v = 1.0 / (u*u); + return 1 - w * (exp(c1*v) + exp(c2*v) + exp(c3*v)) / u; + } + + if (u < 6.8116) { + double r[4] = { 0, 0, 0, 0 }; + long int maxj = (long int)(3.0 / u + 0.5); + long int j; + + if (maxj < 1) + maxj = 1; + + v = u*u; + for (j = 0; j < maxj; j++) { + r[j] = exp(fj[j] * v); + } + + return 2*(r[0] - r[1] + r[2] - r[3]); + } + + return 0; +} + +double plfit_ks_test_one_sample_p(double d, size_t n) { + return plfit_kolmogorov(d * sqrt((double) n)); +} + +double plfit_ks_test_two_sample_p(double d, size_t n1, size_t n2) { + return plfit_kolmogorov(d * sqrt(n1*n2 / ((double)(n1+n2)))); +} diff --git a/src/rigraph/vendor/plfit/kolmogorov.h b/src/rigraph/vendor/plfit/kolmogorov.h new file mode 100644 index 0000000..358e697 --- /dev/null +++ b/src/rigraph/vendor/plfit/kolmogorov.h @@ -0,0 +1,43 @@ +/* kolmogorov.h + * + * Copyright (C) 2010-2011 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __KOLMOGOROV_H__ +#define __KOLMOGOROV_H__ + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS /* empty */ +# define __END_DECLS /* empty */ +#endif + +#include + +__BEGIN_DECLS + +double plfit_kolmogorov(double z); +double plfit_ks_test_one_sample_p(double d, size_t n); +double plfit_ks_test_two_sample_p(double d, size_t n1, size_t n2); + +__END_DECLS + +#endif diff --git a/src/rigraph/vendor/plfit/lbfgs.c b/src/rigraph/vendor/plfit/lbfgs.c new file mode 100644 index 0000000..3fcd2c3 --- /dev/null +++ b/src/rigraph/vendor/plfit/lbfgs.c @@ -0,0 +1,1378 @@ +/* + * Limited memory BFGS (L-BFGS). + * + * Copyright (c) 1990, Jorge Nocedal + * Copyright (c) 2007-2010 Naoaki Okazaki + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* $Id: lbfgs.c 65 2010-01-29 12:19:16Z naoaki $ */ + +/* +This library is a C port of the FORTRAN implementation of Limited-memory +Broyden-Fletcher-Goldfarb-Shanno (L-BFGS) method written by Jorge Nocedal. +The original FORTRAN source code is available at: +http://www.ece.northwestern.edu/~nocedal/lbfgs.html + +The L-BFGS algorithm is described in: + - Jorge Nocedal. + Updating Quasi-Newton Matrices with Limited Storage. + Mathematics of Computation, Vol. 35, No. 151, pp. 773--782, 1980. + - Dong C. Liu and Jorge Nocedal. + On the limited memory BFGS method for large scale optimization. + Mathematical Programming B, Vol. 45, No. 3, pp. 503-528, 1989. + +The line search algorithms used in this implementation are described in: + - John E. Dennis and Robert B. Schnabel. + Numerical Methods for Unconstrained Optimization and Nonlinear + Equations, Englewood Cliffs, 1983. + - Jorge J. More and David J. Thuente. + Line search algorithm with guaranteed sufficient decrease. + ACM Transactions on Mathematical Software (TOMS), Vol. 20, No. 3, + pp. 286-307, 1994. + +This library also implements Orthant-Wise Limited-memory Quasi-Newton (OWL-QN) +method presented in: + - Galen Andrew and Jianfeng Gao. + Scalable training of L1-regularized log-linear models. + In Proceedings of the 24th International Conference on Machine + Learning (ICML 2007), pp. 33-40, 2007. + +I would like to thank the original author, Jorge Nocedal, who has been +distributing the effieicnt and explanatory implementation in an open source +licence. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif/*HAVE_CONFIG_H*/ + +#ifndef _MSC_VER +#include +#endif + +#include +#include +#include + +#include "lbfgs.h" +#include "platform.h" + +#ifdef _MSC_VER +#define inline __inline +typedef unsigned int uint32_t; +#endif/*_MSC_VER*/ + +#if defined(USE_SSE) && defined(__SSE2__) && LBFGS_FLOAT == 64 +/* Use SSE2 optimization for 64bit double precision. */ +#include "arithmetic_sse_double.h" + +#elif defined(USE_SSE) && defined(__SSE__) && LBFGS_FLOAT == 32 +/* Use SSE optimization for 32bit float precision. */ +#include "arithmetic_sse_float.h" + +#else +/* No CPU specific optimization. */ +#include "arithmetic_ansi.h" + +#endif + +#define min2(a, b) ((a) <= (b) ? (a) : (b)) +#define max2(a, b) ((a) >= (b) ? (a) : (b)) +#define max3(a, b, c) max2(max2((a), (b)), (c)); + +#define is_aligned(p, bytes) \ + (((uintptr_t)(const void*)(p)) % (bytes) == 0) + +struct tag_callback_data { + int n; + void *instance; + lbfgs_evaluate_t proc_evaluate; + lbfgs_progress_t proc_progress; +}; +typedef struct tag_callback_data callback_data_t; + +struct tag_iteration_data { + lbfgsfloatval_t alpha; + lbfgsfloatval_t *s; /* [n] */ + lbfgsfloatval_t *y; /* [n] */ + lbfgsfloatval_t ys; /* vecdot(y, s) */ +}; +typedef struct tag_iteration_data iteration_data_t; + +static const lbfgs_parameter_t _defparam = { + 6, 1e-5, 0, 1e-5, + 0, LBFGS_LINESEARCH_DEFAULT, 40, + 1e-20, 1e20, 1e-4, 0.9, 0.9, 1.0e-16, + 0.0, 0, -1, +}; + +/* Forward function declarations. */ + +typedef int (*line_search_proc)( + int n, + lbfgsfloatval_t *x, + lbfgsfloatval_t *f, + lbfgsfloatval_t *g, + lbfgsfloatval_t *s, + lbfgsfloatval_t *stp, + const lbfgsfloatval_t* xp, + const lbfgsfloatval_t* gp, + lbfgsfloatval_t *wa, + callback_data_t *cd, + const lbfgs_parameter_t *param + ); + +static int line_search_backtracking( + int n, + lbfgsfloatval_t *x, + lbfgsfloatval_t *f, + lbfgsfloatval_t *g, + lbfgsfloatval_t *s, + lbfgsfloatval_t *stp, + const lbfgsfloatval_t* xp, + const lbfgsfloatval_t* gp, + lbfgsfloatval_t *wa, + callback_data_t *cd, + const lbfgs_parameter_t *param + ); + +static int line_search_backtracking_owlqn( + int n, + lbfgsfloatval_t *x, + lbfgsfloatval_t *f, + lbfgsfloatval_t *g, + lbfgsfloatval_t *s, + lbfgsfloatval_t *stp, + const lbfgsfloatval_t* xp, + const lbfgsfloatval_t* gp, + lbfgsfloatval_t *wp, + callback_data_t *cd, + const lbfgs_parameter_t *param + ); + +static int line_search_morethuente( + int n, + lbfgsfloatval_t *x, + lbfgsfloatval_t *f, + lbfgsfloatval_t *g, + lbfgsfloatval_t *s, + lbfgsfloatval_t *stp, + const lbfgsfloatval_t* xp, + const lbfgsfloatval_t* gp, + lbfgsfloatval_t *wa, + callback_data_t *cd, + const lbfgs_parameter_t *param + ); + +static int update_trial_interval( + lbfgsfloatval_t *x, + lbfgsfloatval_t *fx, + lbfgsfloatval_t *dx, + lbfgsfloatval_t *y, + lbfgsfloatval_t *fy, + lbfgsfloatval_t *dy, + lbfgsfloatval_t *t, + lbfgsfloatval_t *ft, + lbfgsfloatval_t *dt, + const lbfgsfloatval_t tmin, + const lbfgsfloatval_t tmax, + int *brackt + ); + +static lbfgsfloatval_t owlqn_x1norm( + const lbfgsfloatval_t* x, + const int start, + const int n + ); + +static void owlqn_pseudo_gradient( + lbfgsfloatval_t* pg, + const lbfgsfloatval_t* x, + const lbfgsfloatval_t* g, + const int n, + const lbfgsfloatval_t c, + const int start, + const int end + ); + +static void owlqn_project( + lbfgsfloatval_t* d, + const lbfgsfloatval_t* sign, + const int start, + const int end + ); + + +#if defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__)) +static int round_out_variables(int n) +{ + n += 7; + n /= 8; + n *= 8; + return n; +} +#endif/*defined(USE_SSE)*/ + +lbfgsfloatval_t* lbfgs_malloc(int n) +{ +#if defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__)) + n = round_out_variables(n); +#endif/*defined(USE_SSE)*/ + return (lbfgsfloatval_t*)vecalloc(sizeof(lbfgsfloatval_t) * (size_t) n); +} + +void lbfgs_free(lbfgsfloatval_t *x) +{ + vecfree(x); +} + +void lbfgs_parameter_init(lbfgs_parameter_t *param) +{ + memcpy(param, &_defparam, sizeof(*param)); +} + +int lbfgs( + int n, + lbfgsfloatval_t *x, + lbfgsfloatval_t *ptr_fx, + lbfgs_evaluate_t proc_evaluate, + lbfgs_progress_t proc_progress, + void *instance, + lbfgs_parameter_t *_param + ) +{ + int ret; + int i, j, k, ls, end, bound; + lbfgsfloatval_t step; + + /* Constant parameters and their default values. */ + lbfgs_parameter_t param = (_param != NULL) ? (*_param) : _defparam; + const int m = param.m; + + lbfgsfloatval_t *xp = NULL; + lbfgsfloatval_t *g = NULL, *gp = NULL, *pg = NULL; + lbfgsfloatval_t *d = NULL, *w = NULL, *pf = NULL; + iteration_data_t *lm = NULL, *it = NULL; + lbfgsfloatval_t ys, yy; + lbfgsfloatval_t xnorm, gnorm, beta; + lbfgsfloatval_t fx = 0.; + lbfgsfloatval_t rate = 0.; + line_search_proc linesearch = line_search_morethuente; + + /* Construct a callback data. */ + callback_data_t cd; + cd.n = n; + cd.instance = instance; + cd.proc_evaluate = proc_evaluate; + cd.proc_progress = proc_progress; + +#if defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__)) + /* Round out the number of variables. */ + n = round_out_variables(n); +#endif/*defined(USE_SSE)*/ + + /* Check the input parameters for errors. */ + if (n <= 0) { + return LBFGSERR_INVALID_N; + } +#if defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__)) + if (n % 8 != 0) { + return LBFGSERR_INVALID_N_SSE; + } + if (!is_aligned(x, 16)) { + return LBFGSERR_INVALID_X_SSE; + } +#endif/*defined(USE_SSE)*/ + if (param.epsilon < 0.) { + return LBFGSERR_INVALID_EPSILON; + } + if (param.past < 0) { + return LBFGSERR_INVALID_TESTPERIOD; + } + if (param.delta < 0.) { + return LBFGSERR_INVALID_DELTA; + } + if (param.min_step < 0.) { + return LBFGSERR_INVALID_MINSTEP; + } + if (param.max_step < param.min_step) { + return LBFGSERR_INVALID_MAXSTEP; + } + if (param.ftol < 0.) { + return LBFGSERR_INVALID_FTOL; + } + if (param.linesearch == LBFGS_LINESEARCH_BACKTRACKING_WOLFE || + param.linesearch == LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE) { + if (param.wolfe <= param.ftol || 1. <= param.wolfe) { + return LBFGSERR_INVALID_WOLFE; + } + } + if (param.gtol < 0.) { + return LBFGSERR_INVALID_GTOL; + } + if (param.xtol < 0.) { + return LBFGSERR_INVALID_XTOL; + } + if (param.max_linesearch <= 0) { + return LBFGSERR_INVALID_MAXLINESEARCH; + } + if (param.orthantwise_c < 0.) { + return LBFGSERR_INVALID_ORTHANTWISE; + } + if (param.orthantwise_start < 0 || n < param.orthantwise_start) { + return LBFGSERR_INVALID_ORTHANTWISE_START; + } + if (param.orthantwise_end < 0) { + param.orthantwise_end = n; + } + if (n < param.orthantwise_end) { + return LBFGSERR_INVALID_ORTHANTWISE_END; + } + if (param.orthantwise_c != 0.) { + switch (param.linesearch) { + case LBFGS_LINESEARCH_BACKTRACKING: + linesearch = line_search_backtracking_owlqn; + break; + default: + /* Only the backtracking method is available. */ + return LBFGSERR_INVALID_LINESEARCH; + } + } else { + switch (param.linesearch) { + case LBFGS_LINESEARCH_MORETHUENTE: + linesearch = line_search_morethuente; + break; + case LBFGS_LINESEARCH_BACKTRACKING_ARMIJO: + case LBFGS_LINESEARCH_BACKTRACKING_WOLFE: + case LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE: + linesearch = line_search_backtracking; + break; + default: + return LBFGSERR_INVALID_LINESEARCH; + } + } + + /* Allocate working space. */ + xp = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); + g = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); + gp = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); + d = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); + w = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); + if (xp == NULL || g == NULL || gp == NULL || d == NULL || w == NULL) { + ret = LBFGSERR_OUTOFMEMORY; + goto lbfgs_exit; + } + + if (param.orthantwise_c != 0.) { + /* Allocate working space for OW-LQN. */ + pg = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); + if (pg == NULL) { + ret = LBFGSERR_OUTOFMEMORY; + goto lbfgs_exit; + } + } + + /* Allocate limited memory storage. */ + lm = (iteration_data_t*)vecalloc((size_t) m * sizeof(iteration_data_t)); + if (lm == NULL) { + ret = LBFGSERR_OUTOFMEMORY; + goto lbfgs_exit; + } + + /* Initialize the limited memory. */ + for (i = 0;i < m;++i) { + it = &lm[i]; + it->alpha = 0; + it->ys = 0; + it->s = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); + it->y = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); + if (it->s == NULL || it->y == NULL) { + ret = LBFGSERR_OUTOFMEMORY; + goto lbfgs_exit; + } + } + + /* Allocate an array for storing previous values of the objective function. */ + if (0 < param.past) { + pf = (lbfgsfloatval_t*)vecalloc((size_t) param.past * sizeof(lbfgsfloatval_t)); + } + + /* Evaluate the function value and its gradient. */ + fx = cd.proc_evaluate(cd.instance, x, g, cd.n, 0); + if (0. != param.orthantwise_c) { + /* Compute the L1 norm of the variable and add it to the object value. */ + xnorm = owlqn_x1norm(x, param.orthantwise_start, param.orthantwise_end); + fx += xnorm * param.orthantwise_c; + owlqn_pseudo_gradient( + pg, x, g, n, + param.orthantwise_c, param.orthantwise_start, param.orthantwise_end + ); + } + + /* Store the initial value of the objective function. */ + if (pf != NULL) { + pf[0] = fx; + } + + /* + Compute the direction; + we assume the initial hessian matrix H_0 as the identity matrix. + */ + if (param.orthantwise_c == 0.) { + vecncpy(d, g, n); + } else { + vecncpy(d, pg, n); + } + + /* + Make sure that the initial variables are not a minimizer. + */ + vec2norm(&xnorm, x, n); + if (param.orthantwise_c == 0.) { + vec2norm(&gnorm, g, n); + } else { + vec2norm(&gnorm, pg, n); + } + if (xnorm < 1.0) xnorm = 1.0; + if (gnorm / xnorm <= param.epsilon) { + ret = LBFGS_ALREADY_MINIMIZED; + goto lbfgs_exit; + } + + /* Compute the initial step: + step = 1.0 / sqrt(vecdot(d, d, n)) + */ + vec2norminv(&step, d, n); + + k = 1; + end = 0; + for (;;) { + /* Store the current position and gradient vectors. */ + veccpy(xp, x, n); + veccpy(gp, g, n); + + /* Search for an optimal step. */ + if (param.orthantwise_c == 0.) { + ls = linesearch(n, x, &fx, g, d, &step, xp, gp, w, &cd, ¶m); + } else { + ls = linesearch(n, x, &fx, g, d, &step, xp, pg, w, &cd, ¶m); + owlqn_pseudo_gradient( + pg, x, g, n, + param.orthantwise_c, param.orthantwise_start, param.orthantwise_end + ); + } + if (ls < 0) { + /* Revert to the previous point. */ + veccpy(x, xp, n); + veccpy(g, gp, n); + ret = ls; + goto lbfgs_exit; + } + + /* Compute x and g norms. */ + vec2norm(&xnorm, x, n); + if (param.orthantwise_c == 0.) { + vec2norm(&gnorm, g, n); + } else { + vec2norm(&gnorm, pg, n); + } + + /* Report the progress. */ + if (cd.proc_progress) { + ret = cd.proc_progress(cd.instance, x, g, fx, xnorm, gnorm, step, cd.n, k, ls); + if (ret) { + goto lbfgs_exit; + } + } + + /* + Convergence test. + The criterion is given by the following formula: + |g(x)| / \max(1, |x|) < \epsilon + */ + if (xnorm < 1.0) xnorm = 1.0; + if (gnorm / xnorm <= param.epsilon) { + /* Convergence. */ + ret = LBFGS_SUCCESS; + break; + } + + /* + Test for stopping criterion. + The criterion is given by the following formula: + (f(past_x) - f(x)) / f(x) < \delta + */ + if (pf != NULL) { + /* We don't test the stopping criterion while k < past. */ + if (param.past <= k) { + /* Compute the relative improvement from the past. */ + rate = (pf[k % param.past] - fx) / fx; + + /* The stopping criterion. */ + if (rate < param.delta) { + ret = LBFGS_STOP; + break; + } + } + + /* Store the current value of the objective function. */ + pf[k % param.past] = fx; + } + + if (param.max_iterations != 0 && param.max_iterations < k+1) { + /* Maximum number of iterations. */ + ret = LBFGSERR_MAXIMUMITERATION; + break; + } + + /* + Update vectors s and y: + s_{k+1} = x_{k+1} - x_{k} = \step * d_{k}. + y_{k+1} = g_{k+1} - g_{k}. + */ + it = &lm[end]; + vecdiff(it->s, x, xp, n); + vecdiff(it->y, g, gp, n); + + /* + Compute scalars ys and yy: + ys = y^t \cdot s = 1 / \rho. + yy = y^t \cdot y. + Notice that yy is used for scaling the hessian matrix H_0 (Cholesky factor). + */ + vecdot(&ys, it->y, it->s, n); + vecdot(&yy, it->y, it->y, n); + it->ys = ys; + + /* + Recursive formula to compute dir = -(H \cdot g). + This is described in page 779 of: + Jorge Nocedal. + Updating Quasi-Newton Matrices with Limited Storage. + Mathematics of Computation, Vol. 35, No. 151, + pp. 773--782, 1980. + */ + bound = (m <= k) ? m : k; + ++k; + end = (end + 1) % m; + + /* Compute the steepest direction. */ + if (param.orthantwise_c == 0.) { + /* Compute the negative of gradients. */ + vecncpy(d, g, n); + } else { + vecncpy(d, pg, n); + } + + j = end; + for (i = 0;i < bound;++i) { + j = (j + m - 1) % m; /* if (--j == -1) j = m-1; */ + it = &lm[j]; + /* \alpha_{j} = \rho_{j} s^{t}_{j} \cdot q_{k+1}. */ + vecdot(&it->alpha, it->s, d, n); + it->alpha /= it->ys; + /* q_{i} = q_{i+1} - \alpha_{i} y_{i}. */ + vecadd(d, it->y, -it->alpha, n); + } + + vecscale(d, ys / yy, n); + + for (i = 0;i < bound;++i) { + it = &lm[j]; + /* \beta_{j} = \rho_{j} y^t_{j} \cdot \gamma_{i}. */ + vecdot(&beta, it->y, d, n); + beta /= it->ys; + /* \gamma_{i+1} = \gamma_{i} + (\alpha_{j} - \beta_{j}) s_{j}. */ + vecadd(d, it->s, it->alpha - beta, n); + j = (j + 1) % m; /* if (++j == m) j = 0; */ + } + + /* + Constrain the search direction for orthant-wise updates. + */ + if (param.orthantwise_c != 0.) { + for (i = param.orthantwise_start;i < param.orthantwise_end;++i) { + if (d[i] * pg[i] >= 0) { + d[i] = 0; + } + } + } + + /* + Now the search direction d is ready. We try step = 1 first. + */ + step = 1.0; + } + +lbfgs_exit: + /* Return the final value of the objective function. */ + if (ptr_fx != NULL) { + *ptr_fx = fx; + } + + vecfree(pf); + + /* Free memory blocks used by this function. */ + if (lm != NULL) { + for (i = 0;i < m;++i) { + vecfree(lm[i].s); + vecfree(lm[i].y); + } + vecfree(lm); + } + vecfree(pg); + vecfree(w); + vecfree(d); + vecfree(gp); + vecfree(g); + vecfree(xp); + + return ret; +} + + + +static int line_search_backtracking( + int n, + lbfgsfloatval_t *x, + lbfgsfloatval_t *f, + lbfgsfloatval_t *g, + lbfgsfloatval_t *s, + lbfgsfloatval_t *stp, + const lbfgsfloatval_t* xp, + const lbfgsfloatval_t* gp, + lbfgsfloatval_t *wp, + callback_data_t *cd, + const lbfgs_parameter_t *param + ) +{ + int count = 0; + lbfgsfloatval_t width, dg; + lbfgsfloatval_t finit, dginit = 0., dgtest; + const lbfgsfloatval_t dec = 0.5, inc = 2.1; + + /* Check the input parameters for errors. */ + if (*stp <= 0.) { + return LBFGSERR_INVALIDPARAMETERS; + } + + /* Compute the initial gradient in the search direction. */ + vecdot(&dginit, g, s, n); + + /* Make sure that s points to a descent direction. */ + if (0 < dginit) { + return LBFGSERR_INCREASEGRADIENT; + } + + /* The initial value of the objective function. */ + finit = *f; + dgtest = param->ftol * dginit; + + for (;;) { + veccpy(x, xp, n); + vecadd(x, s, *stp, n); + + /* Evaluate the function and gradient values. */ + *f = cd->proc_evaluate(cd->instance, x, g, cd->n, *stp); + + ++count; + + if (*f > finit + *stp * dgtest) { + width = dec; + } else { + /* The sufficient decrease condition (Armijo condition). */ + if (param->linesearch == LBFGS_LINESEARCH_BACKTRACKING_ARMIJO) { + /* Exit with the Armijo condition. */ + return count; + } + + /* Check the Wolfe condition. */ + vecdot(&dg, g, s, n); + if (dg < param->wolfe * dginit) { + width = inc; + } else { + if(param->linesearch == LBFGS_LINESEARCH_BACKTRACKING_WOLFE) { + /* Exit with the regular Wolfe condition. */ + return count; + } + + /* Check the strong Wolfe condition. */ + if(dg > -param->wolfe * dginit) { + width = dec; + } else { + /* Exit with the strong Wolfe condition. */ + return count; + } + } + } + + if (*stp < param->min_step) { + /* The step is the minimum value. */ + return LBFGSERR_MINIMUMSTEP; + } + if (*stp > param->max_step) { + /* The step is the maximum value. */ + return LBFGSERR_MAXIMUMSTEP; + } + if (param->max_linesearch <= count) { + /* Maximum number of iteration. */ + return LBFGSERR_MAXIMUMLINESEARCH; + } + + (*stp) *= width; + } +} + + + +static int line_search_backtracking_owlqn( + int n, + lbfgsfloatval_t *x, + lbfgsfloatval_t *f, + lbfgsfloatval_t *g, + lbfgsfloatval_t *s, + lbfgsfloatval_t *stp, + const lbfgsfloatval_t* xp, + const lbfgsfloatval_t* gp, + lbfgsfloatval_t *wp, + callback_data_t *cd, + const lbfgs_parameter_t *param + ) +{ + int i, count = 0; + lbfgsfloatval_t width = 0.5, norm = 0.; + lbfgsfloatval_t finit = *f, dgtest; + + /* Check the input parameters for errors. */ + if (*stp <= 0.) { + return LBFGSERR_INVALIDPARAMETERS; + } + + /* Choose the orthant for the new point. */ + for (i = 0;i < n;++i) { + wp[i] = (xp[i] == 0.) ? -gp[i] : xp[i]; + } + + for (;;) { + /* Update the current point. */ + veccpy(x, xp, n); + vecadd(x, s, *stp, n); + + /* The current point is projected onto the orthant. */ + owlqn_project(x, wp, param->orthantwise_start, param->orthantwise_end); + + /* Evaluate the function and gradient values. */ + *f = cd->proc_evaluate(cd->instance, x, g, cd->n, *stp); + + /* Compute the L1 norm of the variables and add it to the object value. */ + norm = owlqn_x1norm(x, param->orthantwise_start, param->orthantwise_end); + *f += norm * param->orthantwise_c; + + ++count; + + dgtest = 0.; + for (i = 0;i < n;++i) { + dgtest += (x[i] - xp[i]) * gp[i]; + } + + if (*f <= finit + param->ftol * dgtest) { + /* The sufficient decrease condition. */ + return count; + } + + if (*stp < param->min_step) { + /* The step is the minimum value. */ + return LBFGSERR_MINIMUMSTEP; + } + if (*stp > param->max_step) { + /* The step is the maximum value. */ + return LBFGSERR_MAXIMUMSTEP; + } + if (param->max_linesearch <= count) { + /* Maximum number of iteration. */ + return LBFGSERR_MAXIMUMLINESEARCH; + } + + (*stp) *= width; + } +} + + + +static int line_search_morethuente( + int n, + lbfgsfloatval_t *x, + lbfgsfloatval_t *f, + lbfgsfloatval_t *g, + lbfgsfloatval_t *s, + lbfgsfloatval_t *stp, + const lbfgsfloatval_t* xp, + const lbfgsfloatval_t* gp, + lbfgsfloatval_t *wa, + callback_data_t *cd, + const lbfgs_parameter_t *param + ) +{ + int count = 0; + int brackt, stage1, uinfo = 0; + lbfgsfloatval_t dg; + lbfgsfloatval_t stx, fx, dgx; + lbfgsfloatval_t sty, fy, dgy; + lbfgsfloatval_t fxm, dgxm, fym, dgym, fm, dgm; + lbfgsfloatval_t finit, ftest1, dginit, dgtest; + lbfgsfloatval_t width, prev_width; + lbfgsfloatval_t stmin, stmax; + + /* Check the input parameters for errors. */ + if (*stp <= 0.) { + return LBFGSERR_INVALIDPARAMETERS; + } + + /* Compute the initial gradient in the search direction. */ + vecdot(&dginit, g, s, n); + + /* Make sure that s points to a descent direction. */ + if (0 < dginit) { + return LBFGSERR_INCREASEGRADIENT; + } + + /* Initialize local variables. */ + brackt = 0; + stage1 = 1; + finit = *f; + dgtest = param->ftol * dginit; + width = param->max_step - param->min_step; + prev_width = 2.0 * width; + + /* + The variables stx, fx, dgx contain the values of the step, + function, and directional derivative at the best step. + The variables sty, fy, dgy contain the value of the step, + function, and derivative at the other endpoint of + the interval of uncertainty. + The variables stp, f, dg contain the values of the step, + function, and derivative at the current step. + */ + stx = sty = 0.; + fx = fy = finit; + dgx = dgy = dginit; + + for (;;) { + /* + Set the minimum and maximum steps to correspond to the + present interval of uncertainty. + */ + if (brackt) { + stmin = min2(stx, sty); + stmax = max2(stx, sty); + } else { + stmin = stx; + stmax = *stp + 4.0 * (*stp - stx); + } + + /* Clip the step in the range of [stpmin, stpmax]. */ + if (*stp < param->min_step) *stp = param->min_step; + if (param->max_step < *stp) *stp = param->max_step; + + /* + If an unusual termination is to occur then let + stp be the lowest point obtained so far. + */ + if ((brackt && ((*stp <= stmin || stmax <= *stp) || param->max_linesearch <= count + 1 || uinfo != 0)) || (brackt && (stmax - stmin <= param->xtol * stmax))) { + *stp = stx; + } + + /* + Compute the current value of x: + x <- x + (*stp) * s. + */ + veccpy(x, xp, n); + vecadd(x, s, *stp, n); + + /* Evaluate the function and gradient values. */ + *f = cd->proc_evaluate(cd->instance, x, g, cd->n, *stp); + vecdot(&dg, g, s, n); + + ftest1 = finit + *stp * dgtest; + ++count; + + /* Test for errors and convergence. */ + if (brackt && ((*stp <= stmin || stmax <= *stp) || uinfo != 0)) { + /* Rounding errors prevent further progress. */ + return LBFGSERR_ROUNDING_ERROR; + } + if (*stp == param->max_step && *f <= ftest1 && dg <= dgtest) { + /* The step is the maximum value. */ + return LBFGSERR_MAXIMUMSTEP; + } + if (*stp == param->min_step && (ftest1 < *f || dgtest <= dg)) { + /* The step is the minimum value. */ + return LBFGSERR_MINIMUMSTEP; + } + if (brackt && (stmax - stmin) <= param->xtol * stmax) { + /* Relative width of the interval of uncertainty is at most xtol. */ + return LBFGSERR_WIDTHTOOSMALL; + } + if (param->max_linesearch <= count) { + /* Maximum number of iteration. */ + return LBFGSERR_MAXIMUMLINESEARCH; + } + if (*f <= ftest1 && fabs(dg) <= param->gtol * (-dginit)) { + /* The sufficient decrease condition and the directional derivative condition hold. */ + return count; + } + + /* + In the first stage we seek a step for which the modified + function has a nonpositive value and nonnegative derivative. + */ + if (stage1 && *f <= ftest1 && min2(param->ftol, param->gtol) * dginit <= dg) { + stage1 = 0; + } + + /* + A modified function is used to predict the step only if + we have not obtained a step for which the modified + function has a nonpositive function value and nonnegative + derivative, and if a lower function value has been + obtained but the decrease is not sufficient. + */ + if (stage1 && ftest1 < *f && *f <= fx) { + /* Define the modified function and derivative values. */ + fm = *f - *stp * dgtest; + fxm = fx - stx * dgtest; + fym = fy - sty * dgtest; + dgm = dg - dgtest; + dgxm = dgx - dgtest; + dgym = dgy - dgtest; + + /* + Call update_trial_interval() to update the interval of + uncertainty and to compute the new step. + */ + uinfo = update_trial_interval( + &stx, &fxm, &dgxm, + &sty, &fym, &dgym, + stp, &fm, &dgm, + stmin, stmax, &brackt + ); + + /* Reset the function and gradient values for f. */ + fx = fxm + stx * dgtest; + fy = fym + sty * dgtest; + dgx = dgxm + dgtest; + dgy = dgym + dgtest; + } else { + /* + Call update_trial_interval() to update the interval of + uncertainty and to compute the new step. + */ + uinfo = update_trial_interval( + &stx, &fx, &dgx, + &sty, &fy, &dgy, + stp, f, &dg, + stmin, stmax, &brackt + ); + } + + /* + Force a sufficient decrease in the interval of uncertainty. + */ + if (brackt) { + if (0.66 * prev_width <= fabs(sty - stx)) { + *stp = stx + 0.5 * (sty - stx); + } + prev_width = width; + width = fabs(sty - stx); + } + } +} + + + +/** + * Define the local variables for computing minimizers. + */ +#define USES_MINIMIZER \ + lbfgsfloatval_t a, d, gamma, theta, p, q, r, s; + +/** + * Find a minimizer of an interpolated cubic function. + * @param cm The minimizer of the interpolated cubic. + * @param u The value of one point, u. + * @param fu The value of f(u). + * @param du The value of f'(u). + * @param v The value of another point, v. + * @param fv The value of f(v). + * @param du The value of f'(v). + */ +#define CUBIC_MINIMIZER(cm, u, fu, du, v, fv, dv) \ + d = (v) - (u); \ + theta = ((fu) - (fv)) * 3 / d + (du) + (dv); \ + p = fabs(theta); \ + q = fabs(du); \ + r = fabs(dv); \ + s = max3(p, q, r); \ + /* gamma = s*sqrt((theta/s)**2 - (du/s) * (dv/s)) */ \ + a = theta / s; \ + gamma = s * sqrt(a * a - ((du) / s) * ((dv) / s)); \ + if ((v) < (u)) gamma = -gamma; \ + p = gamma - (du) + theta; \ + q = gamma - (du) + gamma + (dv); \ + r = p / q; \ + (cm) = (u) + r * d; + +/** + * Find a minimizer of an interpolated cubic function. + * @param cm The minimizer of the interpolated cubic. + * @param u The value of one point, u. + * @param fu The value of f(u). + * @param du The value of f'(u). + * @param v The value of another point, v. + * @param fv The value of f(v). + * @param du The value of f'(v). + * @param xmin The maximum value. + * @param xmin The minimum value. + */ +#define CUBIC_MINIMIZER2(cm, u, fu, du, v, fv, dv, xmin, xmax) \ + d = (v) - (u); \ + theta = ((fu) - (fv)) * 3 / d + (du) + (dv); \ + p = fabs(theta); \ + q = fabs(du); \ + r = fabs(dv); \ + s = max3(p, q, r); \ + /* gamma = s*sqrt((theta/s)**2 - (du/s) * (dv/s)) */ \ + a = theta / s; \ + gamma = s * sqrt(max2(0, a * a - ((du) / s) * ((dv) / s))); \ + if ((u) < (v)) gamma = -gamma; \ + p = gamma - (dv) + theta; \ + q = gamma - (dv) + gamma + (du); \ + r = p / q; \ + if (r < 0. && gamma != 0.) { \ + (cm) = (v) - r * d; \ + } else if (a < 0) { \ + (cm) = (xmax); \ + } else { \ + (cm) = (xmin); \ + } + +/** + * Find a minimizer of an interpolated quadratic function. + * @param qm The minimizer of the interpolated quadratic. + * @param u The value of one point, u. + * @param fu The value of f(u). + * @param du The value of f'(u). + * @param v The value of another point, v. + * @param fv The value of f(v). + */ +#define QUARD_MINIMIZER(qm, u, fu, du, v, fv) \ + a = (v) - (u); \ + (qm) = (u) + (du) / (((fu) - (fv)) / a + (du)) / 2 * a; + +/** + * Find a minimizer of an interpolated quadratic function. + * @param qm The minimizer of the interpolated quadratic. + * @param u The value of one point, u. + * @param du The value of f'(u). + * @param v The value of another point, v. + * @param dv The value of f'(v). + */ +#define QUARD_MINIMIZER2(qm, u, du, v, dv) \ + a = (u) - (v); \ + (qm) = (v) + (dv) / ((dv) - (du)) * a; + +/** + * Update a safeguarded trial value and interval for line search. + * + * The parameter x represents the step with the least function value. + * The parameter t represents the current step. This function assumes + * that the derivative at the point of x in the direction of the step. + * If the bracket is set to true, the minimizer has been bracketed in + * an interval of uncertainty with endpoints between x and y. + * + * @param x The pointer to the value of one endpoint. + * @param fx The pointer to the value of f(x). + * @param dx The pointer to the value of f'(x). + * @param y The pointer to the value of another endpoint. + * @param fy The pointer to the value of f(y). + * @param dy The pointer to the value of f'(y). + * @param t The pointer to the value of the trial value, t. + * @param ft The pointer to the value of f(t). + * @param dt The pointer to the value of f'(t). + * @param tmin The minimum value for the trial value, t. + * @param tmax The maximum value for the trial value, t. + * @param brackt The pointer to the predicate if the trial value is + * bracketed. + * @retval int Status value. Zero indicates a normal termination. + * + * @see + * Jorge J. More and David J. Thuente. Line search algorithm with + * guaranteed sufficient decrease. ACM Transactions on Mathematical + * Software (TOMS), Vol 20, No 3, pp. 286-307, 1994. + */ +static int update_trial_interval( + lbfgsfloatval_t *x, + lbfgsfloatval_t *fx, + lbfgsfloatval_t *dx, + lbfgsfloatval_t *y, + lbfgsfloatval_t *fy, + lbfgsfloatval_t *dy, + lbfgsfloatval_t *t, + lbfgsfloatval_t *ft, + lbfgsfloatval_t *dt, + const lbfgsfloatval_t tmin, + const lbfgsfloatval_t tmax, + int *brackt + ) +{ + int bound; + int dsign = fsigndiff(dt, dx); + lbfgsfloatval_t mc; /* minimizer of an interpolated cubic. */ + lbfgsfloatval_t mq; /* minimizer of an interpolated quadratic. */ + lbfgsfloatval_t newt; /* new trial value. */ + USES_MINIMIZER; /* for CUBIC_MINIMIZER and QUARD_MINIMIZER. */ + + /* Check the input parameters for errors. */ + if (*brackt) { + if (*t <= min2(*x, *y) || max2(*x, *y) <= *t) { + /* The trival value t is out of the interval. */ + return LBFGSERR_OUTOFINTERVAL; + } + if (0. <= *dx * (*t - *x)) { + /* The function must decrease from x. */ + return LBFGSERR_INCREASEGRADIENT; + } + if (tmax < tmin) { + /* Incorrect tmin and tmax specified. */ + return LBFGSERR_INCORRECT_TMINMAX; + } + } + + /* + Trial value selection. + */ + if (*fx < *ft) { + /* + Case 1: a higher function value. + The minimum is brackt. If the cubic minimizer is closer + to x than the quadratic one, the cubic one is taken, else + the average of the minimizers is taken. + */ + *brackt = 1; + bound = 1; + CUBIC_MINIMIZER(mc, *x, *fx, *dx, *t, *ft, *dt); + QUARD_MINIMIZER(mq, *x, *fx, *dx, *t, *ft); + if (fabs(mc - *x) < fabs(mq - *x)) { + newt = mc; + } else { + newt = mc + 0.5 * (mq - mc); + } + } else if (dsign) { + /* + Case 2: a lower function value and derivatives of + opposite sign. The minimum is brackt. If the cubic + minimizer is closer to x than the quadratic (secant) one, + the cubic one is taken, else the quadratic one is taken. + */ + *brackt = 1; + bound = 0; + CUBIC_MINIMIZER(mc, *x, *fx, *dx, *t, *ft, *dt); + QUARD_MINIMIZER2(mq, *x, *dx, *t, *dt); + if (fabs(mc - *t) > fabs(mq - *t)) { + newt = mc; + } else { + newt = mq; + } + } else if (fabs(*dt) < fabs(*dx)) { + /* + Case 3: a lower function value, derivatives of the + same sign, and the magnitude of the derivative decreases. + The cubic minimizer is only used if the cubic tends to + infinity in the direction of the minimizer or if the minimum + of the cubic is beyond t. Otherwise the cubic minimizer is + defined to be either tmin or tmax. The quadratic (secant) + minimizer is also computed and if the minimum is brackt + then the the minimizer closest to x is taken, else the one + farthest away is taken. + */ + bound = 1; + CUBIC_MINIMIZER2(mc, *x, *fx, *dx, *t, *ft, *dt, tmin, tmax); + QUARD_MINIMIZER2(mq, *x, *dx, *t, *dt); + if (*brackt) { + if (fabs(*t - mc) < fabs(*t - mq)) { + newt = mc; + } else { + newt = mq; + } + } else { + if (fabs(*t - mc) > fabs(*t - mq)) { + newt = mc; + } else { + newt = mq; + } + } + } else { + /* + Case 4: a lower function value, derivatives of the + same sign, and the magnitude of the derivative does + not decrease. If the minimum is not brackt, the step + is either tmin or tmax, else the cubic minimizer is taken. + */ + bound = 0; + if (*brackt) { + CUBIC_MINIMIZER(newt, *t, *ft, *dt, *y, *fy, *dy); + } else if (*x < *t) { + newt = tmax; + } else { + newt = tmin; + } + } + + /* + Update the interval of uncertainty. This update does not + depend on the new step or the case analysis above. + + - Case a: if f(x) < f(t), + x <- x, y <- t. + - Case b: if f(t) <= f(x) && f'(t)*f'(x) > 0, + x <- t, y <- y. + - Case c: if f(t) <= f(x) && f'(t)*f'(x) < 0, + x <- t, y <- x. + */ + if (*fx < *ft) { + /* Case a */ + *y = *t; + *fy = *ft; + *dy = *dt; + } else { + /* Case c */ + if (dsign) { + *y = *x; + *fy = *fx; + *dy = *dx; + } + /* Cases b and c */ + *x = *t; + *fx = *ft; + *dx = *dt; + } + + /* Clip the new trial value in [tmin, tmax]. */ + if (tmax < newt) newt = tmax; + if (newt < tmin) newt = tmin; + + /* + Redefine the new trial value if it is close to the upper bound + of the interval. + */ + if (*brackt && bound) { + mq = *x + 0.66 * (*y - *x); + if (*x < *y) { + if (mq < newt) newt = mq; + } else { + if (newt < mq) newt = mq; + } + } + + /* Return the new trial value. */ + *t = newt; + return 0; +} + + + + + +static lbfgsfloatval_t owlqn_x1norm( + const lbfgsfloatval_t* x, + const int start, + const int n + ) +{ + int i; + lbfgsfloatval_t norm = 0.; + + for (i = start;i < n;++i) { + norm += fabs(x[i]); + } + + return norm; +} + +static void owlqn_pseudo_gradient( + lbfgsfloatval_t* pg, + const lbfgsfloatval_t* x, + const lbfgsfloatval_t* g, + const int n, + const lbfgsfloatval_t c, + const int start, + const int end + ) +{ + int i; + + /* Compute the negative of gradients. */ + for (i = 0;i < start;++i) { + pg[i] = g[i]; + } + + /* Compute the psuedo-gradients. */ + for (i = start;i < end;++i) { + if (x[i] < 0.) { + /* Differentiable. */ + pg[i] = g[i] - c; + } else if (0. < x[i]) { + /* Differentiable. */ + pg[i] = g[i] + c; + } else { + if (g[i] < -c) { + /* Take the right partial derivative. */ + pg[i] = g[i] + c; + } else if (c < g[i]) { + /* Take the left partial derivative. */ + pg[i] = g[i] - c; + } else { + pg[i] = 0.; + } + } + } + + for (i = end;i < n;++i) { + pg[i] = g[i]; + } +} + +static void owlqn_project( + lbfgsfloatval_t* d, + const lbfgsfloatval_t* sign, + const int start, + const int end + ) +{ + int i; + + for (i = start;i < end;++i) { + if (d[i] * sign[i] <= 0) { + d[i] = 0; + } + } +} diff --git a/src/rigraph/vendor/plfit/lbfgs.h b/src/rigraph/vendor/plfit/lbfgs.h new file mode 100644 index 0000000..f26ae87 --- /dev/null +++ b/src/rigraph/vendor/plfit/lbfgs.h @@ -0,0 +1,736 @@ +/* + * C library of Limited memory BFGS (L-BFGS). + * + * Copyright (c) 1990, Jorge Nocedal + * Copyright (c) 2007-2010 Naoaki Okazaki + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* $Id: lbfgs.h 65 2010-01-29 12:19:16Z naoaki $ */ + +#ifndef __LBFGS_H__ +#define __LBFGS_H__ + +#ifdef __cplusplus +extern "C" { +#endif/*__cplusplus*/ + +/* + * The default precision of floating point values is 64bit (double). + */ +#ifndef LBFGS_FLOAT +#define LBFGS_FLOAT 64 +#endif/*LBFGS_FLOAT*/ + +/* + * Activate optimization routines for IEEE754 floating point values. + */ +#ifndef LBFGS_IEEE_FLOAT +#define LBFGS_IEEE_FLOAT 1 +#endif/*LBFGS_IEEE_FLOAT*/ + +#if LBFGS_FLOAT == 32 +typedef float lbfgsfloatval_t; + +#elif LBFGS_FLOAT == 64 +typedef double lbfgsfloatval_t; + +#else +#error "libLBFGS supports single (float; LBFGS_FLOAT = 32) or double (double; LBFGS_FLOAT=64) precision only." + +#endif + + +/** + * \addtogroup liblbfgs_api libLBFGS API + * @{ + * + * The libLBFGS API. + */ + +/** + * Return values of lbfgs(). + * + * Roughly speaking, a negative value indicates an error. + */ +enum { + /** L-BFGS reaches convergence. */ + LBFGS_SUCCESS = 0, + LBFGS_CONVERGENCE = 0, + LBFGS_STOP, + /** The initial variables already minimize the objective function. */ + LBFGS_ALREADY_MINIMIZED, + + /** Unknown error. */ + LBFGSERR_UNKNOWNERROR = -1024, + /** Logic error. */ + LBFGSERR_LOGICERROR, + /** Insufficient memory. */ + LBFGSERR_OUTOFMEMORY, + /** The minimization process has been canceled. */ + LBFGSERR_CANCELED, + /** Invalid number of variables specified. */ + LBFGSERR_INVALID_N, + /** Invalid number of variables (for SSE) specified. */ + LBFGSERR_INVALID_N_SSE, + /** The array x must be aligned to 16 (for SSE). */ + LBFGSERR_INVALID_X_SSE, + /** Invalid parameter lbfgs_parameter_t::epsilon specified. */ + LBFGSERR_INVALID_EPSILON, + /** Invalid parameter lbfgs_parameter_t::past specified. */ + LBFGSERR_INVALID_TESTPERIOD, + /** Invalid parameter lbfgs_parameter_t::delta specified. */ + LBFGSERR_INVALID_DELTA, + /** Invalid parameter lbfgs_parameter_t::linesearch specified. */ + LBFGSERR_INVALID_LINESEARCH, + /** Invalid parameter lbfgs_parameter_t::max_step specified. */ + LBFGSERR_INVALID_MINSTEP, + /** Invalid parameter lbfgs_parameter_t::max_step specified. */ + LBFGSERR_INVALID_MAXSTEP, + /** Invalid parameter lbfgs_parameter_t::ftol specified. */ + LBFGSERR_INVALID_FTOL, + /** Invalid parameter lbfgs_parameter_t::wolfe specified. */ + LBFGSERR_INVALID_WOLFE, + /** Invalid parameter lbfgs_parameter_t::gtol specified. */ + LBFGSERR_INVALID_GTOL, + /** Invalid parameter lbfgs_parameter_t::xtol specified. */ + LBFGSERR_INVALID_XTOL, + /** Invalid parameter lbfgs_parameter_t::max_linesearch specified. */ + LBFGSERR_INVALID_MAXLINESEARCH, + /** Invalid parameter lbfgs_parameter_t::orthantwise_c specified. */ + LBFGSERR_INVALID_ORTHANTWISE, + /** Invalid parameter lbfgs_parameter_t::orthantwise_start specified. */ + LBFGSERR_INVALID_ORTHANTWISE_START, + /** Invalid parameter lbfgs_parameter_t::orthantwise_end specified. */ + LBFGSERR_INVALID_ORTHANTWISE_END, + /** The line-search step went out of the interval of uncertainty. */ + LBFGSERR_OUTOFINTERVAL, + /** A logic error occurred; alternatively, the interval of uncertainty + became too small. */ + LBFGSERR_INCORRECT_TMINMAX, + /** A rounding error occurred; alternatively, no line-search step + satisfies the sufficient decrease and curvature conditions. */ + LBFGSERR_ROUNDING_ERROR, + /** The line-search step became smaller than lbfgs_parameter_t::min_step. */ + LBFGSERR_MINIMUMSTEP, + /** The line-search step became larger than lbfgs_parameter_t::max_step. */ + LBFGSERR_MAXIMUMSTEP, + /** The line-search routine reaches the maximum number of evaluations. */ + LBFGSERR_MAXIMUMLINESEARCH, + /** The algorithm routine reaches the maximum number of iterations. */ + LBFGSERR_MAXIMUMITERATION, + /** Relative width of the interval of uncertainty is at most + lbfgs_parameter_t::xtol. */ + LBFGSERR_WIDTHTOOSMALL, + /** A logic error (negative line-search step) occurred. */ + LBFGSERR_INVALIDPARAMETERS, + /** The current search direction increases the objective function value. */ + LBFGSERR_INCREASEGRADIENT, +}; + +/** + * Line search algorithms. + */ +enum { + /** The default algorithm (MoreThuente method). */ + LBFGS_LINESEARCH_DEFAULT = 0, + /** MoreThuente method proposd by More and Thuente. */ + LBFGS_LINESEARCH_MORETHUENTE = 0, + /** + * Backtracking method with the Armijo condition. + * The backtracking method finds the step length such that it satisfies + * the sufficient decrease (Armijo) condition, + * - f(x + a * d) <= f(x) + lbfgs_parameter_t::ftol * a * g(x)^T d, + * + * where x is the current point, d is the current search direction, and + * a is the step length. + */ + LBFGS_LINESEARCH_BACKTRACKING_ARMIJO = 1, + /** The backtracking method with the defualt (regular Wolfe) condition. */ + LBFGS_LINESEARCH_BACKTRACKING = 2, + /** + * Backtracking method with regular Wolfe condition. + * The backtracking method finds the step length such that it satisfies + * both the Armijo condition (LBFGS_LINESEARCH_BACKTRACKING_ARMIJO) + * and the curvature condition, + * - g(x + a * d)^T d >= lbfgs_parameter_t::wolfe * g(x)^T d, + * + * where x is the current point, d is the current search direction, and + * a is the step length. + */ + LBFGS_LINESEARCH_BACKTRACKING_WOLFE = 2, + /** + * Backtracking method with strong Wolfe condition. + * The backtracking method finds the step length such that it satisfies + * both the Armijo condition (LBFGS_LINESEARCH_BACKTRACKING_ARMIJO) + * and the following condition, + * - |g(x + a * d)^T d| <= lbfgs_parameter_t::wolfe * |g(x)^T d|, + * + * where x is the current point, d is the current search direction, and + * a is the step length. + */ + LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE = 3, +}; + +/** + * L-BFGS optimization parameters. + * Call lbfgs_parameter_init() function to initialize parameters to the + * default values. + */ +typedef struct { + /** + * The number of corrections to approximate the inverse hessian matrix. + * The L-BFGS routine stores the computation results of previous \ref m + * iterations to approximate the inverse hessian matrix of the current + * iteration. This parameter controls the size of the limited memories + * (corrections). The default value is \c 6. Values less than \c 3 are + * not recommended. Large values will result in excessive computing time. + */ + int m; + + /** + * Epsilon for convergence test. + * This parameter determines the accuracy with which the solution is to + * be found. A minimization terminates when + * ||g|| < \ref epsilon * max(1, ||x||), + * where ||.|| denotes the Euclidean (L2) norm. The default value is + * \c 1e-5. + */ + lbfgsfloatval_t epsilon; + + /** + * Distance for delta-based convergence test. + * This parameter determines the distance, in iterations, to compute + * the rate of decrease of the objective function. If the value of this + * parameter is zero, the library does not perform the delta-based + * convergence test. The default value is \c 0. + */ + int past; + + /** + * Delta for convergence test. + * This parameter determines the minimum rate of decrease of the + * objective function. The library stops iterations when the + * following condition is met: + * (f' - f) / f < \ref delta, + * where f' is the objective value of \ref past iterations ago, and f is + * the objective value of the current iteration. + * The default value is \c 0. + */ + lbfgsfloatval_t delta; + + /** + * The maximum number of iterations. + * The lbfgs() function terminates an optimization process with + * ::LBFGSERR_MAXIMUMITERATION status code when the iteration count + * exceedes this parameter. Setting this parameter to zero continues an + * optimization process until a convergence or error. The default value + * is \c 0. + */ + int max_iterations; + + /** + * The line search algorithm. + * This parameter specifies a line search algorithm to be used by the + * L-BFGS routine. + */ + int linesearch; + + /** + * The maximum number of trials for the line search. + * This parameter controls the number of function and gradients evaluations + * per iteration for the line search routine. The default value is \c 20. + */ + int max_linesearch; + + /** + * The minimum step of the line search routine. + * The default value is \c 1e-20. This value need not be modified unless + * the exponents are too large for the machine being used, or unless the + * problem is extremely badly scaled (in which case the exponents should + * be increased). + */ + lbfgsfloatval_t min_step; + + /** + * The maximum step of the line search. + * The default value is \c 1e+20. This value need not be modified unless + * the exponents are too large for the machine being used, or unless the + * problem is extremely badly scaled (in which case the exponents should + * be increased). + */ + lbfgsfloatval_t max_step; + + /** + * A parameter to control the accuracy of the line search routine. + * The default value is \c 1e-4. This parameter should be greater + * than zero and smaller than \c 0.5. + */ + lbfgsfloatval_t ftol; + + /** + * A coefficient for the Wolfe condition. + * This parameter is valid only when the backtracking line-search + * algorithm is used with the Wolfe condition, + * ::LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE or + * ::LBFGS_LINESEARCH_BACKTRACKING_WOLFE . + * The default value is \c 0.9. This parameter should be greater + * the \ref ftol parameter and smaller than \c 1.0. + */ + lbfgsfloatval_t wolfe; + + /** + * A parameter to control the accuracy of the line search routine. + * The default value is \c 0.9. If the function and gradient + * evaluations are inexpensive with respect to the cost of the + * iteration (which is sometimes the case when solving very large + * problems) it may be advantageous to set this parameter to a small + * value. A typical small value is \c 0.1. This parameter shuold be + * greater than the \ref ftol parameter (\c 1e-4) and smaller than + * \c 1.0. + */ + lbfgsfloatval_t gtol; + + /** + * The machine precision for floating-point values. + * This parameter must be a positive value set by a client program to + * estimate the machine precision. The line search routine will terminate + * with the status code (::LBFGSERR_ROUNDING_ERROR) if the relative width + * of the interval of uncertainty is less than this parameter. + */ + lbfgsfloatval_t xtol; + + /** + * Coeefficient for the L1 norm of variables. + * This parameter should be set to zero for standard minimization + * problems. Setting this parameter to a positive value activates + * Orthant-Wise Limited-memory Quasi-Newton (OWL-QN) method, which + * minimizes the objective function F(x) combined with the L1 norm |x| + * of the variables, {F(x) + C |x|}. This parameter is the coeefficient + * for the |x|, i.e., C. As the L1 norm |x| is not differentiable at + * zero, the library modifies function and gradient evaluations from + * a client program suitably; a client program thus have only to return + * the function value F(x) and gradients G(x) as usual. The default value + * is zero. + */ + lbfgsfloatval_t orthantwise_c; + + /** + * Start index for computing L1 norm of the variables. + * This parameter is valid only for OWL-QN method + * (i.e., \ref orthantwise_c != 0). This parameter b (0 <= b < N) + * specifies the index number from which the library computes the + * L1 norm of the variables x, + * |x| := |x_{b}| + |x_{b+1}| + ... + |x_{N}| . + * In other words, variables x_1, ..., x_{b-1} are not used for + * computing the L1 norm. Setting b (0 < b < N), one can protect + * variables, x_1, ..., x_{b-1} (e.g., a bias term of logistic + * regression) from being regularized. The default value is zero. + */ + int orthantwise_start; + + /** + * End index for computing L1 norm of the variables. + * This parameter is valid only for OWL-QN method + * (i.e., \ref orthantwise_c != 0). This parameter e (0 < e <= N) + * specifies the index number at which the library stops computing the + * L1 norm of the variables x, + */ + int orthantwise_end; +} lbfgs_parameter_t; + + +/** + * Callback interface to provide objective function and gradient evaluations. + * + * The lbfgs() function call this function to obtain the values of objective + * function and its gradients when needed. A client program must implement + * this function to evaluate the values of the objective function and its + * gradients, given current values of variables. + * + * @param instance The user data sent for lbfgs() function by the client. + * @param x The current values of variables. + * @param g The gradient vector. The callback function must compute + * the gradient values for the current variables. + * @param n The number of variables. + * @param step The current step of the line search routine. + * @retval lbfgsfloatval_t The value of the objective function for the current + * variables. + */ +typedef lbfgsfloatval_t (*lbfgs_evaluate_t)( + void *instance, + const lbfgsfloatval_t *x, + lbfgsfloatval_t *g, + const int n, + const lbfgsfloatval_t step + ); + +/** + * Callback interface to receive the progress of the optimization process. + * + * The lbfgs() function call this function for each iteration. Implementing + * this function, a client program can store or display the current progress + * of the optimization process. + * + * @param instance The user data sent for lbfgs() function by the client. + * @param x The current values of variables. + * @param g The current gradient values of variables. + * @param fx The current value of the objective function. + * @param xnorm The Euclidean norm of the variables. + * @param gnorm The Euclidean norm of the gradients. + * @param step The line-search step used for this iteration. + * @param n The number of variables. + * @param k The iteration count. + * @param ls The number of evaluations called for this iteration. + * @retval int Zero to continue the optimization process. Returning a + * non-zero value will cancel the optimization process. + */ +typedef int (*lbfgs_progress_t)( + void *instance, + const lbfgsfloatval_t *x, + const lbfgsfloatval_t *g, + const lbfgsfloatval_t fx, + const lbfgsfloatval_t xnorm, + const lbfgsfloatval_t gnorm, + const lbfgsfloatval_t step, + int n, + int k, + int ls + ); + +/* +A user must implement a function compatible with ::lbfgs_evaluate_t (evaluation +callback) and pass the pointer to the callback function to lbfgs() arguments. +Similarly, a user can implement a function compatible with ::lbfgs_progress_t +(progress callback) to obtain the current progress (e.g., variables, function +value, ||G||, etc) and to cancel the iteration process if necessary. +Implementation of a progress callback is optional: a user can pass \c NULL if +progress notification is not necessary. + +In addition, a user must preserve two requirements: + - The number of variables must be multiples of 16 (this is not 4). + - The memory block of variable array ::x must be aligned to 16. + +This algorithm terminates an optimization +when: + + ||G|| < \epsilon \cdot \max(1, ||x||) . + +In this formula, ||.|| denotes the Euclidean norm. +*/ + +/** + * Start a L-BFGS optimization. + * + * @param n The number of variables. + * @param x The array of variables. A client program can set + * default values for the optimization and receive the + * optimization result through this array. This array + * must be allocated by ::lbfgs_malloc function + * for libLBFGS built with SSE/SSE2 optimization routine + * enabled. The library built without SSE/SSE2 + * optimization does not have such a requirement. + * @param ptr_fx The pointer to the variable that receives the final + * value of the objective function for the variables. + * This argument can be set to \c NULL if the final + * value of the objective function is unnecessary. + * @param proc_evaluate The callback function to provide function and + * gradient evaluations given a current values of + * variables. A client program must implement a + * callback function compatible with \ref + * lbfgs_evaluate_t and pass the pointer to the + * callback function. + * @param proc_progress The callback function to receive the progress + * (the number of iterations, the current value of + * the objective function) of the minimization + * process. This argument can be set to \c NULL if + * a progress report is unnecessary. + * @param instance A user data for the client program. The callback + * functions will receive the value of this argument. + * @param param The pointer to a structure representing parameters for + * L-BFGS optimization. A client program can set this + * parameter to \c NULL to use the default parameters. + * Call lbfgs_parameter_init() function to fill a + * structure with the default values. + * @retval int The status code. This function returns zero if the + * minimization process terminates without an error. A + * non-zero value indicates an error. + */ +int lbfgs( + int n, + lbfgsfloatval_t *x, + lbfgsfloatval_t *ptr_fx, + lbfgs_evaluate_t proc_evaluate, + lbfgs_progress_t proc_progress, + void *instance, + lbfgs_parameter_t *param + ); + +/** + * Initialize L-BFGS parameters to the default values. + * + * Call this function to fill a parameter structure with the default values + * and overwrite parameter values if necessary. + * + * @param param The pointer to the parameter structure. + */ +void lbfgs_parameter_init(lbfgs_parameter_t *param); + +/** + * Allocate an array for variables. + * + * This function allocates an array of variables for the convenience of + * ::lbfgs function; the function has a requreiemt for a variable array + * when libLBFGS is built with SSE/SSE2 optimization routines. A user does + * not have to use this function for libLBFGS built without SSE/SSE2 + * optimization. + * + * @param n The number of variables. + */ +lbfgsfloatval_t* lbfgs_malloc(int n); + +/** + * Free an array of variables. + * + * @param x The array of variables allocated by ::lbfgs_malloc + * function. + */ +void lbfgs_free(lbfgsfloatval_t *x); + +/** @} */ + +#ifdef __cplusplus +} +#endif/*__cplusplus*/ + + + +/** +@mainpage libLBFGS: a library of Limited-memory Broyden-Fletcher-Goldfarb-Shanno (L-BFGS) + +@section intro Introduction + +This library is a C port of the implementation of Limited-memory +Broyden-Fletcher-Goldfarb-Shanno (L-BFGS) method written by Jorge Nocedal. +The original FORTRAN source code is available at: +http://www.ece.northwestern.edu/~nocedal/lbfgs.html + +The L-BFGS method solves the unconstrainted minimization problem, + +
+    minimize F(x), x = (x1, x2, ..., xN),
+
+ +only if the objective function F(x) and its gradient G(x) are computable. The +well-known Newton's method requires computation of the inverse of the hessian +matrix of the objective function. However, the computational cost for the +inverse hessian matrix is expensive especially when the objective function +takes a large number of variables. The L-BFGS method iteratively finds a +minimizer by approximating the inverse hessian matrix by information from last +m iterations. This innovation saves the memory storage and computational time +drastically for large-scaled problems. + +Among the various ports of L-BFGS, this library provides several features: +- Optimization with L1-norm (Orthant-Wise Limited-memory Quasi-Newton + (OWL-QN) method): + In addition to standard minimization problems, the library can minimize + a function F(x) combined with L1-norm |x| of the variables, + {F(x) + C |x|}, where C is a constant scalar parameter. This feature is + useful for estimating parameters of sparse log-linear models (e.g., + logistic regression and maximum entropy) with L1-regularization (or + Laplacian prior). +- Clean C code: + Unlike C codes generated automatically by f2c (Fortran 77 into C converter), + this port includes changes based on my interpretations, improvements, + optimizations, and clean-ups so that the ported code would be well-suited + for a C code. In addition to comments inherited from the original code, + a number of comments were added through my interpretations. +- Callback interface: + The library receives function and gradient values via a callback interface. + The library also notifies the progress of the optimization by invoking a + callback function. In the original implementation, a user had to set + function and gradient values every time the function returns for obtaining + updated values. +- Thread safe: + The library is thread-safe, which is the secondary gain from the callback + interface. +- Cross platform. The source code can be compiled on Microsoft Visual + Studio 2005, GNU C Compiler (gcc), etc. +- Configurable precision: A user can choose single-precision (float) + or double-precision (double) accuracy by changing ::LBFGS_FLOAT macro. +- SSE/SSE2 optimization: + This library includes SSE/SSE2 optimization (written in compiler intrinsics) + for vector arithmetic operations on Intel/AMD processors. The library uses + SSE for float values and SSE2 for double values. The SSE/SSE2 optimization + routine is disabled by default. + +This library is used by: +- CRFsuite: A fast implementation of Conditional Random Fields (CRFs) +- Classias: A collection of machine-learning algorithms for classification +- mlegp: an R package for maximum likelihood estimates for Gaussian processes +- imaging2: the imaging2 class library +- Algorithm::LBFGS - Perl extension for L-BFGS +- YAP-LBFGS (an interface to call libLBFGS from YAP Prolog) + +@section download Download + +- Source code + +libLBFGS is distributed under the term of the +MIT license. + +@section changelog History +- Version 1.9 (2010-01-29): + - Fixed a mistake in checking the validity of the parameters "ftol" and + "wolfe"; this was discovered by Kevin S. Van Horn. +- Version 1.8 (2009-07-13): + - Accepted the patch submitted by Takashi Imamichi; + the backtracking method now has three criteria for choosing the step + length: + - ::LBFGS_LINESEARCH_BACKTRACKING_ARMIJO: sufficient decrease (Armijo) + condition only + - ::LBFGS_LINESEARCH_BACKTRACKING_WOLFE: regular Wolfe condition + (sufficient decrease condition + curvature condition) + - ::LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE: strong Wolfe condition + - Updated the documentation to explain the above three criteria. +- Version 1.7 (2009-02-28): + - Improved OWL-QN routines for stability. + - Removed the support of OWL-QN method in MoreThuente algorithm because + it accidentally fails in early stages of iterations for some objectives. + Because of this change, the OW-LQN method must be used with the + backtracking algorithm (::LBFGS_LINESEARCH_BACKTRACKING), or the + library returns ::LBFGSERR_INVALID_LINESEARCH. + - Renamed line search algorithms as follows: + - ::LBFGS_LINESEARCH_BACKTRACKING: regular Wolfe condition. + - ::LBFGS_LINESEARCH_BACKTRACKING_LOOSE: regular Wolfe condition. + - ::LBFGS_LINESEARCH_BACKTRACKING_STRONG: strong Wolfe condition. + - Source code clean-up. +- Version 1.6 (2008-11-02): + - Improved line-search algorithm with strong Wolfe condition, which was + contributed by Takashi Imamichi. This routine is now default for + ::LBFGS_LINESEARCH_BACKTRACKING. The previous line search algorithm + with regular Wolfe condition is still available as + ::LBFGS_LINESEARCH_BACKTRACKING_LOOSE. + - Configurable stop index for L1-norm computation. A member variable + ::lbfgs_parameter_t::orthantwise_end was added to specify the index + number at which the library stops computing the L1 norm of the + variables. This is useful to prevent some variables from being + regularized by the OW-LQN method. + - A sample program written in C++ (sample/sample.cpp). +- Version 1.5 (2008-07-10): + - Configurable starting index for L1-norm computation. A member variable + ::lbfgs_parameter_t::orthantwise_start was added to specify the index + number from which the library computes the L1 norm of the variables. + This is useful to prevent some variables from being regularized by the + OWL-QN method. + - Fixed a zero-division error when the initial variables have already + been a minimizer (reported by Takashi Imamichi). In this case, the + library returns ::LBFGS_ALREADY_MINIMIZED status code. + - Defined ::LBFGS_SUCCESS status code as zero; removed unused constants, + LBFGSFALSE and LBFGSTRUE. + - Fixed a compile error in an implicit down-cast. +- Version 1.4 (2008-04-25): + - Configurable line search algorithms. A member variable + ::lbfgs_parameter_t::linesearch was added to choose either MoreThuente + method (::LBFGS_LINESEARCH_MORETHUENTE) or backtracking algorithm + (::LBFGS_LINESEARCH_BACKTRACKING). + - Fixed a bug: the previous version did not compute psuedo-gradients + properly in the line search routines for OWL-QN. This bug might quit + an iteration process too early when the OWL-QN routine was activated + (0 < ::lbfgs_parameter_t::orthantwise_c). + - Configure script for POSIX environments. + - SSE/SSE2 optimizations with GCC. + - New functions ::lbfgs_malloc and ::lbfgs_free to use SSE/SSE2 routines + transparently. It is uncessary to use these functions for libLBFGS built + without SSE/SSE2 routines; you can still use any memory allocators if + SSE/SSE2 routines are disabled in libLBFGS. +- Version 1.3 (2007-12-16): + - An API change. An argument was added to lbfgs() function to receive the + final value of the objective function. This argument can be set to + \c NULL if the final value is unnecessary. + - Fixed a null-pointer bug in the sample code (reported by Takashi Imamichi). + - Added build scripts for Microsoft Visual Studio 2005 and GCC. + - Added README file. +- Version 1.2 (2007-12-13): + - Fixed a serious bug in orthant-wise L-BFGS. + An important variable was used without initialization. +- Version 1.1 (2007-12-01): + - Implemented orthant-wise L-BFGS. + - Implemented lbfgs_parameter_init() function. + - Fixed several bugs. + - API documentation. +- Version 1.0 (2007-09-20): + - Initial release. + +@section api Documentation + +- @ref liblbfgs_api "libLBFGS API" + +@section sample Sample code + +@include sample.c + +@section ack Acknowledgements + +The L-BFGS algorithm is described in: + - Jorge Nocedal. + Updating Quasi-Newton Matrices with Limited Storage. + Mathematics of Computation, Vol. 35, No. 151, pp. 773--782, 1980. + - Dong C. Liu and Jorge Nocedal. + On the limited memory BFGS method for large scale optimization. + Mathematical Programming B, Vol. 45, No. 3, pp. 503-528, 1989. + +The line search algorithms used in this implementation are described in: + - John E. Dennis and Robert B. Schnabel. + Numerical Methods for Unconstrained Optimization and Nonlinear + Equations, Englewood Cliffs, 1983. + - Jorge J. More and David J. Thuente. + Line search algorithm with guaranteed sufficient decrease. + ACM Transactions on Mathematical Software (TOMS), Vol. 20, No. 3, + pp. 286-307, 1994. + +This library also implements Orthant-Wise Limited-memory Quasi-Newton (OWL-QN) +method presented in: + - Galen Andrew and Jianfeng Gao. + Scalable training of L1-regularized log-linear models. + In Proceedings of the 24th International Conference on Machine + Learning (ICML 2007), pp. 33-40, 2007. + +Special thanks go to: + - Yoshimasa Tsuruoka and Daisuke Okanohara for technical information about + OWL-QN + - Takashi Imamichi for the useful enhancements of the backtracking method + +Finally I would like to thank the original author, Jorge Nocedal, who has been +distributing the effieicnt and explanatory implementation in an open source +licence. + +@section reference Reference + +- L-BFGS by Jorge Nocedal. +- Orthant-Wise Limited-memory Quasi-Newton Optimizer for L1-regularized Objectives by Galen Andrew. +- C port (via f2c) by Taku Kudo. +- C#/C++/Delphi/VisualBasic6 port in ALGLIB. +- Computational Crystallography Toolbox includes + scitbx::lbfgs. +*/ + +#endif/*__LBFGS_H__*/ diff --git a/src/rigraph/vendor/plfit/mt.c b/src/rigraph/vendor/plfit/mt.c new file mode 100644 index 0000000..615c6bc --- /dev/null +++ b/src/rigraph/vendor/plfit/mt.c @@ -0,0 +1,93 @@ +/* mt.c + * + * Mersenne Twister random number generator, based on the implementation of + * Michael Brundage (which has been placed in the public domain). + * + * Author: Tamas Nepusz (original by Michael Brundage) + * + * See the following URL for the original implementation: + * http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation.html + * + * This file has been placed in the public domain. + */ + +#include + +#include "igraph_random.h" + +#include "plfit_mt.h" + +static uint16_t get_random_uint16() { + return RNG_INT31() & 0xFFFF; +} + +void plfit_mt_init(plfit_mt_rng_t* rng) { + plfit_mt_init_from_rng(rng, 0); +} + +void plfit_mt_init_from_rng(plfit_mt_rng_t* rng, plfit_mt_rng_t* seeder) { + int i; + + if (seeder == 0) { + for (i = 0; i < PLFIT_MT_LEN; i++) { + /* RAND_MAX is guaranteed to be at least 32767, so we can use two + * calls to rand() to produce a random 32-bit number */ + rng->mt_buffer[i] = (get_random_uint16() << 16) + get_random_uint16(); + } + } else { + for (i = 0; i < PLFIT_MT_LEN; i++) { + rng->mt_buffer[i] = plfit_mt_random(seeder); + } + } + + rng->mt_index = 0; +} + +#define MT_IA 397 +#define MT_IB (PLFIT_MT_LEN - MT_IA) +#define UPPER_MASK 0x80000000 +#define LOWER_MASK 0x7FFFFFFF +#define MATRIX_A 0x9908B0DF +#define TWIST(b,i,j) ((b)[i] & UPPER_MASK) | ((b)[j] & LOWER_MASK) +#define MAGIC(s) (((s)&1)*MATRIX_A) + +uint32_t plfit_mt_random(plfit_mt_rng_t* rng) { + uint32_t * b = rng->mt_buffer; + int idx = rng->mt_index; + uint32_t s; + int i; + + if (idx == PLFIT_MT_LEN * sizeof(uint32_t)) { + idx = 0; + i = 0; + for (; i < MT_IB; i++) { + s = TWIST(b, i, i+1); + b[i] = b[i + MT_IA] ^ (s >> 1) ^ MAGIC(s); + } + for (; i < PLFIT_MT_LEN-1; i++) { + s = TWIST(b, i, i+1); + b[i] = b[i - MT_IB] ^ (s >> 1) ^ MAGIC(s); + } + + s = TWIST(b, PLFIT_MT_LEN-1, 0); + b[PLFIT_MT_LEN-1] = b[MT_IA-1] ^ (s >> 1) ^ MAGIC(s); + } + + rng->mt_index = idx + sizeof(uint32_t); + return *(uint32_t *)((unsigned char *)b + idx); + /* + Matsumoto and Nishimura additionally confound the bits returned to the caller + but this doesn't increase the randomness, and slows down the generator by + as much as 25%. So I omit these operations here. + + r ^= (r >> 11); + r ^= (r << 7) & 0x9D2C5680; + r ^= (r << 15) & 0xEFC60000; + r ^= (r >> 18); + */ +} + + +double plfit_mt_uniform_01(plfit_mt_rng_t* rng) { + return ((double)plfit_mt_random(rng)) / PLFIT_MT_RAND_MAX; +} diff --git a/src/rigraph/vendor/plfit/options.c b/src/rigraph/vendor/plfit/options.c new file mode 100644 index 0000000..1665560 --- /dev/null +++ b/src/rigraph/vendor/plfit/options.c @@ -0,0 +1,52 @@ +/* options.c + * + * Copyright (C) 2012 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "plfit_error.h" +#include "plfit.h" + +const plfit_continuous_options_t plfit_continuous_default_options = { + /* .finite_size_correction = */ 0, + /* .xmin_method = */ PLFIT_DEFAULT_CONTINUOUS_METHOD, + /* .p_value_method = */ PLFIT_DEFAULT_P_VALUE_METHOD, + /* .p_value_precision = */ 0.01, + /* .rng = */ 0 +}; + +const plfit_discrete_options_t plfit_discrete_default_options = { + /* .finite_size_correction = */ 0, + /* .alpha_method = */ PLFIT_DEFAULT_DISCRETE_METHOD, + /* .alpha = */ { + /* .min = */ 1.01, + /* .max = */ 5, + /* .step = */ 0.01 + }, + /* .p_value_method = */ PLFIT_DEFAULT_P_VALUE_METHOD, + /* .p_value_precision = */ 0.01, + /* .rng = */ 0 +}; + +int plfit_continuous_options_init(plfit_continuous_options_t* options) { + *options = plfit_continuous_default_options; + return PLFIT_SUCCESS; +} + +int plfit_discrete_options_init(plfit_discrete_options_t* options) { + *options = plfit_discrete_default_options; + return PLFIT_SUCCESS; +} diff --git a/src/rigraph/vendor/plfit/platform.c b/src/rigraph/vendor/plfit/platform.c new file mode 100644 index 0000000..e9560e4 --- /dev/null +++ b/src/rigraph/vendor/plfit/platform.c @@ -0,0 +1,36 @@ +/* platform.c + * + * Copyright (C) 2014 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "platform.h" + +#ifdef _MSC_VER + +inline double _plfit_fmin(double a, double b) { + return (a < b) ? a : b; +} + +inline double _plfit_round(double x) { + return floor(x+0.5); +} + +#endif + +/* Dummy function to prevent a warning when compiling with Clang - the file + * would contain no symbols */ +void _plfit_i_unused() {} diff --git a/src/rigraph/vendor/plfit/platform.h b/src/rigraph/vendor/plfit/platform.h new file mode 100644 index 0000000..6512955 --- /dev/null +++ b/src/rigraph/vendor/plfit/platform.h @@ -0,0 +1,69 @@ +/* platform.h + * + * Copyright (C) 2010-2011 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __PLATFORM_H__ +#define __PLATFORM_H__ + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS /* empty */ +# define __END_DECLS /* empty */ +#endif + +#include + +__BEGIN_DECLS + +#ifdef _MSC_VER +#include + +#define snprintf _snprintf +#define inline __inline + +#ifndef isfinite +# define isfinite(x) _finite(x) +#endif + +extern double _plfit_fmin(double a, double b); +extern double _plfit_round(double x); + +#define fmin _plfit_fmin +#define round _plfit_round + +#endif /* _MSC_VER */ + +#ifndef isnan +# define isnan(x) ((x) != (x)) +#endif + +#ifndef INFINITY +# define INFINITY (1.0/0.0) +#endif + +#ifndef NAN +# define NAN ((double)0.0 / (double)DBL_MIN) +#endif + +__END_DECLS + +#endif /* __PLATFORM_H__ */ diff --git a/src/rigraph/vendor/plfit/plfit.c b/src/rigraph/vendor/plfit/plfit.c new file mode 100644 index 0000000..266a9b0 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit.c @@ -0,0 +1,1342 @@ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* plfit.c + * + * Copyright (C) 2010-2011 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include "plfit_error.h" +#include "gss.h" +#include "lbfgs.h" +#include "platform.h" +#include "plfit.h" +#include "kolmogorov.h" +#include "plfit_sampling.h" +#include "hzeta.h" + +/* #define PLFIT_DEBUG */ + +#define DATA_POINTS_CHECK \ + if (n <= 0) { \ + PLFIT_ERROR("no data points", PLFIT_EINVAL); \ + } + +#define XMIN_CHECK_ZERO \ + if (xmin <= 0) { \ + PLFIT_ERROR("xmin must be greater than zero", PLFIT_EINVAL); \ + } +#define XMIN_CHECK_ONE \ + if (xmin < 1) { \ + PLFIT_ERROR("xmin must be at least 1", PLFIT_EINVAL); \ + } + +static int plfit_i_resample_continuous(double* xs_head, size_t num_smaller, + size_t n, double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, + double* result); +static int plfit_i_resample_discrete(double* xs_head, size_t num_smaller, + size_t n, double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, + double* result); + +static int double_comparator(const void *a, const void *b) { + const double *da = (const double*)a; + const double *db = (const double*)b; + return (*da > *db) - (*da < *db); +} + +static int plfit_i_copy_and_sort(double* xs, size_t n, double** result) { + *result = (double*)malloc(sizeof(double) * n); + if (*result == 0) { + PLFIT_ERROR("cannot create sorted copy of input data", PLFIT_ENOMEM); + } + + memcpy(*result, xs, sizeof(double) * n); + qsort(*result, n, sizeof(double), double_comparator); + + return PLFIT_SUCCESS; +} + +/** + * Given an unsorted array of doubles, counts how many elements there are that + * are smaller than a given value. + * + * \param begin pointer to the beginning of the array + * \param end pointer to the first element after the end of the array + * \param xmin the threshold value + * + * \return the nubmer of elements in the array that are smaller than the given + * value. + */ +static size_t count_smaller(double* begin, double* end, double xmin) { + double* p; + size_t counter = 0; + + for (p = begin; p < end; p++) { + if (*p < xmin) { + counter++; + } + } + + return counter; +} + +/** + * Given an unsorted array of doubles, return another array that contains the + * elements that are smaller than a given value + * + * \param begin pointer to the beginning of the array + * \param end pointer to the first element after the end of the array + * \param xmin the threshold value + * \param result_length if not \c NULL, the number of unique elements in the + * given array is returned here + * + * \return pointer to the head of the new array or 0 if there is not enough + * memory + */ +static double* extract_smaller(double* begin, double* end, double xmin, + size_t* result_length) { + size_t counter = count_smaller(begin, end, xmin); + double *p, *result; + + result = calloc(counter, sizeof(double)); + if (result == 0) + return 0; + + for (p = result; begin < end; begin++) { + if (*begin < xmin) { + *p = *begin; + p++; + } + } + + if (result_length) { + *result_length = counter; + } + + return result; +} + +/** + * Given a sorted array of doubles, return another array that contains pointers + * into the array for the start of each block of identical elements. + * + * \param begin pointer to the beginning of the array + * \param end pointer to the first element after the end of the array + * \param result_length if not \c NULL, the number of unique elements in the + * given array is returned here. It is left unchanged if + * the function returns with an error. + * + * \return pointer to the head of the new array or 0 if there is not enough + * memory + */ +static double** unique_element_pointers(double* begin, double* end, size_t* result_length) { + double* ptr = begin; + double** result; + double prev_x; + size_t num_elts = 15; + size_t used_elts = 0; + + /* Special case: empty array */ + if (begin == end) { + result = calloc(1, sizeof(double*)); + if (result != 0) { + result[0] = 0; + if (result_length != 0) { + *result_length = 0; + } + } + return result; + } + + /* Allocate initial result array, including the guard element */ + result = calloc(num_elts+1, sizeof(double*)); + if (result == 0) + return 0; + + prev_x = *begin; + result[used_elts++] = begin; + + /* Process the input array */ + for (ptr = begin+1; ptr < end; ptr++) { + if (*ptr == prev_x) + continue; + + /* New block found */ + if (used_elts >= num_elts) { + /* Array full; allocate a new chunk */ + num_elts = num_elts*2 + 1; + result = realloc(result, sizeof(double*) * (num_elts+1)); + if (result == 0) + return 0; + } + + /* Store the new element */ + result[used_elts++] = ptr; + prev_x = *ptr; + } + + /* Calculate the result length */ + if (result_length != 0) { + *result_length = used_elts; + } + + /* Add the guard entry to the end of the result */ + result[used_elts++] = 0; + + return result; +} + +static void plfit_i_perform_finite_size_correction(plfit_result_t* result, size_t n) { + result->alpha = result->alpha * (n-1) / n + 1.0 / n; +} + +/********** Continuous power law distribution fitting **********/ + +static void plfit_i_logsum_less_than_continuous(double* begin, double* end, + double xmin, double* result, size_t* m) { + double logsum = 0.0; + size_t count = 0; + + for (; begin != end; begin++) { + if (*begin >= xmin) { + count++; + logsum += log(*begin / xmin); + } + } + + *m = count; + *result = logsum; +} + +static double plfit_i_logsum_continuous(double* begin, double* end, double xmin) { + double logsum = 0.0; + for (; begin != end; begin++) + logsum += log(*begin / xmin); + return logsum; +} + +static int plfit_i_estimate_alpha_continuous(double* xs, size_t n, + double xmin, double* alpha) { + double result; + size_t m; + + XMIN_CHECK_ZERO; + + plfit_i_logsum_less_than_continuous(xs, xs+n, xmin, &result, &m); + + if (m == 0) { + PLFIT_ERROR("no data point was larger than xmin", PLFIT_EINVAL); + } + + *alpha = 1 + m / result; + + return PLFIT_SUCCESS; +} + +static int plfit_i_estimate_alpha_continuous_sorted(double* xs, size_t n, + double xmin, double* alpha) { + double* end = xs+n; + + XMIN_CHECK_ZERO; + + for (; xs != end && *xs < xmin; xs++); + if (xs == end) { + PLFIT_ERROR("no data point was larger than xmin", PLFIT_EINVAL); + } + + *alpha = 1 + (end-xs) / plfit_i_logsum_continuous(xs, end, xmin); + + return PLFIT_SUCCESS; +} + +static int plfit_i_ks_test_continuous(double* xs, double* xs_end, + const double alpha, const double xmin, double* D) { + /* Assumption: xs is sorted and cut off at xmin so the first element is + * always larger than or equal to xmin. */ + double result = 0, n; + int m = 0; + + n = xs_end - xs; + + while (xs < xs_end) { + double d = fabs(1-pow(xmin / *xs, alpha-1) - m / n); + + if (d > result) + result = d; + + xs++; m++; + } + + *D = result; + + return PLFIT_SUCCESS; +} + +static int plfit_i_calculate_p_value_continuous(double* xs, size_t n, + const plfit_continuous_options_t *options, plfit_bool_t xmin_fixed, + plfit_result_t *result) { + long int num_trials; + long int successes = 0; + double *xs_head; + size_t num_smaller; + plfit_continuous_options_t options_no_p_value = *options; + int retval = PLFIT_SUCCESS; + + if (options->p_value_method == PLFIT_P_VALUE_SKIP) { + result->p = NAN; + return PLFIT_SUCCESS; + } + + if (options->p_value_method == PLFIT_P_VALUE_APPROXIMATE) { + num_smaller = count_smaller(xs, xs + n, result->xmin); + result->p = plfit_ks_test_one_sample_p(result->D, n - num_smaller); + return PLFIT_SUCCESS; + } + + options_no_p_value.p_value_method = PLFIT_P_VALUE_SKIP; + num_trials = (long int)(0.25 / options->p_value_precision / options->p_value_precision); + if (num_trials <= 0) { + PLFIT_ERROR("invalid p-value precision", PLFIT_EINVAL); + } + + /* Extract the head of xs that contains elements smaller than xmin */ + xs_head = extract_smaller(xs, xs+n, result->xmin, &num_smaller); + if (xs_head == 0) + PLFIT_ERROR("cannot calculate exact p-value", PLFIT_ENOMEM); + +#ifdef _OPENMP +#pragma omp parallel +#endif + { + /* Parallel section starts here. If we are compiling using OpenMP, each + * thread will use its own RNG that is seeded from the master RNG. If + * we are compiling without OpenMP, there is only one thread and it uses + * the master RNG. This section must be critical to ensure that only one + * thread is using the master RNG at the same time. */ +#ifdef _OPENMP + plfit_mt_rng_t private_rng; +#endif + plfit_mt_rng_t *p_rng; + double *ys; + long int i; + plfit_result_t result_synthetic; + +#ifdef _OPENMP +#pragma omp critical + { + p_rng = &private_rng; + plfit_mt_init_from_rng(p_rng, options->rng); + } +#else + p_rng = options->rng; +#endif + + /* Allocate memory to sample into */ + ys = calloc(n, sizeof(double)); + if (ys == 0) { + retval = PLFIT_ENOMEM; + } else { + /* The main for loop starts here. */ +#ifdef _OPENMP +#pragma omp for reduction(+:successes) +#endif + for (i = 0; i < num_trials; i++) { + plfit_i_resample_continuous(xs_head, num_smaller, n, result->alpha, + result->xmin, n, p_rng, ys); + if (xmin_fixed) { + plfit_estimate_alpha_continuous(ys, n, result->xmin, + &options_no_p_value, &result_synthetic); + } else { + plfit_continuous(ys, n, &options_no_p_value, &result_synthetic); + } + if (result_synthetic.D > result->D) + successes++; + } + free(ys); + } + + /* End of parallelized part */ + } + + free(xs_head); + + if (retval == PLFIT_SUCCESS) { + result->p = successes / ((double)num_trials); + } else { + PLFIT_ERROR("cannot calculate exact p-value", retval); + } + + return retval; +} + +int plfit_log_likelihood_continuous(double* xs, size_t n, double alpha, + double xmin, double* L) { + double logsum, c; + size_t m; + + if (alpha <= 1) { + PLFIT_ERROR("alpha must be greater than one", PLFIT_EINVAL); + } + XMIN_CHECK_ZERO; + + c = (alpha - 1) / xmin; + plfit_i_logsum_less_than_continuous(xs, xs+n, xmin, &logsum, &m); + *L = -alpha * logsum + log(c) * m; + + return PLFIT_SUCCESS; +} + +int plfit_estimate_alpha_continuous_sorted(double* xs, size_t n, double xmin, + const plfit_continuous_options_t* options, plfit_result_t *result) { + double *begin, *end; + + if (!options) + options = &plfit_continuous_default_options; + + begin = xs; + end = xs + n; + while (begin < end && *begin < xmin) + begin++; + + PLFIT_CHECK(plfit_i_estimate_alpha_continuous_sorted(begin, end-begin, + xmin, &result->alpha)); + PLFIT_CHECK(plfit_i_ks_test_continuous(begin, end, result->alpha, + xmin, &result->D)); + + if (options->finite_size_correction) + plfit_i_perform_finite_size_correction(result, end-begin); + result->xmin = xmin; + + PLFIT_CHECK(plfit_log_likelihood_continuous(begin, end-begin, result->alpha, + result->xmin, &result->L)); + PLFIT_CHECK(plfit_i_calculate_p_value_continuous(xs, n, options, 1, result)); + + return PLFIT_SUCCESS; +} + +int plfit_estimate_alpha_continuous(double* xs, size_t n, double xmin, + const plfit_continuous_options_t* options, plfit_result_t *result) { + double *xs_copy; + + if (!options) + options = &plfit_continuous_default_options; + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + PLFIT_CHECK(plfit_estimate_alpha_continuous_sorted(xs_copy, n, xmin, + options, result)); + free(xs_copy); + + return PLFIT_SUCCESS; +} + +typedef struct { + double *begin; /**< Pointer to the beginning of the array holding the data */ + double *end; /**< Pointer to after the end of the array holding the data */ + double **probes; /**< Pointers to the elements of the array that will be probed */ + size_t num_probes; /**< Number of probes */ + plfit_result_t last; /**< Result of the last evaluation */ +} plfit_continuous_xmin_opt_data_t; + +static double plfit_i_continuous_xmin_opt_evaluate(void* instance, double x) { + plfit_continuous_xmin_opt_data_t* data = (plfit_continuous_xmin_opt_data_t*)instance; + double* begin = data->probes[(long int)x]; + + data->last.xmin = *begin; + +#ifdef PLFIT_DEBUG + printf("Trying with probes[%ld] = %.4f\n", (long int)x, *begin); +#endif + + plfit_i_estimate_alpha_continuous_sorted(begin, data->end-begin, *begin, + &data->last.alpha); + plfit_i_ks_test_continuous(begin, data->end, data->last.alpha, *begin, + &data->last.D); + + return data->last.D; +} + +static int plfit_i_continuous_xmin_opt_progress(void* instance, double x, double fx, + double min, double fmin, double left, double right, int k) { +#ifdef PLFIT_DEBUG + printf("Iteration #%d: [%.4f; %.4f), x=%.4f, fx=%.4f, min=%.4f, fmin=%.4f\n", + k, left, right, x, fx, min, fmin); +#endif + + /* Continue only if `left' and `right' point to different integers */ + return (int)left == (int)right; +} + +static int plfit_i_continuous_xmin_opt_linear_scan( + plfit_continuous_xmin_opt_data_t* opt_data, plfit_result_t* best_result, + size_t* best_n) { + /* this must be signed because OpenMP with Windows MSVC needs signed for + * loop index variables. ssize_t will not work because that is a POSIX + * extension */ + ptrdiff_t i = 0; /* initialize to work around incorrect warning issued by Clang 9.0 */ + plfit_result_t global_best_result; + size_t global_best_n; + + /* Prepare some variables */ + global_best_n = 0; + global_best_result.D = DBL_MAX; + global_best_result.xmin = 0; + global_best_result.alpha = 0; + + /* Due to the OpenMP parallelization, we do things as follows. Each + * OpenMP thread will search for the best D-score on its own and store + * the result in a private local_best_result variable. The end of the + * parallel block contains a critical section that threads will enter + * one by one and compare their private local_best_result with a + * global_best that is shared among the threads. + */ +#ifdef _OPENMP +#pragma omp parallel shared(global_best_result, global_best_n) private(i) firstprivate(opt_data) +#endif + { + /* These variables are private since they are declared within the + * parallel block */ + plfit_result_t local_best_result; + plfit_continuous_xmin_opt_data_t local_opt_data = *opt_data; + size_t local_best_n; + + /* Initialize the local_best_result and local_best_n variables */ + local_best_n = 0; + local_best_result.D = DBL_MAX; + local_best_result.xmin = 0; + local_best_result.alpha = 0; + local_best_result.p = NAN; + local_best_result.L = NAN; + + /* The range of the for loop below is divided among the threads. + * nowait means that there will be no implicit barrier at the end + * of the loop so threads that get there earlier can enter the + * critical section without waiting for the others */ +#ifdef _OPENMP +#pragma omp for nowait schedule(dynamic,10) +#endif + for (i = 0; i < local_opt_data.num_probes-1; i++) { + plfit_i_continuous_xmin_opt_evaluate(&local_opt_data, i); + if (local_opt_data.last.D < local_best_result.D) { +#ifdef PLFIT_DEBUG + printf("Found new local best at %g with D=%g\n", + local_opt_data.last.xmin, local_opt_data.last.D); +#endif + local_best_result = local_opt_data.last; + local_best_n = local_opt_data.end - local_opt_data.probes[i] + 1; + } + } + + /* Critical section that finds the global best result from the + * local ones collected by each thread */ +#ifdef _OPENMP +#pragma omp critical +#endif + if (local_best_result.D < global_best_result.D) { + global_best_result = local_best_result; + global_best_n = local_best_n; +#ifdef PLFIT_DEBUG + printf("Found new global best at %g with D=%g\n", global_best_result.xmin, + global_best_result.D); +#endif + } + } + + *best_result = global_best_result; + *best_n = global_best_n; + +#ifdef PLFIT_DEBUG + printf("Returning global best: %g\n", best_result->xmin); +#endif + + return PLFIT_SUCCESS; +} + +int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* options, + plfit_result_t* result) { + gss_parameter_t gss_param; + plfit_continuous_xmin_opt_data_t opt_data; + plfit_result_t best_result = { + /* alpha = */ NAN, + /* xmin = */ NAN, + /* L = */ NAN, + /* D = */ NAN, + /* p = */ NAN + }; + + int success; + size_t i, best_n, num_uniques = 0; + double x, *px, **uniques; + + DATA_POINTS_CHECK; + + /* Sane defaults */ + best_n = n; + if (!options) + options = &plfit_continuous_default_options; + + /* Make a copy of xs and sort it */ + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &opt_data.begin)); + opt_data.end = opt_data.begin + n; + + /* Create an array containing pointers to the unique elements of the input. From + * each block of unique elements, we add the pointer to the first one. */ + uniques = unique_element_pointers(opt_data.begin, opt_data.end, &num_uniques); + if (uniques == 0) + PLFIT_ERROR("cannot fit continuous power-law", PLFIT_ENOMEM); + + /* We will now determine the best xmin that yields the lowest D-score. The + * 'success' variable will denote whether the search procedure we tried was + * successful. If it is false after having exhausted all options, we fall + * back to a linear search. */ + success = 0; + switch (options->xmin_method) { + case PLFIT_GSS_OR_LINEAR: + /* Try golden section search first. */ + if (num_uniques > 5) { + opt_data.probes = uniques; + opt_data.num_probes = num_uniques; + gss_parameter_init(&gss_param); + success = (gss(0, opt_data.num_probes-5, &x, 0, + plfit_i_continuous_xmin_opt_evaluate, + plfit_i_continuous_xmin_opt_progress, &opt_data, &gss_param) == 0); + if (success) { + px = opt_data.probes[(int)x]; + best_n = opt_data.end-px+1; + best_result = opt_data.last; + } + } + break; + + case PLFIT_STRATIFIED_SAMPLING: + if (num_uniques >= 50) { + /* Try stratified sampling to narrow down the interval where the minimum + * is likely to reside. We check 10% of the unique items, distributed + * evenly, find the one with the lowest D-score, and then check the + * area around it more thoroughly. */ + const size_t subdivision_length = 10; + size_t num_strata = num_uniques / subdivision_length; + double **strata = calloc(num_strata, sizeof(double*)); + int error_code; + + for (i = 0; i < num_strata; i++) { + strata[i] = uniques[i * subdivision_length]; + } + + opt_data.probes = strata; + opt_data.num_probes = num_strata; + error_code = plfit_i_continuous_xmin_opt_linear_scan(&opt_data, &best_result, &best_n); + if (error_code != PLFIT_SUCCESS) { + free(strata); + return error_code; + } + + opt_data.num_probes = 0; + for (i = 0; i < num_strata; i++) { + if (*strata[i] == best_result.xmin) { + /* Okay, scan more thoroughly from strata[i-1] to strata[i+1], + * which is from uniques[(i-1)*subdivision_length] to + * uniques[(i+1)*subdivision_length */ + opt_data.probes = uniques + (i > 0 ? (i-1)*subdivision_length : 0); + opt_data.num_probes = 0; + if (i != 0) + opt_data.num_probes += subdivision_length; + if (i != num_strata-1) + opt_data.num_probes += subdivision_length; + break; + } + } + + free(strata); + if (opt_data.num_probes > 0) { + /* Do a strict linear scan in the subrange determined above */ + PLFIT_CHECK( + plfit_i_continuous_xmin_opt_linear_scan( + &opt_data, &best_result, &best_n + ) + ); + success = 1; + } else { + /* This should not happen, but we handle it anyway */ + success = 0; + } + } + break; + + default: + /* Just use the linear search */ + break; + } + + if (!success) { + /* More advanced search methods failed or were skipped; try linear search */ + opt_data.probes = uniques; + opt_data.num_probes = num_uniques; + PLFIT_CHECK(plfit_i_continuous_xmin_opt_linear_scan(&opt_data, &best_result, &best_n)); + success = 1; + } + + /* Get rid of the uniques array, we don't need it any more */ + free(uniques); + + /* Sort out the result */ + *result = best_result; + if (options->finite_size_correction) + plfit_i_perform_finite_size_correction(result, best_n); + + PLFIT_CHECK(plfit_log_likelihood_continuous(opt_data.begin + n - best_n, best_n, + result->alpha, result->xmin, &result->L)); + PLFIT_CHECK(plfit_i_calculate_p_value_continuous(opt_data.begin, n, options, 0, result)); + + /* Get rid of the copied data as well */ + free(opt_data.begin); + + return PLFIT_SUCCESS; +} + +/********** Discrete power law distribution fitting **********/ + +typedef struct { + size_t m; + double logsum; + double xmin; +} plfit_i_estimate_alpha_discrete_data_t; + +static double plfit_i_logsum_discrete(double* begin, double* end, double xmin) { + double logsum = 0.0; + for (; begin != end; begin++) + logsum += log(*begin); + return logsum; +} + +static void plfit_i_logsum_less_than_discrete(double* begin, double* end, double xmin, + double* logsum, size_t* m) { + double result = 0.0; + size_t count = 0; + + for (; begin != end; begin++) { + if (*begin < xmin) + continue; + + result += log(*begin); + count++; + } + + *logsum = result; + *m = count; +} + +static lbfgsfloatval_t plfit_i_estimate_alpha_discrete_lbfgs_evaluate( + void* instance, const lbfgsfloatval_t* x, + lbfgsfloatval_t* g, const int n, + const lbfgsfloatval_t step) { + plfit_i_estimate_alpha_discrete_data_t* data; + lbfgsfloatval_t result; + double dx = step; + double huge = 1e10; /* pseudo-infinity; apparently DBL_MAX does not work */ + double lnhzeta_x=NAN; + double lnhzeta_deriv_x=NAN; + + data = (plfit_i_estimate_alpha_discrete_data_t*)instance; + +#ifdef PLFIT_DEBUG + printf("- Evaluating at %.4f (step = %.4f, xmin = %.4f)\n", *x, step, data->xmin); +#endif + + if (isnan(*x)) { + g[0] = huge; + return huge; + } + + /* Find the delta X value to estimate the gradient */ + if (dx > 0.001 || dx == 0) + dx = 0.001; + else if (dx < -0.001) + dx = -0.001; + + /* Is x[0] in its valid range? */ + if (x[0] <= 1.0) { + /* The Hurwitz zeta function is infinite in this case */ + g[0] = (dx > 0) ? -huge : huge; + return huge; + } + if (x[0] + dx <= 1.0) { + g[0] = huge; + result = x[0] * data->logsum + data->m * hsl_sf_lnhzeta(x[0], data->xmin); + } else { + hsl_sf_lnhzeta_deriv_tuple(x[0], data->xmin, &lnhzeta_x, &lnhzeta_deriv_x); + g[0] = data->logsum + data->m * lnhzeta_deriv_x; + result = x[0] * data->logsum + data->m * lnhzeta_x; + } + +#ifdef PLFIT_DEBUG + printf(" - Gradient: %.4f\n", g[0]); + printf(" - Result: %.4f\n", result); +#endif + + return result; +} + +static int plfit_i_estimate_alpha_discrete_lbfgs_progress(void* instance, + const lbfgsfloatval_t* x, const lbfgsfloatval_t* g, + const lbfgsfloatval_t fx, const lbfgsfloatval_t xnorm, + const lbfgsfloatval_t gnorm, const lbfgsfloatval_t step, + int n, int k, int ls) { + return 0; +} + +static int plfit_i_estimate_alpha_discrete_linear_scan(double* xs, size_t n, + double xmin, double* alpha, const plfit_discrete_options_t* options, + plfit_bool_t sorted) { + double curr_alpha, best_alpha, L, L_max; + double logsum; + size_t m; + + XMIN_CHECK_ONE; + if (options->alpha.min <= 1.0) { + PLFIT_ERROR("alpha.min must be greater than 1.0", PLFIT_EINVAL); + } + if (options->alpha.max < options->alpha.min) { + PLFIT_ERROR("alpha.max must be greater than alpha.min", PLFIT_EINVAL); + } + if (options->alpha.step <= 0) { + PLFIT_ERROR("alpha.step must be positive", PLFIT_EINVAL); + } + + if (sorted) { + logsum = plfit_i_logsum_discrete(xs, xs+n, xmin); + m = n; + } else { + plfit_i_logsum_less_than_discrete(xs, xs+n, xmin, &logsum, &m); + } + + best_alpha = options->alpha.min; L_max = -DBL_MAX; + for (curr_alpha = options->alpha.min; curr_alpha <= options->alpha.max; + curr_alpha += options->alpha.step) { + L = -curr_alpha * logsum - m * hsl_sf_lnhzeta(curr_alpha, xmin); + if (L > L_max) { + L_max = L; + best_alpha = curr_alpha; + } + } + + *alpha = best_alpha; + + return PLFIT_SUCCESS; +} + +static int plfit_i_estimate_alpha_discrete_lbfgs(double* xs, size_t n, double xmin, + double* alpha, const plfit_discrete_options_t* options, plfit_bool_t sorted) { + lbfgs_parameter_t param; + lbfgsfloatval_t* variables; + plfit_i_estimate_alpha_discrete_data_t data; + int ret; + + XMIN_CHECK_ONE; + + /* Initialize algorithm parameters */ + lbfgs_parameter_init(¶m); + param.max_iterations = 0; /* proceed until infinity */ + + /* Set up context for optimization */ + data.xmin = xmin; + if (sorted) { + data.logsum = plfit_i_logsum_discrete(xs, xs+n, xmin); + data.m = n; + } else { + plfit_i_logsum_less_than_discrete(xs, xs+n, xmin, &data.logsum, &data.m); + } + + /* Allocate space for the single alpha variable */ + variables = lbfgs_malloc(1); + variables[0] = 3.0; /* initial guess */ + + /* Optimization */ + ret = lbfgs(1, variables, /* ptr_fx = */ 0, + plfit_i_estimate_alpha_discrete_lbfgs_evaluate, + plfit_i_estimate_alpha_discrete_lbfgs_progress, + &data, ¶m); + + if (ret < 0 && + ret != LBFGSERR_ROUNDING_ERROR && + ret != LBFGSERR_MAXIMUMLINESEARCH && + ret != LBFGSERR_MINIMUMSTEP && + ret != LBFGSERR_CANCELED) { + char buf[4096]; + snprintf(buf, 4096, "L-BFGS optimization signaled an error (error code = %d)", ret); + lbfgs_free(variables); + PLFIT_ERROR(buf, PLFIT_FAILURE); + } + *alpha = variables[0]; + + /* Deallocate the variable array */ + lbfgs_free(variables); + + return PLFIT_SUCCESS; +} + +static int plfit_i_estimate_alpha_discrete_fast(double* xs, size_t n, double xmin, + double* alpha, const plfit_discrete_options_t* options, plfit_bool_t sorted) { + plfit_continuous_options_t cont_options; + + if (!options) + options = &plfit_discrete_default_options; + + plfit_continuous_options_init(&cont_options); + cont_options.finite_size_correction = options->finite_size_correction; + + XMIN_CHECK_ONE; + + if (sorted) { + return plfit_i_estimate_alpha_continuous_sorted(xs, n, xmin-0.5, alpha); + } else { + return plfit_i_estimate_alpha_continuous(xs, n, xmin-0.5, alpha); + } +} + +static int plfit_i_estimate_alpha_discrete(double* xs, size_t n, double xmin, + double* alpha, const plfit_discrete_options_t* options, + plfit_bool_t sorted) { + switch (options->alpha_method) { + case PLFIT_LBFGS: + PLFIT_CHECK(plfit_i_estimate_alpha_discrete_lbfgs(xs, n, xmin, alpha, + options, sorted)); + break; + + case PLFIT_LINEAR_SCAN: + PLFIT_CHECK(plfit_i_estimate_alpha_discrete_linear_scan(xs, n, xmin, + alpha, options, sorted)); + break; + + case PLFIT_PRETEND_CONTINUOUS: + PLFIT_CHECK(plfit_i_estimate_alpha_discrete_fast(xs, n, xmin, + alpha, options, sorted)); + break; + + default: + PLFIT_ERROR("unknown optimization method specified", PLFIT_EINVAL); + } + + return PLFIT_SUCCESS; +} + +static int plfit_i_ks_test_discrete(double* xs, double* xs_end, const double alpha, + const double xmin, double* D) { + /* Assumption: xs is sorted and cut off at xmin so the first element is + * always larger than or equal to xmin. */ + double result = 0, n, lnhzeta, x; + int m = 0; + + n = xs_end - xs; + lnhzeta = hsl_sf_lnhzeta(alpha, xmin); + + while (xs < xs_end) { + double d; + + x = *xs; + + /* Re the next line: this used to be the following: + * + * fabs( 1 - hzeta(alpha, x) / hzeta(alpha, xmin) - m / n) + * + * However, using the Hurwitz zeta directly sometimes yields + * underflows (see Github pull request #17 and related issues). + * hzeta(alpha, x) / hzeta(alpha, xmin) can be replaced with + * exp(lnhzeta(alpha, x) - lnhzeta(alpha, xmin)), but then + * we have 1 - exp(something), which is better to calculate + * with a dedicated expm1() function. + */ + d = fabs( expm1( hsl_sf_lnhzeta(alpha, x) - lnhzeta ) + m / n); + + if (d > result) + result = d; + + do { + xs++; m++; + } while (xs < xs_end && *xs == x); + } + + *D = result; + + return PLFIT_SUCCESS; +} + +static int plfit_i_calculate_p_value_discrete(double* xs, size_t n, + const plfit_discrete_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result) { + long int num_trials; + long int successes = 0; + double *xs_head; + size_t num_smaller; + plfit_discrete_options_t options_no_p_value = *options; + int retval = PLFIT_SUCCESS; + + if (options->p_value_method == PLFIT_P_VALUE_SKIP) { + /* skipping p-value calculation */ + result->p = NAN; + return PLFIT_SUCCESS; + } + + if (options->p_value_method == PLFIT_P_VALUE_APPROXIMATE) { + /* p-value approximation; most likely an upper bound */ + num_smaller = count_smaller(xs, xs + n, result->xmin); + result->p = plfit_ks_test_one_sample_p(result->D, n - num_smaller); + return PLFIT_SUCCESS; + } + + options_no_p_value.p_value_method = PLFIT_P_VALUE_SKIP; + num_trials = (long int)(0.25 / options->p_value_precision / options->p_value_precision); + if (num_trials <= 0) { + PLFIT_ERROR("invalid p-value precision", PLFIT_EINVAL); + } + + /* Extract the head of xs that contains elements smaller than xmin */ + xs_head = extract_smaller(xs, xs+n, result->xmin, &num_smaller); + if (xs_head == 0) + PLFIT_ERROR("cannot calculate exact p-value", PLFIT_ENOMEM); + +#ifdef _OPENMP +#pragma omp parallel +#endif + { + /* Parallel section starts here. If we are compiling using OpenMP, each + * thread will use its own RNG that is seeded from the master RNG. If + * we are compiling without OpenMP, there is only one thread and it uses + * the master RNG. This section must be critical to ensure that only one + * thread is using the master RNG at the same time. */ +#ifdef _OPENMP + plfit_mt_rng_t private_rng; +#endif + plfit_mt_rng_t *p_rng; + double *ys; + long int i; + plfit_result_t result_synthetic; + +#ifdef _OPENMP +#pragma omp critical + { + p_rng = &private_rng; + plfit_mt_init_from_rng(p_rng, options->rng); + } +#else + p_rng = options->rng; +#endif + + /* Allocate memory to sample into */ + ys = calloc(n, sizeof(double)); + if (ys == 0) { + retval = PLFIT_ENOMEM; + } else { + /* The main for loop starts here. */ +#ifdef _OPENMP +#pragma omp for reduction(+:successes) +#endif + for (i = 0; i < num_trials; i++) { + plfit_i_resample_discrete(xs_head, num_smaller, n, result->alpha, + result->xmin, n, p_rng, ys); + if (xmin_fixed) { + plfit_estimate_alpha_discrete(ys, n, result->xmin, + &options_no_p_value, &result_synthetic); + } else { + plfit_discrete(ys, n, &options_no_p_value, &result_synthetic); + } + if (result_synthetic.D > result->D) + successes++; + } + + free(ys); + } + + /* End of parallelized part */ + } + + free(xs_head); + + if (retval == PLFIT_SUCCESS) { + result->p = successes / ((double)num_trials); + } else { + PLFIT_ERROR("cannot calculate exact p-value", retval); + } + + return retval; +} + +int plfit_log_likelihood_discrete(double* xs, size_t n, double alpha, double xmin, double* L) { + double result; + size_t m; + + if (alpha <= 1) { + PLFIT_ERROR("alpha must be greater than one", PLFIT_EINVAL); + } + XMIN_CHECK_ONE; + + plfit_i_logsum_less_than_discrete(xs, xs+n, xmin, &result, &m); + result = - alpha * result - m * hsl_sf_lnhzeta(alpha, xmin); + + *L = result; + + return PLFIT_SUCCESS; +} + +int plfit_estimate_alpha_discrete(double* xs, size_t n, double xmin, + const plfit_discrete_options_t* options, plfit_result_t *result) { + double *xs_copy, *begin, *end; + + if (!options) + options = &plfit_discrete_default_options; + + /* Check the validity of the input parameters */ + DATA_POINTS_CHECK; + if (options->alpha_method == PLFIT_LINEAR_SCAN) { + if (options->alpha.min <= 1.0) { + PLFIT_ERROR("alpha.min must be greater than 1.0", PLFIT_EINVAL); + } + if (options->alpha.max < options->alpha.min) { + PLFIT_ERROR("alpha.max must be greater than alpha.min", PLFIT_EINVAL); + } + if (options->alpha.step <= 0) { + PLFIT_ERROR("alpha.step must be positive", PLFIT_EINVAL); + } + } + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + + begin = xs_copy; end = xs_copy + n; + while (begin < end && *begin < xmin) + begin++; + + PLFIT_CHECK(plfit_i_estimate_alpha_discrete(begin, end-begin, xmin, &result->alpha, + options, /* sorted = */ 1)); + PLFIT_CHECK(plfit_i_ks_test_discrete(begin, end, result->alpha, xmin, &result->D)); + + result->xmin = xmin; + if (options->finite_size_correction) + plfit_i_perform_finite_size_correction(result, end-begin); + + PLFIT_CHECK(plfit_log_likelihood_discrete(begin, end-begin, result->alpha, + result->xmin, &result->L)); + PLFIT_CHECK(plfit_i_calculate_p_value_discrete(xs, n, options, 1, result)); + + free(xs_copy); + + return PLFIT_SUCCESS; +} + +int plfit_discrete(double* xs, size_t n, const plfit_discrete_options_t* options, + plfit_result_t* result) { + double curr_D, curr_alpha; + plfit_result_t best_result; + double *xs_copy, *px, *end, *end_xmin, prev_x; + size_t best_n; + int m; + + if (!options) + options = &plfit_discrete_default_options; + + /* Check the validity of the input parameters */ + DATA_POINTS_CHECK; + if (options->alpha_method == PLFIT_LINEAR_SCAN) { + if (options->alpha.min <= 1.0) { + PLFIT_ERROR("alpha.min must be greater than 1.0", PLFIT_EINVAL); + } + if (options->alpha.max < options->alpha.min) { + PLFIT_ERROR("alpha.max must be greater than alpha.min", PLFIT_EINVAL); + } + if (options->alpha.step <= 0) { + PLFIT_ERROR("alpha.step must be positive", PLFIT_EINVAL); + } + } + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + + best_result.D = DBL_MAX; + best_result.xmin = 1; + best_result.alpha = 1; + best_n = 0; + + /* Skip initial values from xs_copy until we get to a positive element or + * until we reach the end of the array */ + px = xs_copy; end = px + n; end_xmin = end - 1; + while (px < end && *px < 1) { + px++; + } + + /* Make sure there are at least three distinct values if possible */ + m = px - xs_copy; + prev_x = *end_xmin; + while (end_xmin > px && *end_xmin == prev_x) { + end_xmin--; + } + prev_x = *end_xmin; + while (end_xmin > px && *end_xmin == prev_x) { + end_xmin--; + } + + prev_x = 0; + end_xmin++; + while (px < end_xmin) { + while (px < end_xmin && *px == prev_x) { + px++; m++; + } + + PLFIT_CHECK( + plfit_i_estimate_alpha_discrete( + px, n-m, *px, &curr_alpha, options, /* sorted = */ 1 + ) + ); + PLFIT_CHECK( + plfit_i_ks_test_discrete(px, end, curr_alpha, *px, &curr_D) + ); + + if (curr_D < best_result.D) { + best_result.alpha = curr_alpha; + best_result.xmin = *px; + best_result.D = curr_D; + best_n = n-m; + } + + prev_x = *px; + px++; m++; + } + + *result = best_result; + if (options->finite_size_correction) + plfit_i_perform_finite_size_correction(result, best_n); + + PLFIT_CHECK(plfit_log_likelihood_discrete(xs_copy+(n-best_n), best_n, + result->alpha, result->xmin, &result->L)); + PLFIT_CHECK(plfit_i_calculate_p_value_discrete(xs_copy, n, options, 0, result)); + + free(xs_copy); + + return PLFIT_SUCCESS; +} + +/***** resampling routines to generate synthetic replicates ****/ + +static int plfit_i_resample_continuous(double* xs_head, size_t num_smaller, + size_t n, double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, + double* result) +{ + size_t num_orig_samples, i; + + /* Calculate how many samples have to be drawn from xs_head */ + num_orig_samples = (size_t) plfit_rbinom(num_samples, num_smaller / (double)n, rng); + + /* Draw the samples from xs_head */ + for (i = 0; i < num_orig_samples; i++, result++) { + *result = xs_head[(size_t)plfit_runif(0, num_smaller, rng)]; + } + + /* Draw the remaining samples from the fitted distribution */ + PLFIT_CHECK(plfit_rpareto_array(xmin, alpha-1, num_samples-num_orig_samples, rng, + result)); + + return PLFIT_SUCCESS; +} + +int plfit_resample_continuous(double* xs, size_t n, double alpha, double xmin, + size_t num_samples, plfit_mt_rng_t* rng, double* result) { + double *xs_head; + size_t num_smaller = 0; + int retval; + + /* Extract the head of xs that contains elements smaller than xmin */ + xs_head = extract_smaller(xs, xs+n, xmin, &num_smaller); + if (xs_head == 0) + PLFIT_ERROR("cannot resample continuous dataset", PLFIT_ENOMEM); + + retval = plfit_i_resample_continuous(xs_head, num_smaller, n, alpha, xmin, + num_samples, rng, result); + + /* Free xs_head; we don't need it any more */ + free(xs_head); + + return retval; +} + +static int plfit_i_resample_discrete(double* xs_head, size_t num_smaller, size_t n, + double alpha, double xmin, size_t num_samples, plfit_mt_rng_t* rng, + double* result) +{ + size_t num_orig_samples, i; + + /* Calculate how many samples have to be drawn from xs_head */ + num_orig_samples = (size_t) plfit_rbinom(num_samples, num_smaller / (double)n, rng); + + /* Draw the samples from xs_head */ + for (i = 0; i < num_orig_samples; i++, result++) { + *result = xs_head[(size_t)plfit_runif(0, num_smaller, rng)]; + } + + /* Draw the remaining samples from the fitted distribution */ + PLFIT_CHECK(plfit_rzeta_array((long int)xmin, alpha, + num_samples-num_orig_samples, rng, result)); + + return PLFIT_SUCCESS; +} + +int plfit_resample_discrete(double* xs, size_t n, double alpha, double xmin, + size_t num_samples, plfit_mt_rng_t* rng, double* result) { + double *xs_head; + size_t num_smaller = 0; + int retval; + + /* Extract the head of xs that contains elements smaller than xmin */ + xs_head = extract_smaller(xs, xs+n, xmin, &num_smaller); + if (xs_head == 0) + PLFIT_ERROR("cannot resample discrete dataset", PLFIT_ENOMEM); + + retval = plfit_i_resample_discrete(xs_head, num_smaller, n, alpha, xmin, + num_samples, rng, result); + + /* Free xs_head; we don't need it any more */ + free(xs_head); + + return retval; +} + +/******** calculating the p-value of a fitted model only *******/ + +int plfit_calculate_p_value_continuous(double* xs, size_t n, + const plfit_continuous_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result) { + double* xs_copy; + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + PLFIT_CHECK(plfit_i_calculate_p_value_continuous(xs_copy, n, options, + xmin_fixed, result)); + free(xs_copy); + + return PLFIT_SUCCESS; +} + +int plfit_calculate_p_value_discrete(double* xs, size_t n, + const plfit_discrete_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result) { + double* xs_copy; + + PLFIT_CHECK(plfit_i_copy_and_sort(xs, n, &xs_copy)); + PLFIT_CHECK(plfit_i_calculate_p_value_discrete(xs_copy, n, options, + xmin_fixed, result)); + free(xs_copy); + + return PLFIT_SUCCESS; +} diff --git a/src/rigraph/vendor/plfit/plfit.h b/src/rigraph/vendor/plfit/plfit.h new file mode 100644 index 0000000..afc0348 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit.h @@ -0,0 +1,140 @@ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* plfit.h + * + * Copyright (C) 2010-2011 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __PLFIT_H__ +#define __PLFIT_H__ + +#include +#include "plfit_mt.h" +#include "plfit_version.h" + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS /* empty */ +# define __END_DECLS /* empty */ +#endif + +__BEGIN_DECLS + +typedef unsigned short int plfit_bool_t; + +typedef enum { + PLFIT_LINEAR_ONLY, + PLFIT_STRATIFIED_SAMPLING, + PLFIT_GSS_OR_LINEAR, + PLFIT_DEFAULT_CONTINUOUS_METHOD = PLFIT_STRATIFIED_SAMPLING +} plfit_continuous_method_t; + +typedef enum { + PLFIT_LBFGS, + PLFIT_LINEAR_SCAN, + PLFIT_PRETEND_CONTINUOUS, + PLFIT_DEFAULT_DISCRETE_METHOD = PLFIT_LBFGS +} plfit_discrete_method_t; + +typedef enum { + PLFIT_P_VALUE_SKIP, + PLFIT_P_VALUE_APPROXIMATE, + PLFIT_P_VALUE_EXACT, + PLFIT_DEFAULT_P_VALUE_METHOD = PLFIT_P_VALUE_EXACT +} plfit_p_value_method_t; + +typedef struct _plfit_result_t { + double alpha; /* fitted power-law exponent */ + double xmin; /* cutoff where the power-law behaviour kicks in */ + double L; /* log-likelihood of the sample */ + double D; /* test statistic for the KS test */ + double p; /* p-value of the KS test */ +} plfit_result_t; + +/********** structure that holds the options of plfit **********/ + +typedef struct _plfit_continuous_options_t { + plfit_bool_t finite_size_correction; + plfit_continuous_method_t xmin_method; + plfit_p_value_method_t p_value_method; + double p_value_precision; + plfit_mt_rng_t* rng; +} plfit_continuous_options_t; + +typedef struct _plfit_discrete_options_t { + plfit_bool_t finite_size_correction; + plfit_discrete_method_t alpha_method; + struct { + double min; + double max; + double step; + } alpha; + plfit_p_value_method_t p_value_method; + double p_value_precision; + plfit_mt_rng_t* rng; +} plfit_discrete_options_t; + +int plfit_continuous_options_init(plfit_continuous_options_t* options); +int plfit_discrete_options_init(plfit_discrete_options_t* options); + +extern const plfit_continuous_options_t plfit_continuous_default_options; +extern const plfit_discrete_options_t plfit_discrete_default_options; + +/********** continuous power law distribution fitting **********/ + +int plfit_log_likelihood_continuous(double* xs, size_t n, double alpha, + double xmin, double* l); +int plfit_estimate_alpha_continuous(double* xs, size_t n, double xmin, + const plfit_continuous_options_t* options, plfit_result_t* result); +int plfit_continuous(double* xs, size_t n, + const plfit_continuous_options_t* options, plfit_result_t* result); + +/*********** discrete power law distribution fitting ***********/ + +int plfit_estimate_alpha_discrete(double* xs, size_t n, double xmin, + const plfit_discrete_options_t* options, plfit_result_t *result); +int plfit_log_likelihood_discrete(double* xs, size_t n, double alpha, double xmin, double* l); +int plfit_discrete(double* xs, size_t n, const plfit_discrete_options_t* options, + plfit_result_t* result); + +/***** resampling routines to generate synthetic replicates ****/ + +int plfit_resample_continuous(double* xs, size_t n, double alpha, double xmin, + size_t num_samples, plfit_mt_rng_t* rng, double* result); +int plfit_resample_discrete(double* xs, size_t n, double alpha, double xmin, + size_t num_samples, plfit_mt_rng_t* rng, double* result); + +/******** calculating the p-value of a fitted model only *******/ + +int plfit_calculate_p_value_continuous(double* xs, size_t n, + const plfit_continuous_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result); +int plfit_calculate_p_value_discrete(double* xs, size_t n, + const plfit_discrete_options_t* options, plfit_bool_t xmin_fixed, + plfit_result_t *result); + +/************* calculating descriptive statistics **************/ + +int plfit_moments(double* data, size_t n, double* mean, double* variance, + double* skewness, double* kurtosis); + +__END_DECLS + +#endif /* __PLFIT_H__ */ diff --git a/src/rigraph/vendor/plfit/plfit_error.c b/src/rigraph/vendor/plfit/plfit_error.c new file mode 100644 index 0000000..d93d1c0 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_error.c @@ -0,0 +1,66 @@ +/* error.c + * + * Copyright (C) 2010-2011 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include "plfit_error.h" +#include "platform.h" + +static char *plfit_i_error_strings[] = { + "No error", + "Failed", + "Invalid value", + "Underflow", + "Overflow", + "Not enough memory" +}; + +#ifndef USING_R +static plfit_error_handler_t* plfit_error_handler = plfit_error_handler_printignore; +#else +/* This is overwritten, anyway */ +static plfit_error_handler_t* plfit_error_handler = plfit_error_handler_ignore; +#endif + +const char* plfit_strerror(const int plfit_errno) { + return plfit_i_error_strings[plfit_errno]; +} + +plfit_error_handler_t* plfit_set_error_handler(plfit_error_handler_t* new_handler) { + plfit_error_handler_t* old_handler = plfit_error_handler; + plfit_error_handler = new_handler; + return old_handler; +} + +void plfit_error(const char *reason, const char *file, int line, + int plfit_errno) { + plfit_error_handler(reason, file, line, plfit_errno); +} + +#ifndef USING_R +void plfit_error_handler_printignore(const char *reason, const char *file, int line, + int plfit_errno) { + fprintf(stderr, "Error at %s:%i : %s, %s\n", file, line, reason, + plfit_strerror(plfit_errno)); +} +#endif + +void plfit_error_handler_ignore(const char* reason, const char* file, int line, + int plfit_errno) { +} diff --git a/src/rigraph/vendor/plfit/plfit_error.h b/src/rigraph/vendor/plfit/plfit_error.h new file mode 100644 index 0000000..e542997 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_error.h @@ -0,0 +1,86 @@ +/* plfit_error.h + * + * Copyright (C) 2010-2011 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ERROR_H__ +#define __ERROR_H__ + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS /* empty */ +# define __END_DECLS /* empty */ +#endif + +__BEGIN_DECLS + +enum { + PLFIT_SUCCESS = 0, + PLFIT_FAILURE = 1, + PLFIT_EINVAL = 2, + PLFIT_UNDRFLOW = 3, + PLFIT_OVERFLOW = 4, + PLFIT_ENOMEM = 5 +}; + +#if (defined(__GNUC__) && GCC_VERSION_MAJOR >= 3) +# define PLFIT_UNLIKELY(a) __builtin_expect((a), 0) +# define PLFIT_LIKELY(a) __builtin_expect((a), 1) +#else +# define PLFIT_UNLIKELY(a) a +# define PLFIT_LIKELY(a) a +#endif + +#define PLFIT_CHECK(a) \ + do {\ + int plfit_i_ret=(a); \ + if (PLFIT_UNLIKELY(plfit_i_ret != PLFIT_SUCCESS)) {\ + return plfit_i_ret; \ + } \ + } while(0) + +#define PLFIT_ERROR(reason,plfit_errno) \ + do {\ + plfit_error (reason, __FILE__, __LINE__, plfit_errno) ; \ + return plfit_errno ; \ + } while (0) + +typedef void plfit_error_handler_t(const char*, const char*, int, int); + +extern plfit_error_handler_t plfit_error_handler_abort; +extern plfit_error_handler_t plfit_error_handler_ignore; +extern plfit_error_handler_t plfit_error_handler_printignore; + +plfit_error_handler_t* plfit_set_error_handler(plfit_error_handler_t* new_handler); + +void plfit_error(const char *reason, const char *file, int line, int plfit_errno); +const char* plfit_strerror(const int plfit_errno); + +void plfit_error_handler_abort(const char *reason, const char *file, int line, + int plfit_errno); +void plfit_error_handler_ignore(const char *reason, const char *file, int line, + int plfit_errno); +void plfit_error_handler_printignore(const char *reason, const char *file, int line, + int plfit_errno); + +__END_DECLS + +#endif /* __ERROR_H__ */ diff --git a/src/rigraph/vendor/plfit/plfit_mt.h b/src/rigraph/vendor/plfit/plfit_mt.h new file mode 100644 index 0000000..c7ed0dc --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_mt.h @@ -0,0 +1,101 @@ +/* plfit_mt.h + * + * Mersenne Twister random number generator, based on the implementation of + * Michael Brundage (which has been placed in the public domain). + * + * Author: Tamas Nepusz (original by Michael Brundage) + * + * See the following URL for the original implementation: + * http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation.html + * + * This file has been placed in the public domain. + */ + +#ifndef __PLFIT_MT_H__ +#define __PLFIT_MT_H__ + +/* VS 2010, i.e. _MSC_VER == 1600, already has stdint.h */ +#if defined(_MSC_VER) && _MSC_VER < 1600 +# define uint32_t unsigned __int32 +#else +# include +#endif + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS /* empty */ +# define __END_DECLS /* empty */ +#endif + +__BEGIN_DECLS + +#define PLFIT_MT_LEN 624 + +/** + * \def PLFIT_MT_RAND_MAX + * + * The maximum random number that \c plfit_mt_random() can generate. + */ +#define PLFIT_MT_RAND_MAX 0xFFFFFFFF + +/** + * Struct that stores the internal state of a Mersenne Twister random number + * generator. + */ +typedef struct { + int mt_index; + uint32_t mt_buffer[PLFIT_MT_LEN]; +} plfit_mt_rng_t; + +/** + * \brief Initializes a Mersenne Twister random number generator. + * + * The random number generator is seeded with random 32-bit numbers obtained + * from the \em built-in random number generator using consecutive calls to + * \c rand(). + * + * \param rng the random number generator to initialize + */ +void plfit_mt_init(plfit_mt_rng_t* rng); + +/** + * \brief Initializes a Mersenne Twister random number generator, seeding it + * from another one. + * + * The random number generator is seeded with random 32-bit numbers obtained + * from another, initialized Mersenne Twister random number generator. + * + * \param rng the random number generator to initialize + * \param seeder the random number generator that will seed the one being + * initialized. When null, the random number generator will + * be initialized from the built-in RNG as if \ref plfit_mt_init() + * was called. + */ +void plfit_mt_init_from_rng(plfit_mt_rng_t* rng, plfit_mt_rng_t* seeder); + +/** + * \brief Returns the next 32-bit random number from the given Mersenne Twister + * random number generator. + * + * \param rng the random number generator to use + * \return the next 32-bit random number from the generator + */ +uint32_t plfit_mt_random(plfit_mt_rng_t* rng); + +/** + * \brief Returns a uniformly distributed double from the interval [0;1) + * based on the next value of the given Mersenne Twister random number + * generator. + * + * \param rng the random number generator to use + * \return a uniformly distributed random number from the interval [0;1) + */ +double plfit_mt_uniform_01(plfit_mt_rng_t* rng); + +__END_DECLS + +#endif diff --git a/src/rigraph/vendor/plfit/plfit_sampling.h b/src/rigraph/vendor/plfit/plfit_sampling.h new file mode 100644 index 0000000..f63407c --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_sampling.h @@ -0,0 +1,177 @@ +/* plfit_sampling.h + * + * Copyright (C) 2012 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __SAMPLING_H__ +#define __SAMPLING_H__ + +#include +#include "plfit_mt.h" + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS /* empty */ +# define __END_DECLS /* empty */ +#endif + +__BEGIN_DECLS + +/** + * Draws a sample from a binomial distribution with the given count and + * probability values. + * + * This function is borrowed from R; see the corresponding license in + * \c rbinom.c. The return value is always an integer. + * + * The function is \em not thread-safe. + * + * \param n the number of trials + * \param p the success probability of each trial + * \param rng the Mersenne Twister random number generator to use + * \return the value drawn from the given binomial distribution. + */ +double plfit_rbinom(double n, double p, plfit_mt_rng_t* rng); + +/** + * Draws a sample from a Pareto distribution with the given minimum value and + * power-law exponent. + * + * \param xmin the minimum value of the distribution. Must be positive. + * \param alpha the exponent. Must be positive + * \param rng the Mersenne Twister random number generator to use + * + * \return the sample or NaN if one of the parameters is invalid + */ +extern double plfit_rpareto(double xmin, double alpha, plfit_mt_rng_t* rng); + +/** + * Draws a given number of samples from a Pareto distribution with the given + * minimum value and power-law exponent. + * + * \param xmin the minimum value of the distribution. Must be positive. + * \param alpha the exponent. Must be positive + * \param n the number of samples to draw + * \param rng the Mersenne Twister random number generator to use + * \param result the array where the result should be written. It must + * have enough space to store n items + * + * \return \c PLFIT_EINVAL if one of the parameters is invalid, zero otherwise + */ +int plfit_rpareto_array(double xmin, double alpha, size_t n, plfit_mt_rng_t* rng, + double* result); + +/** + * Draws a sample from a zeta distribution with the given minimum value and + * power-law exponent. + * + * \param xmin the minimum value of the distribution. Must be positive. + * \param alpha the exponent. Must be positive + * \param rng the Mersenne Twister random number generator to use + * + * \return the sample or NaN if one of the parameters is invalid + */ +extern double plfit_rzeta(long int xmin, double alpha, plfit_mt_rng_t* rng); + +/** + * Draws a given number of samples from a zeta distribution with the given + * minimum value and power-law exponent. + * + * \param xmin the minimum value of the distribution. Must be positive. + * \param alpha the exponent. Must be positive + * \param n the number of samples to draw + * \param rng the Mersenne Twister random number generator to use + * \param result the array where the result should be written. It must + * have enough space to store n items + * + * \return \c PLFIT_EINVAL if one of the parameters is invalid, zero otherwise + */ +int plfit_rzeta_array(long int xmin, double alpha, size_t n, plfit_mt_rng_t* rng, + double* result); + +/** + * Draws a sample from a uniform distribution with the given lower and + * upper bounds. + * + * The lower bound is inclusive, the uppoer bound is not. + * + * \param lo the lower bound + * \param hi the upper bound + * \param rng the Mersenne Twister random number generator to use + * \return the value drawn from the given uniform distribution. + */ +extern double plfit_runif(double lo, double hi, plfit_mt_rng_t* rng); + +/** + * Draws a sample from a uniform distribution over the [0; 1) interval. + * + * The interval is closed from the left and open from the right. + * + * \param rng the Mersenne Twister random number generator to use + * \return the value drawn from the given uniform distribution. + */ +extern double plfit_runif_01(plfit_mt_rng_t* rng); + +/** + * Random sampler using Walker's alias method. + */ +typedef struct { + long int num_bins; /**< Number of bins */ + long int* indexes; /**< Index of the "other" element in each bin */ + double* probs; /**< Probability of drawing the "own" element from a bin */ +} plfit_walker_alias_sampler_t; + +/** + * \brief Initializes the sampler with item probabilities. + * + * \param sampler the sampler to initialize + * \param ps pointer to an array containing a value proportional to the + * sampling probability of each item in the set being sampled. + * \param n the number of items in the array + * \return error code + */ +int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler, + double* ps, size_t n); + +/** + * \brief Destroys an initialized sampler and frees the allocated memory. + * + * \param sampler the sampler to destroy + */ +void plfit_walker_alias_sampler_destroy(plfit_walker_alias_sampler_t* sampler); + +/** + * \brief Draws a given number of samples from the sampler and writes them + * to a given array. + * + * \param sampler the sampler to use + * \param xs pointer to an array where the sampled items should be + * written + * \param n the number of samples to draw + * \param rng the Mersenne Twister random number generator to use + * \return error code + */ +int plfit_walker_alias_sampler_sample(const plfit_walker_alias_sampler_t* sampler, + long int* xs, size_t n, plfit_mt_rng_t* rng); + +__END_DECLS + +#endif diff --git a/src/rigraph/vendor/plfit/plfit_version.h b/src/rigraph/vendor/plfit/plfit_version.h new file mode 100644 index 0000000..1212dd1 --- /dev/null +++ b/src/rigraph/vendor/plfit/plfit_version.h @@ -0,0 +1,28 @@ +/* plfit_version.h + * + * Copyright (C) 2021 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PLFIT_VERSION_H +#define PLFIT_VERSION_H + +#define PLFIT_VERSION_MAJOR 0 +#define PLFIT_VERSION_MINOR 9 +#define PLFIT_VERSION_PATCH 3 +#define PLFIT_VERSION_STRING "0.9.3" + +#endif diff --git a/src/rigraph/vendor/plfit/rbinom.c b/src/rigraph/vendor/plfit/rbinom.c new file mode 100644 index 0000000..771f972 --- /dev/null +++ b/src/rigraph/vendor/plfit/rbinom.c @@ -0,0 +1,208 @@ +/* + * Mathlib : A C Library of Special Functions + * Copyright (C) 1998 Ross Ihaka + * Copyright (C) 2000-2002 The R Core Team + * Copyright (C) 2007 The R Foundation + * + * 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 2 of the License, or + * (at your option) 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, a copy is available at + * http://www.r-project.org/Licenses/ + * + * SYNOPSIS + * + * #include + * double rbinom(double nin, double pp) + * + * DESCRIPTION + * + * Random variates from the binomial distribution. + * + * REFERENCE + * + * Kachitvichyanukul, V. and Schmeiser, B. W. (1988). + * Binomial random variate generation. + * Communications of the ACM 31, 216-222. + * (Algorithm BTPEC). + */ + +/* + * Modifications for this file were performed by Tamas Nepusz to make it fit + * better with plfit. The license of the original file applies to the + * modifications as well. + */ + +#include +#include +#include +#include "plfit_sampling.h" +#include "platform.h" + +#define repeat for(;;) + +double plfit_rbinom(double nin, double pp, plfit_mt_rng_t* rng) +{ + /* FIXME: These should become THREAD_specific globals : */ + + static double c, fm, npq, p1, p2, p3, p4, qn; + static double xl, xll, xlr, xm, xr; + + static double psave = -1.0; + static int nsave = -1; + static int m; + + double f, f1, f2, u, v, w, w2, x, x1, x2, z, z2; + double p, q, np, g, r, al, alv, amaxp, ffm, ynorm; + int i, ix, k, n; + + if (!isfinite(nin)) return NAN; + r = floor(nin + 0.5); + if (r != nin) return NAN; + if (!isfinite(pp) || + /* n=0, p=0, p=1 are not errors */ + r < 0 || pp < 0. || pp > 1.) return NAN; + + if (r == 0 || pp == 0.) return 0; + if (pp == 1.) return r; + + n = (int) r; + + p = fmin(pp, 1. - pp); + q = 1. - p; + np = n * p; + r = p / q; + g = r * (n + 1); + + /* Setup, perform only when parameters change [using static (globals): */ + + /* FIXING: Want this thread safe + -- use as little (thread globals) as possible + */ + if (pp != psave || n != nsave) { + psave = pp; + nsave = n; + if (np < 30.0) { + /* inverse cdf logic for mean less than 30 */ + qn = pow(q, (double) n); + goto L_np_small; + } else { + ffm = np + p; + m = (int) ffm; + fm = m; + npq = np * q; + p1 = (int)(2.195 * sqrt(npq) - 4.6 * q) + 0.5; + xm = fm + 0.5; + xl = xm - p1; + xr = xm + p1; + c = 0.134 + 20.5 / (15.3 + fm); + al = (ffm - xl) / (ffm - xl * p); + xll = al * (1.0 + 0.5 * al); + al = (xr - ffm) / (xr * q); + xlr = al * (1.0 + 0.5 * al); + p2 = p1 * (1.0 + c + c); + p3 = p2 + c / xll; + p4 = p3 + c / xlr; + } + } else if (n == nsave) { + if (np < 30.0) + goto L_np_small; + } + + /*-------------------------- np = n*p >= 30 : ------------------- */ + repeat { + u = plfit_runif_01(rng) * p4; + v = plfit_runif_01(rng); + /* triangular region */ + if (u <= p1) { + ix = (int)(xm - p1 * v + u); + goto finis; + } + /* parallelogram region */ + if (u <= p2) { + x = xl + (u - p1) / c; + v = v * c + 1.0 - fabs(xm - x) / p1; + if (v > 1.0 || v <= 0.) + continue; + ix = (int) x; + } else { + if (u > p3) { /* right tail */ + ix = (int)(xr - log(v) / xlr); + if (ix > n) + continue; + v = v * (u - p3) * xlr; + } else {/* left tail */ + ix = (int)(xl + log(v) / xll); + if (ix < 0) + continue; + v = v * (u - p2) * xll; + } + } + /* determine appropriate way to perform accept/reject test */ + k = abs(ix - m); + if (k <= 20 || k >= npq / 2 - 1) { + /* explicit evaluation */ + f = 1.0; + if (m < ix) { + for (i = m + 1; i <= ix; i++) + f *= (g / i - r); + } else if (m != ix) { + for (i = ix + 1; i <= m; i++) + f /= (g / i - r); + } + if (v <= f) + goto finis; + } else { + /* squeezing using upper and lower bounds on log(f(x)) */ + amaxp = (k / npq) * ((k * (k / 3. + 0.625) + 0.1666666666666) / npq + 0.5); + ynorm = -k * k / (2.0 * npq); + alv = log(v); + if (alv < ynorm - amaxp) + goto finis; + if (alv <= ynorm + amaxp) { + /* stirling's formula to machine accuracy */ + /* for the final acceptance/rejection test */ + x1 = ix + 1; + f1 = fm + 1.0; + z = n + 1 - fm; + w = n - ix + 1.0; + z2 = z * z; + x2 = x1 * x1; + f2 = f1 * f1; + w2 = w * w; + if (alv <= xm * log(f1 / x1) + (n - m + 0.5) * log(z / w) + (ix - m) * log(w * p / (x1 * q)) + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / f2) / f2) / f2) / f2) / f1 / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / z2) / z2) / z2) / z2) / z / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / x2) / x2) / x2) / x2) / x1 / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / w2) / w2) / w2) / w2) / w / 166320.) + goto finis; + } + } + } + + L_np_small: + /*---------------------- np = n*p < 30 : ------------------------- */ + + repeat { + ix = 0; + f = qn; + u = plfit_runif_01(rng); + repeat { + if (u < f) + goto finis; + if (ix > 110) + break; + u -= f; + ix++; + f *= (g / ix - r); + } + } + finis: + if (psave > 0.5) + ix = n - ix; + return (double)ix; +} diff --git a/src/rigraph/vendor/plfit/sampling.c b/src/rigraph/vendor/plfit/sampling.c new file mode 100644 index 0000000..4becf13 --- /dev/null +++ b/src/rigraph/vendor/plfit/sampling.c @@ -0,0 +1,312 @@ +/* sampling.c + * + * Copyright (C) 2012 Tamas Nepusz + * + * 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 2 of the License, or (at + * your option) 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "igraph_random.h" + +#include "plfit_error.h" +#include "plfit_sampling.h" +#include "platform.h" + +inline double plfit_runif(double lo, double hi, plfit_mt_rng_t* rng) { + if (rng == 0) { + return RNG_UNIF(lo, hi); + } + return lo + plfit_mt_uniform_01(rng) * (hi-lo); +} + +inline double plfit_runif_01(plfit_mt_rng_t* rng) { + if (rng == 0) { + return RNG_UNIF01(); + } + return plfit_mt_uniform_01(rng); +} + +inline double plfit_rpareto(double xmin, double alpha, plfit_mt_rng_t* rng) { + if (alpha <= 0 || xmin <= 0) + return NAN; + + /* 1-u is used in the base here because we want to avoid the case of + * sampling zero */ + return pow(1-plfit_runif_01(rng), -1.0 / alpha) * xmin; +} + +int plfit_rpareto_array(double xmin, double alpha, size_t n, plfit_mt_rng_t* rng, + double* result) { + double gamma; + + if (alpha <= 0 || xmin <= 0) + return PLFIT_EINVAL; + + if (result == 0 || n == 0) + return PLFIT_SUCCESS; + + gamma = -1.0 / alpha; + while (n > 0) { + /* 1-u is used in the base here because we want to avoid the case of + * sampling zero */ + *result = pow(1-plfit_runif_01(rng), gamma) * xmin; + result++; n--; + } + + return PLFIT_SUCCESS; +} + +inline double plfit_rzeta(long int xmin, double alpha, plfit_mt_rng_t* rng) { + double u, v, t; + long int x; + double alpha_minus_1 = alpha-1; + double minus_1_over_alpha_minus_1 = -1.0 / (alpha-1); + double b; + double one_over_b_minus_1; + + if (alpha <= 0 || xmin < 1) + return NAN; + + xmin = (long int) round(xmin); + + /* Rejection sampling for the win. We use Y=floor(U^{-1/alpha} * xmin) as the + * envelope distribution, similarly to Chapter X.6 of Luc Devroye's book + * (where xmin is assumed to be 1): http://luc.devroye.org/chapter_ten.pdf + * + * Some notes that should help me recover what I was doing: + * + * p_i = 1/zeta(alpha, xmin) * i^-alpha + * q_i = (xmin/i)^{alpha-1} - (xmin/(i+1))^{alpha-1} + * = (i/xmin)^{1-alpha} - ((i+1)/xmin)^{1-alpha} + * = [i^{1-alpha} - (i+1)^{1-alpha}] / xmin^{1-alpha} + * + * p_i / q_i attains its maximum at xmin=i, so the rejection constant is: + * + * c = p_xmin / q_xmin + * + * We have to accept the sample if V <= (p_i / q_i) * (q_xmin / p_xmin) = + * (i/xmin)^-alpha * [xmin^{1-alpha} - (xmin+1)^{1-alpha}] / [i^{1-alpha} - (i+1)^{1-alpha}] = + * [xmin - xmin^alpha / (xmin+1)^{alpha-1}] / [i - i^alpha / (i+1)^{alpha-1}] = + * xmin/i * [1-(xmin/(xmin+1))^{alpha-1}]/[1-(i/(i+1))^{alpha-1}] + * + * In other words (and substituting i with X, which is the same), + * + * V * (X/xmin) <= [1 - (1+1/xmin)^{1-alpha}] / [1 - (1+1/i)^{1-alpha}] + * + * Let b := (1+1/xmin)^{alpha-1} and let T := (1+1/i)^{alpha-1}. Then: + * + * V * (X/xmin) <= [(b-1)/b] / [(T-1)/T] + * V * (X/xmin) * (T-1) / (b-1) <= T / b + * + * which is the same as in Devroye's book, except for the X/xmin term, and + * the definition of b. + */ + b = pow(1 + 1.0/xmin, alpha_minus_1); + one_over_b_minus_1 = 1.0/(b-1); + do { + do { + u = plfit_runif_01(rng); + v = plfit_runif_01(rng); + /* 1-u is used in the base here because we want to avoid the case of + * having zero in x */ + x = (long int) floor(pow(1-u, minus_1_over_alpha_minus_1) * xmin); + } while (x < xmin); + t = pow((x+1.0)/x, alpha_minus_1); + } while (v*x*(t-1)*one_over_b_minus_1*b > t*xmin); + + return x; +} + +int plfit_rzeta_array(long int xmin, double alpha, size_t n, plfit_mt_rng_t* rng, + double* result) { + double u, v, t; + long int x; + double alpha_minus_1 = alpha-1; + double minus_1_over_alpha_minus_1 = -1.0 / (alpha-1); + double b, one_over_b_minus_1; + + if (alpha <= 0 || xmin < 1) + return PLFIT_EINVAL; + + if (result == 0 || n == 0) + return PLFIT_SUCCESS; + + /* See the comments in plfit_rzeta for an explanation of the algorithm + * below. */ + xmin = (long int) round(xmin); + b = pow(1 + 1.0/xmin, alpha_minus_1); + one_over_b_minus_1 = 1.0/(b-1); + + while (n > 0) { + do { + do { + u = plfit_runif_01(rng); + v = plfit_runif_01(rng); + /* 1-u is used in the base here because we want to avoid the case of + * having zero in x */ + x = (long int) floor(pow(1-u, minus_1_over_alpha_minus_1) * xmin); + } while (x < xmin); /* handles overflow as well */ + t = pow((x+1.0)/x, alpha_minus_1); + } while (v*x*(t-1)*one_over_b_minus_1*b > t*xmin); + *result = x; + if (x < 0) return PLFIT_EINVAL; + result++; n--; + } + + return PLFIT_SUCCESS; +} + +int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler, + double* ps, size_t n) { + double *p, *p2, *ps_end; + double sum; + long int *short_sticks, *long_sticks; + long int num_short_sticks, num_long_sticks; + long int i; + + if (n > LONG_MAX) { + return PLFIT_EINVAL; + } + + sampler->num_bins = (long int) n; + + ps_end = ps + n; + + /* Initialize indexes and probs */ + sampler->indexes = (long int*)calloc(n, sizeof(long int)); + if (sampler->indexes == 0) { + return PLFIT_ENOMEM; + } + sampler->probs = (double*)calloc(n, sizeof(double)); + if (sampler->probs == 0) { + free(sampler->indexes); + return PLFIT_ENOMEM; + } + + /* Normalize the probability vector; count how many short and long sticks + * are there initially */ + for (sum = 0.0, p = ps; p != ps_end; p++) { + sum += *p; + } + sum = n / sum; + + num_short_sticks = num_long_sticks = 0; + for (p = ps, p2 = sampler->probs; p != ps_end; p++, p2++) { + *p2 = *p * sum; + if (*p2 < 1) { + num_short_sticks++; + } else if (*p2 > 1) { + num_long_sticks++; + } + } + + /* Allocate space for short & long stick indexes */ + long_sticks = (long int*)calloc(num_long_sticks, sizeof(long int)); + if (long_sticks == 0) { + free(sampler->probs); + free(sampler->indexes); + return PLFIT_ENOMEM; + } + short_sticks = (long int*)calloc(num_long_sticks, sizeof(long int)); + if (short_sticks == 0) { + free(sampler->probs); + free(sampler->indexes); + free(long_sticks); + return PLFIT_ENOMEM; + } + + /* Initialize short_sticks and long_sticks */ + num_short_sticks = num_long_sticks = 0; + for (i = 0, p = sampler->probs; i < n; i++, p++) { + if (*p < 1) { + short_sticks[num_short_sticks++] = i; + } else if (*p > 1) { + long_sticks[num_long_sticks++] = i; + } + } + + /* Prepare the index table */ + while (num_short_sticks && num_long_sticks) { + long int short_index, long_index; + short_index = short_sticks[--num_short_sticks]; + long_index = long_sticks[num_long_sticks-1]; + sampler->indexes[short_index] = long_index; + sampler->probs[long_index] = /* numerical stability */ + (sampler->probs[long_index] + sampler->probs[short_index]) - 1; + if (sampler->probs[long_index] < 1) { + short_sticks[num_short_sticks++] = long_index; + num_long_sticks--; + } + } + + /* Fix numerical stability issues */ + while (num_long_sticks) { + i = long_sticks[--num_long_sticks]; + sampler->probs[i] = 1; + } + while (num_short_sticks) { + i = short_sticks[--num_short_sticks]; + sampler->probs[i] = 1; + } + + free(short_sticks); + free(long_sticks); + + return PLFIT_SUCCESS; +} + + +void plfit_walker_alias_sampler_destroy(plfit_walker_alias_sampler_t* sampler) { + if (sampler->indexes) { + free(sampler->indexes); + sampler->indexes = 0; + } + if (sampler->probs) { + free(sampler->probs); + sampler->probs = 0; + } +} + + +int plfit_walker_alias_sampler_sample(const plfit_walker_alias_sampler_t* sampler, + long int *xs, size_t n, plfit_mt_rng_t* rng) { + double u; + long int j; + long int *x; + + x = xs; + + if (rng == 0) { + /* Using built-in RNG */ + while (n > 0) { + u = RNG_UNIF01(); + j = RNG_INTEGER(0, sampler->num_bins - 1); + *x = (u < sampler->probs[j]) ? j : sampler->indexes[j]; + n--; x++; + } + } else { + /* Using Mersenne Twister */ + while (n > 0) { + u = plfit_mt_uniform_01(rng); + j = plfit_mt_random(rng) % sampler->num_bins; + *x = (u < sampler->probs[j]) ? j : sampler->indexes[j]; + n--; x++; + } + } + + return PLFIT_SUCCESS; +} diff --git a/src/rigraph/vendor/simpleraytracer/Color.cpp b/src/rigraph/vendor/simpleraytracer/Color.cpp new file mode 100644 index 0000000..3b31cf0 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Color.cpp @@ -0,0 +1,96 @@ +#include "Color.h" +#include "unit_limiter.h" + +namespace igraph { + +Color::Color() +{ +} + +Color::Color(double vRed, double vGreen, double vBlue, + double vTransparent) +{ + Red(vRed); + Green(vGreen); + Blue(vBlue); + Transparent(vTransparent); +} + +Color::~Color() +{ +} + +// returns multiplication of a scalar with this vector +Color Color::operator* (double vRhs) const +{ + return Color(mRed*vRhs, mGreen*vRhs, mBlue*vRhs, mTransparent); +} + +// returns the addition of this color with another color +Color Color::operator+ (const Color& vRhs) const +{ + double trans=Transparent() > vRhs.Transparent() ? Transparent() : + vRhs.Transparent(); + return Color(Red()+vRhs.Red(),Green()+vRhs.Green(),Blue()+vRhs.Blue(), + trans); +} + +void Color::Red(double vRed) +{ + mRed = unit_limiter(vRed); +} +double Color::Red() const +{ + return mRed; +} +void Color::Green(double vGreen) +{ + mGreen = unit_limiter(vGreen); + +} +double Color::Green() const +{ + return mGreen; +} +void Color::Blue(double vBlue) +{ + mBlue = unit_limiter(vBlue); +} +double Color::Blue() const +{ + return mBlue; +} + +void Color::Transparent(double vTransparent) +{ + mTransparent = unit_limiter(vTransparent); +} + +double Color::Transparent() const +{ + return mTransparent; +} + +unsigned char Color::RedByte() const +{ + return ByteValue(mRed); +} +unsigned char Color::GreenByte() const +{ + return ByteValue(mGreen); +} +unsigned char Color::BlueByte() const +{ + return ByteValue(mBlue); +} +unsigned char Color::TransparentByte() const +{ + return ByteValue(mTransparent); +} + +unsigned char Color::ByteValue(double vZeroToOne) const +{ + return (unsigned char)(vZeroToOne*255.0); +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Color.h b/src/rigraph/vendor/simpleraytracer/Color.h new file mode 100644 index 0000000..8a10430 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Color.h @@ -0,0 +1,40 @@ +/** Color.h + */ + +#ifndef COLOR_H +#define COLOR_H + +namespace igraph { + +class Color +{ +public: + Color(); + Color(double vRed, double vGreen, double vBlue, + double vTransparent=1.0); + ~Color(); + + Color operator* (double vRhs) const; // returns multiplication of a scalar with a vector + Color operator+ (const Color& vRhs) const; // returns the addition of this color with another color + + void Red(double vRed); + double Red() const; + void Green(double vGreen); + double Green() const; + void Blue(double vBlue); + double Blue() const; + void Transparent(double vTransparent); + double Transparent() const; + + unsigned char RedByte() const; + unsigned char GreenByte() const; + unsigned char BlueByte() const; + unsigned char TransparentByte() const; +private: + unsigned char ByteValue(double vZeroToOne) const; + double mRed, mGreen, mBlue, mTransparent; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Light.cpp b/src/rigraph/vendor/simpleraytracer/Light.cpp new file mode 100644 index 0000000..d74cf0c --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Light.cpp @@ -0,0 +1,46 @@ +#include "Light.h" +#include "unit_limiter.h" + +namespace igraph { + +Light::Light() : mLightPoint(0,0,0) +{ + mIntensity = 0.1; +} + +Light::Light(const Point& rLightPoint) : mLightPoint(rLightPoint) +{ + mIntensity = 0.1; +} + +Light::~Light() +{} + +const Point& Light::LightPoint() const +{ + return mLightPoint; +} +void Light::LightPoint(const Point& rLightPoint) +{ + mLightPoint = rLightPoint; +} +double Light::Intensity() const +{ + return mIntensity; +} +void Light::Intensity(double vIntensity) +{ + mIntensity = unit_limiter(vIntensity); +} + +const Color& Light::LightColor() const +{ + return mLightColor; +} + +void Light::LightColor(const Color& rLightColor) +{ + mLightColor = rLightColor; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Light.h b/src/rigraph/vendor/simpleraytracer/Light.h new file mode 100644 index 0000000..369b926 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Light.h @@ -0,0 +1,39 @@ +#ifndef LIGHT_H +#define LIGHT_H + +#include "Point.h" +#include "Color.h" + +#include +using namespace std; + +namespace igraph { + +class Light +{ +public: + Light(); // creates a light at the origin + Light(const Point& rLightPoint); + ~Light(); + + const Point& LightPoint() const; + void LightPoint(const Point& rLightPoint); + + double Intensity() const; + void Intensity(double vIntensity); + + const Color& LightColor() const; + void LightColor(const Color& rLightColor); + +private: + Point mLightPoint; + double mIntensity; // 0 to 1 + Color mLightColor; +}; + +typedef list LightList; +typedef list::iterator LightListIterator; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Point.cpp b/src/rigraph/vendor/simpleraytracer/Point.cpp new file mode 100644 index 0000000..8ccbc27 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Point.cpp @@ -0,0 +1,106 @@ +#include "Point.h" +#include + +namespace igraph { + +Point::Point() +{ + X(0.0); + Y(0.0); + Z(0.0); + Name(0); +} + +Point::Point(double vX, double vY, double vZ, int vName) +{ + X(vX); + Y(vY); + Z(vZ); + Name(vName); +} + +Point::Point(double vX, double vY, double vZ) +{ + X(vX); + Y(vY); + Z(vZ); + Name(0); +} + +Point::~Point() +{} + +double Point::X() const +{ + return mX; +} + +void Point::X(double vX) +{ + mX = vX; +} + +double Point::Y() const +{ + return mY; +} + +void Point::Y(double vY) +{ + mY = vY; +} + +double Point::Z() const +{ + return mZ; +} + +void Point::Z(double vZ) +{ + mZ = vZ; +} + +int Point::Name() const +{ + return mName; +} + +void Point::Name(int vName) +{ + mName = vName; +} + +double Point::Distance(const Point& rPoint) const +{ + return sqrt( (rPoint.X() - mX)*(rPoint.X() - mX) + (rPoint.Y() - mY)*(rPoint.Y() - mY) + (rPoint.Z() - mZ)*(rPoint.Z() - mZ) ); +} + +bool Point::operator==(const Point& vRhs) const +{ + bool result = true; +/* + if ( mX + .001 <= vRhs.X() ) + result = false; + if ( mX - .001 >= vRhs.X() ) + result = false; + if ( mY + .001 <= vRhs.Y() ) + result = false; + if ( mY - .001 >= vRhs.Y() ) + result = false; + if ( mZ + .001 <= vRhs.Z() ) + result = false; + if ( mZ - .001 >= vRhs.Z() ) + result = false; +*/ + if ( mX != vRhs.X() ) + result = false; + if ( mY != vRhs.Y() ) + result = false; + if ( mZ != vRhs.Z() ) + result = false; + + + return result; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Point.h b/src/rigraph/vendor/simpleraytracer/Point.h new file mode 100644 index 0000000..647bc47 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Point.h @@ -0,0 +1,45 @@ +/** + this is a simple generic class representing a 3d point with a name. + it also defines the PointList type, which is a linked list of Points +*/ + +#ifndef POINT_H +#define POINT_H + +#include +using namespace std; + +namespace igraph { + +class Point +{ +public: + Point(); // creates a point at the origin with name 0 + Point(double vX, double vY, double vZ, int vName); + Point(double vX, double vY, double vZ); + ~Point(); + + double X() const; + void X(double vX); + double Y() const; + void Y(double vY); + double Z() const; + void Z(double vZ); + + int Name() const; + void Name(int vName); + double Distance(const Point& rPoint) const; + + bool operator==(const Point& vRhs) const; + +private: + double mX, mY, mZ; + int mName; +}; + +typedef list PointList; +typedef list::iterator PointListIterator; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp b/src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp new file mode 100644 index 0000000..8bc9ddb --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RIgraphRay.cpp @@ -0,0 +1,93 @@ +/* -*- mode: C -*- */ +/* + IGraph library R interface. + Copyright (C) 2005-2012 Gabor Csardi + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include "igraph.h" +#include "igraph_error.h" + +#include "RayTracer.h" +#include "Sphere.h" + +#include "config.h" + +#include +#include +#include + +using namespace igraph; + +extern "C" { + +SEXP R_igraph_getsphere(SEXP pos, SEXP radius, SEXP color, SEXP bgcolor, + SEXP lightpos, SEXP lightcolor, SEXP width, + SEXP height) { + + /* All error checking is done at the R level */ + + int i; + double *spos=REAL(pos); + double *scolor=REAL(color); + int no_lights=GET_LENGTH(lightpos); + RayTracer* p_ray_tracer; + Sphere * sphere; + int swidth=INTEGER(width)[0]; + int sheight=INTEGER(height)[0]; + int nopixels=swidth * sheight; + SEXP result, dim; + Image image; + + p_ray_tracer = new RayTracer(); + p_ray_tracer->EyePoint(Point(0,0,0)); + + for (i=0; iIntensity(1); + light->LightColor(Color(lcol[0], lcol[1], lcol[2])); + p_ray_tracer->AddLight(light); + } + + sphere = new Sphere(Point(spos[0], spos[1], spos[2]), REAL(radius)[0]); + sphere->ShapeColor(Color(scolor[0], scolor[1], scolor[2])); + p_ray_tracer->AddShape(sphere); + + PROTECT(result=NEW_NUMERIC(nopixels * 4)); + PROTECT(dim=NEW_INTEGER(3)); + INTEGER(dim)[0]=swidth; INTEGER(dim)[1]=sheight; INTEGER(dim)[2]=4; + SET_DIM(result, dim); + + image.width=swidth; + image.height=sheight; + image.red=REAL(result); + image.green=image.red + nopixels; + image.blue=image.green + nopixels; + image.trans=image.blue + nopixels; + + p_ray_tracer->RayTrace(image); + delete p_ray_tracer; + + UNPROTECT(2); + return result; +} + +} // extern C diff --git a/src/rigraph/vendor/simpleraytracer/Ray.cpp b/src/rigraph/vendor/simpleraytracer/Ray.cpp new file mode 100644 index 0000000..7f6744a --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Ray.cpp @@ -0,0 +1,44 @@ +#include "Ray.h" + +namespace igraph { + +Ray::Ray() +{} + +Ray::~Ray() +{} + +Ray::Ray(const Point& rOrigin, const Vector& rDirection) +{ + Direction(rDirection); + Origin(rOrigin); + +} + +Ray::Ray(const Point& rOrigin, const Point& rEndPoint) +{ + Direction(Vector(rOrigin,rEndPoint)); + Origin(rOrigin); +} + +const Point& Ray::Origin() const +{ + return mOrigin; +} + +void Ray::Origin(Point vOrigin) +{ + mOrigin = vOrigin; +} + +const Vector& Ray::Direction() const +{ + return mDirection; +} + +void Ray::Direction(Vector vDirection) +{ + mDirection = vDirection; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Ray.h b/src/rigraph/vendor/simpleraytracer/Ray.h new file mode 100644 index 0000000..cd14ca1 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Ray.h @@ -0,0 +1,33 @@ +/** Ray.h + */ + +#ifndef RAY_H +#define RAY_H + +#include "RayVector.h" +#include "Point.h" + +namespace igraph { + +class Ray +{ +public: + Ray(); + Ray(const Point& rOrigin, const Vector& rDirection); + Ray(const Point& rOrigin, const Point& rEndPoint); + ~Ray(); + + void Origin(Point vPoint); + const Point& Origin() const; + + const Vector& Direction() const; + void Direction(Vector vDirection); + +private: + Vector mDirection; + Point mOrigin; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/RayTracer.cpp b/src/rigraph/vendor/simpleraytracer/RayTracer.cpp new file mode 100644 index 0000000..27e3cc5 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RayTracer.cpp @@ -0,0 +1,266 @@ +#include "RayTracer.h" +#include "unit_limiter.h" +#include +#include + +namespace igraph { + +RayTracer::RayTracer() : mBackgroundColor(0,0,0,0), mAmbientColor(0,0,0), mEyePoint(0,0,0), mSpecularColor(1,1,1) +{ + // begin settings + mAmbientIntensity = .7; + mRecursionLimit = 700; + mAntiAliasDetail = 1; + // end settings + + mRecursions = 0; + + mpShapes = new ShapeList; + mpLights = new LightList; +} + +RayTracer::~RayTracer() +{ + ShapeListIterator iter1 = mpShapes->begin(); + while ( iter1 != mpShapes->end() ) + { + delete *iter1; + iter1++; + } + delete mpShapes; + + LightListIterator iter2 = mpLights->begin(); + while ( iter2 != mpLights->end() ) + { + delete *iter2; + iter2++; + } + + delete mpLights; + +} + +void RayTracer::RayTrace(Image &result) +{ + int mWidth=result.width; + int mHeight=result.height; + Ray eye_ray(mEyePoint,Vector(0,0,1)); + Color draw_color; + double i_inc, j_inc, anti_alias_i_inc, anti_alias_j_inc; // amount to increment the ray in each direction + double i, j, anti_alias_i, anti_alias_j; // the i and j values of the ray + int pixel_x, pixel_y, anti_alias_pixel_x, anti_alias_pixel_y; // the pixels being drawn + + double average_red_byte, average_green_byte, average_blue_byte, average_trans_byte; + int anti_alias_count; // the number of anti aliases (used in averaging) + int idx=0; + + i_inc = 2.0/(double)mWidth; + j_inc = 2.0/(double)mHeight; + anti_alias_i_inc = 1.0/(double)mAntiAliasDetail; + anti_alias_j_inc = 1.0/(double)mAntiAliasDetail; + + pixel_y = 0; + j = 1.0; + for (; pixel_y < mHeight; j -= j_inc, pixel_y++) + { + pixel_x = 0; + i = -1.0; + for (; pixel_x < mWidth; i += i_inc, pixel_x++) + { + anti_alias_pixel_y = 0; + anti_alias_j = 0.0; + average_red_byte = 0; + average_green_byte = 0; + average_blue_byte = 0; + average_trans_byte = 0; + anti_alias_count = 0; + for (; anti_alias_pixel_y < mAntiAliasDetail; anti_alias_j += anti_alias_j_inc, anti_alias_pixel_y++) + { + anti_alias_pixel_x = 0; + anti_alias_i = 0.0; + + for (; anti_alias_pixel_x < mAntiAliasDetail; anti_alias_i += anti_alias_i_inc, anti_alias_pixel_x++) + { + anti_alias_count++; + eye_ray.Direction( Vector(i+(anti_alias_i*i_inc),j+(anti_alias_j*j_inc),1.0) ); + draw_color = Render(eye_ray); + + average_red_byte = average_red_byte + ((double)draw_color.RedByte() - average_red_byte)/(double)anti_alias_count; + average_green_byte = average_green_byte + ((double)draw_color.GreenByte() - average_green_byte)/(double)anti_alias_count; + average_blue_byte = average_blue_byte + ((double)draw_color.BlueByte() - average_blue_byte)/(double)anti_alias_count; + average_trans_byte = average_trans_byte + ((double)draw_color.TransparentByte() - average_trans_byte)/(double)anti_alias_count; + } + } + + result.red [idx] = average_red_byte/255; + result.green[idx] = average_green_byte/255; + result.blue [idx] = average_blue_byte/255; + result.trans[idx] = average_trans_byte/255; + idx++; + } + } +} + +Color RayTracer::Render(const Ray& rRay, bool vIsReflecting, const Shape* pReflectingFrom ) +{ + mRecursions++; + Shape* closest_shape; + Point intersect_point; + Color result; + if (vIsReflecting) + closest_shape = QueryScene(rRay, intersect_point, vIsReflecting, pReflectingFrom); + else + closest_shape = QueryScene(rRay, intersect_point); + + if (closest_shape == NULL && !vIsReflecting) + { + mRecursions = 0; + return mBackgroundColor; + } + if (closest_shape == NULL && vIsReflecting) + { + mRecursions = 0; + return mAmbientColor*mAmbientIntensity; + } + if ( mRecursions > mRecursionLimit ) + { + mRecursions = 0; + return Color(0,0,0); // mAmbientColor*mAmbientIntensity; + } + result = closest_shape->ShapeColor()*Shade(closest_shape, intersect_point); + + Ray backwards_ray(intersect_point,rRay.Direction()*-1); + if ( closest_shape->DiffuseReflectivity() > 0.0 ) + result = result + (Render( closest_shape->Reflect(intersect_point,backwards_ray), true, closest_shape )*closest_shape->DiffuseReflectivity()); + + return (result + mSpecularColor); +} + +double RayTracer::Shade(const Shape* pShapeToShade, const Point& rPointOnShapeToShade) +{ + double intensity = mAmbientIntensity * pShapeToShade->AmbientReflectivity(); // the ambient intensity of the scene + Ray light_ray; // the ray that goes from the intersection point to the light sources + double dot_product; + Shape* closest_shape; // the shape closest from the intersection point to the light source + Point light_intersect; // the intersection point of the ray that goes from the intersection point to the light source + light_ray.Origin(rPointOnShapeToShade); // lightRay. org= object. intersect; + Ray light_ray_from_light; + + LightListIterator iter = mpLights->begin(); + mSpecularColor.Red(0); + mSpecularColor.Green(0); + mSpecularColor.Blue(0); + + while ( iter != mpLights->end() ) // foreach light in LightList do + { + + light_ray.Direction(Vector(rPointOnShapeToShade,(*iter)->LightPoint())); // lightRay. dir= light. dir + light_ray_from_light.Origin((*iter)->LightPoint()); + light_ray_from_light.Direction(Vector((*iter)->LightPoint(),rPointOnShapeToShade)); + + closest_shape = QueryScene(light_ray_from_light, light_intersect); + if ( closest_shape == NULL || (closest_shape == pShapeToShade && light_ray.Direction().Dot(pShapeToShade->Normal(rPointOnShapeToShade, light_ray_from_light.Origin() )) >= 0.0 ) ) //if (QueryScene( lightRay)= NIL) + { + Vector normal_vector = pShapeToShade->Normal(rPointOnShapeToShade, Point() ); + dot_product = normal_vector.Dot(light_ray.Direction().Normalize()); + dot_product *= (*iter)->Intensity(); + + if (dot_product < 0.0) + { + if (pShapeToShade->Type() == "Triangle") + dot_product = dot_product*-1.0; + else + dot_product = 0.0; + } + intensity = unit_limiter( intensity + dot_product ); + + if ( light_ray.Direction().Dot(pShapeToShade->Normal(rPointOnShapeToShade, light_ray_from_light.Origin() )) >= 0.0 ) + { + double specular = Specular(pShapeToShade, rPointOnShapeToShade, *iter); + mSpecularColor = mSpecularColor + Color(specular,specular,specular); + } + } + + iter++; + } + + return intensity; +} + +double RayTracer::Specular(const Shape* pShapeToShade, const Point& rPointOnShapeToShade, const Light* pLight) +{ + Ray reflected = pShapeToShade->Reflect(rPointOnShapeToShade,Ray(rPointOnShapeToShade, pLight->LightPoint())); + + Vector eye_vector(rPointOnShapeToShade, mEyePoint); + Vector reflected_vector = reflected.Direction().Normalize(); + eye_vector.NormalizeThis(); + double dot_product = eye_vector.Dot(reflected_vector); + + int n = pShapeToShade->SpecularSize(); + double specular_intensity = dot_product/(n - n*dot_product+ dot_product); + return unit_limiter(specular_intensity*pLight->Intensity()); +} + +Shape* RayTracer::QueryScene(const Ray& rRay, Point& rIntersectionPoint, bool vIsReflecting, const Shape* pReflectingFrom) +{ + Shape* closest_shape = NULL; + Point intersect_point; + double closest_distance; + double intersect_distance; + bool found_intersection = false; + + ShapeListIterator iter = mpShapes->begin(); + while ( iter != mpShapes->end() ) + { + if ( (*iter)->Intersect( rRay, intersect_point ) ) + { + intersect_distance = intersect_point.Distance(rRay.Origin()); + if ( !found_intersection && (*iter) != pReflectingFrom) + { + found_intersection = true; + rIntersectionPoint = intersect_point; + closest_shape = *iter; + closest_distance = intersect_distance; + } + else if ( intersect_distance < closest_distance && (*iter) != pReflectingFrom ) + { + rIntersectionPoint = intersect_point; + closest_shape = *iter; + closest_distance = intersect_distance; + } + } + iter++; + } + + return closest_shape; +} + +void RayTracer::AddShape(Shape* pShape) +{ + // should check if a shape with the same name already exists + mpShapes->push_back(pShape); +} +void RayTracer::AddLight(Light* pLight) +{ + // should check if a shape with the same name already exists + mpLights->push_back(pLight); +} + +void RayTracer::BackgroundColor(const Color& rBackgroundColor) +{ + mBackgroundColor = rBackgroundColor; +} +void RayTracer::EyePoint(const Point& rEyePoint) +{ + mEyePoint = rEyePoint; +} +void RayTracer::AmbientColor(const Color& rAmbientColor) +{ + mAmbientColor = rAmbientColor; +} +void RayTracer::AmbientIntensity(double vAmbientIntensity) +{ + mAmbientIntensity = unit_limiter(vAmbientIntensity); +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/RayTracer.h b/src/rigraph/vendor/simpleraytracer/RayTracer.h new file mode 100644 index 0000000..cc4830d --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RayTracer.h @@ -0,0 +1,63 @@ +/** RayTraceCanvas.h + */ + +#ifndef RAY_TRACER_H +#define RAY_TRACER_H + + +#include +#include "Point.h" +#include "Shape.h" +#include "Color.h" +#include "Light.h" + +namespace igraph { + +class Image +{ + public: + int width, height; + double *red, *green, *blue, *trans; +}; + +class RayTracer +{ + +public: + RayTracer(); + ~RayTracer(); + + void RayTrace(Image &result); + + void AddShape(Shape* pShape); + void AddLight(Light* pLight); + + void BackgroundColor(const Color& rBackgroundColor); + void EyePoint(const Point& rEyePoint); + void AmbientColor(const Color& rAmbient); + void AmbientIntensity(double vAmbientIntensity); + +private: + + Color Render(const Ray& rRay, bool vIsReflecting = false, const Shape* pReflectingFrom = 0 ); // vEyeRay should be true if the ray we are tracing is a ray from the eye, otherwise it should be false + Shape* QueryScene(const Ray& rRay, Point& rIntersectionPoint, bool vIsReflecting = false, const Shape* pReflectingFrom = 0); + double Shade(const Shape* pShapeToShade, const Point& rPointOnShapeToShade); + double Specular(const Shape* pShapeToShade, const Point& rPointOnShapeToShade, const Light* pLight); + + Color mBackgroundColor; + Color mAmbientColor; + Point mEyePoint; + Color mSpecularColor; + double mAmbientIntensity; + + ShapeList* mpShapes; + LightList* mpLights; + + int mRecursions; + int mRecursionLimit; + int mAntiAliasDetail; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/RayVector.cpp b/src/rigraph/vendor/simpleraytracer/RayVector.cpp new file mode 100644 index 0000000..76f21aa --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RayVector.cpp @@ -0,0 +1,128 @@ +#include "RayVector.h" +#include + +namespace igraph { + +Vector::Vector() +{ + mI = mJ = mK = 0.0; +} + +Vector::Vector(const Point& vStartPoint, const Point& vEndPoint) +{ + mI = vEndPoint.X() - vStartPoint.X(); + mJ = vEndPoint.Y() - vStartPoint.Y(); + mK = vEndPoint.Z() - vStartPoint.Z(); +} + +Vector::Vector(double vI, double vJ, double vK) +{ + mI = vI; + mJ = vJ; + mK = vK; +} + +Vector::~Vector() +{} + +// returns a unit vector of this vector +Vector Vector::Normalize() const +{ + double magnitude = Magnitude(); + return Vector(mI/magnitude, mJ/magnitude, mK/magnitude); +} + +void Vector::NormalizeThis() +{ + *this = Normalize(); +} + +void Vector::ReverseDirection() +{ + *this = *this * -1.0; +} + +bool Vector::IsSameDirection(const Vector& rVector) const +{ + return ( this->Normalize().Dot(rVector.Normalize()) > 0.0 ); +} + + +void Vector::I(double vI) +{ + mI = vI; +} + +double Vector::I() const +{ + return mI; +} + +void Vector::J(double vJ) +{ + mJ = vJ; +} + +double Vector::J() const +{ + return mJ; +} +void Vector::K(double vK) +{ + mK = vK; +} + +double Vector::K() const +{ + return mK; +} + +// returns the dot product of this and rVector +double Vector::Dot(const Vector& rVector) const +{ + return mI*rVector.I() + mJ*rVector.J() + mK*rVector.K(); +} + +// returns the cross product of this and vVector +Vector Vector::Cross(const Vector& rVector) const +{ + return Vector(mJ*rVector.K() - rVector.J()*mK, -1.0*(mI*rVector.K() - rVector.I()*mK), mI*rVector.J() - rVector.I()*mJ); +} + +// returns the sum of this vector with another vector +Vector Vector::operator+ (Vector vRhs) const +{ + return Vector(mI + vRhs.I(), mJ + vRhs.J(), mK + vRhs.K()); +} + +// returns the sume of a vector and a Point +Point Vector::operator+ (Point vRhs) const +{ + return Point(mI + vRhs.X(), mJ + vRhs.Y(), mK + vRhs.Z()); +} + +// returns the difference of two vectors +Vector Vector::operator- (Vector vRhs) const +{ + return Vector(mI-vRhs.I(), mJ-vRhs.J(), mK-vRhs.K()); +} + +// returns multiplication of a scalar with this vector +Vector Vector::operator* (double vRhs) const +{ + return Vector(mI*vRhs, mJ*vRhs, mK*vRhs); +} + +// converts this vector to a point +Point Vector::ToPoint() const +{ + return Point(mI,mJ,mK); +} + +// returns the magnitude +double Vector::Magnitude() const +{ + return sqrt(mI*mI + mJ*mJ + mK*mK); +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/RayVector.h b/src/rigraph/vendor/simpleraytracer/RayVector.h new file mode 100644 index 0000000..7157ba9 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/RayVector.h @@ -0,0 +1,49 @@ +/** Vector.h + */ + +#ifndef VECTOR_H +#define VECTOR_H + +#include "Point.h" + +namespace igraph { + +class Vector +{ +public: + Vector(); + Vector(const Point& vStartPoint, const Point& vEndPoint); + Vector(double vI, double vJ, double vK); + ~Vector(); + + Vector Normalize() const; // returns a unit vector of this vector + void NormalizeThis(); + void ReverseDirection(); + bool IsSameDirection(const Vector& rVector) const; + + void I(double vI); + double I() const; + void J(double vJ); + double J() const; + void K(double vK); + double K() const; + + double Dot(const Vector& rVector) const; // returns the dot product of this and rVector + Vector Cross(const Vector& rVector) const; // returns the cross product of this and rVector + Vector operator+ (Vector vRhs) const; // returns the sum of two vectors + Vector operator- (Vector vRhs) const; // returns the difference of two vectors + Point operator+ (Point vRhs) const; // returns the sum of a vector and a Point + Vector operator* (double vRhs) const; // returns multiplication of a scalar with a vector + Point ToPoint() const; // converts a vector to a point + + double Magnitude() const; + +private: + + + double mI, mJ, mK; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Shape.cpp b/src/rigraph/vendor/simpleraytracer/Shape.cpp new file mode 100644 index 0000000..3ca2b14 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Shape.cpp @@ -0,0 +1,106 @@ +#include "Shape.h" +#include "unit_limiter.h" + +namespace igraph { + +Shape::Shape() +{ + mName = 0; + mAmbientReflectivity = .6; + mSpecularReflectivity = 0; + mDiffuseReflectivity = 0; + mSpecularSize = 64; +} + +Shape::~Shape() +{} + +int Shape::Name() const +{ + return mName; +} + +void Shape::Name(int vName) +{ + mName = vName; +} + +const Color& Shape::ShapeColor() const +{ + return mShapeColor; +} + +void Shape::ShapeColor(const Color& rColor) +{ + mShapeColor = rColor; +} + +double Shape::AmbientReflectivity() const +{ + return mAmbientReflectivity; +} +double Shape::SpecularReflectivity() const +{ + return mSpecularReflectivity; +} +double Shape::DiffuseReflectivity() const +{ + return mDiffuseReflectivity; +} + +void Shape::AmbientReflectivity(double rReflectivity) +{ + mAmbientReflectivity = unit_limiter(rReflectivity); +} +void Shape::SpecularReflectivity(double rReflectivity) +{ + mSpecularReflectivity = unit_limiter(rReflectivity); +} +void Shape::DiffuseReflectivity(double rReflectivity) +{ + mDiffuseReflectivity = unit_limiter(rReflectivity); +} + +Ray Shape::Reflect(const Point& rReflectFrom, const Ray& rIncidentRay) const +{ + Ray result; // the reflected ray + Vector result_direction; // the reflected direction vector + Vector incident_unit = rIncidentRay.Direction().Normalize(); + Vector normal = this->Normal(rReflectFrom, rIncidentRay.Origin() ); + if ( !normal.IsSameDirection(incident_unit) ) + normal.ReverseDirection(); // we want the normal in the same direction of the incident ray. + + result.Origin(rReflectFrom); + result.Direction( normal*2.0*normal.Dot(incident_unit) - incident_unit ); +/* + if ( normal.Dot(rIncidentRay.Direction().Normalize()) < 0.0 ) + normal.ReverseDirection(); + + result.Origin(rReflectFrom); + result.Direction((normal*2.0) - rIncidentRay.Direction().Normalize()); +*/ + + return result; +} + +const string& Shape::Type() const +{ + return mType; +} + +void Shape::Type(const string& rType) +{ + mType = rType; +} + +int Shape::SpecularSize() const +{ + return mSpecularSize; +} + +void Shape::SpecularSize(int vSpecularSize) +{ + mSpecularSize = vSpecularSize; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Shape.h b/src/rigraph/vendor/simpleraytracer/Shape.h new file mode 100644 index 0000000..fc9c7c1 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Shape.h @@ -0,0 +1,65 @@ +/** Shape.h + */ + +#ifndef SHAPE_H +#define SHAPE_H + +#include +#include "Color.h" +#include "Ray.h" +#include "Point.h" + +#include +using namespace std; + +namespace igraph { + +class Shape +{ +public: + Shape(); + virtual ~Shape(); + + virtual bool Intersect(const Ray& rRay, Point& rIntersectPoint) const = 0; + virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const = 0; + // returns a normalized vector that is the normal of this shape from the surface point + // it also takes the rOffSurface point into account, for example: + // if rSurfacePoint is on top of a triangle, then the normal returned will be going up. + + Ray Reflect(const Point& rReflectFrom, const Ray& rRay) const; + + void Name(int vName); + int Name() const; + + const Color& ShapeColor() const; + void ShapeColor(const Color& rColor); + + double SpecularReflectivity() const; + void SpecularReflectivity(double rReflectivity); + double DiffuseReflectivity() const; + void DiffuseReflectivity(double rReflectivity); + double AmbientReflectivity() const; + void AmbientReflectivity(double rReflectivity); + + int SpecularSize() const; + void SpecularSize(int vSpecularSize); + + const string& Type() const; + void Type(const string& rType); + +private: + int mName; + string mType; + Color mShapeColor; + double mSpecularReflectivity; // from 0 to 1 + int mSpecularSize; // 1 to 64 + double mDiffuseReflectivity; // from 0 to 1 + double mAmbientReflectivity; // from 0 to 1 +}; + +typedef list ShapeList; +typedef list::iterator ShapeListIterator; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Sphere.cpp b/src/rigraph/vendor/simpleraytracer/Sphere.cpp new file mode 100644 index 0000000..c449b80 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Sphere.cpp @@ -0,0 +1,70 @@ +#include "Sphere.h" +#include + +namespace igraph { + +Sphere::Sphere() +{} + +Sphere::Sphere(Point vCenter, double vRadius) +{ + Type("Sphere"); + mCenter = vCenter; + mRadius = vRadius; +} + +Sphere::~Sphere() +{ +} + +bool Sphere::Intersect(const Ray& vRay, Point& vIntersectPoint) const +{ + Vector V; + Vector EO(vRay.Origin(), mCenter); + double v; + double disc; + double d; + Vector E(Point(0,0,0), vRay.Origin()); // E = vector from origin to ray origin + Vector P; + mCenter.Distance(vRay.Origin()); //c = distance from eye to center of sphere + V = vRay.Direction(); + V.NormalizeThis(); + v = EO.Dot(V); + double v2 = V.Dot(EO.Normalize()); + if (v2 >= 0.0) + { + disc = mRadius*mRadius - (EO.Dot(EO) - v*v); + if (disc <= 0) + return false; + else + { + d = sqrt(disc); + P = E + V*(v-d); + vIntersectPoint = P.ToPoint(); + return true; + } + } + else + return false; +} + +Vector Sphere::Normal(const Point& rSurfacePoint, const Point& rOffSurface) const +{ + // currently does not take rOffSurface point into account, + // it should check if this point is inside the sphere, if it is + // return a normal facing the center. + + Vector radius_vector (mCenter, rSurfacePoint); + return (radius_vector.Normalize()); +} + +double Sphere::Radius() const +{ + return mRadius; +} +const Point& Sphere::Center() const +{ + return mCenter; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Sphere.h b/src/rigraph/vendor/simpleraytracer/Sphere.h new file mode 100644 index 0000000..faff6da --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Sphere.h @@ -0,0 +1,31 @@ +/** Sphere.h +*/ + +#ifndef SPHERE_H +#define SPHERE_H + +#include "Shape.h" + +namespace igraph { + +class Sphere : public Shape +{ +public: + Sphere(); + Sphere(Point vCenter, double vRadius); + ~Sphere(); + + virtual bool Intersect(const Ray& vRay, Point& vIntersectPoint) const; + virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const; + + double Radius() const; + const Point& Center() const; + +private: + Point mCenter; + double mRadius; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/Triangle.cpp b/src/rigraph/vendor/simpleraytracer/Triangle.cpp new file mode 100644 index 0000000..7946c26 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Triangle.cpp @@ -0,0 +1,90 @@ +#include "Triangle.h" +#include + +namespace igraph { + +Triangle::Triangle() +{} + +Triangle::Triangle(const Point& rPoint1, const Point& rPoint2, const Point& rPoint3) +{ + Type("Triangle"); + mPoint1 = rPoint1; + mPoint2 = rPoint2; + mPoint3 = rPoint3; +} + +Triangle::~Triangle() +{ +} + +bool Triangle::Intersect(const Ray& vRay, Point& rIntersectPoint) const +{ + + Vector pointb_minus_pointa (mPoint1, mPoint2); + Vector pointb_minus_pointc (mPoint1, mPoint3); +/* + Vector plane_normal = pointb_minus_pointa.Cross(pointb_minus_pointc); + + // get the plane normal facing the right way: + Vector plane_normal_normalized = plane_normal.Normalize(); + Vector triangle_to_ray_origin = Vector(mPoint1, vRay.Origin() ); + triangle_to_ray_origin.NormalizeThis(); + if ( plane_normal_normalized.Dot(triangle_to_ray_origin) < 0.0 ) + { + plane_normal = plane_normal * -1.0; + plane_normal_normalized = plane_normal_normalized * -1.0; + } + + // check that the ray is actually facing the triangle + Vector ray_direction_normalized = vRay.Direction().Normalize(); + if ( plane_normal_normalized.Dot(ray_direction_normalized) > 0.0 ) + return false; +*/ + Vector plane_normal = this->Normal(mPoint1, vRay.Origin()); + Vector ray_direction_normalized = vRay.Direction().Normalize(); + if ( plane_normal.IsSameDirection(ray_direction_normalized) ) + return false; + + Vector b_minus_u (vRay.Origin(), mPoint2); + + double t = plane_normal.Dot(b_minus_u) / plane_normal.Dot(vRay.Direction()); + Point p = (vRay.Direction() * t) + vRay.Origin(); + + Vector p_minus_a (mPoint1, p); + Vector p_minus_b (mPoint2, p); + Vector p_minus_c (mPoint3, p); + Vector pointc_minus_pointb (mPoint2, mPoint3); + Vector pointa_minus_pointc (mPoint3, mPoint1); + + double test1 = (pointb_minus_pointa.Cross(p_minus_a)).Dot(plane_normal); + double test2 = (pointc_minus_pointb.Cross(p_minus_b)).Dot(plane_normal); + double test3 = (pointa_minus_pointc.Cross(p_minus_c)).Dot(plane_normal); + + if ((test1 > 0 && test2 > 0 && test3 > 0) || (test1 < 0 && test2 < 0 && test3 < 0)) + { + rIntersectPoint = p; + return true; + } + else + return false; +} + +Vector Triangle::Normal(const Point& rSurfacePoint, const Point& rOffSurface) const +{ + Vector pointb_minus_pointa (mPoint1, mPoint2); + Vector pointb_minus_pointc (mPoint1, mPoint3); + Vector plane_normal = pointb_minus_pointa.Cross(pointb_minus_pointc).Normalize(); + + // get the plane normal facing the right way: + Vector triangle_to_off_surface_point = Vector(mPoint1, rOffSurface ); + triangle_to_off_surface_point.NormalizeThis(); + if ( !plane_normal.IsSameDirection(triangle_to_off_surface_point) ) + { + plane_normal.ReverseDirection(); + } + + return plane_normal; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/Triangle.h b/src/rigraph/vendor/simpleraytracer/Triangle.h new file mode 100644 index 0000000..198d241 --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/Triangle.h @@ -0,0 +1,27 @@ +/** Triangle.h +*/ + +#ifndef TRIANGLE_H +#define TRIANGLE_H + +#include "Shape.h" + +namespace igraph { + +class Triangle : public Shape +{ +public: + Triangle(); + Triangle(const Point& rPoint1, const Point& rPoint2, const Point& rPoint3); + ~Triangle(); + + virtual bool Intersect(const Ray& vRay, Point& vIntersectPoint) const; + virtual Vector Normal(const Point& rSurfacePoint, const Point& rOffSurface) const; + +private: + Point mPoint1, mPoint2, mPoint3; +}; + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/simpleraytracer/unit_limiter.cpp b/src/rigraph/vendor/simpleraytracer/unit_limiter.cpp new file mode 100644 index 0000000..75a59da --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/unit_limiter.cpp @@ -0,0 +1,15 @@ +#include "unit_limiter.h" + +namespace igraph { + +double unit_limiter(double vUnitDouble) +{ + double result = vUnitDouble; + if (result < 0.0) + result = 0.0; + else if (result > 1.0) + result = 1.0; + return result; +} + +} // namespace igraph diff --git a/src/rigraph/vendor/simpleraytracer/unit_limiter.h b/src/rigraph/vendor/simpleraytracer/unit_limiter.h new file mode 100644 index 0000000..9c6f61b --- /dev/null +++ b/src/rigraph/vendor/simpleraytracer/unit_limiter.h @@ -0,0 +1,10 @@ +#ifndef ZERO_TO_ONE_H +#define ZERO_TO_ONE_H + +namespace igraph { + +double unit_limiter(double vUnitDouble); + +} // namespace igraph + +#endif diff --git a/src/rigraph/vendor/uuid/COPYING b/src/rigraph/vendor/uuid/COPYING new file mode 100644 index 0000000..e7a8054 --- /dev/null +++ b/src/rigraph/vendor/uuid/COPYING @@ -0,0 +1,28 @@ +This library is free software; you can redistribute it and/or +modify it under the terms of the Modified BSD License: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, and the entire permission notice in its entirety, + including the disclaimer of warranties. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF +WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/src/rigraph/vendor/uuid/Makevars.in b/src/rigraph/vendor/uuid/Makevars.in new file mode 100644 index 0000000..0ab302b --- /dev/null +++ b/src/rigraph/vendor/uuid/Makevars.in @@ -0,0 +1,2 @@ +PKG_CPPFLAGS=@CPPFLAGS@ +PKG_LIBS=@LIBS@ diff --git a/src/rigraph/vendor/uuid/Makevars.win b/src/rigraph/vendor/uuid/Makevars.win new file mode 100644 index 0000000..248e1ce --- /dev/null +++ b/src/rigraph/vendor/uuid/Makevars.win @@ -0,0 +1 @@ +PKG_CPPFLAGS=-Iwin32 diff --git a/src/rigraph/vendor/uuid/R.c b/src/rigraph/vendor/uuid/R.c new file mode 100644 index 0000000..b956b8d --- /dev/null +++ b/src/rigraph/vendor/uuid/R.c @@ -0,0 +1,25 @@ +#include "uuid.h" + +#include +#include "igraph_random.h" + +SEXP UUID_gen(SEXP sTime) { + + RNG_BEGIN(); + + uuid_t u; + char c[40]; + int use_time = asInteger(sTime); + if (use_time == TRUE) + uuid_generate_time(u); + else if (use_time == FALSE) + uuid_generate_random(u); + else + uuid_generate(u); + uuid_unparse_lower(u, c); + + RNG_END(); + + return mkString(c); +} + diff --git a/src/rigraph/vendor/uuid/clear.c b/src/rigraph/vendor/uuid/clear.c new file mode 100644 index 0000000..ad1f066 --- /dev/null +++ b/src/rigraph/vendor/uuid/clear.c @@ -0,0 +1,43 @@ +/* + * clear.c -- Clear a UUID + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include + +#include "uuidP.h" + +void uuid_clear(uuid_t uu) +{ + memset(uu, 0, 16); +} + diff --git a/src/rigraph/vendor/uuid/compare.c b/src/rigraph/vendor/uuid/compare.c new file mode 100644 index 0000000..8f3437a --- /dev/null +++ b/src/rigraph/vendor/uuid/compare.c @@ -0,0 +1,55 @@ +/* + * compare.c --- compare whether or not two UUIDs are the same + * + * Returns 0 if the two UUIDs are different, and 1 if they are the same. + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "uuidP.h" +#include + +#define UUCMP(u1,u2) if (u1 != u2) return((u1 < u2) ? -1 : 1); + +int uuid_compare(const uuid_t uu1, const uuid_t uu2) +{ + struct uuid uuid1, uuid2; + + uuid_unpack(uu1, &uuid1); + uuid_unpack(uu2, &uuid2); + + UUCMP(uuid1.time_low, uuid2.time_low); + UUCMP(uuid1.time_mid, uuid2.time_mid); + UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version); + UUCMP(uuid1.clock_seq, uuid2.clock_seq); + return memcmp(uuid1.node, uuid2.node, 6); +} + diff --git a/src/rigraph/vendor/uuid/config.h.in b/src/rigraph/vendor/uuid/config.h.in new file mode 100644 index 0000000..b6f72b3 --- /dev/null +++ b/src/rigraph/vendor/uuid/config.h.in @@ -0,0 +1,82 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `jrand48' function. */ +#undef HAVE_JRAND48 + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_DL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_H + +/* Define if struct sockaddr contains sa_len */ +#undef HAVE_SA_LEN + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SYSCALL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS diff --git a/src/rigraph/vendor/uuid/copy.c b/src/rigraph/vendor/uuid/copy.c new file mode 100644 index 0000000..ead33aa --- /dev/null +++ b/src/rigraph/vendor/uuid/copy.c @@ -0,0 +1,45 @@ +/* + * copy.c --- copy UUIDs + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "uuidP.h" + +void uuid_copy(uuid_t dst, const uuid_t src) +{ + unsigned char *cp1; + const unsigned char *cp2; + int i; + + for (i=0, cp1 = dst, cp2 = src; i < 16; i++) + *cp1++ = *cp2++; +} diff --git a/src/rigraph/vendor/uuid/gen_uuid.c b/src/rigraph/vendor/uuid/gen_uuid.c new file mode 100644 index 0000000..d549706 --- /dev/null +++ b/src/rigraph/vendor/uuid/gen_uuid.c @@ -0,0 +1,541 @@ +/* + * gen_uuid.c --- generate a DCE-compatible uuid + * + * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +/* + * Force inclusion of SVID stuff since we need it if we're compiling in + * gcc-wall wall mode + */ +#define _DEFAULT_SOURCE + +#include "config.h" + +#ifdef _WIN32 +#define _WIN32_WINNT 0x0500 +#include +#define UUID MYUUID +#endif +#include + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#ifdef HAVE_SYS_FILE_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_SYS_UN_H +#include +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NET_IF_DL_H +#include +#endif +#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H) +#include +#endif + +#include "uuidP.h" +#include "uuidd.h" + +#include + +#include + +#include "igraph_random.h" +#define srand(x) ; +#define rand() RNG_INTEGER(0, RAND_MAX) + + +#ifdef HAVE_TLS +#define THREAD_LOCAL static __thread +#else +#define THREAD_LOCAL static +#endif + +#ifdef _WIN32 +#if 0 /* MinGW has gettimeofday so we don't need this */ +static int gettimeofday (struct timeval *tv, void *dummy) +{ + FILETIME ftime; + uint64_t n; + + GetSystemTimeAsFileTime (&ftime); + n = (((uint64_t) ftime.dwHighDateTime << 32) + + (uint64_t) ftime.dwLowDateTime); + if (n) { + n /= 10; + n -= ((369 * 365 + 89) * (uint64_t) 86400) * 1000000; + } + + tv->tv_sec = n / 1000000; + tv->tv_usec = n % 1000000; +} +#endif + +#ifdef __MINGW32__ +int gettimeofday (struct timeval *tv, void *dummy); +#endif +#ifdef __MINGW64__ +int gettimeofday (struct timeval *tv, void *dummy); +#endif + +static int getuid (void) +{ + return 1; +} +#endif + +/* + * Get the ethernet hardware address, if we can find it... + * + * XXX for a windows version, probably should use GetAdaptersInfo: + * http://www.codeguru.com/cpp/i-n/network/networkinformation/article.php/c5451 + * commenting out get_node_id just to get gen_uuid to compile under windows + * is not the right way to go! + */ +static int get_node_id(unsigned char *node_id) +{ +#ifdef HAVE_NET_IF_H + int sd; + struct ifreq ifr, *ifrp; + struct ifconf ifc; + char buf[1024]; + int n, i; + unsigned char *a; +#ifdef HAVE_NET_IF_DL_H + struct sockaddr_dl *sdlp; +#endif + +/* + * BSD 4.4 defines the size of an ifreq to be + * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len + * However, under earlier systems, sa_len isn't present, so the size is + * just sizeof(struct ifreq) + */ +#ifdef HAVE_SA_LEN +#define max(x, y) (((x) > (y)) ? (x) : (y)) +#define ifreq_size(i) max(sizeof(struct ifreq),\ + sizeof((i).ifr_name)+(i).ifr_addr.sa_len) +#else +#define ifreq_size(i) sizeof(struct ifreq) +#endif /* HAVE_SA_LEN */ + + sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sd < 0) { + return -1; + } + memset(buf, 0, sizeof(buf)); + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) { + close(sd); + return -1; + } + n = ifc.ifc_len; + for (i = 0; i < n; i+= ifreq_size(*ifrp) ) { + ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i); + strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ); +#if defined(SIOCGIFHWADDR) && (!defined(__sun__)) + if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) + continue; + a = (unsigned char *) &ifr.ifr_hwaddr.sa_data; +#else +#ifdef SIOCGENADDR + if (ioctl(sd, SIOCGENADDR, &ifr) < 0) + continue; + a = (unsigned char *) ifr.ifr_enaddr; +#else +#ifdef HAVE_NET_IF_DL_H + sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr; + if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6)) + continue; + a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen]; +#else + /* + * XXX we don't have a way of getting the hardware + * address + */ + close(sd); + return 0; +#endif /* HAVE_NET_IF_DL_H */ +#endif /* SIOCGENADDR */ +#endif /* SIOCGIFHWADDR */ + if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) + continue; + if (node_id) { + memcpy(node_id, a, 6); + close(sd); + return 1; + } + } + close(sd); +#endif + return 0; +} + +#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48) +#define DO_JRAND_MIX +static unsigned short ul_jrand_seed[3]; +#endif + +static int random_get_fd(void) +{ + int i, fd = -1; + struct timeval tv; + + gettimeofday(&tv, NULL); +#ifndef _WIN32 + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd >= 0) { + i = fcntl(fd, F_GETFD); + if (i >= 0) + fcntl(fd, F_SETFD, i | FD_CLOEXEC); + } +#endif + srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec); + +#ifdef DO_JRAND_MIX + ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF); + ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF); + ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16; +#endif + /* Crank the random number generator a few times */ + gettimeofday(&tv, NULL); + for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) + rand(); + return fd; +} + +/* + * Generate a stream of random nbytes into buf. + * Use /dev/urandom if possible, and if not, + * use glibc pseudo-random functions. + */ +static void random_get_bytes(void *buf, size_t nbytes) +{ + size_t i, n = nbytes; + int fd = random_get_fd(); + int lose_counter = 0; + unsigned char *cp = (unsigned char *) buf; + + if (fd >= 0) { + while (n > 0) { + ssize_t x = read(fd, cp, n); + if (x <= 0) { + if (lose_counter++ > 16) + break; + continue; + } + n -= x; + cp += x; + lose_counter = 0; + } + + close(fd); + } + + /* + * We do this all the time, but this is the only source of + * randomness if /dev/random/urandom is out to lunch. + */ + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (rand() >> 7) & 0xFF; + +#ifdef DO_JRAND_MIX + { + unsigned short tmp_seed[3]; + + memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed)); + ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid); + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF; + memcpy(ul_jrand_seed, tmp_seed, + sizeof(ul_jrand_seed)-sizeof(unsigned short)); + } +#endif + + return; +} + +/* Assume that the gettimeofday() has microsecond granularity */ +#define MAX_ADJUSTMENT 10 + + +#include +/* + * Get clock from global sequence clock counter. + * + * Return -1 if the clock counter could not be opened/locked (in this case + * pseudorandom value is returned in @ret_clock_seq), otherwise return 0. + */ +static int get_clock(uint32_t *clock_high, uint32_t *clock_low, + uint16_t *ret_clock_seq, int *num) +{ + THREAD_LOCAL int adjustment = 0; + THREAD_LOCAL struct timeval last = {0, 0}; + THREAD_LOCAL int state_fd = -2; + THREAD_LOCAL FILE *state_f; + THREAD_LOCAL uint16_t clock_seq; + struct timeval tv; + uint64_t clock_reg; + mode_t save_umask; + int len; + int ret = 0; + + if (state_fd == -2) { + save_umask = umask(0); + state_fd = open(LIBUUID_CLOCK_FILE, O_RDWR|O_CREAT, 0660); + (void) umask(save_umask); + if (state_fd != -1) { + state_f = fdopen(state_fd, "r+"); + if (!state_f) { + close(state_fd); + state_fd = -1; + ret = -1; + } + } + else + ret = -1; + } + if (state_fd >= 0) { + rewind(state_f); + } + if (state_fd >= 0) { + unsigned int cl; + unsigned long tv1, tv2; + int a; + + if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n", + &cl, &tv1, &tv2, &a) == 4) { + clock_seq = cl & 0x3FFF; + last.tv_sec = tv1; + last.tv_usec = tv2; + adjustment = a; + } + } + + if ((last.tv_sec == 0) && (last.tv_usec == 0)) { + random_get_bytes(&clock_seq, sizeof(clock_seq)); + clock_seq &= 0x3FFF; + gettimeofday(&last, NULL); + last.tv_sec--; + } + +try_again: + gettimeofday(&tv, NULL); + if ((tv.tv_sec < last.tv_sec) || + ((tv.tv_sec == last.tv_sec) && + (tv.tv_usec < last.tv_usec))) { + clock_seq = (clock_seq+1) & 0x3FFF; + adjustment = 0; + last = tv; + } else if ((tv.tv_sec == last.tv_sec) && + (tv.tv_usec == last.tv_usec)) { + if (adjustment >= MAX_ADJUSTMENT) + goto try_again; + adjustment++; + } else { + adjustment = 0; + last = tv; + } + + clock_reg = tv.tv_usec*10 + adjustment; + clock_reg += ((uint64_t) tv.tv_sec)*10000000; + clock_reg += (((uint64_t) 0x01B21DD2) << 32) + 0x13814000; + + if (num && (*num > 1)) { + adjustment += *num - 1; + last.tv_usec += adjustment / 10; + adjustment = adjustment % 10; + last.tv_sec += last.tv_usec / 1000000; + last.tv_usec = last.tv_usec % 1000000; + } + + if (state_fd >= 0) { + rewind(state_f); + len = fprintf(state_f, + "clock: %04x tv: %016lu %08lu adj: %08d\n", + clock_seq, (unsigned long) last.tv_sec, (unsigned long) last.tv_usec, adjustment); + fflush(state_f); + if (ftruncate(state_fd, len) < 0) { + fprintf(state_f, " \n"); + fflush(state_f); + } + rewind(state_f); + } + + *clock_high = clock_reg >> 32; + *clock_low = clock_reg; + *ret_clock_seq = clock_seq; + return ret; +} + + +int __uuid_generate_time(uuid_t out, int *num) +{ + static unsigned char node_id[6]; + static int has_init = 0; + struct uuid uu; + uint32_t clock_mid; + int ret; + + if (!has_init) { + if (get_node_id(node_id) <= 0) { + random_get_bytes(node_id, 6); + /* + * Set multicast bit, to prevent conflicts + * with IEEE 802 addresses obtained from + * network cards + */ + node_id[0] |= 0x01; + } + has_init = 1; + } + ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num); + uu.clock_seq |= 0x8000; + uu.time_mid = (uint16_t) clock_mid; + uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000; + memcpy(uu.node, node_id, 6); + uuid_pack(&uu, out); + return ret; +} + +/* + * Generate time-based UUID and store it to @out + * + * Since there is no daemon here, use fall-back right away + */ +static int uuid_generate_time_generic(uuid_t out) { + return __uuid_generate_time(out, 0); +} + +/* + * Generate time-based UUID and store it to @out. + * + * Discards return value from uuid_generate_time_generic() + */ +void uuid_generate_time(uuid_t out) +{ + (void)uuid_generate_time_generic(out); +} + + +int uuid_generate_time_safe(uuid_t out) +{ + return uuid_generate_time_generic(out); +} + + +void __uuid_generate_random(uuid_t out, int *num) +{ + uuid_t buf; + struct uuid uu; + int i, n; + + if (!num || !*num) + n = 1; + else + n = *num; + + for (i = 0; i < n; i++) { + random_get_bytes(buf, sizeof(buf)); + uuid_unpack(buf, &uu); + + uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; + uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) + | 0x4000; + uuid_pack(&uu, out); + out += sizeof(uuid_t); + } +} + +void uuid_generate_random(uuid_t out) +{ + int num = 1; + /* No real reason to use the daemon for random uuid's -- yet */ + + __uuid_generate_random(out, &num); +} + +/* + * Check whether good random source (/dev/random or /dev/urandom) + * is available. + */ +static int have_random_source(void) +{ + struct stat s; + + return (!stat("/dev/random", &s) || !stat("/dev/urandom", &s)); +} + + +/* + * This is the generic front-end to uuid_generate_random and + * uuid_generate_time. It uses uuid_generate_random only if + * /dev/urandom is available, since otherwise we won't have + * high-quality randomness. + */ +void uuid_generate(uuid_t out) +{ + if (have_random_source()) + uuid_generate_random(out); + else + uuid_generate_time(out); +} diff --git a/src/rigraph/vendor/uuid/isnull.c b/src/rigraph/vendor/uuid/isnull.c new file mode 100644 index 0000000..931e7e7 --- /dev/null +++ b/src/rigraph/vendor/uuid/isnull.c @@ -0,0 +1,48 @@ +/* + * isnull.c --- Check whether or not the UUID is null + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "uuidP.h" + +/* Returns 1 if the uuid is the NULL uuid */ +int uuid_is_null(const uuid_t uu) +{ + const unsigned char *cp; + int i; + + for (i=0, cp = uu; i < 16; i++) + if (*cp++) + return 0; + return 1; +} + diff --git a/src/rigraph/vendor/uuid/pack.c b/src/rigraph/vendor/uuid/pack.c new file mode 100644 index 0000000..6e12476 --- /dev/null +++ b/src/rigraph/vendor/uuid/pack.c @@ -0,0 +1,69 @@ +/* + * Internal routine for packing UUIDs + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include "uuidP.h" + +void uuid_pack(const struct uuid *uu, uuid_t ptr) +{ + uint32_t tmp; + unsigned char *out = ptr; + + tmp = uu->time_low; + out[3] = (unsigned char) tmp; + tmp >>= 8; + out[2] = (unsigned char) tmp; + tmp >>= 8; + out[1] = (unsigned char) tmp; + tmp >>= 8; + out[0] = (unsigned char) tmp; + + tmp = uu->time_mid; + out[5] = (unsigned char) tmp; + tmp >>= 8; + out[4] = (unsigned char) tmp; + + tmp = uu->time_hi_and_version; + out[7] = (unsigned char) tmp; + tmp >>= 8; + out[6] = (unsigned char) tmp; + + tmp = uu->clock_seq; + out[9] = (unsigned char) tmp; + tmp >>= 8; + out[8] = (unsigned char) tmp; + + memcpy(out+10, uu->node, 6); +} + diff --git a/src/rigraph/vendor/uuid/parse.c b/src/rigraph/vendor/uuid/parse.c new file mode 100644 index 0000000..074383e --- /dev/null +++ b/src/rigraph/vendor/uuid/parse.c @@ -0,0 +1,79 @@ +/* + * parse.c --- UUID parsing + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include +#include +#include + +#include "uuidP.h" + +int uuid_parse(const char *in, uuid_t uu) +{ + struct uuid uuid; + int i; + const char *cp; + char buf[3]; + + if (strlen(in) != 36) + return -1; + for (i=0, cp = in; i <= 36; i++,cp++) { + if ((i == 8) || (i == 13) || (i == 18) || + (i == 23)) { + if (*cp == '-') + continue; + else + return -1; + } + if (i== 36) + if (*cp == 0) + continue; + if (!isxdigit(*cp)) + return -1; + } + uuid.time_low = strtoul(in, NULL, 16); + uuid.time_mid = strtoul(in+9, NULL, 16); + uuid.time_hi_and_version = strtoul(in+14, NULL, 16); + uuid.clock_seq = strtoul(in+19, NULL, 16); + cp = in+24; + buf[2] = 0; + for (i=0; i < 6; i++) { + buf[0] = *cp++; + buf[1] = *cp++; + uuid.node[i] = strtoul(buf, NULL, 16); + } + + uuid_pack(&uuid, uu); + return 0; +} diff --git a/src/rigraph/vendor/uuid/unpack.c b/src/rigraph/vendor/uuid/unpack.c new file mode 100644 index 0000000..beaaff3 --- /dev/null +++ b/src/rigraph/vendor/uuid/unpack.c @@ -0,0 +1,63 @@ +/* + * Internal routine for unpacking UUID + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include "uuidP.h" + +void uuid_unpack(const uuid_t in, struct uuid *uu) +{ + const uint8_t *ptr = in; + uint32_t tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_low = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_mid = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_hi_and_version = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->clock_seq = tmp; + + memcpy(uu->node, ptr, 6); +} + diff --git a/src/rigraph/vendor/uuid/unparse.c b/src/rigraph/vendor/uuid/unparse.c new file mode 100644 index 0000000..a95bbb0 --- /dev/null +++ b/src/rigraph/vendor/uuid/unparse.c @@ -0,0 +1,76 @@ +/* + * unparse.c -- convert a UUID to string + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include + +#include "uuidP.h" + +static const char *fmt_lower = + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; + +static const char *fmt_upper = + "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"; + +#ifdef UUID_UNPARSE_DEFAULT_UPPER +#define FMT_DEFAULT fmt_upper +#else +#define FMT_DEFAULT fmt_lower +#endif + +static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt) +{ + struct uuid uuid; + + uuid_unpack(uu, &uuid); + sprintf(out, fmt, + uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, + uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, + uuid.node[0], uuid.node[1], uuid.node[2], + uuid.node[3], uuid.node[4], uuid.node[5]); +} + +void uuid_unparse_lower(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, fmt_lower); +} + +void uuid_unparse_upper(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, fmt_upper); +} + +void uuid_unparse(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, FMT_DEFAULT); +} diff --git a/src/rigraph/vendor/uuid/uuid.h b/src/rigraph/vendor/uuid/uuid.h new file mode 100644 index 0000000..874d65a --- /dev/null +++ b/src/rigraph/vendor/uuid/uuid.h @@ -0,0 +1,104 @@ +/* + * Public include file for the UUID library + * + * Copyright (C) 1996, 1997, 1998 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifndef _UUID_UUID_H +#define _UUID_UUID_H + +#include +#ifndef _WIN32 +#include +#endif +#include + +typedef unsigned char uuid_t[16]; + +/* UUID Variant definitions */ +#define UUID_VARIANT_NCS 0 +#define UUID_VARIANT_DCE 1 +#define UUID_VARIANT_MICROSOFT 2 +#define UUID_VARIANT_OTHER 3 + +/* UUID Type definitions */ +#define UUID_TYPE_DCE_TIME 1 +#define UUID_TYPE_DCE_RANDOM 4 + +/* Allow UUID constants to be defined */ +#ifdef __GNUC__ +#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ + static const uuid_t name __attribute__ ((unused)) = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} +#else +#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ + static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* clear.c */ +void uuid_clear(uuid_t uu); + +/* compare.c */ +int uuid_compare(const uuid_t uu1, const uuid_t uu2); + +/* copy.c */ +void uuid_copy(uuid_t dst, const uuid_t src); + +/* gen_uuid.c */ +void uuid_generate(uuid_t out); +void uuid_generate_random(uuid_t out); +void uuid_generate_time(uuid_t out); +int uuid_generate_time_safe(uuid_t out); + +/* isnull.c */ +int uuid_is_null(const uuid_t uu); + +/* parse.c */ +int uuid_parse(const char *in, uuid_t uu); + +/* unparse.c */ +void uuid_unparse(const uuid_t uu, char *out); +void uuid_unparse_lower(const uuid_t uu, char *out); +void uuid_unparse_upper(const uuid_t uu, char *out); + +/* uuid_time.c */ +time_t uuid_time(const uuid_t uu, struct timeval *ret_tv); +int uuid_type(const uuid_t uu); +int uuid_variant(const uuid_t uu); + +#ifdef __cplusplus +} +#endif + +#endif /* _UUID_UUID_H */ diff --git a/src/rigraph/vendor/uuid/uuidP.h b/src/rigraph/vendor/uuid/uuidP.h new file mode 100644 index 0000000..604d8bf --- /dev/null +++ b/src/rigraph/vendor/uuid/uuidP.h @@ -0,0 +1,63 @@ +/* + * uuid.h -- private header file for uuids + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include + +#include "config.h" + +#include "uuid.h" + +#define LIBUUID_CLOCK_FILE "/var/lib/libuuid/clock.txt" + +/* + * Offset between 15-Oct-1582 and 1-Jan-70 + */ +#define TIME_OFFSET_HIGH 0x01B21DD2 +#define TIME_OFFSET_LOW 0x13814000 + +struct uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint16_t clock_seq; + uint8_t node[6]; +}; + + +/* + * prototypes + */ +void uuid_pack(const struct uuid *uu, uuid_t ptr); +void uuid_unpack(const uuid_t in, struct uuid *uu); diff --git a/src/rigraph/vendor/uuid/uuidd.h b/src/rigraph/vendor/uuid/uuidd.h new file mode 100644 index 0000000..2f70968 --- /dev/null +++ b/src/rigraph/vendor/uuid/uuidd.h @@ -0,0 +1,54 @@ +/* + * Definitions used by the uuidd daemon + * + * Copyright (C) 2007 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifndef _UUID_UUIDD_H +#define _UUID_UUIDD_H + +#define UUIDD_DIR _PATH_LOCALSTATEDIR "/uuidd" +#define UUIDD_SOCKET_PATH UUIDD_DIR "/request" +#define UUIDD_PIDFILE_PATH UUIDD_DIR "/uuidd.pid" +#define UUIDD_PATH "/usr/sbin/uuidd" + +#define UUIDD_OP_GETPID 0 +#define UUIDD_OP_GET_MAXOP 1 +#define UUIDD_OP_TIME_UUID 2 +#define UUIDD_OP_RANDOM_UUID 3 +#define UUIDD_OP_BULK_TIME_UUID 4 +#define UUIDD_OP_BULK_RANDOM_UUID 5 +#define UUIDD_MAX_OP UUIDD_OP_BULK_RANDOM_UUID + +extern int __uuid_generate_time(uuid_t out, int *num); +extern void __uuid_generate_random(uuid_t out, int *num); + +#endif /* _UUID_UUID_H */ diff --git a/src/rigraph/vendor/uuid/win32/config.h b/src/rigraph/vendor/uuid/win32/config.h new file mode 100644 index 0000000..ffb2aba --- /dev/null +++ b/src/rigraph/vendor/uuid/win32/config.h @@ -0,0 +1,84 @@ +/* src/config.h. Generated from config.h.in by configure. */ +/* src/config.h.in. Generated from configure.ac by autoheader. */ +/* -- reflects MinGW + Win32 -- */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `jrand48' function. */ +/* #undef HAVE_JRAND48 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_IN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NET_IF_DL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NET_IF_H */ + +/* Define if struct sockaddr contains sa_len */ +/* #undef HAVE_SA_LEN */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IOCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOCKET_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOCKIO_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SYSCALL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_UN_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "Simon.Urbanek@r-project.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "uuid" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "uuid 0.1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "uuid" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.1" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1